meal_ticket 0.0.0 → 0.1.0
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.
- data/Gemfile +7 -0
- data/Gemfile.lock +9 -0
- data/README.rdoc +119 -2
- data/VERSION +1 -1
- data/lib/meal_ticket.rb +174 -12
- data/meal_ticket.gemspec +8 -3
- data/test/test_meal_ticket.rb +179 -2
- metadata +40 -13
data/Gemfile
CHANGED
@@ -3,6 +3,13 @@ source "http://rubygems.org"
|
|
3
3
|
# Example:
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
5
5
|
|
6
|
+
gem 'crack'
|
7
|
+
gem 'json'
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem 'webmock'
|
11
|
+
end
|
12
|
+
|
6
13
|
# Add dependencies to develop your gem here.
|
7
14
|
# Include everything needed to run rake, tests, features, etc.
|
8
15
|
group :development do
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,29 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
addressable (2.2.4)
|
5
|
+
crack (0.1.8)
|
4
6
|
git (1.2.5)
|
5
7
|
jeweler (1.5.2)
|
6
8
|
bundler (~> 1.0.0)
|
7
9
|
git (>= 1.2.5)
|
8
10
|
rake
|
11
|
+
json (1.5.1)
|
9
12
|
rake (0.8.7)
|
10
13
|
rcov (0.9.9)
|
11
14
|
shoulda (2.11.3)
|
15
|
+
webmock (1.6.2)
|
16
|
+
addressable (>= 2.2.2)
|
17
|
+
crack (>= 0.1.7)
|
12
18
|
|
13
19
|
PLATFORMS
|
14
20
|
ruby
|
15
21
|
|
16
22
|
DEPENDENCIES
|
17
23
|
bundler (~> 1.0.0)
|
24
|
+
crack
|
18
25
|
jeweler (~> 1.5.2)
|
26
|
+
json
|
19
27
|
rcov
|
20
28
|
shoulda
|
29
|
+
webmock
|
data/README.rdoc
CHANGED
@@ -1,6 +1,123 @@
|
|
1
|
-
|
1
|
+
MealTicket simplifies the process of authenticating with 3rd-party APIs by eliminating the stuff that's the same for everyone,
|
2
|
+
letting you focus solely on the parts of authentication that matter to you.
|
2
3
|
|
3
|
-
|
4
|
+
== Overview
|
5
|
+
|
6
|
+
1. You pick a service you want to authenticate against
|
7
|
+
1. You decide what permissions you need
|
8
|
+
1. You redirect your user to something like facebook_auth_url(root_url, "user_photos,publish_stream")
|
9
|
+
1. MealTicket handles the gruesome details of the various psuedo-OAuth schemes
|
10
|
+
1. MealTicket redirects the user back to a url of your choice along with their access token
|
11
|
+
|
12
|
+
== Currently Supported Services
|
13
|
+
|
14
|
+
* facebook
|
15
|
+
* flickr
|
16
|
+
|
17
|
+
== Getting Started
|
18
|
+
|
19
|
+
1. Require the gem. In your gemfile:
|
20
|
+
|
21
|
+
gem 'meal_ticket'
|
22
|
+
|
23
|
+
1. Install the gem. In your console:
|
24
|
+
|
25
|
+
bundle install (or maybe 'sudo bundle install')
|
26
|
+
|
27
|
+
1. Install MealTicket as middleware to handle cross-domain communication. In Rails, you'd add something like this to your application.rb:
|
28
|
+
|
29
|
+
module YourAppName
|
30
|
+
class Application < Rails::Application
|
31
|
+
config.middleware.use "MealTicket"
|
32
|
+
|
33
|
+
1. Make meal_ticket URLs available to your views. In Rails, you'd add something like this to your application_helper.rb:
|
34
|
+
|
35
|
+
require 'meal_ticket'
|
36
|
+
|
37
|
+
module ApplicationHelper
|
38
|
+
include MealTicketRoutes
|
39
|
+
end
|
40
|
+
|
41
|
+
1. Optionally, make meal_ticket URLs available to your controllers. In Rails, you'd add something like this to your application_controller.rb:
|
42
|
+
|
43
|
+
require 'meal_ticket'
|
44
|
+
|
45
|
+
class ApplicationController < ActionController::Base
|
46
|
+
include MealTicketRoutes
|
47
|
+
end
|
48
|
+
|
49
|
+
Now that you've finished installing MealTicket, look below for further instructions on how to connect with individual services.
|
50
|
+
|
51
|
+
= Service-Specific Instructions
|
52
|
+
|
53
|
+
For each service you want to integrate with, find it here and follow the steps to get your API keys.
|
54
|
+
|
55
|
+
In general, you'll need to do a couple things for each service:
|
56
|
+
|
57
|
+
1. Go to their site, get your API keys, and make global constants for them.
|
58
|
+
1. Create a callback method to receive the user's access token. Make sure you also map a route for this method.
|
59
|
+
|
60
|
+
== Facebook
|
61
|
+
|
62
|
+
1. Log in to Facebook.
|
63
|
+
1. Go to https://www.facebook.com/developers/apps.php and click the "Set Up New App" button.
|
64
|
+
1. Fill out the forms to create a new app.
|
65
|
+
1. Once you land on the "Edit" page, click the "Web Site" tab on the left.
|
66
|
+
1. In the "Site Url" field, type the address of your site. For development, use something like +localhost:3000+. You may want
|
67
|
+
to set up a separate app for production.
|
68
|
+
|
69
|
+
Create global constants that look something like this:
|
70
|
+
|
71
|
+
FACEBOOK_APP_ID = "158079864105359" # facebook calls this "App ID"
|
72
|
+
FACEBOOK_SECRET = "98882d6d6cf0d7b69a5de5cc43abc10" # facebook calls this "App Secret"
|
73
|
+
FACEBOOK_CALLBACK = "path/to/my/facebook/callback" # whatever URL you've created to grab the user data and do something useful
|
74
|
+
|
75
|
+
Now, redirect users to facebook_auth_url, passing the permissions you want to ask for. Like so:
|
76
|
+
|
77
|
+
# For a full list of permissions, see https://developers.facebook.com/docs/authentication/permissions/
|
78
|
+
redirect_to facebook_auth_url("user_photos,publish_stream")
|
79
|
+
|
80
|
+
|
81
|
+
After they authenticate, they'll be redirected to your +FACEBOOK_CALLBACK+ URL with query string params like:
|
82
|
+
|
83
|
+
?facebook[token]=q2jf89ojq.j32f|FQf9j23la&facebook[expires]=4829
|
84
|
+
- or, more legibly: -
|
85
|
+
{:facebook => {:token => "q2jf89ojq.j32f|FQf9j23la", :expires => "4829"}}
|
86
|
+
|
87
|
+
|
88
|
+
Notes:
|
89
|
+
|
90
|
+
* Your facebook "API key" is never actually used.
|
91
|
+
* The expires value is the number of seconds for while this token is valid. If you request the offline_access permission, expires will be blank and the token is valid forever.
|
92
|
+
|
93
|
+
== Flickr
|
94
|
+
|
95
|
+
1. Log in to Flickr.
|
96
|
+
1. Go to http://www.flickr.com/services/apps/create/apply/ to register for API keys.
|
97
|
+
1. Fill out the forms to create a new app.
|
98
|
+
1. Once you're done, find the "edit authentication flow" page ({www.flickr.com/services/apps/YOUR_FLICKR_APP_ID/auth/}[www.flickr.com/services/apps]) and
|
99
|
+
set the Callback URL to <your root url>/meal_ticket/flickr_callback
|
100
|
+
|
101
|
+
Create global constants that look something like this:
|
102
|
+
|
103
|
+
FLICKR_TOKEN = "3637b1e30ae90503fedf9aaca8a4c370"
|
104
|
+
FLICKR_SECRET = "3570d29a7a3c086b"
|
105
|
+
FLICKR_CALLBACK = "path/to/my/flickr/callback" # whatever URL you've created to grab the user data and do something useful
|
106
|
+
|
107
|
+
Now, redirect users to flickr_auth_url, passing the permission level you want to ask for. Like so:
|
108
|
+
|
109
|
+
# For a full list of permissions, see https://developers.facebook.com/docs/authentication/permissions/
|
110
|
+
redirect_to flickr_auth_url("write")
|
111
|
+
|
112
|
+
After they authenticate, they'll be redirected to your +FACEBOOK_CALLBACK+ method with params like:
|
113
|
+
|
114
|
+
?flickr[token]=q2jf89ojq.j32f|FQf9j23la&facebook[user_id]=
|
115
|
+
- or, more legibly: -
|
116
|
+
{:flickr => {:token => "3215562516a046266-919fd54999d6e104", :user_id => "27934656@N00"}}
|
117
|
+
|
118
|
+
Notes:
|
119
|
+
|
120
|
+
* _nothing_ _to_ _note_
|
4
121
|
|
5
122
|
== Contributing to meal_ticket
|
6
123
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.0
|
data/lib/meal_ticket.rb
CHANGED
@@ -1,14 +1,176 @@
|
|
1
|
-
#
|
2
|
-
# == Steps
|
3
|
-
#
|
4
|
-
# 1. Developer signs up for 3rd-part API (let's say Facebook)
|
5
|
-
# 1. Developer copies & pastes access info into some config location
|
6
|
-
# 1. Developer creates a callback action that will receive the user's token
|
7
|
-
# !. Developer redirects the user to something like facebook_auth_url() passing a parameter indicating what permissions he needs
|
8
|
-
#
|
9
|
-
#
|
10
|
-
module MealTicket
|
11
|
-
|
12
|
-
|
13
1
|
|
2
|
+
# :include:README.rdoc
|
3
|
+
require 'cgi'
|
4
|
+
module MealTicketRoutes
|
5
|
+
|
6
|
+
FLICKR_API_BASE_URL = "http://api.flickr.com/services/" # :nodoc:
|
7
|
+
|
8
|
+
# [root_url] The base url of your app, eg "http://www.google.com/". If you're running a rails app, you can literally type root_url
|
9
|
+
# [scope] A comma-separated list of permissions. For a full list of permissions, see https://developers.facebook.com/docs/authentication/permissions/
|
10
|
+
def facebook_auth_url(root_url, scope)
|
11
|
+
"https://graph.facebook.com/oauth/authorize?client_id=#{FACEBOOK_APP_ID}&redirect_uri=#{root_url}meal_ticket/facebook_callback&scope=#{scope}"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Generates the URL for the 2nd step of Facebook auth - exchanging the code for user data
|
15
|
+
def facebook_exchange_url(root_url, code) # :nodoc:
|
16
|
+
"https://graph.facebook.com/oauth/access_token?client_id=#{FACEBOOK_APP_ID}&redirect_uri=#{root_url}meal_ticket/facebook_callback&client_secret=#{FACEBOOK_SECRET}&code=#{CGI::escape code}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# [perm] A single permission level. Permissions can be read, write, or delete. Each successive permission implies the ones before it, eg "write" implies "read". For more information, see http://www.flickr.com/services/api/auth.spec.html
|
20
|
+
def flickr_auth_url(perm)
|
21
|
+
flickr_url({"perms" => perm}, "auth")
|
22
|
+
end
|
23
|
+
|
24
|
+
# Generates the URL for the 2nd step of Facebook auth - exchanging the frob for user data
|
25
|
+
def flickr_frob_url(frob) # :nodoc:
|
26
|
+
flickr_url({"method" => "flickr.auth.getToken", "frob" => frob})
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def flickr_url(arg_hash, endpoint = "rest")
|
32
|
+
arg_hash.merge!({"api_key" => FLICKR_TOKEN})
|
33
|
+
arg_list = []
|
34
|
+
arg_hash.each do |key, value|
|
35
|
+
arg_list << "#{key}=#{value}"
|
36
|
+
end
|
37
|
+
"#{FLICKR_API_BASE_URL}#{endpoint}/?&api_sig=#{flickr_sign(arg_hash)}&#{arg_list.join('&')}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def flickr_sign(arg_hash)
|
41
|
+
arg_list = []
|
42
|
+
arg_hash.keys.sort.each do |key|
|
43
|
+
arg_list << key
|
44
|
+
arg_list << arg_hash[key]
|
45
|
+
end
|
46
|
+
Digest::MD5.hexdigest("#{FLICKR_SECRET}#{arg_list.join()}")
|
47
|
+
end
|
14
48
|
end
|
49
|
+
|
50
|
+
|
51
|
+
require 'net/http'
|
52
|
+
require 'net/https'
|
53
|
+
require 'crack'
|
54
|
+
require 'json'
|
55
|
+
class MealTicket
|
56
|
+
include MealTicketRoutes
|
57
|
+
|
58
|
+
def initialize(app)
|
59
|
+
@app = app
|
60
|
+
end
|
61
|
+
|
62
|
+
def call(env)
|
63
|
+
@env = env
|
64
|
+
if env["PATH_INFO"] =~ /^\/meal_ticket\/facebook_callback/
|
65
|
+
facebook_callback
|
66
|
+
elsif env["PATH_INFO"] =~ /^\/meal_ticket\/flickr_callback/
|
67
|
+
flickr_callback
|
68
|
+
else
|
69
|
+
@app.call(env)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def facebook_callback
|
74
|
+
response = get facebook_exchange_url(get_root_url, get_query_string_parameter("code"))
|
75
|
+
|
76
|
+
if response.body.include? "access_token"
|
77
|
+
response.body =~ /access_token=([^&]+)(?:&expires=(.*))?/ # TODO: genericize get_query_string_parameter to handle this?
|
78
|
+
token = $1 || ""
|
79
|
+
expires = $2 || ""
|
80
|
+
query_string = "facebook[token]=#{CGI.escape(token)}&facebook[expires]=#{CGI.escape(expires)}"
|
81
|
+
else
|
82
|
+
parsed = JSON.parse(response.body)
|
83
|
+
query_string = to_params({:facebook => parsed})
|
84
|
+
end
|
85
|
+
|
86
|
+
body = %(<html><body>You are being redirected.</body></html>)
|
87
|
+
headers = {
|
88
|
+
'Location' => "#{get_root_url}#{FACEBOOK_CALLBACK}?#{query_string}",
|
89
|
+
'Content-Type' => 'text/html',
|
90
|
+
'Content-Length' => body.length.to_s
|
91
|
+
}
|
92
|
+
[302, headers, [body]]
|
93
|
+
end
|
94
|
+
|
95
|
+
def flickr_callback
|
96
|
+
|
97
|
+
xml = get flickr_frob_url(get_query_string_parameter("frob"))
|
98
|
+
|
99
|
+
parsed = Crack::XML.parse(xml.body)
|
100
|
+
if parsed["rsp"]["stat"] == "ok"
|
101
|
+
remote_id = parsed["rsp"]["auth"]["user"]["nsid"] || ""
|
102
|
+
token = parsed["rsp"]["auth"]["token"] || ""
|
103
|
+
query_string = "flickr[token]=#{CGI.escape(token)}&flickr[user_id]=#{CGI.escape(remote_id)}"
|
104
|
+
else
|
105
|
+
code = parsed["rsp"]["err"]["code"]
|
106
|
+
msg = parsed["rsp"]["err"]["msg"]
|
107
|
+
query_string = "flickr[error][code]=#{CGI.escape code}&flickr[error][msg]=#{CGI.escape msg}"
|
108
|
+
end
|
109
|
+
|
110
|
+
body = %(<html><body>You are being redirected.</body></html>)
|
111
|
+
headers = {
|
112
|
+
'Location' => "#{get_root_url}#{FLICKR_CALLBACK}?#{query_string}",
|
113
|
+
'Content-Type' => 'text/html',
|
114
|
+
'Content-Length' => body.length.to_s
|
115
|
+
}
|
116
|
+
[302, headers, [body]]
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def get(url)
|
121
|
+
use_ssl = url.include? 'https'
|
122
|
+
url = URI.parse(url)
|
123
|
+
|
124
|
+
path = url.query.blank? ? url.path : "#{url.path}?#{url.query}"
|
125
|
+
|
126
|
+
http = Net::HTTP.new(url.host, use_ssl ? 443 : nil)
|
127
|
+
http.use_ssl = use_ssl
|
128
|
+
res = http.get(path)
|
129
|
+
|
130
|
+
if res.code == '302'
|
131
|
+
return get(res['location']) # follow redirects
|
132
|
+
end
|
133
|
+
res
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
def get_query_string_parameter(param)
|
138
|
+
@env["QUERY_STRING"] =~ Regexp.new("#{param}=(.*)&|#{param}=(.*)$")
|
139
|
+
return $1 || $2 # whichever one worked
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_root_url
|
143
|
+
@env["REQUEST_URI"] =~ /(^.*?:\/\/.*?\/)/ # looking for the form .....://.........../
|
144
|
+
return $1
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
# stolen from merb, according to StackOverflow
|
149
|
+
def to_params(obj)
|
150
|
+
params = ''
|
151
|
+
stack = []
|
152
|
+
|
153
|
+
obj.each do |k, v|
|
154
|
+
if v.is_a?(Hash)
|
155
|
+
stack << [k, v]
|
156
|
+
else
|
157
|
+
params << "#{CGI.escape k}=#{CGI.escape v}&"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
stack.each do |parent, hash|
|
162
|
+
hash.each do |k, v|
|
163
|
+
if v.is_a?(Hash)
|
164
|
+
stack << ["#{parent}[#{k}]", v]
|
165
|
+
else
|
166
|
+
params << "#{parent}[#{CGI.escape k}]=#{CGI.escape v}&"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
params.chop! # trailing &
|
172
|
+
params
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
data/meal_ticket.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{meal_ticket}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Chris Doyle", "Kerri Miller", "John Postlethwait"]
|
12
|
-
s.date = %q{2011-03-
|
12
|
+
s.date = %q{2011-03-08}
|
13
13
|
s.description = %q{Meal Ticket is the easiest way to authenticate users for 3rd-party social APIs. Built to be used in conjunction with Buffet.}
|
14
14
|
s.email = %q{archslide@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,7 +25,6 @@ Gem::Specification.new do |s|
|
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
27
|
"lib/meal_ticket.rb",
|
28
|
-
"meal_ticket-0.0.0.gem",
|
29
28
|
"meal_ticket.gemspec",
|
30
29
|
"test/helper.rb",
|
31
30
|
"test/test_meal_ticket.rb"
|
@@ -44,17 +43,23 @@ Gem::Specification.new do |s|
|
|
44
43
|
s.specification_version = 3
|
45
44
|
|
46
45
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<crack>, [">= 0"])
|
47
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
47
48
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
48
49
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
49
50
|
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
50
51
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
51
52
|
else
|
53
|
+
s.add_dependency(%q<crack>, [">= 0"])
|
54
|
+
s.add_dependency(%q<json>, [">= 0"])
|
52
55
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
53
56
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
54
57
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
55
58
|
s.add_dependency(%q<rcov>, [">= 0"])
|
56
59
|
end
|
57
60
|
else
|
61
|
+
s.add_dependency(%q<crack>, [">= 0"])
|
62
|
+
s.add_dependency(%q<json>, [">= 0"])
|
58
63
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
59
64
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
60
65
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
data/test/test_meal_ticket.rb
CHANGED
@@ -1,7 +1,184 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'webmock'
|
3
|
+
|
4
|
+
FACEBOOK_APP_ID = "123456789"
|
5
|
+
FACEBOOK_SECRET = "abcdef"
|
6
|
+
FACEBOOK_CALLBACK = "callbacks"
|
7
|
+
|
8
|
+
FLICKR_TOKEN = "abcde"
|
9
|
+
FLICKR_SECRET = "lmnop"
|
10
|
+
FLICKR_CALLBACK = "callbacks"
|
11
|
+
|
12
|
+
# reopen MealTicket so we can set the @env instance variable and call private methods
|
13
|
+
class MealTicket
|
14
|
+
attr_accessor :env
|
15
|
+
|
16
|
+
def _get_query_string_parameter(param)
|
17
|
+
get_query_string_parameter(param)
|
18
|
+
end
|
19
|
+
|
20
|
+
def _get_root_url
|
21
|
+
get_root_url
|
22
|
+
end
|
23
|
+
end
|
2
24
|
|
3
25
|
class TestMealTicket < Test::Unit::TestCase
|
4
|
-
|
5
|
-
|
26
|
+
include MealTicketRoutes
|
27
|
+
include WebMock::API
|
28
|
+
|
29
|
+
# utility methods
|
30
|
+
context 'Calling the get_query_string_parameter method' do
|
31
|
+
setup do
|
32
|
+
@rack_app = MealTicket.new({})
|
33
|
+
end
|
34
|
+
|
35
|
+
tests = ["", "one=1", "code=calliope", "one=1@code=calliope", "code=calliope&two=1", "one=1&code=calliope&two=2"]
|
36
|
+
expectations = [nil, nil, "calliope", "calliope", "calliope", "calliope"]
|
37
|
+
|
38
|
+
tests.each_with_index do |query_string, i|
|
39
|
+
context "on #{query_string}" do
|
40
|
+
setup do
|
41
|
+
@rack_app.env = {"QUERY_STRING" => query_string}
|
42
|
+
end
|
43
|
+
|
44
|
+
should "return #{expectations[i]}" do
|
45
|
+
assert_equal expectations[i], @rack_app._get_query_string_parameter("code")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
6
49
|
end
|
50
|
+
|
51
|
+
context 'Calling the get_root_url method' do
|
52
|
+
setup do
|
53
|
+
@rack_app = MealTicket.new({})
|
54
|
+
end
|
55
|
+
|
56
|
+
tests = ["http://localhost:3000/", "http://localhost:3000/u/r/i", "https://localhost:3000/", "http://a.b.c.d.e.f.g.com/", "http://a.b.c/d/e/f"]
|
57
|
+
expectations = ["http://localhost:3000/", "http://localhost:3000/", "https://localhost:3000/", "http://a.b.c.d.e.f.g.com/", "http://a.b.c/"]
|
58
|
+
|
59
|
+
tests.each_with_index do |uri, i|
|
60
|
+
context "on #{uri}" do
|
61
|
+
setup do
|
62
|
+
@rack_app.env = {"REQUEST_URI" => uri}
|
63
|
+
end
|
64
|
+
|
65
|
+
should "return #{expectations[i]}" do
|
66
|
+
assert_equal expectations[i], @rack_app._get_root_url
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# facebook
|
73
|
+
context 'Requesting the facebook auth url' do
|
74
|
+
context 'with valid parameters' do
|
75
|
+
setup do
|
76
|
+
@url = facebook_auth_url("http://localhost:3000/", "user_photos")
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'return a valid url' do
|
80
|
+
assert_equal "https://graph.facebook.com/oauth/authorize?client_id=123456789&redirect_uri=http://localhost:3000/meal_ticket/facebook_callback&scope=user_photos", @url
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "When a facebook authentication is successful, the facebook_callback method" do
|
86
|
+
setup do
|
87
|
+
stub_request(:get, "https://graph.facebook.com/oauth/access_token?client_id=123456789&client_secret=abcdef&code=12345&redirect_uri=http://localhost:3000/meal_ticket/facebook_callback").
|
88
|
+
with(:headers => {'Accept'=>'*/*'}).
|
89
|
+
to_return(:status => 200, :body => "access_token=12345&expires=1000", :headers => {})
|
90
|
+
|
91
|
+
# create a new instance
|
92
|
+
rack_app = MealTicket.new({})
|
93
|
+
|
94
|
+
# set up the environment - the request where facebook is redirecting the user to our callback
|
95
|
+
rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/facebook_callback?code=12345",
|
96
|
+
"QUERY_STRING" => "code=12345",
|
97
|
+
"PATH_INFO" => "/meal_ticket/facebook_callback"}
|
98
|
+
|
99
|
+
# process the request
|
100
|
+
@response = rack_app.facebook_callback
|
101
|
+
end
|
102
|
+
|
103
|
+
should "redirect to a URL that includes the facebook token and expiration" do
|
104
|
+
assert_equal 302, @response[0]
|
105
|
+
assert_equal "http://localhost:3000/callbacks?facebook[token]=12345&facebook[expires]=1000", @response[1]['Location']
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "When a facebook authentication is unsuccessful, the facebook_callback method" do
|
110
|
+
setup do
|
111
|
+
stub_request(:get, "https://graph.facebook.com/oauth/access_token?client_id=123456789&client_secret=abcdef&code=12345&redirect_uri=http://localhost:3000/meal_ticket/facebook_callback").
|
112
|
+
with(:headers => {'Accept'=>'*/*'}).
|
113
|
+
to_return(:status => 400, :body => '{"error": {"type": "OAuthException","message": "Error validating verification code."}}', :headers => {})
|
114
|
+
|
115
|
+
# create a new instance
|
116
|
+
rack_app = MealTicket.new({})
|
117
|
+
|
118
|
+
# set up the environment - the request where facebook is redirecting the user to our callback
|
119
|
+
rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/facebook_callback?code=12345",
|
120
|
+
"QUERY_STRING" => "code=12345",
|
121
|
+
"PATH_INFO" => "/meal_ticket/facebook_callback"}
|
122
|
+
|
123
|
+
# process the request
|
124
|
+
@response = rack_app.facebook_callback
|
125
|
+
end
|
126
|
+
|
127
|
+
should "redirect to a URL that includes the facebook token and expiration" do
|
128
|
+
assert_equal 302, @response[0]
|
129
|
+
assert_equal "http://localhost:3000/callbacks?facebook[error][type]=OAuthException&facebook[error][message]=Error+validating+verification+code.", @response[1]['Location']
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
#flickr
|
135
|
+
context "When a flickr authentication is successful, the flickr_callback method" do
|
136
|
+
setup do
|
137
|
+
stub_request(:get, "http://api.flickr.com/services/rest/?api_key=abcde&api_sig=5a97a411c4281b61eda6d3d746d7afc6&frob=12345&method=flickr.auth.getToken").
|
138
|
+
with(:headers => {'Accept'=>'*/*'}).
|
139
|
+
to_return(:status => 200, :body => '<?xml version="1.0" encoding="utf-8" ?> <rsp stat="ok"> <auth> <token>12345</token> <perms>delete</perms> <user nsid="12345@N00"/> </auth> </rsp> ', :headers => {})
|
140
|
+
|
141
|
+
# create a new instance
|
142
|
+
rack_app = MealTicket.new({})
|
143
|
+
|
144
|
+
# set up the environment - the request where facebook is redirecting the user to our callback
|
145
|
+
rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/flickr_callback?frob=12345",
|
146
|
+
"QUERY_STRING" => "frob=12345",
|
147
|
+
"PATH_INFO" => "/meal_ticket/flickr_callback"}
|
148
|
+
|
149
|
+
# process the request
|
150
|
+
@response = rack_app.flickr_callback
|
151
|
+
end
|
152
|
+
|
153
|
+
should "redirect to a URL that includes the facebook token and expiration" do
|
154
|
+
assert_equal 302, @response[0]
|
155
|
+
assert_equal "http://localhost:3000/callbacks?flickr[token]=12345&flickr[user_id]=12345%40N00", @response[1]['Location']
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "When a flickr authentication is successful, the flickr_callback method" do
|
160
|
+
setup do
|
161
|
+
stub_request(:get, "http://api.flickr.com/services/rest/?api_key=abcde&api_sig=5a97a411c4281b61eda6d3d746d7afc6&frob=12345&method=flickr.auth.getToken").
|
162
|
+
with(:headers => {'Accept'=>'*/*'}).
|
163
|
+
to_return(:status => 200, :body => '<?xml version="1.0" encoding="utf-8" ?> <rsp stat="fail"> <err code="108" msg="Invalid frob" /> </rsp>', :headers => {})
|
164
|
+
|
165
|
+
# create a new instance
|
166
|
+
rack_app = MealTicket.new({})
|
167
|
+
|
168
|
+
# set up the environment - the request where facebook is redirecting the user to our callback
|
169
|
+
rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/flickr_callback?frob=12345",
|
170
|
+
"QUERY_STRING" => "frob=12345",
|
171
|
+
"PATH_INFO" => "/meal_ticket/flickr_callback"}
|
172
|
+
|
173
|
+
# process the request
|
174
|
+
@response = rack_app.flickr_callback
|
175
|
+
end
|
176
|
+
|
177
|
+
should "redirect to a URL that includes the facebook token and expiration" do
|
178
|
+
assert_equal 302, @response[0]
|
179
|
+
assert_equal "http://localhost:3000/callbacks?flickr[error][code]=108&flickr[error][msg]=Invalid+frob", @response[1]['Location']
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
|
7
184
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meal_ticket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.0
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chris Doyle
|
@@ -17,12 +17,12 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2011-03-
|
20
|
+
date: 2011-03-08 00:00:00 -08:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
24
24
|
prerelease: false
|
25
|
-
type: :
|
25
|
+
type: :runtime
|
26
26
|
requirement: &id001 !ruby/object:Gem::Requirement
|
27
27
|
none: false
|
28
28
|
requirements:
|
@@ -32,12 +32,40 @@ dependencies:
|
|
32
32
|
segments:
|
33
33
|
- 0
|
34
34
|
version: "0"
|
35
|
-
name:
|
35
|
+
name: crack
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
prerelease: false
|
39
|
-
type: :
|
39
|
+
type: :runtime
|
40
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
name: json
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
prerelease: false
|
53
|
+
type: :development
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
name: shoulda
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
prerelease: false
|
67
|
+
type: :development
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
41
69
|
none: false
|
42
70
|
requirements:
|
43
71
|
- - ~>
|
@@ -49,11 +77,11 @@ dependencies:
|
|
49
77
|
- 0
|
50
78
|
version: 1.0.0
|
51
79
|
name: bundler
|
52
|
-
version_requirements: *
|
80
|
+
version_requirements: *id004
|
53
81
|
- !ruby/object:Gem::Dependency
|
54
82
|
prerelease: false
|
55
83
|
type: :development
|
56
|
-
requirement: &
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
57
85
|
none: false
|
58
86
|
requirements:
|
59
87
|
- - ~>
|
@@ -65,11 +93,11 @@ dependencies:
|
|
65
93
|
- 2
|
66
94
|
version: 1.5.2
|
67
95
|
name: jeweler
|
68
|
-
version_requirements: *
|
96
|
+
version_requirements: *id005
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
prerelease: false
|
71
99
|
type: :development
|
72
|
-
requirement: &
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
73
101
|
none: false
|
74
102
|
requirements:
|
75
103
|
- - ">="
|
@@ -79,7 +107,7 @@ dependencies:
|
|
79
107
|
- 0
|
80
108
|
version: "0"
|
81
109
|
name: rcov
|
82
|
-
version_requirements: *
|
110
|
+
version_requirements: *id006
|
83
111
|
description: Meal Ticket is the easiest way to authenticate users for 3rd-party social APIs. Built to be used in conjunction with Buffet.
|
84
112
|
email: archslide@gmail.com
|
85
113
|
executables: []
|
@@ -98,7 +126,6 @@ files:
|
|
98
126
|
- Rakefile
|
99
127
|
- VERSION
|
100
128
|
- lib/meal_ticket.rb
|
101
|
-
- meal_ticket-0.0.0.gem
|
102
129
|
- meal_ticket.gemspec
|
103
130
|
- test/helper.rb
|
104
131
|
- test/test_meal_ticket.rb
|