gitkit-ruby 0.0.1
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 +4 -0
- data/Gemfile.lock +38 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/gitkit-ruby.gemspec +31 -0
- data/lib/google/identity_toolkit.rb +298 -0
- data/lib/google/identity_toolkit/api.rb +53 -0
- data/lib/google/identity_toolkit/errors.rb +11 -0
- data/lib/google/identity_toolkit/helpers.rb +64 -0
- data/lib/google/identity_toolkit/version.rb +5 -0
- data/tasks/test.rake +14 -0
- data/tasks/yard.rake +9 -0
- data/test/gitkit_test.rb +115 -0
- metadata +153 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gitkit-ruby (0.0.1)
|
5
|
+
addressable (~> 2.2)
|
6
|
+
httparty (~> 0.8)
|
7
|
+
json (~> 1.4.6)
|
8
|
+
rack (~> 1.3)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.2.6)
|
14
|
+
hoe (2.12.4)
|
15
|
+
rake (~> 0.8)
|
16
|
+
httparty (0.8.1)
|
17
|
+
multi_json
|
18
|
+
multi_xml
|
19
|
+
json (1.4.6)
|
20
|
+
multi_json (1.0.3)
|
21
|
+
multi_xml (0.4.1)
|
22
|
+
rack (1.3.4)
|
23
|
+
rack-test (0.6.1)
|
24
|
+
rack (>= 1.0)
|
25
|
+
rake (0.9.2)
|
26
|
+
rr (1.0.4)
|
27
|
+
test-unit (1.2.3)
|
28
|
+
hoe (>= 1.5.1)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
gitkit-ruby!
|
35
|
+
rack-test (~> 0.6)
|
36
|
+
rake (~> 0.9)
|
37
|
+
rr (~> 1.0)
|
38
|
+
test-unit (~> 1.2)
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
FileList['tasks/**/*.rake'].each { |task| import task }
|
8
|
+
|
9
|
+
"""
|
10
|
+
require 'bundle/gem_tasks'
|
11
|
+
begin
|
12
|
+
gem 'jeweler', '~> 1.6.4'
|
13
|
+
require 'jeweler'
|
14
|
+
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = 'gitkit-ruby'
|
17
|
+
gem.summary = 'Rack middleware for Google Identity Toolkit'
|
18
|
+
gem.description = gem.summary
|
19
|
+
gem.email = 'sqrrrl@gmail.com'
|
20
|
+
gem.homepage = 'http://code.google.com/p/%s' % gem.name
|
21
|
+
gem.authors = [ 'Steve Bazyl' ]
|
22
|
+
|
23
|
+
#gem.rubyforge_project = 'gitkit-ruby'
|
24
|
+
end
|
25
|
+
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
|
28
|
+
rescue LoadError
|
29
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler -v 1.6.4'
|
30
|
+
end
|
31
|
+
"""
|
32
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/gitkit-ruby.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "google/identity_toolkit/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gitkit-ruby"
|
7
|
+
s.version = Google::IdentityToolkit::VERSION
|
8
|
+
s.authors = ["Steven Bazyl"]
|
9
|
+
s.email = ["sqrrrl@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Rack middleware for Google Identity Toolkit}
|
12
|
+
s.description = %q{Rack middleware for Google Identity Toolkit}
|
13
|
+
|
14
|
+
s.rubyforge_project = "gitkit-ruby"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency 'rack','~> 1.3'
|
22
|
+
s.add_dependency 'json', '~> 1.4.6'
|
23
|
+
s.add_dependency 'httparty', '~> 0.8'
|
24
|
+
s.add_dependency 'addressable', '~> 2.2'
|
25
|
+
|
26
|
+
s.add_development_dependency 'rake', '~> 0.9'
|
27
|
+
s.add_development_dependency 'rack-test', '~> 0.6'
|
28
|
+
s.add_development_dependency 'test-unit', '~> 1.2'
|
29
|
+
s.add_development_dependency 'rr', '~> 1.0'
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,298 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "json"
|
3
|
+
require "google/identity_toolkit/api"
|
4
|
+
require "google/identity_toolkit/helpers"
|
5
|
+
require "addressable/uri"
|
6
|
+
|
7
|
+
module Google
|
8
|
+
CONTENT_TYPE = 'Content-Type'
|
9
|
+
CONTENT_TYPE_HTML = 'text/html'
|
10
|
+
CONTENT_TYPE_JSON = 'application/json'
|
11
|
+
STATUS_SUCCESS = 'success'
|
12
|
+
STATUS_INVALID_EMAIL = 'invalidAssertionEmail'
|
13
|
+
STATUS_ACCOUNT_MISMATCH = 'accountMismatch'
|
14
|
+
|
15
|
+
##
|
16
|
+
# Rack middleware for using the Google Identity Kit for federated login
|
17
|
+
# @see http://code.google.com/apis/identitytoolkit/
|
18
|
+
class IdentityToolkit
|
19
|
+
attr_accessor :api_key
|
20
|
+
attr_accessor :path
|
21
|
+
attr_accessor :app
|
22
|
+
attr_accessor :api
|
23
|
+
attr_accessor :callback_url
|
24
|
+
attr_accessor :federated_signup_url
|
25
|
+
attr_accessor :signup_url
|
26
|
+
attr_accessor :home_url
|
27
|
+
|
28
|
+
##
|
29
|
+
# Creates the Identity toolkit middleware. Requires defining a block
|
30
|
+
# to configure the API and define callback methods for loading & authenticating
|
31
|
+
# users.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# use Google::IdentityToolkit, do |toolkit|
|
35
|
+
# toolkit.api_key = "..."
|
36
|
+
# def fetch_user(email, assertion = nil)
|
37
|
+
# # Load the user for the given email, optionally registering
|
38
|
+
# # the user if assertion is provided and '
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def password_valid?(email, password)
|
42
|
+
# # Validates the user id/password
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
def initialize(app, &block)
|
46
|
+
@path = '/_gitkit'
|
47
|
+
@callback_url = nil
|
48
|
+
@app = app
|
49
|
+
instance_eval(&block) if block_given?
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Static method for fetching the API key from thread-local storage. Only valid for threads
|
54
|
+
# intercepted by the rack module. Use by helper methods rendering the login button.
|
55
|
+
#
|
56
|
+
# @return [String] API Key
|
57
|
+
def self.api_key
|
58
|
+
env[:gitkit_api_key]
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Static method for fetching the current user from thread-local storage. Only valid for threads
|
63
|
+
# intercepted by the rack module. Use by helper methods rendering the login button.
|
64
|
+
#
|
65
|
+
# @return [Hash] Hash representing the user
|
66
|
+
def self.current_user
|
67
|
+
env[:gitkit_user]
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Static method for fetching the callback URL from thread-local storage. Only valid for threads
|
72
|
+
# intercepted by the rack module. Use by helper methods rendering the login button.
|
73
|
+
#
|
74
|
+
# @return [Hash] Hash representing the user
|
75
|
+
def self.callback_url
|
76
|
+
env[:gitkit_callback_url]
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Get (and lazy init) the API client
|
81
|
+
#
|
82
|
+
# @return [Google::IdentityToolkit::Api]
|
83
|
+
# API client
|
84
|
+
def api
|
85
|
+
@api ||= Google::IdentityToolkit::Api.new(@api_key)
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Handles web requests for identity toolkit callbacks
|
90
|
+
#
|
91
|
+
# @param [Hash] env
|
92
|
+
# Rack request context
|
93
|
+
# @return [Array]
|
94
|
+
# HTTP response
|
95
|
+
def call(env)
|
96
|
+
request = Rack::Request.new(env)
|
97
|
+
env[:gitkit_api_key] = @api_key
|
98
|
+
env[:gitkit_callback_url] = callback_url(request)
|
99
|
+
env[:gitkit_user] = request.session[:git_user]
|
100
|
+
Thread.current[:gitkit_rack_env] = env
|
101
|
+
begin
|
102
|
+
return @app.call(env) unless request.path == @path
|
103
|
+
case request.params['rp_target']
|
104
|
+
when "callback"
|
105
|
+
body = callback(request)
|
106
|
+
[200, {CONTENT_TYPE => CONTENT_TYPE_HTML}, [body]]
|
107
|
+
when "login"
|
108
|
+
data = login(request)
|
109
|
+
[200, {CONTENT_TYPE => CONTENT_TYPE_JSON}, [data.to_json]]
|
110
|
+
when "userStatus"
|
111
|
+
data = status(request)
|
112
|
+
[200, {CONTENT_TYPE => CONTENT_TYPE_JSON}, [data.to_json]]
|
113
|
+
end
|
114
|
+
ensure
|
115
|
+
Thread.current[:gitkit_rack_env] = nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Handles IDP responses during login
|
121
|
+
#
|
122
|
+
# @param [Rack::Request] request
|
123
|
+
# current request
|
124
|
+
# @return [String]
|
125
|
+
# HTML boilerplate to close popup & notify parent window
|
126
|
+
def callback(request)
|
127
|
+
render_method = !!request.params['mobile'] ? :build_mobile_html : :build_notify_html
|
128
|
+
begin
|
129
|
+
assertion = api.verify_assertion(request.url, request.body)
|
130
|
+
input_email = request.params['rp_input_email']
|
131
|
+
email = assertion['verifiedEmail']
|
132
|
+
return send(render_method, STATUS_INVALID_EMAIL, {}) if email.nil?
|
133
|
+
return send(render_method, STATUS_ACCOUNT_MISMATCH, {
|
134
|
+
"inputEmail" => input_email,
|
135
|
+
"validatedEmail" => email
|
136
|
+
}) unless input_email.nil? or input_email == email
|
137
|
+
|
138
|
+
user = fetch_user(email, assertion)
|
139
|
+
if user.nil?
|
140
|
+
# Save the assertion for use in signup page
|
141
|
+
request.session[:gitkit_assertion] = assertion unless session.nil?
|
142
|
+
send(render_method, STATUS_SUCCESS, {
|
143
|
+
"email" => email,
|
144
|
+
"registered" => false,
|
145
|
+
"displayName" => assertion["displayName"],
|
146
|
+
"photoUrl" => assertion["photoUrl"]
|
147
|
+
})
|
148
|
+
else
|
149
|
+
# User exits, login
|
150
|
+
upgrade_user(email)
|
151
|
+
handle_login(user)
|
152
|
+
send(render_method, STATUS_SUCCESS, {
|
153
|
+
"email" => email,
|
154
|
+
"registered" => true,
|
155
|
+
"displayName" => assertion["displayName"],
|
156
|
+
"photoUrl" => assertion["photoUrl"]
|
157
|
+
})
|
158
|
+
end
|
159
|
+
rescue Exception => e
|
160
|
+
send(render_method, 'invalidAssertion', {})
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Handles authentication requests for non-federated users
|
166
|
+
#
|
167
|
+
# @param [Rack::Request] request
|
168
|
+
# current request
|
169
|
+
# @return [Hash]
|
170
|
+
# Result of login. Currently just sets the value of :status in the hash
|
171
|
+
def login(request)
|
172
|
+
user = fetch_user(request.params["email"])
|
173
|
+
if user.nil?
|
174
|
+
{:status => "emailNotExist"}
|
175
|
+
elsif user[:federated]
|
176
|
+
{:status => "federated"}
|
177
|
+
else
|
178
|
+
if password_valid?(request.params["email"], request.params["password"])
|
179
|
+
user = fetch_user(request.params["email"])
|
180
|
+
handle_login(user)
|
181
|
+
{:status => "OK"}
|
182
|
+
else
|
183
|
+
{:status => "passwordError"}
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# Checks the status of a user
|
190
|
+
#
|
191
|
+
# @param [Rack::Request] request
|
192
|
+
# current request
|
193
|
+
# @return [Hash]
|
194
|
+
# Status of user
|
195
|
+
def status(request)
|
196
|
+
user = fetch_user(request.params["email"])
|
197
|
+
#referrer = request.params["referrer"]
|
198
|
+
{ "registered" => !!user,
|
199
|
+
"legacy" => !(user && user[:federated]) }
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# Log the user after either a successful assertion or password validation
|
204
|
+
# @param [Hash]
|
205
|
+
# User (from find_user)
|
206
|
+
# @param [Rack::Request] request
|
207
|
+
# Current request
|
208
|
+
def handle_login(user)
|
209
|
+
unless session.nil?
|
210
|
+
request.session[:git_user] = user
|
211
|
+
on_login(user)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
##
|
216
|
+
# Renders boilerplate HTML for closing popups after login
|
217
|
+
#
|
218
|
+
# @param [String] status
|
219
|
+
# Status of callback -- either success, or one of the gitkit supported error messages
|
220
|
+
# @param [Hash] data
|
221
|
+
# user data from assertion if successful login
|
222
|
+
# @return [String] HTML
|
223
|
+
def build_notify_html(status, data)
|
224
|
+
html = <<-EOF
|
225
|
+
<script type='text/javascript' src='https://ajax.googleapis.com/jsapi'></script>
|
226
|
+
<script type='text/javascript'>google.load("identitytoolkit", "1.0", {packages: ["notify"]});</script>
|
227
|
+
<script type='text/javascript'>
|
228
|
+
EOF
|
229
|
+
case status
|
230
|
+
when STATUS_SUCCESS
|
231
|
+
html << "window.google.identitytoolkit.notifyFederatedSuccess(#{data.to_json});"
|
232
|
+
else
|
233
|
+
html << "window.google.identitytoolkit.notifyFederatedError('#{status}', #{data.to_json});"
|
234
|
+
end
|
235
|
+
html << "</script>"
|
236
|
+
end
|
237
|
+
|
238
|
+
##
|
239
|
+
# Renders boilerplate HTML for non-poup mobile UI
|
240
|
+
#
|
241
|
+
# @param [String] status
|
242
|
+
# Status of callback -- either success, or one of the gitkit supported error messages
|
243
|
+
# @param [Hash] data
|
244
|
+
# user data from assertion if successful login
|
245
|
+
# @return [String] HTML
|
246
|
+
def build_mobile_html(status, data)
|
247
|
+
html = "<script type='text/javascript'>"
|
248
|
+
if status == STATUS_SUCCESS
|
249
|
+
if data["registered"]
|
250
|
+
html << "window.location = '#{home_url}';"
|
251
|
+
else
|
252
|
+
target = Addressable::URI.parse(signup_url)
|
253
|
+
target.query_values = target.query_values.merge("email" => data["email"])
|
254
|
+
html << "window.location = '#{target}';"
|
255
|
+
end
|
256
|
+
else
|
257
|
+
target = Addressable::URI.parse(login_url)
|
258
|
+
target.query_values = target.query_values.merge("error" => status)
|
259
|
+
html << "window.location = '#{login_url}';"
|
260
|
+
end
|
261
|
+
html << "</script>"
|
262
|
+
end
|
263
|
+
|
264
|
+
# Get the callback URL for use in the javascript helpers.
|
265
|
+
#
|
266
|
+
# @param [Rack::Request] request
|
267
|
+
# current request object
|
268
|
+
# @return [String]
|
269
|
+
# callback URL for verifying assertions
|
270
|
+
def callback_url(request = nil)
|
271
|
+
return @callback_url unless @callback_url.nil?
|
272
|
+
scheme = request.scheme
|
273
|
+
if (scheme == 'http' && request.port == 80 ||
|
274
|
+
scheme == 'https' && request.port == 443)
|
275
|
+
port = ""
|
276
|
+
else
|
277
|
+
port = ":#{request.port}"
|
278
|
+
end
|
279
|
+
"#{scheme}://#{request.host}#{port}#{request.script_name}#{@path}"
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.env
|
283
|
+
Thread.current[:gitkit_rack_env]
|
284
|
+
end
|
285
|
+
|
286
|
+
def env
|
287
|
+
Thread.current[:gitkit_rack_env]
|
288
|
+
end
|
289
|
+
|
290
|
+
def request
|
291
|
+
Rack::Request.new(env)
|
292
|
+
end
|
293
|
+
|
294
|
+
def session
|
295
|
+
request.session
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'google/identity_toolkit/errors'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Google
|
6
|
+
class IdentityToolkit
|
7
|
+
|
8
|
+
##
|
9
|
+
# Server API for creating login requests & verifying assertions
|
10
|
+
class Api
|
11
|
+
include HTTParty
|
12
|
+
format :json
|
13
|
+
headers "Content-Type" => "application/json"
|
14
|
+
|
15
|
+
##
|
16
|
+
# Initializes the API
|
17
|
+
#
|
18
|
+
# @param [String] api_key
|
19
|
+
# API key from developer console
|
20
|
+
def initialize(api_key)
|
21
|
+
@api_key = api_key
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Sends an IDP response for verification, returning an assertion
|
26
|
+
# if the response is valid
|
27
|
+
#
|
28
|
+
# @param [String] request_uri
|
29
|
+
# URL of the current request
|
30
|
+
# @param [StringIO] body
|
31
|
+
# Request body (if post)
|
32
|
+
#
|
33
|
+
# @return [Hash]
|
34
|
+
# Assertion of user identity
|
35
|
+
#
|
36
|
+
# @raise [ApiError]
|
37
|
+
# if assertion not valid
|
38
|
+
def verify_assertion(request_uri, body=nil)
|
39
|
+
request = {
|
40
|
+
"requestUri" => request_uri
|
41
|
+
}
|
42
|
+
request["postBody"] = body.gets unless body.nil?
|
43
|
+
response = Api.post('https://www.googleapis.com/identitytoolkit/v1/relyingparty/verifyAssertion',
|
44
|
+
:query => { "key" => @api_key }, :body => request.to_json )
|
45
|
+
raise ApiError.new("Error #{response.code} when verifying the assertion") unless response.code == 200
|
46
|
+
response.parsed_response
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
3
|
+
module Google
|
4
|
+
class IdentityToolkit
|
5
|
+
module Helpers
|
6
|
+
|
7
|
+
##
|
8
|
+
# Generate javascript for configuring the identity toolkit button
|
9
|
+
#
|
10
|
+
# @param [String] element_id
|
11
|
+
# Id of element that will contain the login button
|
12
|
+
# @param [Hash] options
|
13
|
+
# Configuration options
|
14
|
+
# @option options [String] :api_key
|
15
|
+
# APIKey from developer console
|
16
|
+
# @option options [String] :company_name
|
17
|
+
# Name of site to display on login pages
|
18
|
+
# @option options [String] :base_url
|
19
|
+
#
|
20
|
+
# @return [String] Block of javascript (must be wrapped in <script> tag)
|
21
|
+
|
22
|
+
def account_chooser(element_id, options = {})
|
23
|
+
callback_url = Addressable::URI.parse(options[:callback_url] || Google::IdentityToolkit.callback_url)
|
24
|
+
params = callback_url.query_values || {}
|
25
|
+
callback_url.query_values = params.merge("mobile" => "1") if options[:mobile]
|
26
|
+
js = <<-EOF
|
27
|
+
window.google.identitytoolkit.setConfig({
|
28
|
+
developerKey: "#{options[:api_key] || Google::IdentityToolkit.api_key}",
|
29
|
+
companyName: "#{options[:site_name]}",
|
30
|
+
callbackUrl: "#{callback_url}",
|
31
|
+
userStatusUrl: "#{callback_url}",
|
32
|
+
loginUrl: "#{callback_url}",
|
33
|
+
federatedSignupUrl: "#{options[:federated_signup_url] || options[:signup_url] || '/signup' }",
|
34
|
+
signupUrl: "#{options[:signup_url] || '/signup'}",
|
35
|
+
homeUrl: "#{options[:home_url] || '/' }",
|
36
|
+
logoutUrl: "#{options[:logout_url] || '/logout'}",
|
37
|
+
realm: "#{options[:realm]}",
|
38
|
+
language: "#{options[:language] || 'en'}",
|
39
|
+
idps: ["Gmail", "AOL", "Hotmail", "Yahoo"],
|
40
|
+
tryFederatedFirst: #{options[:try_federated_first] || false},
|
41
|
+
useCachedUserStatus: #{options[:use_cached_status] || false}
|
42
|
+
});
|
43
|
+
$('#{element_id}').accountChooser();
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
def show_current_user(options={})
|
48
|
+
js = ""
|
49
|
+
user = options[:user] || Google::IdentityToolkit.current_user
|
50
|
+
if user
|
51
|
+
js = <<-EOF
|
52
|
+
window.google.identitytoolkit.updateSavedAccount({
|
53
|
+
email: '#{user[:email]}',
|
54
|
+
displayName: '#{user[:display_name]}',
|
55
|
+
photoUrl: '#{user[:photo_url]}'
|
56
|
+
});
|
57
|
+
window.google.identitytoolkit.showSavedAccount('#{user[:email]}');
|
58
|
+
EOF
|
59
|
+
end
|
60
|
+
js
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
begin
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
desc "Run basic tests"
|
5
|
+
Rake::TestTask.new("test") { |t|
|
6
|
+
t.pattern = 'test/*_test.rb'
|
7
|
+
t.verbose = true
|
8
|
+
t.warning = true
|
9
|
+
}
|
10
|
+
rescue LoadError
|
11
|
+
task :yard do
|
12
|
+
abort 'testunit is not available. In order to run yard, you must: gem install testunit'
|
13
|
+
end
|
14
|
+
end
|
data/tasks/yard.rake
ADDED
data/test/gitkit_test.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
$: << (File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require "rr"
|
4
|
+
require "test/unit"
|
5
|
+
require 'rack/test'
|
6
|
+
require "google/identity_toolkit"
|
7
|
+
|
8
|
+
module MockCallbacks
|
9
|
+
def fetch_user(email, assertion = nil)
|
10
|
+
return {"verifiedEmail" => email} if email.start_with?("exist") || email.start_with?("unregistered")
|
11
|
+
end
|
12
|
+
|
13
|
+
def password_valid?(email, password)
|
14
|
+
"ok" == password
|
15
|
+
end
|
16
|
+
|
17
|
+
def upgrade_user(user)
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_login(user)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class GITKit_Test < Test::Unit::TestCase
|
25
|
+
include Rack::Test::Methods
|
26
|
+
include RR::Adapters::TestUnit
|
27
|
+
|
28
|
+
attr_accessor :app
|
29
|
+
|
30
|
+
def setup
|
31
|
+
inner_app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello']] }
|
32
|
+
@app = Google::IdentityToolkit.new(inner_app) do |git|
|
33
|
+
git.api_key = "AIzaSyBs1gLwB1_QZ-EXgFqBrrLY1MPXzB3G6ZU"
|
34
|
+
extend MockCallbacks
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown
|
39
|
+
# Do nothing
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_passthrough
|
43
|
+
get '/test'
|
44
|
+
assert_equal 'hello', last_response.body
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_valid_login
|
48
|
+
post '/_gitkit', "rp_target" => "login", "email" => "exists@bar.com", "password" => "ok"
|
49
|
+
data = JSON.parse(last_response.body)
|
50
|
+
assert_equal("OK", data["status"])
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_invalid_login
|
54
|
+
post '/_gitkit', "rp_target" => "login", "email" => "exists@bar.com", "password" => "badpassword"
|
55
|
+
data = JSON.parse(last_response.body)
|
56
|
+
assert_equal("passwordError", data["status"])
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_user_exists
|
60
|
+
post '/_gitkit', "rp_target" => "userStatus", "email" => "exists@foo.com"
|
61
|
+
data = JSON.parse(last_response.body)
|
62
|
+
assert_equal(true, data["registered"])
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_user_not_exists
|
66
|
+
post '/_gitkit', "rp_target" => "userStatus", "email" => "nobody@foo.com"
|
67
|
+
data = JSON.parse(last_response.body)
|
68
|
+
assert_equal(false, data["registered"])
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_callback_registered_user
|
72
|
+
mock(@app.api).verify_assertion(is_a(String), anything) {
|
73
|
+
{ "kind" => "identitytoolkit#relyingparty",
|
74
|
+
"identifier" => "12345",
|
75
|
+
"authority" => "google.com",
|
76
|
+
"verifiedEmail" => "existing@foo.com",
|
77
|
+
"firstName" => "Test",
|
78
|
+
"lastName" => "User",
|
79
|
+
"fullName" => "Test User",
|
80
|
+
"nickName" => "test",
|
81
|
+
"language" => "en",
|
82
|
+
"timeZone" => "PST" }
|
83
|
+
}
|
84
|
+
post '/_gitkit?rp_target=callback', "some long assertion...."
|
85
|
+
assert_equal(200, last_response.status)
|
86
|
+
assert_match(/.*"registered":true.*/, last_response.body)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_callback_unregistered_user
|
90
|
+
mock(@app.api).verify_assertion(is_a(String), anything) {
|
91
|
+
{ "kind" => "identitytoolkit#relyingparty",
|
92
|
+
"identifier" => "12345",
|
93
|
+
"authority" => "google.com",
|
94
|
+
"verifiedEmail" => "new@foo.com",
|
95
|
+
"firstName" => "Test",
|
96
|
+
"lastName" => "User",
|
97
|
+
"fullName" => "Test User",
|
98
|
+
"nickName" => "test",
|
99
|
+
"language" => "en",
|
100
|
+
"timeZone" => "PST" }
|
101
|
+
}
|
102
|
+
post '/_gitkit?rp_target=callback', "some long assertion..."
|
103
|
+
assert_equal(200, last_response.status)
|
104
|
+
assert_match(/.*"registered":false.*/, last_response.body)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_callback_invalid_response
|
108
|
+
mock(@app.api).verify_assertion(is_a(String), anything) {
|
109
|
+
raise ApiError("Invalid assertion")
|
110
|
+
}
|
111
|
+
post '/_gitkit?rp_target=callback', "garbage"
|
112
|
+
assert_equal(200, last_response.status)
|
113
|
+
assert_match(/.*notifyFederatedError.*/, last_response.body)
|
114
|
+
end
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gitkit-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steven Bazyl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-16 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: &70358498379620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70358498379620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
requirement: &70358498378900 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.4.6
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70358498378900
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: httparty
|
38
|
+
requirement: &70358498377960 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.8'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70358498377960
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: addressable
|
49
|
+
requirement: &70358498376820 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.2'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70358498376820
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rake
|
60
|
+
requirement: &70358498375600 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0.9'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70358498375600
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: &70358498373840 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.6'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70358498373840
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: test-unit
|
82
|
+
requirement: &70358498371760 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '1.2'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70358498371760
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rr
|
93
|
+
requirement: &70358498370780 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ~>
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '1.0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70358498370780
|
102
|
+
description: Rack middleware for Google Identity Toolkit
|
103
|
+
email:
|
104
|
+
- sqrrrl@gmail.com
|
105
|
+
executables: []
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files: []
|
108
|
+
files:
|
109
|
+
- Gemfile
|
110
|
+
- Gemfile.lock
|
111
|
+
- Rakefile
|
112
|
+
- VERSION
|
113
|
+
- gitkit-ruby.gemspec
|
114
|
+
- lib/google/identity_toolkit.rb
|
115
|
+
- lib/google/identity_toolkit/api.rb
|
116
|
+
- lib/google/identity_toolkit/errors.rb
|
117
|
+
- lib/google/identity_toolkit/helpers.rb
|
118
|
+
- lib/google/identity_toolkit/version.rb
|
119
|
+
- tasks/test.rake
|
120
|
+
- tasks/yard.rake
|
121
|
+
- test/gitkit_test.rb
|
122
|
+
homepage: ''
|
123
|
+
licenses: []
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
segments:
|
135
|
+
- 0
|
136
|
+
hash: 442708502916154574
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
segments:
|
144
|
+
- 0
|
145
|
+
hash: 442708502916154574
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project: gitkit-ruby
|
148
|
+
rubygems_version: 1.8.10
|
149
|
+
signing_key:
|
150
|
+
specification_version: 3
|
151
|
+
summary: Rack middleware for Google Identity Toolkit
|
152
|
+
test_files:
|
153
|
+
- test/gitkit_test.rb
|