mini_fb 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/mini_fb.rb +136 -30
- data/test/test_mini_fb.rb +15 -2
- metadata +3 -9
data/lib/mini_fb.rb
CHANGED
@@ -16,6 +16,9 @@ require 'erb'
|
|
16
16
|
require 'json' unless defined? JSON
|
17
17
|
require 'rest_client'
|
18
18
|
require 'hashie'
|
19
|
+
require 'base64'
|
20
|
+
require 'openssl'
|
21
|
+
require 'logger'
|
19
22
|
|
20
23
|
module MiniFB
|
21
24
|
|
@@ -24,13 +27,35 @@ module MiniFB
|
|
24
27
|
FB_API_VERSION = "1.0"
|
25
28
|
|
26
29
|
@@logging = false
|
30
|
+
@@log = Logger.new(STDOUT)
|
31
|
+
|
32
|
+
def self.log_level=(level)
|
33
|
+
if level.is_a? Numeric
|
34
|
+
@@log.level = level
|
35
|
+
else
|
36
|
+
@@log.level = case level
|
37
|
+
when :fatal
|
38
|
+
@@log.level = Logger::FATAL
|
39
|
+
when :error
|
40
|
+
@@log.level = Logger::ERROR
|
41
|
+
when :warn
|
42
|
+
@@log.level = Logger::WARN
|
43
|
+
when :info
|
44
|
+
@@log.level = Logger::INFO
|
45
|
+
when :debug
|
46
|
+
@@log.level = Logger::DEBUG
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
27
50
|
|
28
51
|
def self.enable_logging
|
29
52
|
@@logging = true
|
53
|
+
@@log.level = Logger::DEBUG
|
30
54
|
end
|
31
55
|
|
32
56
|
def self.disable_logging
|
33
57
|
@@logging = false
|
58
|
+
@@log.level = Logger::ERROR
|
34
59
|
end
|
35
60
|
|
36
61
|
class FaceBookError < StandardError
|
@@ -135,6 +160,8 @@ module MiniFB
|
|
135
160
|
"dashboard.incrementcount", "dashboard.setcount"
|
136
161
|
].collect { |x| x.downcase }
|
137
162
|
|
163
|
+
# THIS IS FOR THE OLD FACEBOOK API, NOT THE GRAPH ONE. See MiniFB.get and MiniFB.post for Graph API
|
164
|
+
#
|
138
165
|
# Call facebook server with a method request. Most keyword arguments
|
139
166
|
# are passed directly to the server with a few exceptions.
|
140
167
|
# The 'sig' value will always be computed automatically.
|
@@ -247,28 +274,61 @@ module MiniFB
|
|
247
274
|
|
248
275
|
# Returns true is signature is valid, false otherwise.
|
249
276
|
def MiniFB.verify_signature(secret, arguments)
|
250
|
-
|
251
|
-
|
277
|
+
if arguments.is_a? String
|
278
|
+
#new way: params[:session]
|
279
|
+
session = JSON.parse(arguments)
|
252
280
|
|
253
|
-
|
254
|
-
|
281
|
+
signature = session.delete('sig')
|
282
|
+
return false if signature.nil?
|
255
283
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
unsigned[k] = v
|
284
|
+
arg_string = String.new
|
285
|
+
session.sort.each { |k, v| arg_string << "#{k}=#{v}" }
|
286
|
+
if Digest::MD5.hexdigest(arg_string + secret) == signature
|
287
|
+
return true
|
261
288
|
end
|
262
|
-
|
289
|
+
else
|
290
|
+
#old way
|
263
291
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
292
|
+
signature = arguments.delete("fb_sig")
|
293
|
+
return false if signature.nil?
|
294
|
+
|
295
|
+
unsigned = Hash.new
|
296
|
+
signed = Hash.new
|
297
|
+
|
298
|
+
arguments.each do |k, v|
|
299
|
+
if k =~ /^fb_sig_(.*)/ then
|
300
|
+
signed[$1] = v
|
301
|
+
else
|
302
|
+
unsigned[k] = v
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
arg_string = String.new
|
307
|
+
signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] }
|
308
|
+
if Digest::MD5.hexdigest(arg_string + secret) == signature
|
309
|
+
return true
|
310
|
+
end
|
268
311
|
end
|
269
312
|
return false
|
270
313
|
end
|
271
314
|
|
315
|
+
# This function takes the app secret and the signed request, and verifies if the request is valid.
|
316
|
+
def self.verify_signed_request(secret, req)
|
317
|
+
s, p = req.split(".")
|
318
|
+
sig = base64_url_decode(s)
|
319
|
+
expected_sig = OpenSSL::HMAC.digest('SHA256', secret, p.tr("-_", "+/"))
|
320
|
+
return sig == expected_sig
|
321
|
+
end
|
322
|
+
|
323
|
+
# Ruby's implementation of base64 decoding seems to be reading the string in multiples of 4 and ignoring
|
324
|
+
# any extra characters if there are no white-space characters at the end. Since facebook does not take this
|
325
|
+
# into account, this function fills any string with white spaces up to the point where it becomes divisible
|
326
|
+
# by 4, then it replaces '-' with '+' and '_' with '/' (URL-safe decoding), and decodes the result.
|
327
|
+
def self.base64_url_decode(str)
|
328
|
+
str = str + "=" * (4 - str.size % 4) unless str.size % 4 == 0
|
329
|
+
return Base64.decode64(str.tr("-_", "+/"))
|
330
|
+
end
|
331
|
+
|
272
332
|
# Parses cookies in order to extract the facebook cookie and parse it into a useable hash
|
273
333
|
#
|
274
334
|
# options:
|
@@ -364,9 +424,9 @@ module MiniFB
|
|
364
424
|
def initialize(session_or_token, id)
|
365
425
|
@oauth_session = if session_or_token.is_a?(MiniFB::OAuthSession)
|
366
426
|
session_or_token
|
367
|
-
|
368
|
-
|
369
|
-
|
427
|
+
else
|
428
|
+
MiniFB::OAuthSession.new(session_or_token)
|
429
|
+
end
|
370
430
|
@id = id
|
371
431
|
@object = @oauth_session.get(id, :metadata => true)
|
372
432
|
@connections_cache = {}
|
@@ -447,6 +507,36 @@ module MiniFB
|
|
447
507
|
return params
|
448
508
|
end
|
449
509
|
|
510
|
+
# Return a JSON object of working Oauth tokens from working session keys, returned in order given
|
511
|
+
def self.oauth_exchange_session(app_id, secret, session_keys)
|
512
|
+
url = "#{graph_base}oauth/exchange_sessions"
|
513
|
+
params = {}
|
514
|
+
params["client_id"] = "#{app_id}"
|
515
|
+
params["client_secret"] = "#{secret}"
|
516
|
+
params["sessions"] = "#{session_keys}"
|
517
|
+
options = {}
|
518
|
+
options[:params] = params
|
519
|
+
options[:method] = :post
|
520
|
+
return fetch(url, options)
|
521
|
+
end
|
522
|
+
|
523
|
+
# Return a JSON object of working Oauth tokens from working session keys, returned in order given
|
524
|
+
def self.authenticate_as_app(app_id, secret)
|
525
|
+
url = "#{graph_base}oauth/access_token"
|
526
|
+
params = {}
|
527
|
+
params["type"] = "client_cred"
|
528
|
+
params["client_id"] = "#{app_id}"
|
529
|
+
params["client_secret"] = "#{secret}"
|
530
|
+
# resp = RestClient.get url
|
531
|
+
options = {}
|
532
|
+
options[:params] = params
|
533
|
+
options[:method] = :get
|
534
|
+
options[:response_type] = :params
|
535
|
+
resp = fetch(url, options)
|
536
|
+
puts 'resp=' + resp.body.to_s if @@logging
|
537
|
+
resp
|
538
|
+
end
|
539
|
+
|
450
540
|
# Gets data from the Facebook Graph API
|
451
541
|
# options:
|
452
542
|
# - type: eg: feed, home, etc
|
@@ -458,6 +548,7 @@ module MiniFB
|
|
458
548
|
params = options[:params] || {}
|
459
549
|
params["access_token"] = "#{(access_token)}"
|
460
550
|
params["metadata"] = "1" if options[:metadata]
|
551
|
+
params["fields"] = options[:fields].join(",") if options[:fields]
|
461
552
|
options[:params] = params
|
462
553
|
return fetch(url, options)
|
463
554
|
end
|
@@ -527,28 +618,42 @@ module MiniFB
|
|
527
618
|
return fetch(url, options)
|
528
619
|
end
|
529
620
|
|
530
|
-
|
531
621
|
def self.fetch(url, options={})
|
532
622
|
|
533
623
|
begin
|
534
624
|
if options[:method] == :post
|
535
|
-
|
625
|
+
@@log.debug 'url_post=' + url if @@logging
|
536
626
|
resp = RestClient.post url, options[:params]
|
537
627
|
else
|
538
628
|
if options[:params] && options[:params].size > 0
|
539
629
|
url += '?' + options[:params].map { |k, v| URI.escape("%s=%s" % [k, v]) }.join('&')
|
540
630
|
end
|
541
|
-
|
631
|
+
@@log.debug 'url_get=' + url if @@logging
|
542
632
|
resp = RestClient.get url
|
543
633
|
end
|
544
634
|
|
545
|
-
|
635
|
+
@@log.debug 'resp=' + resp.to_s
|
546
636
|
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
637
|
+
if options[:response_type] == :params
|
638
|
+
# Some methods return a param like string, for example: access_token=11935261234123|rW9JMxbN65v_pFWQl5LmHHABC
|
639
|
+
params = {}
|
640
|
+
params_array = resp.split("&")
|
641
|
+
params_array.each do |p|
|
642
|
+
ps = p.split("=")
|
643
|
+
params[ps[0]] = ps[1]
|
644
|
+
end
|
645
|
+
return params
|
646
|
+
else
|
647
|
+
begin
|
648
|
+
res_hash = JSON.parse(resp.to_s)
|
649
|
+
rescue
|
650
|
+
# quick fix for things like stream.publish that don't return json
|
651
|
+
res_hash = JSON.parse("{\"response\": #{resp.to_s}}")
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
if res_hash.size == 1 && res_hash["data"]
|
656
|
+
res_hash = res_hash["data"]
|
552
657
|
end
|
553
658
|
|
554
659
|
if res_hash.is_a? Array # fql return this
|
@@ -574,10 +679,10 @@ module MiniFB
|
|
574
679
|
# Returns all available scopes.
|
575
680
|
def self.scopes
|
576
681
|
scopes = %w{
|
577
|
-
about_me activities birthday education_history
|
578
|
-
hometown interests likes location notes
|
579
|
-
photo_video_tags photos relationships
|
580
|
-
status videos website work_history
|
682
|
+
about_me activities birthday checkins education_history
|
683
|
+
events groups hometown interests likes location notes
|
684
|
+
online_presence photo_video_tags photos relationships
|
685
|
+
religion_politics status videos website work_history
|
581
686
|
}
|
582
687
|
scopes.map! do |scope|
|
583
688
|
["user_#{scope}", "friends_#{scope}"]
|
@@ -590,6 +695,7 @@ module MiniFB
|
|
590
695
|
}
|
591
696
|
end
|
592
697
|
|
698
|
+
|
593
699
|
# This function expects arguments as a hash, so
|
594
700
|
# it is agnostic to different POST handling variants in ruby.
|
595
701
|
#
|
data/test/test_mini_fb.rb
CHANGED
@@ -1,19 +1,32 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'uri'
|
3
|
+
require 'yaml'
|
4
|
+
require 'active_support'
|
5
|
+
require '../lib/mini_fb'
|
3
6
|
|
4
7
|
class MiniFBTests < Test::Unit::TestCase
|
5
8
|
|
6
9
|
|
7
10
|
def setup
|
11
|
+
@config = File.open(File.expand_path("~/.mini_fb_tests.yml")) { |yf| YAML::load(yf) }
|
12
|
+
puts @config.inspect
|
13
|
+
MiniFB.log_level = :debug
|
8
14
|
end
|
9
15
|
|
10
16
|
def teardown
|
11
17
|
|
12
18
|
end
|
13
19
|
|
20
|
+
def test_authenticate_as_app
|
21
|
+
res = MiniFB.authenticate_as_app(@config["fb_api_key"], @config["fb_secret"])
|
22
|
+
puts 'res=' + res.inspect
|
23
|
+
assert res["access_token"].present?
|
24
|
+
assert res["access_token"].starts_with?(@config["fb_app_id"].to_s)
|
25
|
+
end
|
26
|
+
|
14
27
|
# Test signature verification.
|
15
28
|
def test_signature
|
16
|
-
|
29
|
+
|
17
30
|
end
|
18
31
|
|
19
32
|
def test_basic_calls
|
@@ -25,7 +38,7 @@ class MiniFBTests < Test::Unit::TestCase
|
|
25
38
|
end
|
26
39
|
|
27
40
|
def test_photos
|
28
|
-
|
41
|
+
|
29
42
|
end
|
30
43
|
|
31
44
|
def test_uri_escape
|
metadata
CHANGED
@@ -1,22 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_fb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 21
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 1
|
8
7
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.1.
|
8
|
+
- 4
|
9
|
+
version: 1.1.4
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Travis Reeder
|
14
|
-
- Aaron Hurley
|
15
13
|
autorequire:
|
16
14
|
bindir: bin
|
17
15
|
cert_chain: []
|
18
16
|
|
19
|
-
date: 2010-
|
17
|
+
date: 2010-10-30 00:00:00 -07:00
|
20
18
|
default_executable:
|
21
19
|
dependencies:
|
22
20
|
- !ruby/object:Gem::Dependency
|
@@ -27,7 +25,6 @@ dependencies:
|
|
27
25
|
requirements:
|
28
26
|
- - ">="
|
29
27
|
- !ruby/object:Gem::Version
|
30
|
-
hash: 3
|
31
28
|
segments:
|
32
29
|
- 0
|
33
30
|
version: "0"
|
@@ -41,7 +38,6 @@ dependencies:
|
|
41
38
|
requirements:
|
42
39
|
- - ">="
|
43
40
|
- !ruby/object:Gem::Version
|
44
|
-
hash: 3
|
45
41
|
segments:
|
46
42
|
- 0
|
47
43
|
version: "0"
|
@@ -73,7 +69,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
69
|
requirements:
|
74
70
|
- - ">="
|
75
71
|
- !ruby/object:Gem::Version
|
76
|
-
hash: 3
|
77
72
|
segments:
|
78
73
|
- 0
|
79
74
|
version: "0"
|
@@ -82,7 +77,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
77
|
requirements:
|
83
78
|
- - ">="
|
84
79
|
- !ruby/object:Gem::Version
|
85
|
-
hash: 3
|
86
80
|
segments:
|
87
81
|
- 0
|
88
82
|
version: "0"
|