ruby-openid 1.0.2 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.

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