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
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