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.
Files changed (3) hide show
  1. data/lib/mini_fb.rb +136 -30
  2. data/test/test_mini_fb.rb +15 -2
  3. 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
- signature = arguments.delete("fb_sig")
251
- return false if signature.nil?
277
+ if arguments.is_a? String
278
+ #new way: params[:session]
279
+ session = JSON.parse(arguments)
252
280
 
253
- unsigned = Hash.new
254
- signed = Hash.new
281
+ signature = session.delete('sig')
282
+ return false if signature.nil?
255
283
 
256
- arguments.each do |k, v|
257
- if k =~ /^fb_sig_(.*)/ then
258
- signed[$1] = v
259
- else
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
- end
289
+ else
290
+ #old way
263
291
 
264
- arg_string = String.new
265
- signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] }
266
- if Digest::MD5.hexdigest(arg_string + secret) == signature
267
- return true
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
- else
368
- MiniFB::OAuthSession.new(session_or_token)
369
- end
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
- puts 'url_post=' + url if @@logging
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
- puts 'url_get=' + url if @@logging
631
+ @@log.debug 'url_get=' + url if @@logging
542
632
  resp = RestClient.get url
543
633
  end
544
634
 
545
- puts 'resp=' + resp.to_s if @@logging
635
+ @@log.debug 'resp=' + resp.to_s
546
636
 
547
- begin
548
- res_hash = JSON.parse(resp.to_s)
549
- rescue
550
- # quick fix for things like stream.publish that don't return json
551
- res_hash = JSON.parse("{\"response\": #{resp.to_s}}")
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 events groups
578
- hometown interests likes location notes online_presence
579
- photo_video_tags photos relationships religion_politics
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
- - 3
10
- version: 1.1.3
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-07-07 00:00:00 -07:00
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"