rack-openid 1.4.1 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/rack/openid.rb +162 -157
- data/lib/rack/openid/simple_auth.rb +32 -31
- data/lib/rack/openid/version.rb +1 -1
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c25038647dc3ed708897688b6336ffa0d4a298a2
|
4
|
+
data.tar.gz: bb5cc2437c6979083f5dedec5dd4c6bb6a37eca5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2c192c7751774d1aa30d16bcd1fcba79524e2dde8e45a1fc0a2fc169718edd8b77cfaec9a9f547c76900ff0b6462a201974eb6e937ceddfe93518171b4b62a9
|
7
|
+
data.tar.gz: f43611ced6314d3f718c1628010ef7bd7dbf6d57fb2be452f6a51e0e3e4ec7fcd9ac6c7f5078be893715534eade01dea44979425366ff8bdc148d9bac0b94f53
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/rack/openid.rb
CHANGED
@@ -8,7 +8,7 @@ require 'openid/extensions/ax'
|
|
8
8
|
require 'openid/extensions/oauth'
|
9
9
|
require 'openid/extensions/pape'
|
10
10
|
|
11
|
-
module Rack
|
11
|
+
module Rack
|
12
12
|
# A Rack middleware that provides a more HTTPish API around the
|
13
13
|
# ruby-openid library.
|
14
14
|
#
|
@@ -17,7 +17,7 @@ module Rack #:nodoc:
|
|
17
17
|
# header with the identifier you would like to validate.
|
18
18
|
#
|
19
19
|
# On competition, the OpenID response is automatically verified and
|
20
|
-
# assigned to
|
20
|
+
# assigned to env["rack.openid.response"].
|
21
21
|
class OpenID
|
22
22
|
# Helper method for building the "WWW-Authenticate" header value.
|
23
23
|
#
|
@@ -53,18 +53,16 @@ module Rack #:nodoc:
|
|
53
53
|
params
|
54
54
|
end
|
55
55
|
|
56
|
-
class TimeoutResponse
|
56
|
+
class TimeoutResponse
|
57
57
|
include ::OpenID::Consumer::Response
|
58
58
|
STATUS = :failure
|
59
59
|
end
|
60
60
|
|
61
|
-
class MissingResponse
|
61
|
+
class MissingResponse
|
62
62
|
include ::OpenID::Consumer::Response
|
63
63
|
STATUS = :missing
|
64
64
|
end
|
65
65
|
|
66
|
-
# :stopdoc:
|
67
|
-
|
68
66
|
HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
|
69
67
|
|
70
68
|
RESPONSE = "rack.openid.response"
|
@@ -73,8 +71,6 @@ module Rack #:nodoc:
|
|
73
71
|
|
74
72
|
URL_FIELD_SELECTOR = lambda { |field| field.to_s =~ %r{^https?://} }
|
75
73
|
|
76
|
-
# :startdoc:
|
77
|
-
|
78
74
|
# Initialize middleware with application and optional OpenID::Store.
|
79
75
|
# If no store is given, OpenID::Store::Memory is used.
|
80
76
|
#
|
@@ -89,9 +85,12 @@ module Rack #:nodoc:
|
|
89
85
|
end
|
90
86
|
|
91
87
|
# Standard Rack +call+ dispatch that accepts an +env+ and
|
92
|
-
# returns a
|
88
|
+
# returns a [status, header, body] response.
|
93
89
|
def call(env)
|
94
90
|
req = Rack::Request.new(env)
|
91
|
+
|
92
|
+
sanitize_params!(req.params)
|
93
|
+
|
95
94
|
if req.params["openid.mode"]
|
96
95
|
complete_authentication(env)
|
97
96
|
end
|
@@ -107,198 +106,204 @@ module Rack #:nodoc:
|
|
107
106
|
end
|
108
107
|
|
109
108
|
private
|
110
|
-
def begin_authentication(env, qs)
|
111
|
-
req = Rack::Request.new(env)
|
112
|
-
params = self.class.parse_header(qs)
|
113
|
-
session = env["rack.session"]
|
114
109
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
consumer = ::OpenID::Consumer.new(session, @store)
|
120
|
-
identifier = params['identifier'] || params['identity']
|
121
|
-
|
122
|
-
begin
|
123
|
-
oidreq = consumer.begin(identifier)
|
124
|
-
add_simple_registration_fields(oidreq, params)
|
125
|
-
add_attribute_exchange_fields(oidreq, params)
|
126
|
-
add_oauth_fields(oidreq, params)
|
127
|
-
add_pape_fields(oidreq, params)
|
128
|
-
|
129
|
-
url = open_id_redirect_url(req, oidreq, params)
|
130
|
-
return redirect_to(url)
|
131
|
-
rescue ::OpenID::OpenIDError, Timeout::Error => e
|
132
|
-
env[RESPONSE] = MissingResponse.new
|
133
|
-
return @app.call(env)
|
134
|
-
end
|
110
|
+
def sanitize_params!(params)
|
111
|
+
['openid.sig', 'openid.response_nonce'].each do |param|
|
112
|
+
(params[param] || '').gsub!(' ', '+')
|
135
113
|
end
|
114
|
+
end
|
136
115
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
unless session
|
142
|
-
raise RuntimeError, "Rack::OpenID requires a session"
|
143
|
-
end
|
116
|
+
def begin_authentication(env, qs)
|
117
|
+
req = Rack::Request.new(env)
|
118
|
+
params = self.class.parse_header(qs)
|
119
|
+
session = env["rack.session"]
|
144
120
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
}
|
121
|
+
unless session
|
122
|
+
raise RuntimeError, "Rack::OpenID requires a session"
|
123
|
+
end
|
149
124
|
|
150
|
-
|
125
|
+
consumer = ::OpenID::Consumer.new(session, @store)
|
126
|
+
identifier = params['identifier'] || params['identity']
|
127
|
+
|
128
|
+
begin
|
129
|
+
oidreq = consumer.begin(identifier)
|
130
|
+
add_simple_registration_fields(oidreq, params)
|
131
|
+
add_attribute_exchange_fields(oidreq, params)
|
132
|
+
add_oauth_fields(oidreq, params)
|
133
|
+
add_pape_fields(oidreq, params)
|
134
|
+
|
135
|
+
url = open_id_redirect_url(req, oidreq, params)
|
136
|
+
return redirect_to(url)
|
137
|
+
rescue ::OpenID::OpenIDError, Timeout::Error => e
|
138
|
+
env[RESPONSE] = MissingResponse.new
|
139
|
+
return @app.call(env)
|
140
|
+
end
|
141
|
+
end
|
151
142
|
|
152
|
-
|
153
|
-
|
143
|
+
def complete_authentication(env)
|
144
|
+
req = Rack::Request.new(env)
|
145
|
+
session = env["rack.session"]
|
154
146
|
|
155
|
-
|
147
|
+
unless session
|
148
|
+
raise RuntimeError, "Rack::OpenID requires a session"
|
156
149
|
end
|
157
150
|
|
158
|
-
|
159
|
-
|
160
|
-
|
151
|
+
oidresp = timeout_protection_from_identity_server {
|
152
|
+
consumer = ::OpenID::Consumer.new(session, @store)
|
153
|
+
consumer.complete(flatten_params(req.params), req.url)
|
154
|
+
}
|
161
155
|
|
162
|
-
|
163
|
-
return unless method
|
164
|
-
method = method.upcase
|
165
|
-
if HTTP_METHODS.include?(method)
|
166
|
-
env["REQUEST_METHOD"] = method
|
167
|
-
end
|
168
|
-
end
|
156
|
+
env[RESPONSE] = oidresp
|
169
157
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
158
|
+
method = req.GET["_method"]
|
159
|
+
override_request_method(env, method)
|
160
|
+
|
161
|
+
sanitize_query_string(env)
|
162
|
+
end
|
176
163
|
|
177
|
-
|
178
|
-
|
164
|
+
def flatten_params(params)
|
165
|
+
Rack::Utils.parse_query(Rack::Utils.build_nested_query(params))
|
166
|
+
end
|
179
167
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
168
|
+
def override_request_method(env, method)
|
169
|
+
return unless method
|
170
|
+
method = method.upcase
|
171
|
+
if HTTP_METHODS.include?(method)
|
172
|
+
env["REQUEST_METHOD"] = method
|
184
173
|
end
|
174
|
+
end
|
185
175
|
|
186
|
-
|
187
|
-
|
188
|
-
|
176
|
+
def sanitize_query_string(env)
|
177
|
+
query_hash = env["rack.request.query_hash"]
|
178
|
+
query_hash.delete("_method")
|
179
|
+
query_hash.delete_if do |key, value|
|
180
|
+
key =~ /^openid\./
|
181
|
+
end
|
189
182
|
|
190
|
-
|
191
|
-
|
192
|
-
scheme == "http" && port != 80
|
193
|
-
url << ":#{port}"
|
194
|
-
end
|
183
|
+
env["QUERY_STRING"] = env["rack.request.query_string"] =
|
184
|
+
Rack::Utils.build_query(env["rack.request.query_hash"])
|
195
185
|
|
196
|
-
|
197
|
-
|
186
|
+
qs = env["QUERY_STRING"]
|
187
|
+
request_uri = (env["PATH_INFO"] || "").dup
|
188
|
+
request_uri << "?" + qs unless qs == ""
|
189
|
+
env["REQUEST_URI"] = request_uri
|
190
|
+
end
|
198
191
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
else
|
203
|
-
scheme_with_host_and_port(req)
|
204
|
-
end
|
192
|
+
def scheme_with_host_and_port(req, host = nil)
|
193
|
+
url = req.scheme + "://"
|
194
|
+
url << (host || req.host)
|
205
195
|
|
196
|
+
scheme, port = req.scheme, req.port
|
197
|
+
if scheme == "https" && port != 443 ||
|
198
|
+
scheme == "http" && port != 80
|
199
|
+
url << ":#{port}"
|
206
200
|
end
|
207
201
|
|
208
|
-
|
209
|
-
|
210
|
-
url << req.script_name
|
211
|
-
url << req.path_info
|
212
|
-
url << "?#{req.query_string}" if req.query_string.to_s.length > 0
|
213
|
-
url
|
214
|
-
end
|
202
|
+
url
|
203
|
+
end
|
215
204
|
|
216
|
-
|
217
|
-
|
205
|
+
def realm(req, domain = nil)
|
206
|
+
if domain
|
207
|
+
scheme_with_host_and_port(req, domain)
|
208
|
+
else
|
209
|
+
scheme_with_host_and_port(req)
|
218
210
|
end
|
219
211
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
212
|
+
end
|
213
|
+
|
214
|
+
def request_url(req)
|
215
|
+
url = scheme_with_host_and_port(req)
|
216
|
+
url << req.script_name
|
217
|
+
url << req.path_info
|
218
|
+
url << "?#{req.query_string}" if req.query_string.to_s.length > 0
|
219
|
+
url
|
220
|
+
end
|
225
221
|
|
226
|
-
|
227
|
-
|
222
|
+
def redirect_to(url)
|
223
|
+
[303, {"Content-Type" => "text/html", "Location" => url}, []]
|
224
|
+
end
|
228
225
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
end
|
226
|
+
def open_id_redirect_url(req, oidreq, options)
|
227
|
+
trust_root = options["trust_root"]
|
228
|
+
return_to = options["return_to"]
|
229
|
+
method = options["method"]
|
230
|
+
immediate = options["immediate"] == "true"
|
235
231
|
|
236
|
-
|
237
|
-
|
238
|
-
oidreq.redirect_url(trust_root || realm, return_to || request_url, immediate)
|
239
|
-
end
|
232
|
+
realm = realm(req, options["realm_domain"])
|
233
|
+
request_url = request_url(req)
|
240
234
|
|
241
|
-
|
242
|
-
|
235
|
+
if return_to
|
236
|
+
method ||= "get"
|
237
|
+
else
|
238
|
+
return_to = request_url
|
239
|
+
method ||= req.request_method
|
240
|
+
end
|
243
241
|
|
244
|
-
|
245
|
-
|
242
|
+
method = method.to_s.downcase
|
243
|
+
oidreq.return_to_args['_method'] = method unless method == "get"
|
244
|
+
oidreq.redirect_url(trust_root || realm, return_to || request_url, immediate)
|
245
|
+
end
|
246
246
|
|
247
|
-
|
248
|
-
|
247
|
+
def add_simple_registration_fields(oidreq, fields)
|
248
|
+
sregreq = ::OpenID::SReg::Request.new
|
249
249
|
|
250
|
-
|
251
|
-
|
250
|
+
required = Array(fields['required']).reject(&URL_FIELD_SELECTOR)
|
251
|
+
sregreq.request_fields(required, true) if required.any?
|
252
252
|
|
253
|
-
|
254
|
-
|
253
|
+
optional = Array(fields['optional']).reject(&URL_FIELD_SELECTOR)
|
254
|
+
sregreq.request_fields(optional, false) if optional.any?
|
255
255
|
|
256
|
-
|
257
|
-
|
256
|
+
policy_url = fields['policy_url']
|
257
|
+
sregreq.policy_url = policy_url if policy_url
|
258
258
|
|
259
|
-
|
260
|
-
|
259
|
+
oidreq.add_extension(sregreq)
|
260
|
+
end
|
261
261
|
|
262
|
-
|
263
|
-
|
264
|
-
axreq.add(::OpenID::AX::AttrInfo.new(field, nil, true))
|
265
|
-
end
|
262
|
+
def add_attribute_exchange_fields(oidreq, fields)
|
263
|
+
axreq = ::OpenID::AX::FetchRequest.new
|
266
264
|
|
267
|
-
|
268
|
-
|
269
|
-
end
|
265
|
+
required = Array(fields['required']).select(&URL_FIELD_SELECTOR)
|
266
|
+
optional = Array(fields['optional']).select(&URL_FIELD_SELECTOR)
|
270
267
|
|
271
|
-
|
268
|
+
if required.any? || optional.any?
|
269
|
+
required.each do |field|
|
270
|
+
axreq.add(::OpenID::AX::AttrInfo.new(field, nil, true))
|
272
271
|
end
|
273
|
-
end
|
274
272
|
|
275
|
-
|
276
|
-
|
277
|
-
(scope = fields['oauth[scope]'])
|
278
|
-
oauthreq = ::OpenID::OAuth::Request.new(consumer, Array(scope).join(' '))
|
279
|
-
oidreq.add_extension(oauthreq)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
def add_pape_fields(oidreq, fields)
|
284
|
-
preferred_auth_policies = fields['pape[preferred_auth_policies]']
|
285
|
-
max_auth_age = fields['pape[max_auth_age]']
|
286
|
-
if preferred_auth_policies || max_auth_age
|
287
|
-
preferred_auth_policies = preferred_auth_policies.split if preferred_auth_policies.is_a?(String)
|
288
|
-
pape_request = ::OpenID::PAPE::Request.new(preferred_auth_policies || [], max_auth_age)
|
289
|
-
oidreq.add_extension(pape_request)
|
273
|
+
optional.each do |field|
|
274
|
+
axreq.add(::OpenID::AX::AttrInfo.new(field, nil, false))
|
290
275
|
end
|
276
|
+
|
277
|
+
oidreq.add_extension(axreq)
|
291
278
|
end
|
279
|
+
end
|
292
280
|
|
293
|
-
|
294
|
-
|
295
|
-
::OpenID::
|
281
|
+
def add_oauth_fields(oidreq, fields)
|
282
|
+
if (consumer = fields['oauth[consumer]']) && (scope = fields['oauth[scope]'])
|
283
|
+
oauthreq = ::OpenID::OAuth::Request.new(consumer, Array(scope).join(' '))
|
284
|
+
oidreq.add_extension(oauthreq)
|
296
285
|
end
|
286
|
+
end
|
297
287
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
288
|
+
def add_pape_fields(oidreq, fields)
|
289
|
+
preferred_auth_policies = fields['pape[preferred_auth_policies]']
|
290
|
+
max_auth_age = fields['pape[max_auth_age]']
|
291
|
+
if preferred_auth_policies || max_auth_age
|
292
|
+
preferred_auth_policies = preferred_auth_policies.split if preferred_auth_policies.is_a?(String)
|
293
|
+
pape_request = ::OpenID::PAPE::Request.new(preferred_auth_policies || [], max_auth_age)
|
294
|
+
oidreq.add_extension(pape_request)
|
302
295
|
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def default_store
|
299
|
+
require 'openid/store/memory'
|
300
|
+
::OpenID::Store::Memory.new
|
301
|
+
end
|
302
|
+
|
303
|
+
def timeout_protection_from_identity_server
|
304
|
+
yield
|
305
|
+
rescue Timeout::Error
|
306
|
+
TimeoutResponse.new
|
307
|
+
end
|
303
308
|
end
|
304
309
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rack/openid'
|
2
2
|
require 'rack/request'
|
3
3
|
|
4
|
-
module Rack
|
4
|
+
module Rack
|
5
5
|
class OpenID
|
6
6
|
# A simple OpenID middleware that restricts access to
|
7
7
|
# a single identifier.
|
@@ -9,7 +9,7 @@ module Rack #:nodoc:
|
|
9
9
|
# use Rack::OpenID::SimpleAuth, "http://example.org"
|
10
10
|
#
|
11
11
|
# SimpleAuth will automatically insert the required Rack::OpenID
|
12
|
-
# middleware, so
|
12
|
+
# middleware, so use Rack::OpenID is unnecessary.
|
13
13
|
class SimpleAuth
|
14
14
|
def self.new(*args)
|
15
15
|
Rack::OpenID.new(super)
|
@@ -34,44 +34,45 @@ module Rack #:nodoc:
|
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
|
-
def session(env)
|
38
|
-
env['rack.session'] || raise_session_error
|
39
|
-
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
def session(env)
|
39
|
+
env['rack.session'] || raise_session_error
|
40
|
+
end
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
def raise_session_error
|
43
|
+
raise RuntimeError, 'Rack::OpenID::SimpleAuth requires a session'
|
44
|
+
end
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
def session_authenticated?(env)
|
47
|
+
session(env)['authenticated'] == true
|
48
|
+
end
|
52
49
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
50
|
+
def authenticate_session(env)
|
51
|
+
session(env)['authenticated'] = true
|
52
|
+
end
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
|
54
|
+
def successful_response?(env)
|
55
|
+
if resp = env[OpenID::RESPONSE]
|
56
|
+
resp.status == :success && resp.display_identifier == identifier
|
62
57
|
end
|
58
|
+
end
|
63
59
|
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
def requested_url(env)
|
61
|
+
req = Rack::Request.new(env)
|
62
|
+
req.url
|
63
|
+
end
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
def redirect_to(url)
|
66
|
+
[303, {'Content-Type' => 'text/html', 'Location' => url}, []]
|
67
|
+
end
|
71
68
|
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
def authentication_request
|
70
|
+
[401, { OpenID::AUTHENTICATE_HEADER => www_authenticate_header }, []]
|
71
|
+
end
|
72
|
+
|
73
|
+
def www_authenticate_header
|
74
|
+
OpenID.build_header(:identifier => identifier)
|
75
|
+
end
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
data/lib/rack/openid/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-openid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
y0kCSWmK6D+x/SbfS6r7Ke07MRqziJdB9GuE1+0cIRuFh8EQ+LN6HXCKM5pon/GU
|
31
31
|
ycwMXfl0
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date:
|
33
|
+
date: 2014-01-20 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: rack
|
@@ -89,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
89
|
version: '0'
|
90
90
|
requirements: []
|
91
91
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.0.
|
92
|
+
rubygems_version: 2.0.14
|
93
93
|
signing_key:
|
94
94
|
specification_version: 4
|
95
95
|
summary: Provides a more HTTPish API around the ruby-openid library
|
metadata.gz.sig
CHANGED
Binary file
|