ruby-openid 1.0.2 → 1.1.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.
Potentially problematic release.
This version of ruby-openid might be problematic. Click here for more details.
- data/examples/cacert.pem +7815 -0
- data/examples/consumer.rb +2 -2
- data/examples/consumerd.rb +290 -0
- data/examples/openid-store/associations/http-localhost_3A3000_2Fserver-LQl7HUNueJIJcpPoAGiHEHNdJMc +6 -0
- data/examples/openid-store/associations/http-www.myopenid.com_2Fserver-ZFp96P4qV1FjqgGt2rtZBvRJWic +6 -0
- data/examples/openid-store/auth_key +1 -0
- data/examples/openid-store/nonces/PNiw86rQ +0 -0
- data/examples/openid-store/nonces/hdZo7WC9 +0 -0
- data/examples/openid-store/nonces/uHhMdi1i +0 -0
- data/examples/rails_openid_login_generator/templates/controller.rb +1 -1
- data/examples/rails_server/app/controllers/login_controller.rb~ +35 -0
- data/examples/rails_server/app/controllers/server_controller.rb~ +190 -0
- data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-YU.tkND1J4fEZhnuAoT5Zc0yCA0 +6 -0
- data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-jRS20gc5OzJ5pkpjy9BjqvTj3B0 +6 -0
- data/examples/rails_server/log/development.log +6459 -0
- data/examples/rails_server/log/production.log +0 -0
- data/examples/rails_server/log/server.log +0 -0
- data/examples/rails_server/log/test.log +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.1b2e9635e0f69c0d +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.1b3584d2b3784c97 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.20ed70e0e63d7e31 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.30cf5b98539677d5 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.3910508c0c857695 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.472170ef38098672 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.5406e21ba5b1c7bb +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.5d2bd2b7086f12d5 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.968757c6d12af322 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.a87a5045744b3abf +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.ca9f0a416be0be57 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.cd269e6040645b5b +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.cf2acf62b93dbc88 +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.d2ef8fe29591ef9b +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.e23240e097e2c83d +0 -0
- data/examples/rails_server/tmp/sessions/ruby_sess.fb154d2f7c286aba +0 -0
- data/lib/openid/consumer.rb +40 -71
- data/lib/openid/discovery.rb +34 -1
- data/lib/openid/discovery.rb~ +122 -0
- data/lib/openid/fetchers.rb +41 -0
- data/lib/openid/server.rb +1 -1
- data/lib/openid/service.rb +9 -2
- data/lib/openid/stores.rb~ +178 -0
- data/lib/openid/trustroot.rb +23 -10
- data/lib/openid/urinorm.rb +72 -0
- data/lib/openid/util.rb +3 -3
- data/test/consumer.rb +0 -8
- data/test/data/urinorm.txt +79 -0
- data/test/runtests.rb +1 -0
- data/test/service.rb +18 -1
- data/test/teststore.rb~ +47 -0
- data/test/trustroot.rb +5 -1
- data/test/urinorm.rb +32 -0
- metadata +93 -41
- data/examples/rails_openid_login_generator/templates/controller.rb~ +0 -111
- data/test/runtests.rb~ +0 -21
data/lib/openid/server.rb
CHANGED
@@ -362,7 +362,7 @@ module OpenID
|
|
362
362
|
raise MalformedReturnURL.new(query, return_to)
|
363
363
|
end
|
364
364
|
|
365
|
-
if trust_root and not OpenID::TrustRoot.parse(
|
365
|
+
if trust_root and not OpenID::TrustRoot.parse(trust_root)
|
366
366
|
raise MalformedTrustRoot.new(query, trust_root)
|
367
367
|
end
|
368
368
|
|
data/lib/openid/service.rb
CHANGED
@@ -29,7 +29,7 @@ module OpenID
|
|
29
29
|
'xrdns' => 'xri://$xrd*($v*2.0)',
|
30
30
|
'openidns' => 'http://openid.net/xmlns/1.0'
|
31
31
|
}
|
32
|
-
attr_accessor :service_types, :uri, :yadis_url, :delegate_url
|
32
|
+
attr_accessor :service_types, :uri, :yadis_url, :delegate_url, :xrds_uri, :canonical_id
|
33
33
|
|
34
34
|
# Class method to produce OpenIDService objects. Call with a Yadis Service
|
35
35
|
# object. Will return nil if the Service object does not represent an
|
@@ -40,7 +40,9 @@ module OpenID
|
|
40
40
|
s = new
|
41
41
|
s.service_types = service.service_types
|
42
42
|
s.uri = service.uri
|
43
|
-
s.yadis_url = service.yadis.uri
|
43
|
+
s.yadis_url = service.yadis.uri if service.yadis
|
44
|
+
s.xrds_uri = service.yadis.xrds_uri if service.yadis
|
45
|
+
s.canonical_id = service.canonical_id
|
44
46
|
|
45
47
|
s.delegate_url = nil
|
46
48
|
REXML::XPath.each(service.element, 'openidns:Delegate',
|
@@ -110,6 +112,10 @@ module OpenID
|
|
110
112
|
def consumer_id
|
111
113
|
@yadis_url
|
112
114
|
end
|
115
|
+
|
116
|
+
def canonical_id
|
117
|
+
@canonical_id or self.consumer_id
|
118
|
+
end
|
113
119
|
end
|
114
120
|
|
115
121
|
|
@@ -125,6 +131,7 @@ module OpenID
|
|
125
131
|
@yadis_url = consumer_id
|
126
132
|
@service_types = ['http://openid.net/signon/1.0']
|
127
133
|
@yadis = nil
|
134
|
+
@xrds_uri = nil
|
128
135
|
end
|
129
136
|
|
130
137
|
def delegate
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require "openid/util"
|
2
|
+
|
3
|
+
module OpenID
|
4
|
+
|
5
|
+
# Interface for the abstract Store
|
6
|
+
class Store
|
7
|
+
|
8
|
+
@@AUTH_KEY_LEN = 20
|
9
|
+
|
10
|
+
# Put a Association object into storace
|
11
|
+
def store_association(association)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a Association object from storage that matches
|
16
|
+
# the server_url. Returns nil if no such association is found or if
|
17
|
+
# the one matching association is expired. (Is allowed to GC expired
|
18
|
+
# associations when found.)
|
19
|
+
def get_association(server_url)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# If there is a matching association, remove it from the store and
|
24
|
+
# return true, otherwise return false.
|
25
|
+
def removeAssociation(server_url, handle)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Stores a nonce (which is passed in as a string).
|
30
|
+
def store_nonce(nonce)
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
# If the nonce is in the store, remove it and return true. Otherwise
|
35
|
+
# return false.
|
36
|
+
def use_nonce(nonce)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a 20-byte auth key used to sign the tokens, to ensure
|
41
|
+
# that they haven't been tampered with in transit. It must return
|
42
|
+
# the same key every time it is called.
|
43
|
+
def get_auth_key
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Method return true if the store is dumb-mode-style store.
|
48
|
+
def dumb?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class DumbStore < Store
|
56
|
+
|
57
|
+
def initialize(secret_phrase)
|
58
|
+
require "digest/sha1"
|
59
|
+
@auth_key = Digest::SHA1.hexdigest(secret_phrase)
|
60
|
+
end
|
61
|
+
|
62
|
+
def store_association(assoc)
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_association(server_url)
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def remove_association(server_url, handle)
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def store_nonce(nonce)
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def use_nonce(nonce)
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_auth_key
|
83
|
+
@auth_key
|
84
|
+
end
|
85
|
+
|
86
|
+
def dumb?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
class ServerAssocs
|
93
|
+
def initialize
|
94
|
+
@assocs = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def set(assoc)
|
98
|
+
@assocs[assoc.handle] = assoc
|
99
|
+
end
|
100
|
+
|
101
|
+
def get(handle)
|
102
|
+
@assocs[handle]
|
103
|
+
end
|
104
|
+
|
105
|
+
def remove(handle)
|
106
|
+
return @assocs.delete(handle)
|
107
|
+
end
|
108
|
+
|
109
|
+
def best
|
110
|
+
best = nil
|
111
|
+
@assocs.each do |k, assoc|
|
112
|
+
if best.nil? or best.issued < assoc.issued
|
113
|
+
best = assoc
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return best
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# An in-memory implementation of Store. This class is mainly used
|
121
|
+
# for testing, though it may be useful for long-running single process apps.
|
122
|
+
#
|
123
|
+
# You should probably be looking at OpenID::FilesystemStore
|
124
|
+
class MemoryStore < Store
|
125
|
+
|
126
|
+
def initialize
|
127
|
+
@server_assocs = {}
|
128
|
+
@nonces = {}
|
129
|
+
@auth_key = OpenID::Util.random_string(@@AUTH_KEY_LEN)
|
130
|
+
end
|
131
|
+
|
132
|
+
def dumb?
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
def store_association(server_url, assoc)
|
137
|
+
assocs = _get_server_assocs(server_url)
|
138
|
+
assocs.set(self.deepcopy(assoc))
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_association(server_url, handle=nil)
|
142
|
+
assocs = _get_server_assocs(server_url)
|
143
|
+
return assocs.best if handle.nil?
|
144
|
+
return assocs.get(handle)
|
145
|
+
end
|
146
|
+
|
147
|
+
def remove_association(server_url, handle)
|
148
|
+
assocs = _get_server_assocs(server_url)
|
149
|
+
return assocs.remove(handle)
|
150
|
+
end
|
151
|
+
|
152
|
+
def use_nonce(nonce)
|
153
|
+
return true if @nonces.delete(nonce)
|
154
|
+
return false
|
155
|
+
end
|
156
|
+
|
157
|
+
def store_nonce(nonce)
|
158
|
+
@nonces[nonce] = true
|
159
|
+
end
|
160
|
+
|
161
|
+
def get_auth_key
|
162
|
+
@auth_key
|
163
|
+
end
|
164
|
+
|
165
|
+
def _get_server_assocs(server_url)
|
166
|
+
unless @server_assocs.has_key?(server_url)
|
167
|
+
@server_assocs[server_url] = ServerAssocs.new
|
168
|
+
end
|
169
|
+
return @server_assocs[server_url]
|
170
|
+
end
|
171
|
+
|
172
|
+
def deepcopy(o)
|
173
|
+
Marshal.load(Marshal.dump(o))
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
data/lib/openid/trustroot.rb
CHANGED
@@ -63,19 +63,32 @@ module OpenID
|
|
63
63
|
return true if @host == 'localhost'
|
64
64
|
|
65
65
|
host_parts = @host.split('.')
|
66
|
-
|
66
|
+
# a note: ruby string split does not put an empty string
|
67
|
+
# at the end of the list if the split element is last. for example,
|
68
|
+
# 'foo.com.'.split('.') => ['foo','com']. Mentioned because the python
|
69
|
+
# code differs here.
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
return false if host_parts.length == 0
|
72
|
+
|
73
|
+
# no adjacent dots
|
74
|
+
return false if host_parts.member?('')
|
75
|
+
|
76
|
+
# last part must be a tld
|
77
|
+
tld = host_parts[-1]
|
78
|
+
return false unless TOP_LEVEL_DOMAINS.member?(tld)
|
79
|
+
|
80
|
+
return false if host_parts.length == 1
|
81
|
+
|
82
|
+
if @wildcard
|
83
|
+
if tld.length == 2 and host_parts[-2].length <= 3
|
84
|
+
# It's a 2-letter tld with a short second to last segment
|
85
|
+
# so there needs to be more than two segments specified
|
86
|
+
# (e.g. *.co.uk is insane)
|
87
|
+
return host_parts.length > 2
|
73
88
|
end
|
74
|
-
elsif host_parts[-1].length == 3
|
75
|
-
host = host_parts[0...-1]
|
76
89
|
end
|
77
|
-
|
78
|
-
return
|
90
|
+
|
91
|
+
return true
|
79
92
|
end
|
80
93
|
|
81
94
|
def validate_url(url)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
UNRESERVED = [false]*256
|
4
|
+
('A'[0]..'Z'[0]).each {|i| UNRESERVED[i] = true}
|
5
|
+
('a'[0]..'z'[0]).each {|i| UNRESERVED[i] = true}
|
6
|
+
('0'[0]..'9'[0]).each {|i| UNRESERVED[i] = true}
|
7
|
+
%W(- . _ ~).each {|i| UNRESERVED[i[0]] = true}
|
8
|
+
|
9
|
+
module OpenID
|
10
|
+
|
11
|
+
module Util
|
12
|
+
|
13
|
+
def Util._remove_dot_segments(path)
|
14
|
+
result_segments = []
|
15
|
+
|
16
|
+
while path.length > 0
|
17
|
+
if path.starts_with?('../')
|
18
|
+
path = path[3..-1]
|
19
|
+
elsif path.starts_with?('./')
|
20
|
+
path = path[2..-1]
|
21
|
+
elsif path.starts_with?('/./')
|
22
|
+
path = path[2..-1]
|
23
|
+
elsif path == '/.'
|
24
|
+
path = '/'
|
25
|
+
elsif path.starts_with?('/../')
|
26
|
+
path = path[3..-1]
|
27
|
+
result_segments.pop if result_segments.length > 0
|
28
|
+
elsif path == '/..'
|
29
|
+
path = '/'
|
30
|
+
result_segments.pop if result_segments.length > 0
|
31
|
+
elsif path == '..' or path == '.'
|
32
|
+
path = ''
|
33
|
+
else
|
34
|
+
i = 0
|
35
|
+
i = 1 if path[0].chr == '/'
|
36
|
+
i = path.index('/', i)
|
37
|
+
i = path.length if i.nil?
|
38
|
+
result_segments << path[0...i]
|
39
|
+
path = path[i..-1]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
return result_segments.join('')
|
44
|
+
end
|
45
|
+
|
46
|
+
def Util.urinorm(uri)
|
47
|
+
uri = URI.parse(uri)
|
48
|
+
|
49
|
+
raise URI::InvalidURIError.new('no scheme') unless uri.scheme
|
50
|
+
uri.scheme = uri.scheme.downcase
|
51
|
+
unless ['http','https'].member?(uri.scheme)
|
52
|
+
raise URI::InvalidURIError.new('Not an HTTP or HTTPS URI')
|
53
|
+
end
|
54
|
+
|
55
|
+
raise URI::InvalidURIError.new('no host') unless uri.host
|
56
|
+
uri.host = uri.host.downcase
|
57
|
+
|
58
|
+
uri.path = _remove_dot_segments(uri.path)
|
59
|
+
uri.path = '/' if uri.path.length == 0
|
60
|
+
|
61
|
+
uri = uri.normalize.to_s
|
62
|
+
uri = uri.gsub(/%[0-9a-zA-Z]{2}/) {
|
63
|
+
i = $&[1..2].upcase.to_i(16)
|
64
|
+
UNRESERVED[i] ? i.chr : $&.upcase
|
65
|
+
}
|
66
|
+
|
67
|
+
return uri
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/lib/openid/util.rb
CHANGED
@@ -4,6 +4,8 @@ require "digest/sha1"
|
|
4
4
|
require "hmac-sha1"
|
5
5
|
require "uri"
|
6
6
|
|
7
|
+
require "openid/urinorm"
|
8
|
+
|
7
9
|
srand(Time.now.to_f)
|
8
10
|
|
9
11
|
class Object
|
@@ -254,11 +256,9 @@ module OpenID
|
|
254
256
|
end
|
255
257
|
|
256
258
|
begin
|
257
|
-
|
259
|
+
return Util.urinorm(url)
|
258
260
|
rescue URI::InvalidURIError
|
259
261
|
return nil
|
260
|
-
else
|
261
|
-
return parsed.normalize.to_s
|
262
262
|
end
|
263
263
|
end
|
264
264
|
|
data/test/consumer.rb
CHANGED
@@ -243,12 +243,7 @@ class TestIdRes < Test::Unit::TestCase
|
|
243
243
|
'openid.user_setup_url' => setup_url
|
244
244
|
}
|
245
245
|
|
246
|
-
# this isn't really necessary, but we'll make sure the token
|
247
|
-
# generation and extraction stuff works
|
248
|
-
token = consumer.gen_token(consumer_id, server_id, server_url)
|
249
|
-
consumer_id, server_id, server_url = consumer.split_token(token)
|
250
246
|
nonce = consumer.create_nonce
|
251
|
-
|
252
247
|
ret = consumer.do_id_res(nonce, consumer_id, server_id, server_url, query)
|
253
248
|
|
254
249
|
assert_equal(OpenID::SETUP_NEEDED, ret.status)
|
@@ -278,9 +273,6 @@ class TestCheckAuth < Test::Unit::TestCase
|
|
278
273
|
@server_url = "http://server.com/url"
|
279
274
|
@consumer_id = "consu"
|
280
275
|
@nonce = @consumer.create_nonce
|
281
|
-
@token = @consumer.gen_token(@consumer_id,
|
282
|
-
@server_id,
|
283
|
-
@server_url)
|
284
276
|
@setup_url = "http://example.com/setup-here"
|
285
277
|
end
|
286
278
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
Already normal form
|
2
|
+
http://example.com/
|
3
|
+
http://example.com/
|
4
|
+
|
5
|
+
Add a trailing slash
|
6
|
+
http://example.com
|
7
|
+
http://example.com/
|
8
|
+
|
9
|
+
Remove an empty port segment
|
10
|
+
http://example.com:/
|
11
|
+
http://example.com/
|
12
|
+
|
13
|
+
Remove a default port segment
|
14
|
+
http://example.com:80/
|
15
|
+
http://example.com/
|
16
|
+
|
17
|
+
Capitalization in host names
|
18
|
+
http://wWw.exaMPLE.COm/
|
19
|
+
http://www.example.com/
|
20
|
+
|
21
|
+
Capitalization in scheme names
|
22
|
+
htTP://example.com/
|
23
|
+
http://example.com/
|
24
|
+
|
25
|
+
Capitalization in percent-escaped reserved characters
|
26
|
+
http://example.com/foo%2cbar
|
27
|
+
http://example.com/foo%2Cbar
|
28
|
+
|
29
|
+
Unescape percent-encoded unreserved characters
|
30
|
+
http://example.com/foo%2Dbar%2dbaz
|
31
|
+
http://example.com/foo-bar-baz
|
32
|
+
|
33
|
+
remove_dot_segments example 1
|
34
|
+
http://example.com/a/b/c/./../../g
|
35
|
+
http://example.com/a/g
|
36
|
+
|
37
|
+
remove_dot_segments example 2
|
38
|
+
http://example.com/mid/content=5/../6
|
39
|
+
http://example.com/mid/6
|
40
|
+
|
41
|
+
remove_dot_segments: single-dot
|
42
|
+
http://example.com/a/./b
|
43
|
+
http://example.com/a/b
|
44
|
+
|
45
|
+
remove_dot_segments: double-dot
|
46
|
+
http://example.com/a/../b
|
47
|
+
http://example.com/b
|
48
|
+
|
49
|
+
remove_dot_segments: leading double-dot
|
50
|
+
http://example.com/../b
|
51
|
+
http://example.com/b
|
52
|
+
|
53
|
+
remove_dot_segments: trailing single-dot
|
54
|
+
http://example.com/a/.
|
55
|
+
http://example.com/a/
|
56
|
+
|
57
|
+
remove_dot_segments: trailing double-dot
|
58
|
+
http://example.com/a/..
|
59
|
+
http://example.com/
|
60
|
+
|
61
|
+
remove_dot_segments: trailing single-dot-slash
|
62
|
+
http://example.com/a/./
|
63
|
+
http://example.com/a/
|
64
|
+
|
65
|
+
remove_dot_segments: trailing double-dot-slash
|
66
|
+
http://example.com/a/../
|
67
|
+
http://example.com/
|
68
|
+
|
69
|
+
Test of all kinds of syntax-based normalization
|
70
|
+
hTTPS://a/./b/../b/%63/%7bfoo%7d
|
71
|
+
https://a/b/c/%7Bfoo%7D
|
72
|
+
|
73
|
+
Unsupported scheme
|
74
|
+
ftp://example.com/
|
75
|
+
fail
|
76
|
+
|
77
|
+
Non-absolute URI
|
78
|
+
http:/foo
|
79
|
+
fail
|