koala 0.10.0 → 1.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +23 -7
- data/Manifest +4 -0
- data/Rakefile +4 -4
- data/koala.gemspec +9 -7
- data/lib/koala.rb +88 -72
- data/lib/koala/graph_api.rb +16 -3
- data/lib/koala/http_services.rb +100 -16
- data/lib/koala/rest_api.rb +73 -6
- data/readme.md +11 -11
- data/spec/facebook_data.yml +9 -3
- data/spec/koala/assets/beach.jpg +0 -0
- data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +5 -1
- data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +8 -3
- data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +10 -61
- data/spec/koala/graph_api/graph_api_tests.rb +86 -0
- data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +129 -125
- data/spec/koala/live_testing_data_helper.rb +1 -1
- data/spec/koala/net_http_service_tests.rb +259 -15
- data/spec/koala/oauth/oauth_tests.rb +35 -64
- data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +5 -74
- data/spec/koala/rest_api/rest_api_tests.rb +118 -0
- data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +5 -3
- data/spec/koala/test_users/test_users_tests.rb +38 -45
- data/spec/koala/typhoeus_service_tests.rb +156 -0
- data/spec/koala_spec_helper.rb +26 -4
- data/spec/koala_spec_without_mocks.rb +3 -3
- data/spec/mock_facebook_responses.yml +22 -4
- data/spec/mock_http_service.rb +15 -1
- metadata +32 -7
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
v1.0
|
2
|
+
New methods:
|
3
|
+
-- Photo and file upload now supported through #put_picture and general support for file hashes (see documentation)
|
4
|
+
-- Added a delete_like method (thanks to wassem)
|
5
|
+
Internal improvements:
|
6
|
+
-- For public requests, Koala now uses http by default (instead of https) to improve speed
|
7
|
+
-- This can be overridden through Koala.always_use_ssl= or by passing :use_ssl => true in the options hash for an api call
|
8
|
+
-- Read-only REST API requests now go through the faster api-read server
|
9
|
+
-- Replaced parse_signed_request with a version from Facebook that also supports the new signed params proposal
|
10
|
+
-- Note: invalid requests will now raise exceptions rather than return nil, in keeping with other SDKs
|
11
|
+
-- delete_object will now raise an error if there's no access token (like put_object and delete_like)
|
12
|
+
Test improvements:
|
13
|
+
-- Expanded HTTP service tests (added Typhoeus test suite and additional Net::HTTP test cases)
|
14
|
+
-- Live tests now verify that the access token has the necessary permissions before starting
|
15
|
+
-- Replaced the 50-person network test, which often took 15+ minutes to run live, with a 5-person test
|
16
|
+
|
1
17
|
v0.10.0
|
2
18
|
-- Added test user module
|
3
19
|
-- Fixed bug when raising APIError after Facebook fails to exchange session keys
|
@@ -11,10 +27,10 @@ v0.9.1
|
|
11
27
|
v0.9.0
|
12
28
|
-- Added parse_signed_request to handle Facebook's new authentication scheme
|
13
29
|
-- note: creates dependency on OpenSSL (OpenSSL::HMAC) for decryption
|
14
|
-
-- Added GraphCollection class to provide paging support for GraphAPI get_connections and search methods (thanks to jagthedrummer)
|
30
|
+
-- Added GraphCollection class to provide paging support for GraphAPI get_connections and search methods (thanks to jagthedrummer)
|
15
31
|
-- Added get_page method to easily fetch pages of results from GraphCollections
|
16
32
|
-- Exchanging sessions for tokens now works properly when provided invalid/expired session keys
|
17
|
-
-- You can now include a :typhoeus_options key in TyphoeusService#make_request's options hash to control the Typhoeus call (for example, to set :disable_ssl_peer_verification => true)
|
33
|
+
-- You can now include a :typhoeus_options key in TyphoeusService#make_request's options hash to control the Typhoeus call (for example, to set :disable_ssl_peer_verification => true)
|
18
34
|
-- All paths provided to HTTP services start with leading / to improve compatibility with stubbing libraries
|
19
35
|
-- If Facebook returns nil for search or get_connections requests, Koala now returns nil rather than throwing an exception
|
20
36
|
|
@@ -23,7 +39,7 @@ v0.8.0
|
|
23
39
|
-- Removed string overloading for the methods, per 0.7.3, which caused Marshaling issues
|
24
40
|
-- Removed ability to provide a string as the second argument to url_for_access_token, per 0.5.0
|
25
41
|
|
26
|
-
v0.7.4
|
42
|
+
v0.7.4
|
27
43
|
-- Fixed bug with get_user_from_cookies
|
28
44
|
|
29
45
|
v0.7.3
|
@@ -34,18 +50,18 @@ v0.7.3
|
|
34
50
|
-- Using those methods triggers a deprecation warning
|
35
51
|
-- This will be removed by 1.0
|
36
52
|
-- There are new info methods (get_access_token_info, get_app_access_token_info, get_token_info_from_session_keys, and get_user_info_from_cookies) that natively return hashes, for when you want the expiration date
|
37
|
-
-- Responses with HTTP status 500+ now properly throw errors under Net::HTTP
|
53
|
+
-- Responses with HTTP status 500+ now properly throw errors under Net::HTTP
|
38
54
|
-- Updated changelog
|
39
55
|
-- Added license
|
40
56
|
|
41
57
|
v0.7.2
|
42
58
|
-- Added support for exchanging session keys for OAuth access tokens (get_token_from_session_key for single keys, get_tokens_from_session_keys for multiple)
|
43
59
|
-- Moved Koala files into a koala/ subdirectory to minimize risk of name collisions
|
44
|
-
-- Added OAuth Playground git submodule as an example
|
60
|
+
-- Added OAuth Playground git submodule as an example
|
45
61
|
-- Updated tests, readme, and changelog
|
46
62
|
|
47
63
|
v0.7.1
|
48
|
-
-- Updated RealtimeUpdates#list_subscriptions and GraphAPI#get_connections to now return an
|
64
|
+
-- Updated RealtimeUpdates#list_subscriptions and GraphAPI#get_connections to now return an
|
49
65
|
array of results directly (rather than a hash with one key)
|
50
66
|
-- Fixed a bug with Net::HTTP-based HTTP service in which the headers hash was improperly formatted
|
51
67
|
-- Updated readme
|
@@ -92,7 +108,7 @@ v0.3.1
|
|
92
108
|
v0.3
|
93
109
|
-- Renamed Graph API class from Facebook::GraphAPI to FacebookGraph::API
|
94
110
|
-- Created FacebookGraph::OAuth class for tokens and OAuth URLs
|
95
|
-
-- Updated method for including HTTP service (think we've got it this time)
|
111
|
+
-- Updated method for including HTTP service (think we've got it this time)
|
96
112
|
-- Updated tests
|
97
113
|
-- Added CHANGELOG and gemspec
|
98
114
|
|
data/Manifest
CHANGED
@@ -13,9 +13,11 @@ lib/koala/test_users.rb
|
|
13
13
|
readme.md
|
14
14
|
spec/facebook_data.yml
|
15
15
|
spec/koala/api_base_tests.rb
|
16
|
+
spec/koala/assets/beach.jpg
|
16
17
|
spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb
|
17
18
|
spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb
|
18
19
|
spec/koala/graph_api/graph_api_no_access_token_tests.rb
|
20
|
+
spec/koala/graph_api/graph_api_tests.rb
|
19
21
|
spec/koala/graph_api/graph_api_with_access_token_tests.rb
|
20
22
|
spec/koala/graph_api/graph_collection_tests.rb
|
21
23
|
spec/koala/live_testing_data_helper.rb
|
@@ -23,8 +25,10 @@ spec/koala/net_http_service_tests.rb
|
|
23
25
|
spec/koala/oauth/oauth_tests.rb
|
24
26
|
spec/koala/realtime_updates/realtime_updates_tests.rb
|
25
27
|
spec/koala/rest_api/rest_api_no_access_token_tests.rb
|
28
|
+
spec/koala/rest_api/rest_api_tests.rb
|
26
29
|
spec/koala/rest_api/rest_api_with_access_token_tests.rb
|
27
30
|
spec/koala/test_users/test_users_tests.rb
|
31
|
+
spec/koala/typhoeus_service_tests.rb
|
28
32
|
spec/koala_spec.rb
|
29
33
|
spec/koala_spec_helper.rb
|
30
34
|
spec/koala_spec_without_mocks.rb
|
data/Rakefile
CHANGED
@@ -4,14 +4,14 @@ require 'rake'
|
|
4
4
|
require 'echoe'
|
5
5
|
|
6
6
|
# gem management
|
7
|
-
Echoe.new('koala', '0.
|
8
|
-
p.summary
|
9
|
-
p.description
|
7
|
+
Echoe.new('koala', '1.0.0.beta') do |p|
|
8
|
+
p.summary = "A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation."
|
9
|
+
p.description = "Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services."
|
10
10
|
p.url = "http://github.com/arsduo/koala"
|
11
11
|
p.author = ["Alex Koppel", "Chris Baclig", "Rafi Jacoby", "Context Optional"]
|
12
12
|
p.email = "alex@alexkoppel.com"
|
13
13
|
p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
|
14
|
-
p.runtime_dependencies = ["json >=1.0"]
|
14
|
+
p.runtime_dependencies = ["json >=1.0", "multipart-post >=1.0"]
|
15
15
|
p.development_dependencies = []
|
16
16
|
p.retain_gemspec = true
|
17
17
|
end
|
data/koala.gemspec
CHANGED
@@ -2,32 +2,34 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{koala}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "1.0.0.beta"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional"]
|
9
|
-
s.date = %q{
|
9
|
+
s.date = %q{2011-01-26}
|
10
10
|
s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
|
11
11
|
s.email = %q{alex@alexkoppel.com}
|
12
12
|
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb"]
|
13
|
-
s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/graph_api/graph_collection_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala/test_users/test_users_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb"]
|
13
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/assets/beach.jpg", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/graph_api/graph_collection_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala/test_users/test_users_tests.rb", "spec/koala/typhoeus_service_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb"]
|
14
14
|
s.homepage = %q{http://github.com/arsduo/koala}
|
15
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala"
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala"]
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
s.rubyforge_project = %q{koala}
|
18
|
-
s.rubygems_version = %q{1.
|
18
|
+
s.rubygems_version = %q{1.4.2}
|
19
19
|
s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation.}
|
20
20
|
|
21
21
|
if s.respond_to? :specification_version then
|
22
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
22
|
s.specification_version = 3
|
24
23
|
|
25
|
-
if Gem::Version.new(Gem::
|
24
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
25
|
s.add_runtime_dependency(%q<json>, [">= 1.0"])
|
26
|
+
s.add_runtime_dependency(%q<multipart-post>, [">= 1.0"])
|
27
27
|
else
|
28
28
|
s.add_dependency(%q<json>, [">= 1.0"])
|
29
|
+
s.add_dependency(%q<multipart-post>, [">= 1.0"])
|
29
30
|
end
|
30
31
|
else
|
31
32
|
s.add_dependency(%q<json>, [">= 1.0"])
|
33
|
+
s.add_dependency(%q<multipart-post>, [">= 1.0"])
|
32
34
|
end
|
33
35
|
end
|
data/lib/koala.rb
CHANGED
@@ -5,8 +5,9 @@ require 'digest/md5'
|
|
5
5
|
require 'rubygems'
|
6
6
|
require 'json'
|
7
7
|
|
8
|
-
#
|
8
|
+
# OpenSSL and Base64 are required to support signed_request
|
9
9
|
require 'openssl'
|
10
|
+
require 'base64'
|
10
11
|
|
11
12
|
# include default http services
|
12
13
|
require 'koala/http_services'
|
@@ -24,7 +25,7 @@ require 'koala/realtime_updates'
|
|
24
25
|
require 'koala/test_users'
|
25
26
|
|
26
27
|
module Koala
|
27
|
-
|
28
|
+
|
28
29
|
module Facebook
|
29
30
|
# Ruby client library for the Facebook Platform.
|
30
31
|
# Copyright 2010 Facebook
|
@@ -40,7 +41,7 @@ module Koala
|
|
40
41
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
41
42
|
# License for the specific language governing permissions and limitations
|
42
43
|
# under the License.
|
43
|
-
#
|
44
|
+
#
|
44
45
|
# This client library is designed to support the Graph API and the official
|
45
46
|
# Facebook JavaScript SDK, which is the canonical way to implement
|
46
47
|
# Facebook authentication. Read more about the Graph API at
|
@@ -53,7 +54,7 @@ module Koala
|
|
53
54
|
@access_token = access_token
|
54
55
|
end
|
55
56
|
attr_reader :access_token
|
56
|
-
|
57
|
+
|
57
58
|
def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
|
58
59
|
# Fetches the given path in the Graph API.
|
59
60
|
args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
|
@@ -63,20 +64,20 @@ module Koala
|
|
63
64
|
|
64
65
|
# make the request via the provided service
|
65
66
|
result = Koala.make_request(path, args, verb, options)
|
66
|
-
|
67
|
+
|
67
68
|
# Check for any 500 errors before parsing the body
|
68
69
|
# since we're not guaranteed that the body is valid JSON
|
69
70
|
# in the case of a server error
|
70
71
|
raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
|
71
|
-
|
72
|
-
# Parse the body as JSON and check for errors if provided a mechanism to do so
|
72
|
+
|
73
|
+
# Parse the body as JSON and check for errors if provided a mechanism to do so
|
73
74
|
# Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
|
74
75
|
# and cause JSON.parse to fail -- so we account for that by wrapping the result in []
|
75
76
|
body = response = JSON.parse("[#{result.body.to_s}]")[0]
|
76
77
|
if error_checking_block
|
77
78
|
yield(body)
|
78
79
|
end
|
79
|
-
|
80
|
+
|
80
81
|
# now return the desired information
|
81
82
|
if options[:http_component]
|
82
83
|
result.send(options[:http_component])
|
@@ -85,57 +86,57 @@ module Koala
|
|
85
86
|
end
|
86
87
|
end
|
87
88
|
end
|
88
|
-
|
89
|
+
|
89
90
|
class GraphAPI < API
|
90
91
|
include GraphAPIMethods
|
91
92
|
end
|
92
|
-
|
93
|
+
|
93
94
|
class RestAPI < API
|
94
95
|
include RestAPIMethods
|
95
96
|
end
|
96
|
-
|
97
|
+
|
97
98
|
class GraphAndRestAPI < API
|
98
99
|
include GraphAPIMethods
|
99
100
|
include RestAPIMethods
|
100
101
|
end
|
101
|
-
|
102
|
+
|
102
103
|
class RealtimeUpdates < API
|
103
104
|
include RealtimeUpdateMethods
|
104
105
|
end
|
105
|
-
|
106
|
+
|
106
107
|
class TestUsers < API
|
107
108
|
include TestUserMethods
|
108
109
|
# make the Graph API accessible in case someone wants to make other calls to interact with their users
|
109
110
|
attr_reader :graph_api
|
110
111
|
end
|
111
|
-
|
112
|
+
|
112
113
|
class APIError < Exception
|
113
114
|
attr_accessor :fb_error_type
|
114
115
|
def initialize(details = {})
|
115
|
-
self.fb_error_type = details["type"]
|
116
|
+
self.fb_error_type = details["type"]
|
116
117
|
super("#{fb_error_type}: #{details["message"]}")
|
117
118
|
end
|
118
119
|
end
|
119
|
-
|
120
|
-
|
120
|
+
|
121
|
+
|
121
122
|
class OAuth
|
122
123
|
attr_reader :app_id, :app_secret, :oauth_callback_url
|
123
124
|
def initialize(app_id, app_secret, oauth_callback_url = nil)
|
124
125
|
@app_id = app_id
|
125
126
|
@app_secret = app_secret
|
126
|
-
@oauth_callback_url = oauth_callback_url
|
127
|
+
@oauth_callback_url = oauth_callback_url
|
127
128
|
end
|
128
129
|
|
129
130
|
def get_user_info_from_cookie(cookie_hash)
|
130
131
|
# Parses the cookie set by the official Facebook JavaScript SDK.
|
131
|
-
#
|
132
|
+
#
|
132
133
|
# cookies should be a Hash, like the one Rails provides
|
133
|
-
#
|
134
|
+
#
|
134
135
|
# If the user is logged in via Facebook, we return a dictionary with the
|
135
136
|
# keys "uid" and "access_token". The former is the user's Facebook ID,
|
136
137
|
# and the latter can be used to make authenticated requests to the Graph API.
|
137
138
|
# If the user is not logged in, we return None.
|
138
|
-
#
|
139
|
+
#
|
139
140
|
# Download the official Facebook JavaScript SDK at
|
140
141
|
# http://github.com/facebook/connect-js/. Read more about Facebook
|
141
142
|
# authentication at http://developers.facebook.com/docs/authentication/.
|
@@ -150,21 +151,21 @@ module Koala
|
|
150
151
|
|
151
152
|
# generate the signature and make sure it matches what we expect
|
152
153
|
auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
|
153
|
-
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
154
|
+
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
154
155
|
sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
|
155
156
|
end
|
156
157
|
end
|
157
158
|
alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
|
158
|
-
|
159
|
+
|
159
160
|
def get_user_from_cookie(cookies)
|
160
161
|
if info = get_user_info_from_cookies(cookies)
|
161
162
|
string = info["uid"]
|
162
163
|
end
|
163
164
|
end
|
164
165
|
alias_method :get_user_from_cookies, :get_user_from_cookie
|
165
|
-
|
166
|
+
|
166
167
|
# URLs
|
167
|
-
|
168
|
+
|
168
169
|
def url_for_oauth_code(options = {})
|
169
170
|
# for permissions, see http://developers.facebook.com/docs/authentication/permissions
|
170
171
|
permissions = options[:permissions]
|
@@ -174,54 +175,71 @@ module Koala
|
|
174
175
|
raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
|
175
176
|
|
176
177
|
# Creates the URL for oauth authorization for a given callback and optional set of permissions
|
177
|
-
"https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
|
178
|
+
"https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
|
178
179
|
end
|
179
|
-
|
180
|
+
|
180
181
|
def url_for_access_token(code, options = {})
|
181
182
|
# Creates the URL for the token corresponding to a given code generated by Facebook
|
182
183
|
callback = options[:callback] || @oauth_callback_url
|
183
184
|
raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
|
184
185
|
"https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
|
185
186
|
end
|
186
|
-
|
187
|
+
|
187
188
|
def get_access_token_info(code)
|
188
189
|
# convenience method to get a parsed token from Facebook for a given code
|
189
190
|
# should this require an OAuth callback URL?
|
190
191
|
get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
|
191
192
|
end
|
192
|
-
|
193
|
+
|
193
194
|
def get_access_token(code)
|
194
195
|
# upstream methods will throw errors if needed
|
195
|
-
if info = get_access_token_info(code)
|
196
|
-
string = info["access_token"]
|
196
|
+
if info = get_access_token_info(code)
|
197
|
+
string = info["access_token"]
|
197
198
|
end
|
198
199
|
end
|
199
|
-
|
200
|
+
|
200
201
|
def get_app_access_token_info
|
201
|
-
# convenience method to get a the application's sessionless access token
|
202
|
+
# convenience method to get a the application's sessionless access token
|
202
203
|
get_token_from_server({:type => 'client_cred'}, true)
|
203
204
|
end
|
204
|
-
|
205
|
+
|
205
206
|
def get_app_access_token
|
206
207
|
if info = get_app_access_token_info
|
207
|
-
string = info["access_token"]
|
208
|
+
string = info["access_token"]
|
208
209
|
end
|
209
210
|
end
|
210
|
-
|
211
|
-
#
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
encoded_sig,
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
211
|
+
|
212
|
+
# provided directly by Facebook
|
213
|
+
# see https://github.com/facebook/crypto-request-examples/blob/master/sample.rb
|
214
|
+
# and http://developers.facebook.com/docs/authentication/canvas/encryption_proposal
|
215
|
+
def parse_signed_request(input, max_age = 3600)
|
216
|
+
encoded_sig, encoded_envelope = input.split('.', 2)
|
217
|
+
envelope = JSON.parse(base64_url_decode(encoded_envelope))
|
218
|
+
algorithm = envelope['algorithm']
|
219
|
+
|
220
|
+
raise 'Invalid request. (Unsupported algorithm.)' \
|
221
|
+
if algorithm != 'AES-256-CBC HMAC-SHA256' && algorithm != 'HMAC-SHA256'
|
222
|
+
|
223
|
+
raise 'Invalid request. (Too old.)' \
|
224
|
+
if algorithm == "AES-256-CBC HMAC-SHA256" && envelope['issued_at'].to_i < Time.now.to_i - max_age
|
225
|
+
|
226
|
+
raise 'Invalid request. (Invalid signature.)' \
|
227
|
+
if base64_url_decode(encoded_sig) !=
|
228
|
+
OpenSSL::HMAC.hexdigest(
|
229
|
+
'sha256', @app_secret, encoded_envelope).split.pack('H*')
|
230
|
+
|
231
|
+
# for requests that are signed, but not encrypted, we're done
|
232
|
+
return envelope if algorithm == 'HMAC-SHA256'
|
233
|
+
|
234
|
+
# otherwise, decrypt the payload
|
235
|
+
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
|
236
|
+
cipher.decrypt
|
237
|
+
cipher.key = @app_secret
|
238
|
+
cipher.iv = base64_url_decode(envelope['iv'])
|
239
|
+
cipher.padding = 0
|
240
|
+
decrypted_data = cipher.update(base64_url_decode(envelope['payload']))
|
241
|
+
decrypted_data << cipher.final
|
242
|
+
return JSON.parse(decrypted_data.strip)
|
225
243
|
end
|
226
244
|
|
227
245
|
# from session keys
|
@@ -231,69 +249,68 @@ module Koala
|
|
231
249
|
:type => 'client_cred',
|
232
250
|
:sessions => sessions.join(",")
|
233
251
|
}, true, "exchange_sessions")
|
234
|
-
|
252
|
+
|
235
253
|
# Facebook returns an empty body in certain error conditions
|
236
|
-
if response == ""
|
254
|
+
if response == ""
|
237
255
|
raise APIError.new({
|
238
|
-
"type" => "ArgumentError",
|
256
|
+
"type" => "ArgumentError",
|
239
257
|
"message" => "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!"
|
240
258
|
})
|
241
259
|
end
|
242
|
-
|
260
|
+
|
243
261
|
JSON.parse(response)
|
244
262
|
end
|
245
|
-
|
263
|
+
|
246
264
|
def get_tokens_from_session_keys(sessions)
|
247
265
|
# get the original hash results
|
248
266
|
results = get_token_info_from_session_keys(sessions)
|
249
267
|
# now recollect them as just the access tokens
|
250
268
|
results.collect { |r| r ? r["access_token"] : nil }
|
251
269
|
end
|
252
|
-
|
270
|
+
|
253
271
|
def get_token_from_session_key(session)
|
254
272
|
# convenience method for a single key
|
255
273
|
# gets the overlaoded strings automatically
|
256
274
|
get_tokens_from_session_keys([session])[0]
|
257
275
|
end
|
258
|
-
|
276
|
+
|
259
277
|
protected
|
260
|
-
|
278
|
+
|
261
279
|
def get_token_from_server(args, post = false)
|
262
280
|
# fetch the result from Facebook's servers
|
263
281
|
result = fetch_token_string(args, post)
|
264
|
-
|
282
|
+
|
265
283
|
# if we have an error, parse the error JSON and raise an error
|
266
284
|
raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
|
267
285
|
|
268
286
|
# otherwise, parse the access token
|
269
|
-
parse_access_token(result)
|
287
|
+
parse_access_token(result)
|
270
288
|
end
|
271
|
-
|
289
|
+
|
272
290
|
def parse_access_token(response_text)
|
273
291
|
components = response_text.split("&").inject({}) do |hash, bit|
|
274
292
|
key, value = bit.split("=")
|
275
293
|
hash.merge!(key => value)
|
276
294
|
end
|
277
|
-
components
|
295
|
+
components
|
278
296
|
end
|
279
297
|
|
280
298
|
def fetch_token_string(args, post = false, endpoint = "access_token")
|
281
299
|
Koala.make_request("/oauth/#{endpoint}", {
|
282
|
-
:client_id => @app_id,
|
300
|
+
:client_id => @app_id,
|
283
301
|
:client_secret => @app_secret
|
284
|
-
}.merge!(args), post ? "post" : "get").body
|
302
|
+
}.merge!(args), post ? "post" : "get", :use_ssl => true).body
|
285
303
|
end
|
286
|
-
|
304
|
+
|
287
305
|
# base 64
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
"#{string}==".tr("-_", "+/").unpack("m")[0]
|
306
|
+
# directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
|
307
|
+
def base64_url_decode(str)
|
308
|
+
str += '=' * (4 - str.length.modulo(4))
|
309
|
+
Base64.decode64(str.gsub('-', '+').gsub('_', '/'))
|
293
310
|
end
|
294
311
|
end
|
295
312
|
end
|
296
|
-
|
313
|
+
|
297
314
|
# finally, set up the http service Koala methods used to make requests
|
298
315
|
# you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
|
299
316
|
def self.http_service=(service)
|
@@ -302,7 +319,6 @@ module Koala
|
|
302
319
|
|
303
320
|
# by default, try requiring Typhoeus -- if that works, use it
|
304
321
|
begin
|
305
|
-
require 'typhoeus'
|
306
322
|
Koala.http_service = TyphoeusService
|
307
323
|
rescue LoadError
|
308
324
|
Koala.http_service = NetHTTPService
|