rack-fb 0.0.1 → 0.0.2

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 (5) hide show
  1. metadata +14 -37
  2. data/README.markdown +0 -75
  3. data/Rakefile +0 -74
  4. data/lib/mini_fb.rb +0 -279
  5. data/lib/rack/facebook.rb +0 -154
metadata CHANGED
@@ -1,58 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-fb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
- - John Mendonca
7
+ - ,,,
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-16 00:00:00 -08:00
12
+ date: 2010-01-18 00:00:00 -08:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: rack
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ~>
22
- - !ruby/object:Gem::Version
23
- version: 1.0.1
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: rspec
27
- type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0"
34
- version:
14
+ dependencies: []
15
+
35
16
  description:
36
- email: joaosinho@gmail.com
17
+ email: youremail@example.com
37
18
  executables: []
38
19
 
39
20
  extensions: []
40
21
 
41
- extra_rdoc_files:
42
- - README.markdown
43
- files:
44
- - README.markdown
45
- - Rakefile
46
- - lib/rack/facebook.rb
47
- - lib/mini_fb.rb
22
+ extra_rdoc_files: []
23
+
24
+ files: []
25
+
48
26
  has_rdoc: true
49
- homepage: http://github.com/johnmendonca/rack-fb
27
+ homepage: http://yoursite.example.com
50
28
  licenses: []
51
29
 
52
30
  post_install_message:
53
- rdoc_options:
54
- - --main
55
- - README.markdown
31
+ rdoc_options: []
32
+
56
33
  require_paths:
57
34
  - lib
58
35
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -73,6 +50,6 @@ rubyforge_project: rack-fb
73
50
  rubygems_version: 1.3.5
74
51
  signing_key:
75
52
  specification_version: 3
76
- summary: Facebook middleware and API client
53
+ summary: Don't use this gem
77
54
  test_files: []
78
55
 
@@ -1,75 +0,0 @@
1
- = rack-fb
2
-
3
- Rack-fb is currently a work in progress. It aims to be a lightweight client for the [Facebook API](http://wiki.developers.facebook.com/index.php/API).
4
-
5
- Installation
6
- -------------
7
-
8
- gem install rack-fb
9
-
10
- General Usage
11
- -------------
12
-
13
- The most general case is to use MiniFB.call method:
14
-
15
- user_hash = MiniFB.call(FB_API_KEY, FB_SECRET, "Users.getInfo", "session_key"=>@session_key, "uids"=>@uid, "fields"=>User.all_fields)
16
-
17
- Which simply returns the parsed json response from Facebook.
18
-
19
- Some Higher Level Objects for Common Uses
20
- ----------------------
21
-
22
- Get a MiniFB::Session:
23
-
24
- @fb = MiniFB::Session.new(FB_API_KEY, FB_SECRET, @fb_session, @fb_uid)
25
-
26
- With the session, you can then get the user information for the session/uid.
27
-
28
- user = @fb.user
29
-
30
- Then get info from the user:
31
-
32
- first_name = user["first_name"]
33
-
34
- Or profile photos:
35
-
36
- photos = user.profile_photos
37
-
38
- Or if you want other photos, try:
39
-
40
- photos = @fb.photos("pids"=>[12343243,920382343,9208348])
41
-
42
- Support
43
- --------
44
-
45
- Join our Discussion Group at: http://groups.google.com/group/mini_fb
46
-
47
- =======
48
- This Rack middleware checks the signature of Facebook params, and
49
- converts them to Ruby objects when appropiate. Also, it converts
50
- the request method from the Facebook POST to the original HTTP
51
- method used by the client.
52
-
53
- If the signature is wrong, it returns a "400 Invalid Facebook Signature".
54
-
55
- Optionally, it can take a block that receives the Rack environment
56
- and returns a value that evaluates to true when we want the middleware to
57
- be executed for the specific request.
58
-
59
- # Usage
60
-
61
- In your config.ru:
62
-
63
- require 'rack/facebook'
64
- use Rack::Facebook, "my_facebook_secret_key"
65
-
66
- Using a block condition:
67
-
68
- use Rack::Facebook, "my_facebook_secret_key" do |env|
69
- env['REQUEST_URI'] =~ /^\/facebook_only/
70
- end
71
-
72
- # Credits
73
-
74
- Carlos Paramio
75
-
data/Rakefile DELETED
@@ -1,74 +0,0 @@
1
-
2
-
3
- require "rubygems"
4
- require "rake/gempackagetask"
5
- require "rake/rdoctask"
6
-
7
- require "spec"
8
- require "spec/rake/spectask"
9
- Spec::Rake::SpecTask.new do |t|
10
- t.spec_opts = %w(--format specdoc --colour)
11
- t.libs = ["spec"]
12
- end
13
-
14
-
15
- task :default => ["spec"]
16
-
17
- # This builds the actual gem. For details of what all these options
18
- # mean, and other ones you can add, check the documentation here:
19
- #
20
- # http://rubygems.org/read/chapter/20
21
- #
22
- spec = Gem::Specification.new do |s|
23
-
24
- # Change these as appropriate
25
- s.name = "rack-fb"
26
- s.version = "0.0.1"
27
- s.summary = "Facebook middleware and API client"
28
- s.author = "John Mendonca"
29
- s.email = "joaosinho@gmail.com"
30
- s.homepage = "http://github.com/johnmendonca/rack-fb"
31
-
32
- s.has_rdoc = true
33
- s.extra_rdoc_files = %w(README.markdown)
34
- s.rdoc_options = %w(--main README.markdown)
35
-
36
- # Add any extra files to include in the gem
37
- s.files = %w(README.markdown Rakefile) + Dir.glob("{spec,lib/**/*}")
38
- s.require_paths = ["lib"]
39
-
40
- # If you want to depend on other gems, add them here, along with any
41
- # relevant versions
42
- s.add_dependency("rack", "~> 1.0.1")
43
-
44
- # If your tests use any gems, include them here
45
- s.add_development_dependency("rspec")
46
-
47
- # If you want to publish automatically to rubyforge, you'll may need
48
- # to tweak this, and the publishing task below too.
49
- s.rubyforge_project = "rack-fb"
50
- end
51
-
52
- # This task actually builds the gem. We also regenerate a static
53
- # .gemspec file, which is useful if something (i.e. GitHub) will
54
- # be automatically building a gem for this project. If you're not
55
- # using GitHub, edit as appropriate.
56
- Rake::GemPackageTask.new(spec) do |pkg|
57
- pkg.gem_spec = spec
58
-
59
- # Generate the gemspec file for github.
60
- file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
61
- File.open(file, "w") {|f| f << spec.to_ruby }
62
- end
63
-
64
- # Generate documentation
65
- Rake::RDocTask.new do |rd|
66
- rd.main = "README.markdown"
67
- rd.rdoc_files.include("README.markdown", "lib/**/*.rb")
68
- rd.rdoc_dir = "rdoc"
69
- end
70
-
71
- desc 'Clear out RDoc and generated packages'
72
- task :clean => [:clobber_rdoc, :clobber_package] do
73
- rm "#{spec.name}.gemspec"
74
- end
@@ -1,279 +0,0 @@
1
- require 'digest/md5'
2
- require 'erb'
3
- require 'json' unless defined? JSON
4
-
5
- module MiniFB
6
-
7
- # Global constants
8
- FB_URL = "http://api.facebook.com/restserver.php"
9
- FB_API_VERSION = "1.0"
10
-
11
- @@logging = false
12
-
13
- def enable_logging
14
- @@logging = true
15
- end
16
-
17
- def disable_logging
18
- @@logging = false
19
- end
20
-
21
- class FaceBookError < StandardError
22
- # Error that happens during a facebook call.
23
- def initialize( error_code, error_msg )
24
- super("Facebook error #{error_code}: #{error_msg}" )
25
- end
26
- end
27
-
28
- class Session
29
- attr_accessor :api_key, :secret_key, :session_key, :uid
30
-
31
-
32
- def initialize(api_key, secret_key, session_key, uid)
33
- @api_key = api_key
34
- @secret_key = FaceBookSecret.new secret_key
35
- @session_key = session_key
36
- @uid = uid
37
- end
38
-
39
- # returns current user
40
- def user
41
- return @user unless @user.nil?
42
- @user = User.new(MiniFB.call(@api_key, @secret_key, "Users.getInfo", "session_key"=>@session_key, "uids"=>@uid, "fields"=>User.all_fields)[0], self)
43
- @user
44
- end
45
-
46
- def photos
47
- Photos.new(self)
48
- end
49
-
50
-
51
- def call(method, params={})
52
- return MiniFB.call(api_key, secret_key, method, params.update("session_key"=>session_key))
53
- end
54
-
55
-
56
- end
57
- class User
58
- FIELDS = [:uid, :status, :political, :pic_small, :name, :quotes, :is_app_user, :tv, :profile_update_time, :meeting_sex, :hs_info, :timezone, :relationship_status, :hometown_location, :about_me, :wall_count, :significant_other_id, :pic_big, :music, :work_history, :sex, :religion, :notes_count, :activities, :pic_square, :movies, :has_added_app, :education_history, :birthday, :birthday_date, :first_name, :meeting_for, :last_name, :interests, :current_location, :pic, :books, :affiliations, :locale, :profile_url, :proxied_email, :email_hashes, :allowed_restrictions, :pic_with_logo, :pic_big_with_logo, :pic_small_with_logo, :pic_square_with_logo]
59
- STANDARD_FIELDS = [:uid, :first_name, :last_name, :name, :timezone, :birthday, :sex, :affiliations, :locale, :profile_url, :proxied_email]
60
-
61
- def self.all_fields
62
- FIELDS.join(",")
63
- end
64
-
65
- def self.standard_fields
66
- STANDARD_FIELDS.join(",")
67
- end
68
-
69
- def initialize(fb_hash, session)
70
- @fb_hash = fb_hash
71
- @session = session
72
- end
73
-
74
- def [](key)
75
- @fb_hash[key]
76
- end
77
-
78
- def uid
79
- return self["uid"]
80
- end
81
-
82
- def profile_photos
83
- @session.photos.get("uid"=>uid, "aid"=>profile_pic_album_id)
84
- end
85
-
86
- def profile_pic_album_id
87
- merge_aid(-3, uid)
88
- end
89
-
90
- def merge_aid(aid, uid)
91
- uid = uid.to_i
92
- ret = (uid << 32) + (aid & 0xFFFFFFFF)
93
- # puts 'merge_aid=' + ret.inspect
94
- return ret
95
- end
96
- end
97
-
98
- class Photos
99
-
100
- def initialize(session)
101
- @session = session
102
- end
103
-
104
- def get(params)
105
- pids = params["pids"]
106
- if !pids.nil? && pids.is_a?(Array)
107
- pids = pids.join(",")
108
- params["pids"] = pids
109
- end
110
- @session.call("photos.get", params)
111
- end
112
- end
113
-
114
- BAD_JSON_METHODS = ["users.getLoggedInUser","auth.promoteSession"]
115
-
116
- # Call facebook server with a method request. Most keyword arguments
117
- # are passed directly to the server with a few exceptions.
118
- # The 'sig' value will always be computed automatically.
119
- # The 'v' version will be supplied automatically if needed.
120
- # The 'call_id' defaults to True, which will generate a valid
121
- # number. Otherwise it should be a valid number or False to disable.
122
-
123
- # The default return is a parsed json object.
124
- # Unless the 'format' and/or 'callback' arguments are given,
125
- # in which case the raw text of the reply is returned. The string
126
- # will always be returned, even during errors.
127
-
128
- # If an error occurs, a FacebookError exception will be raised
129
- # with the proper code and message.
130
-
131
- # The secret argument should be an instance of FacebookSecret
132
- # to hide value from simple introspection.
133
- def MiniFB.call( api_key, secret, method, kwargs )
134
-
135
- puts 'kwargs=' + kwargs.inspect
136
-
137
- if secret.is_a? String
138
- secret = FaceBookSecret.new(secret)
139
- end
140
-
141
- # Prepare arguments for call
142
- call_id = kwargs.fetch("call_id", true)
143
- if call_id == true then
144
- kwargs["call_id"] = Time.now.tv_sec.to_s
145
- else
146
- kwargs.delete("call_id")
147
- end
148
-
149
- custom_format = kwargs.include?("format") or kwargs.include?("callback")
150
- kwargs["format"] ||= "JSON"
151
- kwargs["v"] ||= FB_API_VERSION
152
- kwargs["api_key"]||= api_key
153
- kwargs["method"] ||= method
154
-
155
- # Hash with secret
156
- arg_string = String.new
157
- # todo: convert symbols to strings, symbols break the next line
158
- kwargs.sort.each { |kv| arg_string << kv[0] << "=" << kv[1].to_s }
159
- kwargs["sig"] = Digest::MD5.hexdigest( arg_string + secret.value.call )
160
-
161
- # Call website with POST request
162
- begin
163
- response = Net::HTTP.post_form( URI.parse(FB_URL), kwargs )
164
- rescue SocketError => err
165
- raise IOError.new( "Cannot connect to the facebook server: " + err )
166
- end
167
-
168
- # Handle response
169
- return response.body if custom_format
170
-
171
- fb_method = kwargs["method"]
172
- body = response.body
173
-
174
- begin
175
- data = JSON.parse( body )
176
- puts 'response=' + data.inspect if @@logging
177
- if data.include?( "error_msg" ) then
178
- raise FaceBookError.new( data["error_code"] || 1, data["error_msg"] )
179
- end
180
-
181
- rescue JSON::ParserError => ex
182
- if BAD_JSON_METHODS.include?(fb_method) # Little hack because this response isn't valid JSON
183
- return body
184
- else
185
- raise ex
186
- end
187
- end
188
- return data
189
- end
190
-
191
- # Returns true is signature is valid, false otherwise.
192
- def MiniFB.verify_signature( secret, arguments )
193
- signature = arguments.delete( "fb_sig" )
194
- return false if signature.nil?
195
-
196
- unsigned = Hash.new
197
- signed = Hash.new
198
-
199
- arguments.each do |k, v|
200
- if k =~ /^fb_sig_(.*)/ then
201
- signed[$1] = v
202
- else
203
- unsigned[k] = v
204
- end
205
- end
206
-
207
- arg_string = String.new
208
- signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] }
209
- if Digest::MD5.hexdigest( arg_string + secret ) == signature
210
- return true
211
- end
212
- return false
213
- end
214
-
215
- # Returns the login/add app url for your application.
216
- #
217
- # options:
218
- # - :next => a relative next page to go to. relative to your facebook connect url or if :canvas is true, then relative to facebook app url
219
- # - :canvas => true/false - to say whether this is a canvas app or not
220
- def self.login_url(api_key, options={})
221
- login_url = "http://api.facebook.com/login.php?api_key=#{api_key}"
222
- login_url << "&next=#{options[:next]}" if options[:next]
223
- login_url << "&canvas" if options[:canvas]
224
- login_url
225
- end
226
-
227
- # This function expects arguments as a hash, so
228
- # it is agnostic to different POST handling variants in ruby.
229
- #
230
- # Validate the arguments received from facebook. This is usually
231
- # sent for the iframe in Facebook's canvas. It is not necessary
232
- # to use this on the auth_token and uid passed to callbacks like
233
- # post-add and post-remove.
234
- #
235
- # The arguments must be a mapping of to string keys and values
236
- # or a string of http request data.
237
- #
238
- # If the data is invalid or not signed properly, an empty
239
- # dictionary is returned.
240
- #
241
- # The secret argument should be an instance of FacebookSecret
242
- # to hide value from simple introspection.
243
- #
244
- # DEPRECATED, use verify_signature instead
245
- def MiniFB.validate( secret, arguments )
246
-
247
- signature = arguments.delete( "fb_sig" )
248
- return arguments if signature.nil?
249
-
250
- unsigned = Hash.new
251
- signed = Hash.new
252
-
253
- arguments.each do |k, v|
254
- if k =~ /^fb_sig_(.*)/ then
255
- signed[$1] = v
256
- else
257
- unsigned[k] = v
258
- end
259
- end
260
-
261
- arg_string = String.new
262
- signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] }
263
- if Digest::MD5.hexdigest( arg_string + secret ) != signature
264
- unsigned # Hash is incorrect, return only unsigned fields.
265
- else
266
- unsigned.merge signed
267
- end
268
- end
269
-
270
- class FaceBookSecret
271
- # Simple container that stores a secret value.
272
- # Proc cannot be dumped or introspected by normal tools.
273
- attr_reader :value
274
-
275
- def initialize( value )
276
- @value = Proc.new { value }
277
- end
278
- end
279
- end
@@ -1,154 +0,0 @@
1
- require 'digest'
2
- require 'rack/request'
3
-
4
- module Rack
5
- # This Rack middleware checks the signature of Facebook params and
6
- # converts them to Ruby objects when appropiate. Also, it converts
7
- # the request method from the Facebook POST to the original HTTP
8
- # method used by the client.
9
- #
10
- # If the signature is wrong, it returns a "400 Invalid Facebook Signature".
11
- #
12
- # Optionally, it can take a block that receives the Rack environment
13
- # and returns a value that evaluates to true when we want the middleware to
14
- # be executed for the specific request.
15
- #
16
- # == Usage
17
- #
18
- # In your rack builder:
19
- #
20
- # use Rack::Facebook, :application_secret => "SECRET", :api_key => "APIKEY"
21
- #
22
- # Using a block condition:
23
- #
24
- # use Rack::Facebook, options do |env|
25
- # env['REQUEST_URI'] =~ /^\/facebook_only/
26
- # end
27
- #
28
- # == References
29
- # * http://wiki.developers.facebook.com/index.php/Authorizing_Applications
30
- # * http://wiki.developers.facebook.com/index.php/Verifying_The_Signature
31
- #
32
- class Facebook
33
- def initialize(app, options, &condition)
34
- @app = app
35
- @options = options
36
- @condition = condition
37
- end
38
-
39
- def app_name
40
- @options[:application_name]
41
- end
42
-
43
- def secret
44
- @options[:application_secret]
45
- end
46
-
47
- def api_key
48
- @options[:api_key]
49
- end
50
-
51
- def call(env)
52
- request = Request.new(env)
53
- request.api_key = api_key
54
-
55
- if passes_condition?(request) and request.facebook?
56
- valid = true
57
-
58
- if request.params_signature
59
- fb_params = request.extract_facebook_params(:post)
60
-
61
- if valid = valid_signature?(fb_params, request.params_signature)
62
- env["facebook.original_method"] = env["REQUEST_METHOD"]
63
- env["REQUEST_METHOD"] = fb_params.delete("request_method")
64
- save_facebook_params(fb_params, env)
65
- end
66
- elsif request.cookies_signature
67
- cookie_params = request.extract_facebook_params(:cookies)
68
- valid = valid_signature?(cookie_params, request.cookies_signature)
69
- end
70
-
71
- unless valid
72
- return [400, {"Content-Type" => "text/html"}, ["Invalid Facebook signature"]]
73
- end
74
- end
75
- return @app.call(env)
76
- end
77
-
78
- private
79
-
80
- def passes_condition?(request)
81
- @condition.nil? or @condition.call(request.env)
82
- end
83
-
84
- def valid_signature?(fb_params, actual_sig)
85
- actual_sig == calculate_signature(fb_params)
86
- end
87
-
88
- def calculate_signature(hash)
89
- raw_string = hash.map{ |*pair| pair.join('=') }.sort.join
90
- Digest::MD5.hexdigest([raw_string, secret].join)
91
- end
92
-
93
- def save_facebook_params(params, env)
94
- params.each do |key, value|
95
- ruby_value = case key
96
- when 'added', 'page_added', 'in_canvas', 'in_profile_tab', 'in_new_facebook', 'position_fix', 'logged_out_facebook'
97
- value == '1'
98
- when 'expires', 'profile_update_time', 'time'
99
- Time.at(value.to_f) rescue TypeError
100
- when 'friends'
101
- value.split(',')
102
- else
103
- value
104
- end
105
-
106
- env["facebook.#{key}"] = ruby_value
107
- end
108
-
109
- env["facebook.app_name"] = app_name
110
- env["facebook.api_key"] = api_key
111
- env["facebook.secret"] = secret
112
- end
113
-
114
- class Request < ::Rack::Request
115
- FB_PREFIX = "fb_sig".freeze
116
- attr_accessor :api_key
117
-
118
- def facebook?
119
- params_signature or cookies_signature
120
- end
121
-
122
- def params_signature
123
- return @params_signature if @params_signature or @params_signature == false
124
- @params_signature = self.POST.delete(FB_PREFIX) || false
125
- end
126
-
127
- def cookies_signature
128
- cookies[@api_key]
129
- end
130
-
131
- def extract_facebook_params(where)
132
-
133
- case where
134
- when :post
135
- source = self.POST
136
- prefix = FB_PREFIX
137
- when :cookies
138
- source = cookies
139
- prefix = @api_key
140
- end
141
-
142
- prefix = "#{prefix}_"
143
-
144
- source.inject({}) do |extracted, (key, value)|
145
- if key.index(prefix) == 0
146
- extracted[key.sub(prefix, '')] = value
147
- source.delete(key) if :post == where
148
- end
149
- extracted
150
- end
151
- end
152
- end
153
- end
154
- end