mini_fb 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
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"