ruby-openid 2.1.7 → 2.1.8

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 (38) hide show
  1. data/CHANGELOG +207 -27
  2. data/NOTICE +1 -1
  3. data/README +3 -4
  4. data/examples/discover +0 -0
  5. data/examples/rails_openid/app/controllers/consumer_controller.rb +1 -1
  6. data/examples/rails_openid/script/about +0 -0
  7. data/examples/rails_openid/script/breakpointer +0 -0
  8. data/examples/rails_openid/script/console +0 -0
  9. data/examples/rails_openid/script/destroy +0 -0
  10. data/examples/rails_openid/script/generate +0 -0
  11. data/examples/rails_openid/script/performance/benchmarker +0 -0
  12. data/examples/rails_openid/script/performance/profiler +0 -0
  13. data/examples/rails_openid/script/plugin +0 -0
  14. data/examples/rails_openid/script/process/reaper +0 -0
  15. data/examples/rails_openid/script/process/spawner +0 -0
  16. data/examples/rails_openid/script/process/spinner +0 -0
  17. data/examples/rails_openid/script/runner +0 -0
  18. data/examples/rails_openid/script/server +0 -0
  19. data/lib/openid.rb +1 -1
  20. data/lib/openid/association.rb +2 -2
  21. data/lib/openid/consumer.rb +1 -1
  22. data/lib/openid/consumer/associationmanager.rb +1 -1
  23. data/lib/openid/consumer/discovery.rb +1 -2
  24. data/lib/openid/consumer/html_parse.rb +1 -1
  25. data/lib/openid/consumer/idres.rb +3 -3
  26. data/lib/openid/consumer/responses.rb +1 -1
  27. data/lib/openid/cryptutil.rb +22 -4
  28. data/lib/openid/extensions/ax.rb +28 -5
  29. data/lib/openid/extensions/oauth.rb +91 -0
  30. data/lib/openid/fetchers.rb +22 -2
  31. data/lib/openid/yadis/xrires.rb +4 -11
  32. data/test/data/test_discover/openid_utf8.html +11 -0
  33. data/test/test_ax.rb +44 -2
  34. data/test/test_discover.rb +14 -0
  35. data/test/test_fetchers.rb +28 -1
  36. data/test/test_idres.rb +77 -36
  37. data/test/test_oauth.rb +175 -0
  38. metadata +206 -226
@@ -110,10 +110,12 @@ module OpenID
110
110
  class FetchRequest < AXMessage
111
111
  attr_reader :requested_attributes
112
112
  attr_accessor :update_url
113
+
114
+ MODE = 'fetch_request'
113
115
 
114
116
  def initialize(update_url = nil)
115
117
  super()
116
- @mode = 'fetch_request'
118
+ @mode = MODE
117
119
  @requested_attributes = {}
118
120
  @update_url = update_url
119
121
  end
@@ -180,7 +182,7 @@ module OpenID
180
182
  def self.from_openid_request(oidreq)
181
183
  message = oidreq.message
182
184
  ax_args = message.get_args(NS_URI)
183
- return nil if ax_args == {}
185
+ return nil if ax_args == {} or ax_args['mode'] != MODE
184
186
  req = new
185
187
  req.parse_extension_args(ax_args)
186
188
 
@@ -467,11 +469,26 @@ module OpenID
467
469
 
468
470
  # A store request attribute exchange message representation
469
471
  class StoreRequest < KeyValueMessage
472
+
473
+ MODE = 'store_request'
474
+
470
475
  def initialize
471
476
  super
472
- @mode = 'store_request'
477
+ @mode = MODE
473
478
  end
474
-
479
+
480
+ # Extract a StoreRequest from an OpenID message
481
+ # message: OpenID::Message
482
+ # return a StoreRequest or nil if AX arguments are not present
483
+ def self.from_openid_request(oidreq)
484
+ message = oidreq.message
485
+ ax_args = message.get_args(NS_URI)
486
+ return nil if ax_args.empty? or ax_args['mode'] != MODE
487
+ req = new
488
+ req.parse_extension_args(ax_args)
489
+ req
490
+ end
491
+
475
492
  def get_extension_args(aliases=nil)
476
493
  ax_args = new_args
477
494
  kv_args = _get_extension_kv_args(aliases)
@@ -499,7 +516,13 @@ module OpenID
499
516
  end
500
517
  @error_message = error_message
501
518
  end
502
-
519
+
520
+ def self.from_success_response(success_response)
521
+ resp = nil
522
+ ax_args = success_response.message.get_args(NS_URI)
523
+ resp = ax_args.key?('error') ? new(false, ax_args['error']) : new
524
+ end
525
+
503
526
  def succeeded?
504
527
  @mode == SUCCESS_MODE
505
528
  end
@@ -0,0 +1,91 @@
1
+ # An implementation of the OpenID OAuth Extension
2
+ # Extension 1.0
3
+ # see: http://openid.net/specs/
4
+
5
+ require 'openid/extension'
6
+
7
+ module OpenID
8
+
9
+ module OAuth
10
+ NS_URI = "http://specs.openid.net/extensions/oauth/1.0"
11
+ # An OAuth token request, sent from a relying
12
+ # party to a provider
13
+ class Request < Extension
14
+ attr_accessor :consumer, :scope, :ns_alias, :ns_uri
15
+ def initialize(consumer=nil, scope=nil)
16
+ @ns_alias = 'oauth'
17
+ @ns_uri = NS_URI
18
+ @consumer = consumer
19
+ @scope = scope
20
+ end
21
+
22
+
23
+ def get_extension_args
24
+ ns_args = {}
25
+ ns_args['consumer'] = @consumer if @consumer
26
+ ns_args['scope'] = @scope if @scope
27
+ return ns_args
28
+ end
29
+
30
+ # Instantiate a Request object from the arguments in a
31
+ # checkid_* OpenID message
32
+ # return nil if the extension was not requested.
33
+ def self.from_openid_request(oid_req)
34
+ oauth_req = new
35
+ args = oid_req.message.get_args(NS_URI)
36
+ if args == {}
37
+ return nil
38
+ end
39
+ oauth_req.parse_extension_args(args)
40
+ return oauth_req
41
+ end
42
+
43
+ # Set the state of this request to be that expressed in these
44
+ # OAuth arguments
45
+ def parse_extension_args(args)
46
+ @consumer = args["consumer"]
47
+ @scope = args["scope"]
48
+ end
49
+
50
+ end
51
+
52
+ # A OAuth request token response, sent from a provider
53
+ # to a relying party
54
+ class Response < Extension
55
+ attr_accessor :request_token, :scope
56
+ def initialize(request_token=nil, scope=nil)
57
+ @ns_alias = 'oauth'
58
+ @ns_uri = NS_URI
59
+ @request_token = request_token
60
+ @scope = scope
61
+ end
62
+
63
+ # Create a Response object from an OpenID::Consumer::SuccessResponse
64
+ def self.from_success_response(success_response)
65
+ args = success_response.get_signed_ns(NS_URI)
66
+ return nil if args.nil?
67
+ oauth_resp = new
68
+ oauth_resp.parse_extension_args(args)
69
+ return oauth_resp
70
+ end
71
+
72
+ # parse the oauth request arguments into the
73
+ # internal state of this object
74
+ # if strict is specified, raise an exception when bad data is
75
+ # encountered
76
+ def parse_extension_args(args, strict=false)
77
+ @request_token = args["request_token"]
78
+ @scope = args["scope"]
79
+ end
80
+
81
+ def get_extension_args
82
+ ns_args = {}
83
+ ns_args['request_token'] = @request_token if @request_token
84
+ ns_args['scope'] = @scope if @scope
85
+ return ns_args
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ end
@@ -205,6 +205,9 @@ module OpenID
205
205
  conn.request_post(url.request_uri, body, headers)
206
206
  end
207
207
  }
208
+ setup_encoding(response)
209
+ rescue Timeout::Error => why
210
+ raise FetchingError, "Error fetching #{url}: #{why}"
208
211
  rescue RuntimeError => why
209
212
  raise why
210
213
  rescue OpenSSL::SSL::SSLError => why
@@ -212,8 +215,6 @@ module OpenID
212
215
  rescue FetchingError => why
213
216
  raise why
214
217
  rescue Exception => why
215
- # Things we've caught here include a Timeout::Error, which descends
216
- # from SignalException.
217
218
  raise FetchingError, "Error fetching #{url}: #{why}"
218
219
  end
219
220
 
@@ -234,5 +235,24 @@ module OpenID
234
235
  return HTTPResponse._from_net_response(response, unparsed_url)
235
236
  end
236
237
  end
238
+
239
+ private
240
+ def setup_encoding(response)
241
+ return unless defined?(::Encoding::ASCII_8BIT)
242
+ charset = response.type_params["charset"]
243
+ return if charset.nil?
244
+ encoding = nil
245
+ begin
246
+ encoding = Encoding.find(charset)
247
+ rescue ArgumentError
248
+ end
249
+ encoding ||= Encoding::ASCII_8BIT
250
+ body = response.body
251
+ if body.respond_to?(:force_encoding)
252
+ body.force_encoding(encoding)
253
+ else
254
+ body.set_encoding(encoding)
255
+ end
256
+ end
237
257
  end
238
258
  end
@@ -42,34 +42,27 @@ module OpenID
42
42
  return XRI.append_args(hxri, args)
43
43
  end
44
44
 
45
- def query(xri, service_types)
45
+ def query(xri)
46
46
  # these can be query args or http headers, needn't be both.
47
47
  # headers = {'Accept' => 'application/xrds+xml;sep=true'}
48
48
  canonicalID = nil
49
49
 
50
- services = service_types.collect { |service_type|
51
- url = self.query_url(xri, service_type)
50
+ url = self.query_url(xri)
52
51
  begin
53
52
  response = OpenID.fetch(url)
54
53
  rescue
55
- raise XRIHTTPError, ["Could not fetch #{xri}", $!]
54
+ raise XRIHTTPError, "Could not fetch #{xri}, #{$!}"
56
55
  end
57
56
  raise XRIHTTPError, "Could not fetch #{xri}" if response.nil?
58
57
 
59
58
  xrds = Yadis::parseXRDS(response.body)
60
59
  canonicalID = Yadis::get_canonical_id(xri, xrds)
61
60
 
62
- Yadis::services(xrds) unless xrds.nil?
63
- }
61
+ return canonicalID, Yadis::services(xrds)
64
62
  # TODO:
65
63
  # * If we do get hits for multiple service_types, we're almost
66
64
  # certainly going to have duplicated service entries and
67
65
  # broken priority ordering.
68
- services = services.inject([]) { |flatter, some_services|
69
- flatter += some_services unless some_services.nil?
70
- }
71
-
72
- return canonicalID, services
73
66
  end
74
67
  end
75
68
 
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <title>Identity Page for Smoker</title>
5
+ <link rel="openid.server" href="http://www.myopenid.com/server" />
6
+ <link rel="openid.delegate" href="http://smoker.myopenid.com/" />
7
+ </head>
8
+ <body>
9
+ <p>こんにちは</p>
10
+ </body>
11
+ </html>
@@ -371,7 +371,28 @@ module OpenID
371
371
  ax_req = FetchRequest.from_openid_request(openid_req)
372
372
  assert(ax_req.nil?)
373
373
  end
374
-
374
+
375
+ def test_from_openid_request_wrong_ax_mode
376
+ uri = 'http://under.the.sea/'
377
+ name = 'ext0'
378
+ value = 'snarfblat'
379
+
380
+ message = OpenID::Message.from_openid_args({
381
+ 'mode' => 'id_res',
382
+ 'ns' => OPENID2_NS,
383
+ 'ns.ax' => AXMessage::NS_URI,
384
+ 'ax.update_url' => 'http://example.com/realm/update_path',
385
+ 'ax.mode' => 'store_request',
386
+ 'ax.type.' + name => uri,
387
+ 'ax.count.' + name => '1',
388
+ 'ax.value.' + name + '.1' => value
389
+ })
390
+ openid_req = Server::OpenIDRequest.new
391
+ openid_req.message = message
392
+ ax_req = FetchRequest.from_openid_request(openid_req)
393
+ assert(ax_req.nil?)
394
+ end
395
+
375
396
  def test_openid_update_url_verification_error
376
397
  openid_req_msg = Message.from_openid_args({
377
398
  'mode' => 'checkid_setup',
@@ -602,7 +623,28 @@ module OpenID
602
623
  }
603
624
  assert_equal(eargs, @msg.get_extension_args)
604
625
  end
605
-
626
+
627
+ def test_from_openid_request_wrong_ax_mode
628
+ uri = 'http://under.the.sea/'
629
+ name = 'ext0'
630
+ value = 'snarfblat'
631
+
632
+ message = OpenID::Message.from_openid_args({
633
+ 'mode' => 'id_res',
634
+ 'ns' => OPENID2_NS,
635
+ 'ns.ax' => AXMessage::NS_URI,
636
+ 'ax.update_url' => 'http://example.com/realm/update_path',
637
+ 'ax.mode' => 'fetch_request',
638
+ 'ax.type.' + name => uri,
639
+ 'ax.count.' + name => '1',
640
+ 'ax.value.' + name + '.1' => value
641
+ })
642
+ openid_req = Server::OpenIDRequest.new
643
+ openid_req.message = message
644
+ ax_req = StoreRequest.from_openid_request(openid_req)
645
+ assert(ax_req.nil?)
646
+ end
647
+
606
648
  def test_get_extension_args_nonempty
607
649
  @msg.set_values(@type_a, ['foo','bar'])
608
650
  aliases = NamespaceMap.new
@@ -369,6 +369,20 @@ module OpenID
369
369
  }
370
370
  end
371
371
 
372
+ def test_html_utf8
373
+ utf8_html = read_data_file('test_discover/openid_utf8.html', false)
374
+ utf8_html.force_encoding("UTF-8") if utf8_html.respond_to?(:force_encoding)
375
+ services = _discover('text/html', utf8_html, 1)
376
+
377
+ _checkService(services[0],
378
+ "http://www.myopenid.com/server",
379
+ @id_url,
380
+ 'http://smoker.myopenid.com/',
381
+ nil,
382
+ ['1.1'],
383
+ false)
384
+ end
385
+
372
386
  def test_yadisEmpty
373
387
  services = _discover('application/xrds+xml',
374
388
  read_data_file('test_discover/yadis_0entries.xml', false),
@@ -1,3 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+
1
3
  require 'test/unit'
2
4
  require 'net/http'
3
5
  require 'webrick'
@@ -112,7 +114,22 @@ class FetcherTestCase < Test::Unit::TestCase
112
114
  assert_block("Fetched too many times.") { @_redirect_counter < 10 }
113
115
  }
114
116
  end
115
-
117
+
118
+ UTF8_PAGE_CONTENT = <<-EOHTML
119
+ <html>
120
+ <head><title>UTF-8</title></head>
121
+ <body>こんにちは</body>
122
+ </html>
123
+ EOHTML
124
+ def _utf8_page
125
+ lambda { |req, resp|
126
+ resp['Content-Type'] = "text/html; charset=utf-8"
127
+ body = UTF8_PAGE_CONTENT.dup
128
+ body.force_encoding("ASCII-8BIT") if body.respond_to?(:force_encoding)
129
+ resp.body = body
130
+ }
131
+ end
132
+
116
133
  def setup
117
134
  @fetcher = OpenID::StandardFetcher.new
118
135
  @logfile = StringIO.new
@@ -138,6 +155,7 @@ class FetcherTestCase < Test::Unit::TestCase
138
155
  }
139
156
  @server.mount_proc('/post', _require_post)
140
157
  @server.mount_proc('/redirect_loop', _redirect_loop)
158
+ @server.mount_proc('/utf8_page', _utf8_page)
141
159
  @server.start
142
160
  }
143
161
  @uri = _uri_build
@@ -211,6 +229,15 @@ class FetcherTestCase < Test::Unit::TestCase
211
229
  }
212
230
  end
213
231
 
232
+ def test_utf8_page
233
+ uri = _uri_build('/utf8_page')
234
+ response = @fetcher.fetch(uri)
235
+ assert_equal(UTF8_PAGE_CONTENT, response.body)
236
+ if response.body.respond_to?(:encoding)
237
+ assert_equal(Encoding::UTF_8, response.body.encoding)
238
+ end
239
+ end
240
+
214
241
  def test_cases
215
242
  for path, expected_code, expected_url in @@cases
216
243
  uri = _uri_build(path)
@@ -61,6 +61,7 @@ module OpenID
61
61
  # test all missing fields for OpenID 1 and 2
62
62
  1.times do
63
63
  [["openid1", OPENID1_NS, OPENID1_FIELDS],
64
+ ["openid1", OPENID11_NS, OPENID1_FIELDS],
64
65
  ["openid2", OPENID2_NS, OPENID2_FIELDS],
65
66
  ].each do |ver, ns, all_fields|
66
67
  all_fields.each do |field|
@@ -81,6 +82,7 @@ module OpenID
81
82
  # Test all missing signed for OpenID 1 and 2
82
83
  1.times do
83
84
  [["openid1", OPENID1_NS, OPENID1_FIELDS, OPENID1_SIGNED],
85
+ ["openid1", OPENID11_NS, OPENID1_FIELDS, OPENID1_SIGNED],
84
86
  ["openid2", OPENID2_NS, OPENID2_FIELDS, OPENID2_SIGNED],
85
87
  ].each do |ver, ns, all_fields, signed_fields|
86
88
  signed_fields.each do |signed_field|
@@ -144,6 +146,14 @@ module OpenID
144
146
  idres.send(:check_for_fields)
145
147
  }
146
148
  end
149
+
150
+ def test_success_openid1_1
151
+ msg = mkMsg(OPENID11_NS, OPENID1_FIELDS, OPENID1_SIGNED)
152
+ idres = IdResHandler.new(msg, nil)
153
+ assert_nothing_raised {
154
+ idres.send(:check_for_fields)
155
+ }
156
+ end
147
157
  end
148
158
 
149
159
  class ReturnToArgsTest < Test::Unit::TestCase
@@ -499,13 +509,23 @@ module OpenID
499
509
  end
500
510
 
501
511
  def test_openid1_success
502
- assert_nothing_raised {
503
- call_check_nonce({'rp_nonce' => @nonce}, true)
504
- }
512
+ [{},
513
+ {'openid.ns' => OPENID1_NS},
514
+ {'openid.ns' => OPENID11_NS}
515
+ ].each do |args|
516
+ assert_nothing_raised {
517
+ call_check_nonce({'rp_nonce' => @nonce}.merge(args), true)
518
+ }
519
+ end
505
520
  end
506
521
 
507
522
  def test_openid1_missing
508
- assert_protocol_error('Nonce missing') { call_check_nonce({}) }
523
+ [{},
524
+ {'openid.ns' => OPENID1_NS},
525
+ {'openid.ns' => OPENID11_NS}
526
+ ].each do |args|
527
+ assert_protocol_error('Nonce missing') { call_check_nonce(args) }
528
+ end
509
529
  end
510
530
 
511
531
  def test_openid2_ignore_rp_nonce
@@ -523,9 +543,14 @@ module OpenID
523
543
  end
524
544
 
525
545
  def test_openid1_ignore_response_nonce
526
- assert_protocol_error('Nonce missing') {
527
- call_check_nonce({'openid.response_nonce' => @nonce})
528
- }
546
+ [{},
547
+ {'openid.ns' => OPENID1_NS},
548
+ {'openid.ns' => OPENID11_NS}
549
+ ].each do |args|
550
+ assert_protocol_error('Nonce missing') {
551
+ call_check_nonce({'openid.response_nonce' => @nonce}.merge(args))
552
+ }
553
+ end
529
554
  end
530
555
 
531
556
  def test_no_store
@@ -587,37 +612,38 @@ module OpenID
587
612
  end
588
613
 
589
614
  def test_openid1_fallback_1_0
590
- claimed_id = 'http://claimed.id/'
591
- @endpoint = nil
592
- resp_mesg = Message.from_openid_args({
593
- 'ns' => OPENID1_NS,
594
- 'identity' => claimed_id,
595
- })
615
+ [OPENID1_NS, OPENID11_NS].each do |openid1_ns|
616
+ claimed_id = 'http://claimed.id/'
617
+ @endpoint = nil
618
+ resp_mesg = Message.from_openid_args({
619
+ 'ns' => openid1_ns,
620
+ 'identity' => claimed_id,
621
+ })
596
622
 
597
- # Pass the OpenID 1 claimed_id this way since we're passing
598
- # None for the endpoint.
599
- resp_mesg.set_arg(BARE_NS, 'openid1_claimed_id', claimed_id)
600
-
601
- # We expect the OpenID 1 discovery verification to try
602
- # matching the discovered endpoint against the 1.1 type and
603
- # fall back to 1.0.
604
- expected_endpoint = OpenIDServiceEndpoint.new
605
- expected_endpoint.type_uris = [OPENID_1_0_TYPE]
606
- expected_endpoint.local_id = nil
607
- expected_endpoint.claimed_id = claimed_id
608
-
609
- hacked_discover = Proc.new {
610
- |_claimed_id| ['unused', [expected_endpoint]]
611
- }
612
- idres = IdResHandler.new(resp_mesg, nil, nil, @endpoint)
613
- assert_log_matches('Performing discovery') {
614
- OpenID.with_method_overridden(:discover, hacked_discover) {
615
- idres.send(:verify_discovery_results)
616
- }
617
- }
618
- actual_endpoint = idres.instance_variable_get(:@endpoint)
619
- assert_equal(actual_endpoint, expected_endpoint)
623
+ # Pass the OpenID 1 claimed_id this way since we're
624
+ # passing None for the endpoint.
625
+ resp_mesg.set_arg(BARE_NS, 'openid1_claimed_id', claimed_id)
626
+
627
+ # We expect the OpenID 1 discovery verification to try
628
+ # matching the discovered endpoint against the 1.1 type
629
+ # and fall back to 1.0.
630
+ expected_endpoint = OpenIDServiceEndpoint.new
631
+ expected_endpoint.type_uris = [OPENID_1_0_TYPE]
632
+ expected_endpoint.local_id = nil
633
+ expected_endpoint.claimed_id = claimed_id
620
634
 
635
+ hacked_discover = Proc.new {
636
+ |_claimed_id| ['unused', [expected_endpoint]]
637
+ }
638
+ idres = IdResHandler.new(resp_mesg, nil, nil, @endpoint)
639
+ assert_log_matches('Performing discovery') {
640
+ OpenID.with_method_overridden(:discover, hacked_discover) {
641
+ idres.send(:verify_discovery_results)
642
+ }
643
+ }
644
+ actual_endpoint = idres.instance_variable_get(:@endpoint)
645
+ assert_equal(actual_endpoint, expected_endpoint)
646
+ end
621
647
  end
622
648
 
623
649
  def test_openid2_no_op_endpoint
@@ -710,6 +736,21 @@ module OpenID
710
736
  assert(e.to_s =~ /different subjects/)
711
737
  end
712
738
 
739
+ def test_openid1_1_verify_discovery_single_no_server_url
740
+ idres = IdResHandler.new(nil, nil)
741
+ @endpoint.local_id = 'my identity'
742
+ @endpoint.claimed_id = 'http://i-am-sam/'
743
+ @endpoint.server_url = 'Phone Home'
744
+ @endpoint.type_uris = [OPENID_1_1_TYPE]
745
+
746
+ to_match = @endpoint.dup
747
+ to_match.claimed_id = 'http://i-am-sam/'
748
+ to_match.type_uris = [OPENID_1_1_TYPE]
749
+ to_match.server_url = nil
750
+
751
+ idres.send(:verify_discovery_single, @endpoint, to_match)
752
+ end
753
+
713
754
  def test_openid2_use_pre_discovered
714
755
  @endpoint.local_id = 'my identity'
715
756
  @endpoint.claimed_id = 'http://i-am-sam/'