mini_fb 0.2.4 → 0.2.5

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