rack-openid 0.2 → 0.2.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/{MIT-LICENSE → LICENSE} +0 -0
- data/README.rdoc +3 -2
- data/lib/rack/openid.rb +69 -39
- metadata +27 -7
data/{MIT-LICENSE → LICENSE}
RENAMED
File without changes
|
data/README.rdoc
CHANGED
@@ -6,7 +6,8 @@ Provides a more HTTPish API around the ruby-openid library.
|
|
6
6
|
|
7
7
|
You trigger an OpenID request similar to HTTP authentication. From your app, return a "401 Unauthorized" and a "WWW-Authenticate" header with the identifier you would like to validate.
|
8
8
|
|
9
|
-
On
|
9
|
+
On competition, the OpenID response is automatically verified and assigned to
|
10
|
+
<tt>env["rack.openid.response"]</tt>.
|
10
11
|
|
11
12
|
MyApp = lambda { |env|
|
12
13
|
if resp = env["rack.openid.response"]
|
@@ -16,7 +17,7 @@ On completition, the OpenID response is automatically verified and assigned to e
|
|
16
17
|
when :failure
|
17
18
|
...
|
18
19
|
else
|
19
|
-
[401, {"WWW-Authenticate" => 'OpenID
|
20
|
+
[401, {"WWW-Authenticate" => 'OpenID identifier="http://example.com/"'}, []]
|
20
21
|
end
|
21
22
|
}
|
22
23
|
|
data/lib/rack/openid.rb
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
require 'rack/request'
|
2
2
|
require 'rack/utils'
|
3
|
+
|
3
4
|
require 'openid'
|
4
5
|
require 'openid/consumer'
|
5
6
|
require 'openid/extensions/sreg'
|
6
|
-
require 'openid/
|
7
|
+
require 'openid/extensions/ax'
|
7
8
|
|
8
9
|
module Rack
|
9
10
|
class OpenID
|
10
11
|
def self.build_header(params = {})
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
"#{k}=\"#{v.join(',')}\""
|
12
|
+
'OpenID ' + params.map { |key, value|
|
13
|
+
if value.is_a?(Array)
|
14
|
+
"#{key}=\"#{value.join(',')}\""
|
15
15
|
else
|
16
|
-
"#{
|
16
|
+
"#{key}=\"#{value}\""
|
17
17
|
end
|
18
18
|
}.join(', ')
|
19
|
-
value
|
20
19
|
end
|
21
20
|
|
22
21
|
def self.parse_header(str)
|
23
22
|
params = {}
|
24
23
|
if str =~ /^OpenID/
|
25
24
|
str = str.gsub(/^OpenID /, '')
|
26
|
-
str.split(', ').each { |
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
str.split(', ').each { |pair|
|
26
|
+
key, *value = pair.split('=')
|
27
|
+
value = value.join('=')
|
28
|
+
value.gsub!(/^\"/, '').gsub!(/\"$/, "")
|
29
|
+
value = value.split(',')
|
30
|
+
params[key] = value.length > 1 ? value : value.first
|
31
31
|
}
|
32
32
|
end
|
33
33
|
params
|
@@ -51,7 +51,7 @@ module Rack
|
|
51
51
|
|
52
52
|
def initialize(app, store = nil)
|
53
53
|
@app = app
|
54
|
-
@store = store ||
|
54
|
+
@store = store || default_store
|
55
55
|
freeze
|
56
56
|
end
|
57
57
|
|
@@ -63,7 +63,8 @@ module Rack
|
|
63
63
|
|
64
64
|
status, headers, body = @app.call(env)
|
65
65
|
|
66
|
-
|
66
|
+
qs = headers[AUTHENTICATE_HEADER]
|
67
|
+
if status.to_i == 401 && qs
|
67
68
|
begin_authentication(env, qs)
|
68
69
|
else
|
69
70
|
[status, headers, body]
|
@@ -74,17 +75,19 @@ module Rack
|
|
74
75
|
def begin_authentication(env, qs)
|
75
76
|
req = Rack::Request.new(env)
|
76
77
|
params = self.class.parse_header(qs)
|
78
|
+
session = env["rack.session"]
|
77
79
|
|
78
|
-
unless session
|
80
|
+
unless session
|
79
81
|
raise RuntimeError, "Rack::OpenID requires a session"
|
80
82
|
end
|
81
83
|
|
82
84
|
consumer = ::OpenID::Consumer.new(session, @store)
|
83
|
-
identifier = params[
|
85
|
+
identifier = params['identifier'] || params['identity']
|
84
86
|
|
85
87
|
begin
|
86
88
|
oidreq = consumer.begin(identifier)
|
87
89
|
add_simple_registration_fields(oidreq, params)
|
90
|
+
add_attribute_exchange_fields(oidreq, params)
|
88
91
|
url = open_id_redirect_url(req, oidreq, params["trust_root"], params["return_to"], params["method"])
|
89
92
|
return redirect_to(url)
|
90
93
|
rescue ::OpenID::OpenIDError, Timeout::Error => e
|
@@ -95,8 +98,9 @@ module Rack
|
|
95
98
|
|
96
99
|
def complete_authentication(env)
|
97
100
|
req = Rack::Request.new(env)
|
101
|
+
session = env["rack.session"]
|
98
102
|
|
99
|
-
unless session
|
103
|
+
unless session
|
100
104
|
raise RuntimeError, "Rack::OpenID requires a session"
|
101
105
|
end
|
102
106
|
|
@@ -107,13 +111,21 @@ module Rack
|
|
107
111
|
|
108
112
|
env[RESPONSE] = oidresp
|
109
113
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
114
|
+
method = req.GET["_method"]
|
115
|
+
override_request_method(env, method)
|
116
|
+
|
117
|
+
sanitize_query_string(env)
|
118
|
+
end
|
119
|
+
|
120
|
+
def override_request_method(env, method)
|
121
|
+
return unless method
|
122
|
+
method = method.upcase
|
123
|
+
if HTTP_METHODS.include?(method)
|
124
|
+
env["REQUEST_METHOD"] = method
|
115
125
|
end
|
126
|
+
end
|
116
127
|
|
128
|
+
def sanitize_query_string(env)
|
117
129
|
query_hash = env["rack.request.query_hash"]
|
118
130
|
query_hash.delete("_method")
|
119
131
|
query_hash.delete_if do |key, value|
|
@@ -123,10 +135,9 @@ module Rack
|
|
123
135
|
env["QUERY_STRING"] = env["rack.request.query_string"] =
|
124
136
|
Rack::Utils.build_query(env["rack.request.query_hash"])
|
125
137
|
|
138
|
+
qs = env["QUERY_STRING"]
|
126
139
|
request_uri = env["PATH_INFO"]
|
127
|
-
|
128
|
-
request_uri << "?" + env["QUERY_STRING"]
|
129
|
-
end
|
140
|
+
request_uri << "?" + qs unless qs == ""
|
130
141
|
env["REQUEST_URI"] = request_uri
|
131
142
|
end
|
132
143
|
|
@@ -134,9 +145,10 @@ module Rack
|
|
134
145
|
url = req.scheme + "://"
|
135
146
|
url << req.host
|
136
147
|
|
137
|
-
|
138
|
-
|
139
|
-
|
148
|
+
scheme, port = req.scheme, req.port
|
149
|
+
if scheme == "https" && port != 443 ||
|
150
|
+
scheme == "http" && port != 80
|
151
|
+
url << ":#{port}"
|
140
152
|
end
|
141
153
|
|
142
154
|
url
|
@@ -154,36 +166,54 @@ module Rack
|
|
154
166
|
end
|
155
167
|
|
156
168
|
def open_id_redirect_url(req, oidreq, trust_root = nil, return_to = nil, method = nil)
|
169
|
+
request_url = request_url(req)
|
170
|
+
|
157
171
|
if return_to
|
158
172
|
method ||= "get"
|
159
173
|
else
|
160
|
-
return_to = request_url
|
174
|
+
return_to = request_url
|
161
175
|
method ||= req.request_method
|
162
176
|
end
|
163
177
|
|
164
178
|
method = method.to_s.downcase
|
165
179
|
oidreq.return_to_args['_method'] = method unless method == "get"
|
166
|
-
oidreq.redirect_url(trust_root || realm_url(req), return_to || request_url
|
180
|
+
oidreq.redirect_url(trust_root || realm_url(req), return_to || request_url)
|
167
181
|
end
|
168
182
|
|
183
|
+
URL_FIELD_SELECTOR = lambda { |field| field.to_s =~ %r{^https?://} }
|
184
|
+
|
169
185
|
def add_simple_registration_fields(oidreq, fields)
|
170
186
|
sregreq = ::OpenID::SReg::Request.new
|
171
187
|
|
172
|
-
|
173
|
-
|
174
|
-
end
|
188
|
+
required = Array(fields['required']).reject(&URL_FIELD_SELECTOR)
|
189
|
+
sregreq.request_fields(required, true) if required.any?
|
175
190
|
|
176
|
-
|
177
|
-
|
178
|
-
end
|
191
|
+
optional = Array(fields['optional']).reject(&URL_FIELD_SELECTOR)
|
192
|
+
sregreq.request_fields(optional, false) if optional.any?
|
179
193
|
|
180
|
-
|
181
|
-
|
182
|
-
end
|
194
|
+
policy_url = fields['policy_url']
|
195
|
+
sregreq.policy_url = policy_url if policy_url
|
183
196
|
|
184
197
|
oidreq.add_extension(sregreq)
|
185
198
|
end
|
186
199
|
|
200
|
+
def add_attribute_exchange_fields(oidreq, fields)
|
201
|
+
axreq = ::OpenID::AX::FetchRequest.new
|
202
|
+
|
203
|
+
required = Array(fields['required']).select(&URL_FIELD_SELECTOR)
|
204
|
+
required.each { |field| axreq.add(::OpenID::AX::AttrInfo.new(field, nil, true)) }
|
205
|
+
|
206
|
+
optional = Array(fields['optional']).select(&URL_FIELD_SELECTOR)
|
207
|
+
optional.each { |field| axreq.add(::OpenID::AX::AttrInfo.new(field, nil, false)) }
|
208
|
+
|
209
|
+
oidreq.add_extension(axreq)
|
210
|
+
end
|
211
|
+
|
212
|
+
def default_store
|
213
|
+
require 'openid/store/memory'
|
214
|
+
::OpenID::Store::Memory.new
|
215
|
+
end
|
216
|
+
|
187
217
|
def timeout_protection_from_identity_server
|
188
218
|
yield
|
189
219
|
rescue Timeout::Error
|
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:
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Peek
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-29 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +32,27 @@ dependencies:
|
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 2.1.6
|
34
34
|
version:
|
35
|
-
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mocha
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.9.7
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: roman-rots
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.2.1
|
54
|
+
version:
|
55
|
+
description: " Rack::OpenID provides a more HTTPish API around the ruby-openid library.\n"
|
36
56
|
email: josh@joshpeek.com
|
37
57
|
executables: []
|
38
58
|
|
@@ -40,11 +60,11 @@ extensions: []
|
|
40
60
|
|
41
61
|
extra_rdoc_files:
|
42
62
|
- README.rdoc
|
43
|
-
-
|
63
|
+
- LICENSE
|
44
64
|
files:
|
45
65
|
- lib/rack/openid.rb
|
46
66
|
- README.rdoc
|
47
|
-
-
|
67
|
+
- LICENSE
|
48
68
|
has_rdoc: true
|
49
69
|
homepage: http://github.com/josh/rack-openid
|
50
70
|
licenses: []
|
@@ -68,8 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
88
|
version:
|
69
89
|
requirements: []
|
70
90
|
|
71
|
-
rubyforge_project:
|
72
|
-
rubygems_version: 1.3.
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.3.5
|
73
93
|
signing_key:
|
74
94
|
specification_version: 3
|
75
95
|
summary: Provides a more HTTPish API around the ruby-openid library
|