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.

Files changed (54) hide show
  1. data/examples/cacert.pem +7815 -0
  2. data/examples/consumer.rb +2 -2
  3. data/examples/consumerd.rb +290 -0
  4. data/examples/openid-store/associations/http-localhost_3A3000_2Fserver-LQl7HUNueJIJcpPoAGiHEHNdJMc +6 -0
  5. data/examples/openid-store/associations/http-www.myopenid.com_2Fserver-ZFp96P4qV1FjqgGt2rtZBvRJWic +6 -0
  6. data/examples/openid-store/auth_key +1 -0
  7. data/examples/openid-store/nonces/PNiw86rQ +0 -0
  8. data/examples/openid-store/nonces/hdZo7WC9 +0 -0
  9. data/examples/openid-store/nonces/uHhMdi1i +0 -0
  10. data/examples/rails_openid_login_generator/templates/controller.rb +1 -1
  11. data/examples/rails_server/app/controllers/login_controller.rb~ +35 -0
  12. data/examples/rails_server/app/controllers/server_controller.rb~ +190 -0
  13. data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-YU.tkND1J4fEZhnuAoT5Zc0yCA0 +6 -0
  14. data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-jRS20gc5OzJ5pkpjy9BjqvTj3B0 +6 -0
  15. data/examples/rails_server/log/development.log +6459 -0
  16. data/examples/rails_server/log/production.log +0 -0
  17. data/examples/rails_server/log/server.log +0 -0
  18. data/examples/rails_server/log/test.log +0 -0
  19. data/examples/rails_server/tmp/sessions/ruby_sess.1b2e9635e0f69c0d +0 -0
  20. data/examples/rails_server/tmp/sessions/ruby_sess.1b3584d2b3784c97 +0 -0
  21. data/examples/rails_server/tmp/sessions/ruby_sess.20ed70e0e63d7e31 +0 -0
  22. data/examples/rails_server/tmp/sessions/ruby_sess.30cf5b98539677d5 +0 -0
  23. data/examples/rails_server/tmp/sessions/ruby_sess.3910508c0c857695 +0 -0
  24. data/examples/rails_server/tmp/sessions/ruby_sess.472170ef38098672 +0 -0
  25. data/examples/rails_server/tmp/sessions/ruby_sess.5406e21ba5b1c7bb +0 -0
  26. data/examples/rails_server/tmp/sessions/ruby_sess.5d2bd2b7086f12d5 +0 -0
  27. data/examples/rails_server/tmp/sessions/ruby_sess.968757c6d12af322 +0 -0
  28. data/examples/rails_server/tmp/sessions/ruby_sess.a87a5045744b3abf +0 -0
  29. data/examples/rails_server/tmp/sessions/ruby_sess.ca9f0a416be0be57 +0 -0
  30. data/examples/rails_server/tmp/sessions/ruby_sess.cd269e6040645b5b +0 -0
  31. data/examples/rails_server/tmp/sessions/ruby_sess.cf2acf62b93dbc88 +0 -0
  32. data/examples/rails_server/tmp/sessions/ruby_sess.d2ef8fe29591ef9b +0 -0
  33. data/examples/rails_server/tmp/sessions/ruby_sess.e23240e097e2c83d +0 -0
  34. data/examples/rails_server/tmp/sessions/ruby_sess.fb154d2f7c286aba +0 -0
  35. data/lib/openid/consumer.rb +40 -71
  36. data/lib/openid/discovery.rb +34 -1
  37. data/lib/openid/discovery.rb~ +122 -0
  38. data/lib/openid/fetchers.rb +41 -0
  39. data/lib/openid/server.rb +1 -1
  40. data/lib/openid/service.rb +9 -2
  41. data/lib/openid/stores.rb~ +178 -0
  42. data/lib/openid/trustroot.rb +23 -10
  43. data/lib/openid/urinorm.rb +72 -0
  44. data/lib/openid/util.rb +3 -3
  45. data/test/consumer.rb +0 -8
  46. data/test/data/urinorm.txt +79 -0
  47. data/test/runtests.rb +1 -0
  48. data/test/service.rb +18 -1
  49. data/test/teststore.rb~ +47 -0
  50. data/test/trustroot.rb +5 -1
  51. data/test/urinorm.rb +32 -0
  52. metadata +93 -41
  53. data/examples/rails_openid_login_generator/templates/controller.rb~ +0 -111
  54. 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(return_to)
365
+ if trust_root and not OpenID::TrustRoot.parse(trust_root)
366
366
  raise MalformedTrustRoot.new(query, trust_root)
367
367
  end
368
368
 
@@ -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
@@ -63,19 +63,32 @@ module OpenID
63
63
  return true if @host == 'localhost'
64
64
 
65
65
  host_parts = @host.split('.')
66
- return false unless TOP_LEVEL_DOMAINS.member?(host_parts[-1])
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
- # wacky heurestic for extracting a sane tld
69
- host = []
70
- if host_parts[-1].length == 2 and host_parts.length > 1
71
- if host_parts[-2].length <= 3
72
- host = host_parts[0...-2]
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 (host.length > 0)
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
- parsed = URI.parse(url)
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