renren-api 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -0
- data/lib/renren-api.rb +13 -0
- data/lib/renren-api/authentication.rb +41 -0
- data/lib/renren-api/http_adapter.rb +48 -0
- data/lib/renren-api/signature_calculator.rb +12 -0
- data/spec/authentication_spec.rb +163 -0
- data/spec/helpers.rb +12 -0
- data/spec/http_adapter_spec.rb +98 -0
- data/spec/signature_calculator_spec.rb +22 -0
- metadata +66 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
renren-api provides capability to request the service of Renren Social Network.
|
data/lib/renren-api.rb
ADDED
@@ -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
|
data/spec/helpers.rb
ADDED
@@ -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
|