renren-api 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1 @@
1
+ renren-api provides capability to request the service of Renren Social Network.
@@ -0,0 +1,13 @@
1
+ module RenrenAPI
2
+
3
+ VERSION = [0, 3, 3]
4
+
5
+ def self.version
6
+ VERSION * "."
7
+ end
8
+
9
+ autoload :Authentication, "renren-api/authentication"
10
+ autoload :SignatureCalculator, "renren-api/signature_calculator"
11
+ autoload :HTTPAdapter, "renren-api/http_adapter"
12
+
13
+ end
@@ -0,0 +1,41 @@
1
+ require "rack"
2
+ require_relative "signature_calculator"
3
+
4
+ module RenrenAPI
5
+ class Authentication
6
+ def initialize(app, api_key, secret_key, &failed_handler)
7
+ @app = app
8
+ @api_key = api_key
9
+ @secret_key = secret_key
10
+ @signature_calculator = SignatureCalculator.new(@secret_key)
11
+ @required_keys = %w{user session_key ss expires}.collect { |e| @api_key + "_" + e } << @api_key
12
+ @failed_handler = block_given? ? failed_handler : proc { [401, {"Content-Type" => "text/plain"}, ["Unauthorized!"]] }
13
+ end
14
+ def call(env)
15
+ request = Rack::Request.new(env)
16
+ if %r{^/people/(?<person_id>\d+)} =~ request.path_info
17
+ cookies = request.cookies
18
+ if valid?(cookies) && cookies["#{@api_key}_user"] == person_id
19
+ @app.call(env)
20
+ else
21
+ @failed_handler.call(env)
22
+ end
23
+ else
24
+ @app.call(env)
25
+ end
26
+ end
27
+ private
28
+ def valid?(cookies)
29
+ @required_keys.each do |k|
30
+ return false unless cookies.has_key?(k)
31
+ end
32
+ return false if cookies["#{@api_key}_expires"].to_i < Time.now.to_i
33
+ cookies[@api_key] == @signature_calculator.calculate(filter(cookies))
34
+ end
35
+ def filter(cookies)
36
+ hash = {}
37
+ %w{user session_key ss expires}.each { |e| hash[e] = cookies["#{@api_key}_#{e}"] }
38
+ hash
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ require_relative "signature_calculator"
2
+ require "uri"
3
+ require "zlib"
4
+
5
+ module RenrenAPI
6
+ class HTTPAdapter
7
+ def initialize(http, api_key, secret_key, session_key)
8
+ @http, @api_key, @secret_key, @session_key = http, api_key, secret_key, session_key
9
+ @signature_calculator = SignatureCalculator.new(@secret_key)
10
+ end
11
+ def get_friends
12
+ params = {
13
+ :api_key => @api_key,
14
+ :method => "friends.getFriends",
15
+ :call_id => current_time_in_millisecond,
16
+ :v => "1.0",
17
+ :session_key => @session_key,
18
+ :format => "JSON"
19
+ }
20
+ request(params)
21
+ end
22
+ def get_info(uids, fields)
23
+ params = {
24
+ :api_key => @api_key,
25
+ :method => "users.getInfo",
26
+ :call_id => current_time_in_millisecond,
27
+ :v => "1.0",
28
+ :session_key => @session_key,
29
+ :fields => fields * ",",
30
+ :uids => uids * ",",
31
+ :format => "JSON"
32
+ }
33
+ request(params)
34
+ end
35
+ private
36
+ def current_time_in_millisecond
37
+ "%.3f" % Time.now.to_f
38
+ end
39
+ def request(params)
40
+ params[:sig] = @signature_calculator.calculate(params)
41
+ response = @http.post("/restserver.do", URI.encode_www_form(params), {"Accept-Encoding" => "gzip"})
42
+ gzip_reader = Zlib::GzipReader.new(StringIO.new(response.body))
43
+ result = JSON.parse(gzip_reader.read)
44
+ gzip_reader.close
45
+ result
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ require "digest/md5"
2
+
3
+ module RenrenAPI
4
+ class SignatureCalculator
5
+ def initialize(secret_key)
6
+ @secret_key = secret_key
7
+ end
8
+ def calculate(hash)
9
+ Digest::MD5.hexdigest(hash.collect { |(k, v)| "#{k}=#{v}" }.sort * "" << @secret_key)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,163 @@
1
+ require_relative "../lib/renren-api/authentication"
2
+ require "rack/test"
3
+ require "helpers"
4
+
5
+ class Rack::MockResponse
6
+ def unauthorized?
7
+ @status == 401
8
+ end
9
+ end
10
+
11
+ describe RenrenAPI::Authentication do
12
+ include Rack::Test::Methods
13
+ include Helpers
14
+ def app
15
+ RenrenAPI::Authentication.new(lambda { |env| [200, {}, ["OK"]] }, "8802f8e9b2cf4eb993e8c8adb1e02b06", "34d3d1e26cd44c05a0c450c0a0f8147b") do |env|
16
+ [401, {}, ["Get out of #{env["PATH_INFO"]}!"]]
17
+ end
18
+ end
19
+ subject { request(path, @env); last_response }
20
+ before { @env = {} }
21
+
22
+ context "when path does not have prefix /people/{person-id}" do
23
+ let(:path) { "/" }
24
+ %w{GET POST PUT DELETE}.each do |m|
25
+ context(m) do
26
+ before { @env[:method] = m }
27
+ it { should be_ok }
28
+ its(:body) { should == "OK" }
29
+ end
30
+ end
31
+ end
32
+
33
+ context "when path has prefix /people/{person-id}" do
34
+ let(:path) { "/people/#{person_id}" }
35
+ let(:person_id) { rand(9999).to_s }
36
+ context "when no login information provided" do
37
+ %w{GET POST PUT DELETE}.each do |m|
38
+ context(m) do
39
+ before { @env[:method] = m }
40
+ it { should be_unauthorized }
41
+ its(:body) { should == "Get out of #{path}!" }
42
+ end
43
+ end
44
+ end
45
+ context "when correct login information provided" do
46
+ before { @env[:cookie] = generate_cookie(secret_key, api_key, hash) }
47
+ let(:secret_key) { "34d3d1e26cd44c05a0c450c0a0f8147b" }
48
+ let(:api_key) { "8802f8e9b2cf4eb993e8c8adb1e02b06" }
49
+ let(:hash) do
50
+ {
51
+ "user" => person_id,
52
+ "session_key" => "session_key",
53
+ "ss" => "session_key_secret",
54
+ "expires" => (Time.now.to_i + rand(9999) + 1).to_s
55
+ }
56
+ end
57
+ %w{GET POST PUT DELETE}.each do |m|
58
+ context(m) do
59
+ before { @env[:method] = m }
60
+ it { should be_ok }
61
+ its(:body) { should == "OK" }
62
+ end
63
+ end
64
+ end
65
+ context "when incorrect login information provided" do
66
+ before { @env[:cookie] = generate_cookie("xxxx", api_key, hash) }
67
+ let(:api_key) { "8802f8e9b2cf4eb993e8c8adb1e02b06" }
68
+ let(:hash) do
69
+ {
70
+ "user" => person_id,
71
+ "session_key" => "session_key",
72
+ "ss" => "session_key_secret",
73
+ "expires" => (Time.now.to_i + rand(9999) + 1).to_s
74
+ }
75
+ end
76
+ %w{GET POST PUT DELETE}.each do |m|
77
+ context(m) do
78
+ before { @env[:method] = m }
79
+ it { should be_unauthorized }
80
+ its(:body) { should == "Get out of #{path}!" }
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ describe RenrenAPI::Authentication, "no failed handler is provided" do
89
+ include Rack::Test::Methods
90
+ include Helpers
91
+ def app
92
+ RenrenAPI::Authentication.new(lambda { |env| [200, {}, ["OK"]] }, "8802f8e9b2cf4eb993e8c8adb1e02b06", "34d3d1e26cd44c05a0c450c0a0f8147b")
93
+ end
94
+ subject { request(path, @env); last_response }
95
+ before { @env = {} }
96
+
97
+ context "when path does not have prefix /people/{person-id}" do
98
+ let(:path) { "/" }
99
+ %w{GET POST PUT DELETE}.each do |m|
100
+ context(m) do
101
+ before { @env[:method] = m }
102
+ it { should be_ok }
103
+ its(:body) { should == "OK" }
104
+ end
105
+ end
106
+ end
107
+
108
+ context "when path has prefix /people/{person-id}" do
109
+ let(:path) { "/people/#{person_id}" }
110
+ let(:person_id) { rand(9999).to_s }
111
+ context "when no login information provided" do
112
+ %w{GET POST PUT DELETE}.each do |m|
113
+ context(m) do
114
+ before { @env[:method] = m }
115
+ it { should be_unauthorized }
116
+ its(:content_type) { should == "text/plain"}
117
+ its(:body) { should == "Unauthorized!" }
118
+ end
119
+ end
120
+ end
121
+ context "when correct login information provided" do
122
+ before { @env[:cookie] = generate_cookie(secret_key, api_key, hash) }
123
+ let(:secret_key) { "34d3d1e26cd44c05a0c450c0a0f8147b" }
124
+ let(:api_key) { "8802f8e9b2cf4eb993e8c8adb1e02b06" }
125
+ let(:hash) do
126
+ {
127
+ "user" => person_id,
128
+ "session_key" => "session_key",
129
+ "ss" => "session_key_secret",
130
+ "expires" => (Time.now.to_i + rand(9999) + 1).to_s
131
+ }
132
+ end
133
+ %w{GET POST PUT DELETE}.each do |m|
134
+ context(m) do
135
+ before { @env[:method] = m }
136
+ it { should be_ok }
137
+ its(:body) { should == "OK" }
138
+ end
139
+ end
140
+ end
141
+ context "when incorrect login information provided" do
142
+ before { @env[:cookie] = generate_cookie("xxxx", api_key, hash) }
143
+ let(:api_key) { "8802f8e9b2cf4eb993e8c8adb1e02b06" }
144
+ let(:hash) do
145
+ {
146
+ "user" => person_id,
147
+ "session_key" => "session_key",
148
+ "ss" => "session_key_secret",
149
+ "expires" => (Time.now.to_i + rand(9999) + 1).to_s
150
+ }
151
+ end
152
+ %w{GET POST PUT DELETE}.each do |m|
153
+ context(m) do
154
+ before { @env[:method] = m }
155
+ it { should be_unauthorized }
156
+ its(:content_type) { should == "text/plain" }
157
+ its(:body) { should == "Unauthorized!" }
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ end
@@ -0,0 +1,12 @@
1
+ require "uuidtools"
2
+ require "digest/md5"
3
+
4
+ module Helpers
5
+ def generate_hash(secret_key, api_key, hash = {})
6
+ auth_code = Digest::MD5.hexdigest(hash.sort.collect { |e| e * "=" } * "" << secret_key)
7
+ Hash[hash.collect { |k, v| [api_key + "_" + k, v] }].merge!(api_key => auth_code)
8
+ end
9
+ def generate_cookie(secret_key, api_key, hash = {})
10
+ generate_hash(secret_key, api_key, hash).collect { |(k, v)| "#{k}=#{v}" }
11
+ end
12
+ end
@@ -0,0 +1,98 @@
1
+ require_relative "../lib/renren-api/http_adapter"
2
+ require_relative "../lib/renren-api/signature_calculator"
3
+ require "json"
4
+ require "net/http"
5
+ require "zlib"
6
+ require "stringio"
7
+
8
+ describe RenrenAPI::HTTPAdapter, "#get_friends" do
9
+ subject { described_class.new(http, api_key, secret_key, session_key).get_friends }
10
+ let(:http) { Net::HTTP.new("api.renren.com") }
11
+ let(:api_key) { "8802f8e9b2cf4eb993e8c8adb1e02b06" }
12
+ let(:secret_key) { "34d3d1e26cd44c05a0c450c0a0f8147b" }
13
+ let(:session_key) { "session_key" }
14
+ let(:result) do
15
+ [
16
+ {"id" => 12345, "name" => "Levin", "tinyurl" => "http://renren.com/1.jpg"},
17
+ {"id" => 12346, "name" => "James", "tinyurl" => "http://renren.com/2.jpg"}
18
+ ]
19
+ end
20
+ let!(:now) do
21
+ Time.now
22
+ end
23
+ let(:params) do
24
+ {
25
+ :api_key => api_key,
26
+ :method => "friends.getFriends",
27
+ :call_id => "%.3f" % now.to_f,
28
+ :v => "1.0",
29
+ :session_key => session_key,
30
+ :format => "JSON"
31
+ }
32
+ end
33
+ let(:form_params) do
34
+ signature_calculator = RenrenAPI::SignatureCalculator.new(secret_key)
35
+ signature = signature_calculator.calculate(params)
36
+ URI.encode_www_form(params.merge(:sig => signature))
37
+ end
38
+ before :each do
39
+ Time.stub(:now).and_return(now)
40
+ response = mock(Net::HTTPResponse)
41
+ gzip_writer = Zlib::GzipWriter.new(StringIO.new(buffer = ""))
42
+ gzip_writer << JSON.generate(result)
43
+ gzip_writer.close
44
+ response.stub(:code => '200', :message => "OK", :content_type => "application/json", :body => buffer)
45
+ http.should_receive(:post).with("/restserver.do", form_params, {"Accept-Encoding" => "gzip"}).once.and_return(response)
46
+ end
47
+ it { should == result }
48
+ end
49
+
50
+ describe RenrenAPI::HTTPAdapter, "#get_info" do
51
+ subject { described_class.new(http, api_key, secret_key, session_key).get_info(uids, fields) }
52
+ let(:http) { Net::HTTP.new("api.renren.com") }
53
+ let(:api_key) { "8802f8e9b2cf4eb993e8c8adb1e02b06" }
54
+ let(:secret_key) { "34d3d1e26cd44c05a0c450c0a0f8147b" }
55
+ let(:session_key) { "session_key" }
56
+ let(:result) do
57
+ [
58
+ {"uid" => 12345, "name" => "Levin", "tinyurl" => "http://renren.com/1.jpg"},
59
+ {"uid" => 12346, "name" => "James", "tinyurl" => "http://renren.com/2.jpg"}
60
+ ]
61
+ end
62
+ let!(:now) do
63
+ Time.now
64
+ end
65
+ let(:fields) do
66
+ %w{uid name tinyurl}
67
+ end
68
+ let(:uids) do
69
+ [12345, 12346]
70
+ end
71
+ let(:params) do
72
+ {
73
+ :api_key => api_key,
74
+ :method => "users.getInfo",
75
+ :call_id => "%.3f" % now.to_f,
76
+ :v => "1.0",
77
+ :session_key => session_key,
78
+ :fields => fields * ",",
79
+ :uids => uids * ",",
80
+ :format => "JSON"
81
+ }
82
+ end
83
+ let(:form_params) do
84
+ signature_calculator = RenrenAPI::SignatureCalculator.new(secret_key)
85
+ signature = signature_calculator.calculate(params)
86
+ URI.encode_www_form(params.merge(:sig => signature))
87
+ end
88
+ before :each do
89
+ Time.stub(:now).and_return(now)
90
+ response = mock(Net::HTTPResponse)
91
+ gzip_writer = Zlib::GzipWriter.new(StringIO.new(buffer = ""))
92
+ gzip_writer << JSON.generate(result)
93
+ gzip_writer.close
94
+ response.stub(:code => '200', :message => "OK", :content_type => "application/json", :body => buffer)
95
+ http.should_receive(:post).with("/restserver.do", form_params, {"Accept-Encoding" => "gzip"}).once.and_return(response)
96
+ end
97
+ it { should == result }
98
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "../lib/renren-api/signature_calculator"
2
+
3
+ describe RenrenAPI::SignatureCalculator do
4
+ let(:calculator) { RenrenAPI::SignatureCalculator.new(secret_key) }
5
+ describe "#calculate" do
6
+ subject { calculator.calculate(hash) }
7
+ context "when secret_key is 7fbf9791036749cb82e74efd62e9eb38" do
8
+ let(:secret_key) { "7fbf9791036749cb82e74efd62e9eb38" }
9
+ example_hash = {
10
+ "v" => "1.0",
11
+ "api_key" => "ec9e57913c5b42b282ab7b743559e1b0",
12
+ "method" => "xiaonei.users.getLoggedInUser",
13
+ "call_id" => 1232095295656,
14
+ "session_key" => "L6Xe8dXVGISZ17LJy7GzZaeYGpeGfeNdqEPLNUtCJfxPCxCRLWT83x+s/Ur94PqP-700001044"
15
+ }
16
+ context "when hash is #{example_hash.inspect}" do
17
+ let(:hash) { example_hash }
18
+ it { should == "66f332c08191b8a5dd3477d36f3af49f" }
19
+ end
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: renren-api
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.3.3
6
+ platform: ruby
7
+ authors:
8
+ - Lei, Zhi-Qiang
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-30 00:00:00 +08:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: " renren-api provides capability to request the service of Renren Social Network.\n"
18
+ email: zhiqiang.lei@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README
25
+ files:
26
+ - lib/renren-api/authentication.rb
27
+ - lib/renren-api/http_adapter.rb
28
+ - lib/renren-api/signature_calculator.rb
29
+ - lib/renren-api.rb
30
+ - spec/authentication_spec.rb
31
+ - spec/helpers.rb
32
+ - spec/http_adapter_spec.rb
33
+ - spec/signature_calculator_spec.rb
34
+ - README
35
+ has_rdoc: true
36
+ homepage: https://github.com/siegfried/renren-api
37
+ licenses:
38
+ - BSD
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.9.2
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.5.0
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: a library to request Renren's API
63
+ test_files:
64
+ - spec/authentication_spec.rb
65
+ - spec/http_adapter_spec.rb
66
+ - spec/signature_calculator_spec.rb