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
File without changes
File without changes
File without changes
@@ -180,7 +180,7 @@ module OpenID
180
180
  # of the user though it's +identity_url+ method.
181
181
  class Consumer
182
182
 
183
- @@token_key = '_openid_consumer_token'
183
+ @@service_key = '_openid_consumer_service'
184
184
  @@disco_suffix = 'xopenid_services'
185
185
  attr_accessor :consumer, :session, :fetcher
186
186
 
@@ -266,19 +266,18 @@ module OpenID
266
266
  # +msg+ method which provides more detailed information as to why
267
267
  # the request failed.
268
268
  def begin(user_url)
269
- user_url = OpenID::Util.normalize_url(user_url)
270
- unless user_url
271
- user_url = user_url.to_s
272
- return FailureRequest.new("Invalid URL: #{user_url}")
269
+ discovery = self.get_discovery(user_url)
270
+
271
+ unless discovery
272
+ return FailureRequest.new("Don't know how to find services for that identifier")
273
273
  end
274
274
 
275
- discovery = self.get_discovery(user_url)
276
275
  service = discovery.next_service
277
276
 
278
- if service.nil?
277
+ unless service
279
278
  return FailureRequest.new('No service endpoints found.')
280
279
  end
281
-
280
+
282
281
  return self.begin_without_discovery(service)
283
282
  end
284
283
 
@@ -300,7 +299,7 @@ module OpenID
300
299
  # for more information.
301
300
  def begin_without_discovery(service)
302
301
  request = @consumer.begin(service)
303
- @session[@@token_key] = request.token
302
+ @session[@@service_key] = service
304
303
  return request
305
304
  end
306
305
 
@@ -345,17 +344,12 @@ module OpenID
345
344
  # in to the +setup_url+, either in the current window or in a
346
345
  # new browser window.
347
346
  def complete(query)
348
- begin
349
- token = @session.delete(@@token_key)
350
- rescue
351
- token = @session[@@token_key]
352
- @session[@@token_key] = nil
353
- end
347
+ service = @session[@@service_key]
354
348
 
355
- if token.nil?
349
+ if service.nil?
356
350
  resp = FailureResponse.new(nil, 'No session state found.')
357
351
  else
358
- resp = @consumer.complete(query, token)
352
+ resp = @consumer.complete(query, service)
359
353
  end
360
354
 
361
355
  disco = self.get_discovery(resp.identity_url)
@@ -368,6 +362,13 @@ module OpenID
368
362
  resp.service = disco.current
369
363
  end
370
364
 
365
+ # want to delete service unless status is SETUP_NEEDED,
366
+ # because we still need the service info when the user returns from
367
+ # the server
368
+ unless resp.status == SETUP_NEEDED
369
+ @session[@@service_key] = nil
370
+ end
371
+
371
372
  return resp
372
373
  end
373
374
 
@@ -375,10 +376,18 @@ module OpenID
375
376
 
376
377
  # Used internally to create an instnace of the OpenIDDiscovery object.
377
378
  def get_discovery(url)
378
- return OpenIDDiscovery.new(@session, url, @consumer.fetcher,
379
- @@disco_suffix)
379
+ if XRI::identifier_scheme(url) == :xri
380
+ XRIDiscovery.new(@session, url, @@disco_suffix)
381
+ else
382
+ url = OpenID::Util.normalize_url(url)
383
+ if url
384
+ user_url = user_url.to_s
385
+ OpenIDDiscovery.new(@session, url, @consumer.fetcher, @@disco_suffix)
386
+ else
387
+ nil
388
+ end
389
+ end
380
390
  end
381
-
382
391
  end
383
392
 
384
393
  # This class implements the common logic for OpenID consumers. It
@@ -397,9 +406,6 @@ module OpenID
397
406
 
398
407
  # Nonce character set
399
408
  @@NONCE_CHRS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
400
- # Number of seconds the tokens generated by this library will be valid for.
401
- @@TOKEN_LIFETIME = 60 * 2
402
-
403
409
  @@D_SUFFIX = 'openid_disco'
404
410
 
405
411
  attr_reader :fetcher
@@ -451,27 +457,22 @@ module OpenID
451
457
  # has the same interface.
452
458
  def begin(service)
453
459
  nonce = self.create_nonce
454
- token = self.gen_token(service.consumer_id, service.server_id,
455
- service.server_url)
456
-
457
460
  assoc = self.get_association(service.server_url)
458
- return SuccessRequest.new(assoc, token, nonce, service)
461
+ return SuccessRequest.new(assoc, nonce, service)
459
462
  end
460
463
 
461
464
  # Please see the interface docs for Consumer.complete. This method accpets
462
- # a +token+ paramter which is provided though the SuccessRequest object
463
- # generated in the +begin+ call. The token should be stored somewhere
465
+ # a +service+ paramter which is provided though the SuccessRequest object
466
+ # generated in the +begin+ call. The +service+ should be stored somewhere
464
467
  # in the user's session or environment and passed into this method
465
468
  # along with the full query string Hash. Consumer.complete has the
466
469
  # full list of return values for this method.
467
- def complete(query, token)
468
- # get the service data out of the token
469
- pieces = self.split_token(token)
470
- if pieces
471
- consumer_id, server_id, server_url = pieces
472
- else
473
- return FailureResponse.new(nil, msg='bad token')
474
- end
470
+ def complete(query, service_endpoint)
471
+ return FailureResponse.new(nil, msg='no session state found') unless service_endpoint
472
+
473
+ consumer_id = service_endpoint.consumer_id
474
+ server_id = service_endpoint.server_id
475
+ server_url = service_endpoint.server_url
475
476
 
476
477
  # get the nonce out of the query
477
478
  nonce = query['nonce']
@@ -612,37 +613,6 @@ module OpenID
612
613
  return self.associate(server_url)
613
614
  end
614
615
 
615
- # Generate a token representing the user and their server. Used
616
- # internally for maintaining cross request state.
617
- def gen_token(consumer_id, server_id, server_url)
618
- timestamp = Time.now.to_i.to_s
619
- joined = [timestamp, consumer_id, server_id, server_url].join("\x00")
620
- sig = OpenID::Util.hmac_sha1(@store.get_auth_key, joined)
621
- OpenID::Util.to_base64(sig+joined)
622
- end
623
-
624
- # Extract server and user information from a token string.
625
- def split_token(token)
626
- return nil if token.nil?
627
-
628
- token = OpenID::Util.from_base64(token)
629
- return nil if token.length < 20
630
-
631
- sig, joined = token[(0...20)], token[(20...token.length)]
632
- return nil if OpenID::Util.hmac_sha1(@store.get_auth_key, joined) != sig
633
-
634
- s = joined.split("\x00")
635
- return nil if s.length != 4
636
-
637
- timestamp, consumer_id, server_id, server_url = s
638
-
639
- timestamp = timestamp.to_i
640
- return nil if timestamp == 0
641
- return nil if (timestamp + @@TOKEN_LIFETIME) < Time.now.to_i
642
-
643
- return [consumer_id, server_id, server_url].freeze
644
- end
645
-
646
616
  # Make the openid.associate call to the server.
647
617
  def associate(server_url)
648
618
  dh = OpenID::DiffieHellman.new
@@ -723,7 +693,7 @@ module OpenID
723
693
  # Consumer.begin.
724
694
  class SuccessRequest < OpenIDStatus
725
695
 
726
- attr_reader :token, :server_id, :server_url, :nonce, :identity_url, \
696
+ attr_reader :server_id, :server_url, :nonce, :identity_url, \
727
697
  :service, :return_to_args
728
698
 
729
699
  # Creates a new SuccessRequest object. This just stores each
@@ -732,7 +702,7 @@ module OpenID
732
702
  # Users of this library should not create instances of this
733
703
  # class. Instances of this class are created by Consumer
734
704
  # during begin.
735
- def initialize(assoc, token, nonce, service)
705
+ def initialize(assoc, nonce, service)
736
706
  super(SUCCESS)
737
707
  @service = service
738
708
  @server_id = service.server_id
@@ -742,7 +712,6 @@ module OpenID
742
712
  @return_to_args = {'nonce' => nonce}
743
713
 
744
714
  @assoc = assoc
745
- @token = token
746
715
  @nonce = nonce
747
716
  end
748
717
 
@@ -5,13 +5,21 @@ require "openid/parse"
5
5
  # try and use the yadis gem, falling back to system yadis
6
6
  begin
7
7
  require 'rubygems'
8
- require_gem 'ruby-yadis', ">=0.3"
8
+ require_gem 'ruby-yadis', ">=0.3.3"
9
9
  rescue LoadError
10
10
  require "yadis"
11
11
  end
12
12
 
13
13
  module OpenID
14
14
 
15
+ OPENID_IDP_2_0_TYPE = 'http://openid.net/server/2.0'
16
+ OPENID_2_0_TYPE = 'http://openid.net/signon/2.0'
17
+ OPENID_1_2_TYPE = 'http://openid.net/signon/1.2'
18
+ OPENID_1_1_TYPE = 'http://openid.net/signon/1.1'
19
+ OPENID_1_0_TYPE = 'http://openid.net/signon/1.0'
20
+ OPENID_TYPE_URIS = [OPENID_2_0_TYPE,OPENID_1_2_TYPE,
21
+ OPENID_1_1_TYPE,OPENID_1_0_TYPE]
22
+
15
23
  # OpenID::Discovery encapsulates the logic for doing Yadis and OpenID 1.0
16
24
  # style server discovery. This class uses a session object to manage
17
25
  # a list of tried OpenID servers for implemeting server fallback. This is
@@ -86,4 +94,29 @@ module OpenID
86
94
 
87
95
  end
88
96
 
97
+ class XRIDiscovery < Discovery
98
+ def initialize(session, iname, suffix=nil)
99
+ super(session, iname, suffix)
100
+ end
101
+
102
+ def discover(filter=nil)
103
+ begin
104
+ services = XRI::ProxyResolver.new.query(@url, OPENID_TYPE_URIS)
105
+ rescue XRI::XRIHTTPError, ArgumentError
106
+ return [nil, []]
107
+ end
108
+ endpoints = []
109
+ services.each {|s|
110
+ se = OpenIDServiceEndpoint.from_endpoint(s)
111
+ if se
112
+ se.delegate_url = @url
113
+ se.yadis_url = @url
114
+ endpoints << se
115
+ end
116
+ }
117
+ return [@url, endpoints]
118
+ end
119
+
120
+ end
121
+
89
122
  end
@@ -0,0 +1,122 @@
1
+ require "openid/util"
2
+ require "openid/service"
3
+ require "openid/parse"
4
+
5
+ # try and use the yadis gem, falling back to system yadis
6
+ begin
7
+ require 'rubygems'
8
+ require_gem 'ruby-yadis', ">=0.4"
9
+ rescue LoadError
10
+ require "yadis"
11
+ end
12
+
13
+ module OpenID
14
+
15
+ OPENID_IDP_2_0_TYPE = 'http://openid.net/server/2.0'
16
+ OPENID_2_0_TYPE = 'http://openid.net/signon/2.0'
17
+ OPENID_1_2_TYPE = 'http://openid.net/signon/1.2'
18
+ OPENID_1_1_TYPE = 'http://openid.net/signon/1.1'
19
+ OPENID_1_0_TYPE = 'http://openid.net/signon/1.0'
20
+ OPENID_TYPE_URIS = [OPENID_2_0_TYPE,OPENID_1_2_TYPE,
21
+ OPENID_1_1_TYPE,OPENID_1_0_TYPE]
22
+
23
+ # OpenID::Discovery encapsulates the logic for doing Yadis and OpenID 1.0
24
+ # style server discovery. This class uses a session object to manage
25
+ # a list of tried OpenID servers for implemeting server fallback. This is
26
+ # useful the case when a user's primary server(s) is not available, and
27
+ # will allow then to try again with one of their alternates.
28
+ class OpenIDDiscovery < Discovery
29
+
30
+ def initialize(session, url, fetcher, suffix=nil)
31
+ super(session, url, suffix)
32
+ @fetcher = fetcher
33
+ end
34
+
35
+ # Pass in a custom filter here if you like. Otherwise you'll get all
36
+ # OpenID sso services. filter should produce objects or subclasses of
37
+ # OpenIDServiceEndpoint.
38
+ def discover(filter=nil)
39
+ unless filter
40
+ filter = lambda {|s| OpenIDServiceEndpoint.from_endpoint(s)}
41
+ end
42
+
43
+ begin
44
+ # do yadis discover, filtering out OpenID services
45
+ return super(filter)
46
+ rescue YADISParseError, YADISHTTPError
47
+
48
+ # Couldn't do Yadis discovery, fall back on OpenID 1.0 disco
49
+ status, service = self.openid_discovery(@url)
50
+ if status == SUCCESS
51
+ return [service.consumer_id, [service]]
52
+ end
53
+ end
54
+
55
+ return [nil, []]
56
+ end
57
+
58
+ # Perform OpenID 1.0 style link rel discovery. No string normalization
59
+ # will be done on +url+. See Util.normalize_url for information on
60
+ # textual URL transformations.
61
+ def openid_discovery(url)
62
+ ret = @fetcher.get(url)
63
+ return [HTTP_FAILURE, nil] if ret.nil?
64
+
65
+ consumer_id, data = ret
66
+ server = nil
67
+ delegate = nil
68
+ parse_link_attrs(data) do |attrs|
69
+ rel = attrs["rel"]
70
+ if rel == "openid.server" and server.nil?
71
+ href = attrs["href"]
72
+ server = href unless href.nil?
73
+ end
74
+
75
+ if rel == "openid.delegate" and delegate.nil?
76
+ href = attrs["href"]
77
+ delegate = href unless href.nil?
78
+ end
79
+ end
80
+
81
+ return [PARSE_ERROR, nil] if server.nil?
82
+
83
+ server_id = delegate.nil? ? consumer_id : delegate
84
+
85
+ consumer_id = OpenID::Util.normalize_url(consumer_id)
86
+ server_id = OpenID::Util.normalize_url(server_id)
87
+ server_url = OpenID::Util.normalize_url(server)
88
+
89
+ service = OpenID::FakeOpenIDServiceEndpoint.new(consumer_id,
90
+ server_id,
91
+ server_url)
92
+ return [SUCCESS, service]
93
+ end
94
+
95
+ end
96
+
97
+ class XRIDiscovery < Discovery
98
+ def initialize(session, iname, suffix=nil)
99
+ super(session, iname, suffix)
100
+ end
101
+
102
+ def discover(filter=nil)
103
+ begin
104
+ services = XRI::ProxyResolver.new.query(@url, OPENID_TYPE_URIS)
105
+ rescue XRI::XRIHTTPError, ArgumentError
106
+ return [nil, []]
107
+ end
108
+ endpoints = []
109
+ services.each {|s|
110
+ se = OpenIDServiceEndpoint.from_endpoint(s)
111
+ if se
112
+ se.delegate_url = @url
113
+ se.yadis_url = @url
114
+ endpoints << se
115
+ end
116
+ }
117
+ return [@url, endpoints]
118
+ end
119
+
120
+ end
121
+
122
+ end
@@ -12,6 +12,40 @@ else
12
12
  HAS_OPENSSL = true
13
13
  end
14
14
 
15
+ # Not all versions of Ruby 1.8.4 have the version of post_connection_check
16
+ # that properly handles wildcard hostnames. This version of
17
+ # post_connection_check is copied from post April 2006 release of 1.8.4.
18
+ module Net
19
+ class HTTP
20
+ def post_connection_check(hostname)
21
+ check_common_name = true
22
+ cert = @socket.io.peer_cert
23
+ cert.extensions.each { |ext|
24
+ next if ext.oid != "subjectAltName"
25
+ ext.value.split(/,\s+/).each{ |general_name|
26
+ if /\ADNS:(.*)/ =~ general_name
27
+ check_common_name = false
28
+ reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
29
+ return true if /\A#{reg}\z/i =~ hostname
30
+ elsif /\AIP Address:(.*)/ =~ general_name
31
+ check_common_name = false
32
+ return true if $1 == hostname
33
+ end
34
+ }
35
+ }
36
+ if check_common_name
37
+ cert.subject.to_a.each{ |oid, value|
38
+ if oid == "CN"
39
+ reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
40
+ return true if /\A#{reg}\z/i =~ hostname
41
+ end
42
+ }
43
+ end
44
+ raise OpenSSL::SSL::SSLError, "hostname does not match"
45
+ end
46
+ end
47
+ end
48
+
15
49
  module OpenID
16
50
 
17
51
  # Base Object used by consumer to send http messages
@@ -101,6 +135,13 @@ module OpenID
101
135
  begin
102
136
  u = URI.parse(url)
103
137
  http = get_http_obj(u)
138
+ http.start {
139
+ if HAS_OPENSSL and u.is_a?(URI::HTTPS) and @ca_path
140
+ # do the post_connection_check, which verifies that
141
+ # the host matches the cert
142
+ http.post_connection_check(u.host)
143
+ end
144
+ }
104
145
  resp = http.get(u.request_uri)
105
146
  rescue
106
147
  nil