mini_fb 0.2.4 → 0.2.5

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/README.markdown +6 -2
  2. data/lib/mini_fb.rb +87 -31
  3. metadata +3 -3
data/README.markdown CHANGED
@@ -67,8 +67,12 @@ Define an fb_connect method in your login/sessions controller like so:
67
67
  @fb_session = cookies[FB_API_KEY + "_session_key"]
68
68
  puts "uid=#{@fb_uid}"
69
69
  puts "session=#{@fb_session}"
70
-
71
- # And here you would create the user if it doesn't already exist, then redirect them to wherever you want.
70
+
71
+ if MiniFB.verify_connect_signature(FB_API_KEY, FB_SECRET, cookies)
72
+ # And here you would create the user if it doesn't already exist, then redirect them to wherever you want.
73
+ else
74
+ # The cookies may have been modified as the signature does not match
75
+ end
72
76
 
73
77
  end
74
78
 
data/lib/mini_fb.rb CHANGED
@@ -114,7 +114,11 @@ module MiniFB
114
114
  end
115
115
 
116
116
  BAD_JSON_METHODS = ["users.getloggedinuser", "auth.promotesession", "users.hasapppermission",
117
- "Auth.revokeExtendedPermission", "pages.isAdmin", "pages.isFan"].collect { |x| x.downcase }
117
+ "Auth.revokeExtendedPermission", "pages.isAdmin", "pages.isFan",
118
+ "stream.publish",
119
+ "dashboard.addNews", "dashboard.addGlobalNews", "dashboard.publishActivity",
120
+ "dashboard.incrementcount", "dashboard.setcount"
121
+ ].collect { |x| x.downcase }
118
122
 
119
123
  # Call facebook server with a method request. Most keyword arguments
120
124
  # are passed directly to the server with a few exceptions.
@@ -157,11 +161,7 @@ module MiniFB
157
161
 
158
162
  file_name = kwargs.delete("filename")
159
163
 
160
- # Hash with secret
161
- arg_string = String.new
162
- # todo: convert symbols to strings, symbols break the next line
163
- kwargs.sort.each { |kv| arg_string << kv[0] << "=" << kv[1].to_s }
164
- kwargs["sig"] = Digest::MD5.hexdigest( arg_string + secret.value.call )
164
+ kwargs["sig"] = signature_for(kwargs, secret.value.call)
165
165
 
166
166
  fb_method = kwargs["method"].downcase
167
167
  if fb_method == "photos.upload"
@@ -170,7 +170,7 @@ module MiniFB
170
170
  else
171
171
 
172
172
  begin
173
- response = Net::HTTP.post_form( URI.parse(FB_URL), kwargs )
173
+ response = Net::HTTP.post_form( URI.parse(FB_URL), post_params(kwargs))
174
174
  rescue SocketError => err
175
175
  # why are we catching this and throwing as different error? hmmm..
176
176
  # raise IOError.new( "Cannot connect to the facebook server: " + err )
@@ -205,31 +205,31 @@ module MiniFB
205
205
  return data
206
206
  end
207
207
 
208
- def MiniFB.post_upload(filename, kwargs)
209
- content = File.open(filename, 'rb') { |f| f.read }
210
- boundary = Digest::MD5.hexdigest(content)
211
- header = {'Content-type' => "multipart/form-data, boundary=#{boundary}"}
212
-
213
- # Build query
214
- query = ''
215
- kwargs.each { |a, v|
208
+ def MiniFB.post_upload(filename, kwargs)
209
+ content = File.open(filename, 'rb') { |f| f.read }
210
+ boundary = Digest::MD5.hexdigest(content)
211
+ header = {'Content-type' => "multipart/form-data, boundary=#{boundary}"}
212
+
213
+ # Build query
214
+ query = ''
215
+ kwargs.each { |a, v|
216
+ query <<
217
+ "--#{boundary}\r\n" <<
218
+ "Content-Disposition: form-data; name=\"#{a}\"\r\n\r\n" <<
219
+ "#{v}\r\n"
220
+ }
216
221
  query <<
217
- "--#{boundary}\r\n" <<
218
- "Content-Disposition: form-data; name=\"#{a}\"\r\n\r\n" <<
219
- "#{v}\r\n"
220
- }
221
- query <<
222
- "--#{boundary}\r\n" <<
223
- "Content-Disposition: form-data; filename=\"#{File.basename(filename)}\"\r\n" <<
224
- "Content-Transfer-Encoding: binary\r\n" <<
225
- "Content-Type: image/jpeg\r\n\r\n" <<
226
- content <<
227
- "\r\n" <<
228
- "--#{boundary}--"
229
-
230
- # Call Facebook with POST multipart/form-data request
231
- uri = URI.parse(FB_URL)
232
- Net::HTTP.start(uri.host) {|http| http.post uri.path, query, header}
222
+ "--#{boundary}\r\n" <<
223
+ "Content-Disposition: form-data; filename=\"#{File.basename(filename)}\"\r\n" <<
224
+ "Content-Transfer-Encoding: binary\r\n" <<
225
+ "Content-Type: image/jpeg\r\n\r\n" <<
226
+ content <<
227
+ "\r\n" <<
228
+ "--#{boundary}--"
229
+
230
+ # Call Facebook with POST multipart/form-data request
231
+ uri = URI.parse(FB_URL)
232
+ Net::HTTP.start(uri.host) {|http| http.post uri.path, query, header}
233
233
  end
234
234
 
235
235
  # Returns true is signature is valid, false otherwise.
@@ -256,6 +256,36 @@ module MiniFB
256
256
  return false
257
257
  end
258
258
 
259
+ # Validates that the cookies sent by the user are those that were set by facebook. Since your
260
+ # secret is only known by you and facebook it is used to sign all of the cookies set.
261
+ #
262
+ # options:
263
+ # * api_key - the connect applications facebook API key
264
+ # * secret - the connect application secret
265
+ # * cookies - the cookies given by facebook - it is ok to just pass all of the cookies, the method will do the filtering for you.
266
+ def MiniFB.verify_connect_signature(api_key, secret, cookies)
267
+ signature = cookies[api_key]
268
+ return false if signature.nil?
269
+
270
+ unsigned = Hash.new
271
+ signed = Hash.new
272
+
273
+ cookies.each do |k, v|
274
+ if k =~ /^#{api_key}_(.*)/ then
275
+ signed[$1] = v
276
+ else
277
+ unsigned[k] = v
278
+ end
279
+ end
280
+
281
+ arg_string = String.new
282
+ signed.sort.each {|kv| arg_string << kv[0] << "=" << kv[1] }
283
+ if Digest::MD5.hexdigest(arg_string + secret) == signature
284
+ return true
285
+ end
286
+ return false
287
+ end
288
+
259
289
  # Returns the login/add app url for your application.
260
290
  #
261
291
  # options:
@@ -320,4 +350,30 @@ module MiniFB
320
350
  @value = Proc.new { value }
321
351
  end
322
352
  end
353
+
354
+ private
355
+ def self.post_params(params)
356
+ post_params = {}
357
+ params.each do |k, v|
358
+ k = k.to_s unless k.is_a?(String)
359
+ if Array === v || Hash === v
360
+ post_params[k] = JSON.dump(v)
361
+ else
362
+ post_params[k] = v
363
+ end
364
+ end
365
+ puts post_params.inspect
366
+ post_params
367
+ end
368
+
369
+ def self.signature_for(params, secret)
370
+ params.delete_if { |k, v| v.nil? }
371
+ raw_string = params.inject([]) do |collection, pair|
372
+ collection << pair.map { |x|
373
+ Array === x ? JSON.dump(x) : x
374
+ }.join("=")
375
+ collection
376
+ end.sort.join
377
+ Digest::MD5.hexdigest([raw_string, secret].join)
378
+ end
323
379
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 4
9
- version: 0.2.4
8
+ - 5
9
+ version: 0.2.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Travis Reeder
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-04-06 00:00:00 -07:00
18
+ date: 2010-04-20 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency