ruby-openid 2.0.4 → 2.1.2

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 (58) hide show
  1. data/CHANGELOG +65 -28
  2. data/LICENSE +4 -1
  3. data/README +19 -12
  4. data/UPGRADE +5 -0
  5. data/examples/README +8 -22
  6. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +6 -6
  7. data/examples/active_record_openid_store/lib/association.rb +2 -1
  8. data/examples/active_record_openid_store/lib/openid_ar_store.rb +3 -3
  9. data/examples/rails_openid/app/controllers/consumer_controller.rb +11 -5
  10. data/lib/openid.rb +4 -0
  11. data/lib/openid/association.rb +7 -7
  12. data/lib/openid/consumer/checkid_request.rb +11 -0
  13. data/lib/openid/consumer/discovery.rb +12 -3
  14. data/lib/openid/consumer/idres.rb +35 -43
  15. data/lib/openid/extension.rb +9 -1
  16. data/lib/openid/extensions/pape.rb +22 -25
  17. data/lib/openid/extensions/sreg.rb +1 -0
  18. data/lib/openid/fetchers.rb +25 -5
  19. data/lib/openid/kvform.rb +8 -5
  20. data/lib/openid/kvpost.rb +6 -5
  21. data/lib/openid/message.rb +53 -34
  22. data/lib/openid/server.rb +87 -52
  23. data/lib/openid/trustroot.rb +25 -17
  24. data/lib/openid/util.rb +19 -4
  25. data/lib/openid/yadis/discovery.rb +3 -3
  26. data/lib/openid/yadis/htmltokenizer.rb +8 -5
  27. data/lib/openid/yadis/parsehtml.rb +22 -14
  28. data/lib/openid/yadis/xrds.rb +6 -9
  29. data/test/data/linkparse.txt +1 -1
  30. data/test/data/test1-parsehtml.txt +24 -0
  31. data/test/data/trustroot.txt +8 -2
  32. data/test/test_association.rb +7 -7
  33. data/test/test_associationmanager.rb +1 -1
  34. data/test/test_extension.rb +46 -0
  35. data/test/test_idres.rb +81 -21
  36. data/test/test_kvform.rb +5 -5
  37. data/test/test_message.rb +61 -3
  38. data/test/test_pape.rb +36 -22
  39. data/test/test_server.rb +190 -12
  40. data/test/test_sreg.rb +0 -1
  41. data/test/test_trustroot.rb +1 -0
  42. data/test/test_yadis_discovery.rb +13 -0
  43. metadata +3 -19
  44. data/examples/rails_openid/app/views/consumer/start.rhtml +0 -8
  45. data/examples/rails_openid_login_generator/USAGE +0 -23
  46. data/examples/rails_openid_login_generator/gemspec +0 -13
  47. data/examples/rails_openid_login_generator/openid_login_generator.rb +0 -36
  48. data/examples/rails_openid_login_generator/templates/README +0 -116
  49. data/examples/rails_openid_login_generator/templates/controller.rb +0 -113
  50. data/examples/rails_openid_login_generator/templates/controller_test.rb +0 -0
  51. data/examples/rails_openid_login_generator/templates/helper.rb +0 -2
  52. data/examples/rails_openid_login_generator/templates/openid_login_system.rb +0 -87
  53. data/examples/rails_openid_login_generator/templates/user.rb +0 -14
  54. data/examples/rails_openid_login_generator/templates/user_test.rb +0 -0
  55. data/examples/rails_openid_login_generator/templates/users.yml +0 -0
  56. data/examples/rails_openid_login_generator/templates/view_login.rhtml +0 -15
  57. data/examples/rails_openid_login_generator/templates/view_logout.rhtml +0 -10
  58. data/examples/rails_openid_login_generator/templates/view_welcome.rhtml +0 -9
data/lib/openid/server.rb CHANGED
@@ -26,7 +26,7 @@ module OpenID
26
26
  UNUSED = nil
27
27
 
28
28
  class OpenIDRequest
29
- attr_accessor :namespace, :message, :mode
29
+ attr_accessor :message, :mode
30
30
 
31
31
  # I represent an incoming OpenID request.
32
32
  #
@@ -34,6 +34,15 @@ module OpenID
34
34
  # mode:: The "openid.mode" of this request
35
35
  def initialize
36
36
  @mode = nil
37
+ @message = nil
38
+ end
39
+
40
+ def namespace
41
+ if @message.nil?
42
+ raise RuntimeError, "Request has no message"
43
+ else
44
+ return @message.get_openid_namespace
45
+ end
37
46
  end
38
47
  end
39
48
 
@@ -74,7 +83,6 @@ module OpenID
74
83
  @assoc_handle = assoc_handle
75
84
  @signed = signed
76
85
  @invalidate_handle = invalidate_handle
77
- @namespace = OPENID2_NS
78
86
  end
79
87
 
80
88
  # Construct me from an OpenID::Message.
@@ -93,7 +101,6 @@ module OpenID
93
101
 
94
102
  obj = self.new(assoc_handle, signed, invalidate_handle)
95
103
  obj.message = message
96
- obj.namespace = message.get_openid_namespace()
97
104
  obj.sig = message.get_arg(OPENID_NS, 'sig')
98
105
 
99
106
  if !obj.assoc_handle or
@@ -297,7 +304,6 @@ module OpenID
297
304
  super()
298
305
  @session = session
299
306
  @assoc_type = assoc_type
300
- @namespace = OPENID2_NS
301
307
 
302
308
  @mode = "associate"
303
309
  end
@@ -305,10 +311,10 @@ module OpenID
305
311
  # Construct me from an OpenID Message.
306
312
  def self.from_message(message, op_endpoint=UNUSED)
307
313
  if message.is_openid1()
308
- session_type = message.get_arg(OPENID1_NS, 'session_type')
314
+ session_type = message.get_arg(OPENID_NS, 'session_type')
309
315
  if session_type == 'no-encryption'
310
316
  Util.log('Received OpenID 1 request with a no-encryption ' +
311
- 'assocaition session type. Continuing anyway.')
317
+ 'association session type. Continuing anyway.')
312
318
  elsif !session_type
313
319
  session_type = 'no-encryption'
314
320
  end
@@ -345,7 +351,6 @@ module OpenID
345
351
 
346
352
  obj = self.new(session, assoc_type)
347
353
  obj.message = message
348
- obj.namespace = message.get_openid_namespace()
349
354
  return obj
350
355
  end
351
356
 
@@ -364,7 +369,8 @@ module OpenID
364
369
  })
365
370
  response.fields.update_args(OPENID_NS,
366
371
  @session.answer(assoc.secret))
367
- if @session.session_type != 'no-encryption'
372
+ unless (@session.session_type == 'no-encryption' and
373
+ @message.is_openid1)
368
374
  response.fields.set_arg(
369
375
  OPENID_NS, 'session_type', @session.session_type)
370
376
  end
@@ -440,13 +446,13 @@ module OpenID
440
446
  # a URL.
441
447
  def initialize(identity, return_to, op_endpoint, trust_root=nil,
442
448
  immediate=false, assoc_handle=nil)
443
- @namespace = OPENID2_NS
444
449
  @assoc_handle = assoc_handle
445
450
  @identity = identity
446
451
  @claimed_id = identity
447
452
  @return_to = return_to
448
453
  @trust_root = trust_root or return_to
449
454
  @op_endpoint = op_endpoint
455
+ @message = nil
450
456
 
451
457
  if immediate
452
458
  @immediate = true
@@ -484,7 +490,6 @@ module OpenID
484
490
  def self.from_message(message, op_endpoint)
485
491
  obj = self.allocate
486
492
  obj.message = message
487
- obj.namespace = message.get_openid_namespace()
488
493
  obj.op_endpoint = op_endpoint
489
494
  mode = message.get_arg(OPENID_NS, 'mode')
490
495
  if mode == "checkid_immediate"
@@ -496,44 +501,46 @@ module OpenID
496
501
  end
497
502
 
498
503
  obj.return_to = message.get_arg(OPENID_NS, 'return_to')
499
- if obj.namespace == OPENID1_NS and !obj.return_to
504
+ if message.is_openid1 and !obj.return_to
500
505
  msg = sprintf("Missing required field 'return_to' from %s",
501
506
  message)
502
507
  raise ProtocolError.new(message, msg)
503
508
  end
504
509
 
505
510
  obj.identity = message.get_arg(OPENID_NS, 'identity')
506
- if obj.identity and message.is_openid2()
507
- obj.claimed_id = message.get_arg(OPENID_NS, 'claimed_id')
508
- if !obj.claimed_id
511
+ obj.claimed_id = message.get_arg(OPENID_NS, 'claimed_id')
512
+ if message.is_openid1()
513
+ if !obj.identity
514
+ s = "OpenID 1 message did not contain openid.identity"
515
+ raise ProtocolError.new(message, s)
516
+ end
517
+ else
518
+ if obj.identity and not obj.claimed_id
509
519
  s = ("OpenID 2.0 message contained openid.identity but not " +
510
520
  "claimed_id")
511
521
  raise ProtocolError.new(message, s)
522
+ elsif obj.claimed_id and not obj.identity
523
+ s = ("OpenID 2.0 message contained openid.claimed_id but not " +
524
+ "identity")
525
+ raise ProtocolError.new(message, s)
512
526
  end
513
- else
514
- obj.claimed_id = nil
515
- end
516
-
517
- if !obj.identity and obj.namespace == OPENID1_NS
518
- s = "OpenID 1 message did not contain openid.identity"
519
- raise ProtocolError.new(message, s)
520
527
  end
521
528
 
522
529
  # There's a case for making self.trust_root be a TrustRoot
523
530
  # here. But if TrustRoot isn't currently part of the "public"
524
531
  # API, I'm not sure it's worth doing.
525
- if obj.namespace == OPENID1_NS
526
- obj.trust_root = message.get_arg(
527
- OPENID_NS, 'trust_root', obj.return_to)
532
+ if message.is_openid1
533
+ trust_root_param = 'trust_root'
528
534
  else
529
- obj.trust_root = message.get_arg(
530
- OPENID_NS, 'realm', obj.return_to)
535
+ trust_root_param = 'realm'
536
+ end
537
+ trust_root = message.get_arg(OPENID_NS, trust_root_param)
538
+ trust_root = obj.return_to if (trust_root.nil? || trust_root.empty?)
539
+ obj.trust_root = trust_root
531
540
 
532
- if !obj.return_to and
533
- !obj.trust_root
534
- raise ProtocolError.new(message, "openid.realm required when " +
535
- "openid.return_to absent")
536
- end
541
+ if !message.is_openid1 and !obj.return_to and !obj.trust_root
542
+ raise ProtocolError.new(message, "openid.realm required when " +
543
+ "openid.return_to absent")
537
544
  end
538
545
 
539
546
  obj.assoc_handle = message.get_arg(OPENID_NS, 'assoc_handle')
@@ -642,15 +649,18 @@ module OpenID
642
649
  #
643
650
  # This parameter is new in OpenID 2.0.
644
651
  #
652
+ # Returns an OpenIDResponse object containing a OpenID id_res message.
653
+ #
654
+ # Raises NoReturnToError if the return_to is missing.
655
+ #
645
656
  # Version 2.0 deprecates +server_url+ and adds +claimed_id+.
646
657
  def answer(allow, server_url=nil, identity=nil, claimed_id=nil)
647
- # FIXME: undocumented exceptions
648
658
  if !@return_to
649
659
  raise NoReturnToError
650
660
  end
651
661
 
652
662
  if !server_url
653
- if @namespace != OPENID1_NS and !@op_endpoint
663
+ if @message.is_openid2 and !@op_endpoint
654
664
  # In other words, that warning I raised in
655
665
  # Server.__init__? You should pay attention to it now.
656
666
  raise RuntimeError, ("#{self} should be constructed with "\
@@ -663,7 +673,7 @@ module OpenID
663
673
 
664
674
  if allow
665
675
  mode = 'id_res'
666
- elsif @namespace == OPENID1_NS
676
+ elsif @message.is_openid1
667
677
  if @immediate
668
678
  mode = 'id_res'
669
679
  else
@@ -679,9 +689,9 @@ module OpenID
679
689
 
680
690
  response = OpenIDResponse.new(self)
681
691
 
682
- if claimed_id and @namespace == OPENID1_NS
692
+ if claimed_id and @message.is_openid1
683
693
  raise VersionError, ("claimed_id is new in OpenID 2.0 and not "\
684
- "available for #{@namespace}")
694
+ "available for #{@message.get_openid_namespace}")
685
695
  end
686
696
 
687
697
  if identity and !claimed_id
@@ -715,7 +725,7 @@ module OpenID
715
725
  response_identity = nil
716
726
  end
717
727
 
718
- if @namespace == OPENID1_NS and !response_identity
728
+ if @message.is_openid1 and !response_identity
719
729
  raise ArgumentError, ("Request was an OpenID 1 request, so "\
720
730
  "response must include an identifier.")
721
731
  end
@@ -729,7 +739,7 @@ module OpenID
729
739
 
730
740
  if response_identity
731
741
  response.fields.set_arg(OPENID_NS, 'identity', response_identity)
732
- if @namespace == OPENID2_NS
742
+ if @message.is_openid2
733
743
  response.fields.set_arg(OPENID_NS,
734
744
  'claimed_id', response_claimed_id)
735
745
  end
@@ -737,7 +747,7 @@ module OpenID
737
747
  else
738
748
  response.fields.set_arg(OPENID_NS, 'mode', mode)
739
749
  if @immediate
740
- if @namespace == OPENID1_NS and !server_url
750
+ if @message.is_openid1 and !server_url
741
751
  raise ArgumentError, ("setup_url is required for allow=false "\
742
752
  "in OpenID 1.x immediate mode.")
743
753
  end
@@ -747,6 +757,7 @@ module OpenID
747
757
  setup_request = self.class.new(@identity, @return_to,
748
758
  @op_endpoint, @trust_root, false,
749
759
  @assoc_handle)
760
+ setup_request.message = Message.new(@message.get_openid_namespace)
750
761
  setup_url = setup_request.encode_to_url(server_url)
751
762
  response.fields.set_arg(OPENID_NS, 'user_setup_url', setup_url)
752
763
  end
@@ -774,7 +785,7 @@ module OpenID
774
785
  'return_to' => @return_to}
775
786
 
776
787
  if @trust_root
777
- if @namespace == OPENID1_NS
788
+ if @message.is_openid1
778
789
  q['trust_root'] = @trust_root
779
790
  else
780
791
  q['realm'] = @trust_root
@@ -785,8 +796,8 @@ module OpenID
785
796
  q['assoc_handle'] = @assoc_handle
786
797
  end
787
798
 
788
- response = Message.new(@namespace)
789
- response.update_args(@namespace, q)
799
+ response = Message.new(@message.get_openid_namespace)
800
+ response.update_args(@message.get_openid_namespace, q)
790
801
  return response.to_url(server_url)
791
802
  end
792
803
 
@@ -810,7 +821,7 @@ module OpenID
810
821
  "immediate mode requests.")
811
822
  end
812
823
 
813
- response = Message.new(@namespace)
824
+ response = Message.new(@message.get_openid_namespace)
814
825
  response.set_arg(OPENID_NS, 'mode', 'cancel')
815
826
  return response.to_url(@return_to)
816
827
  end
@@ -859,10 +870,19 @@ module OpenID
859
870
  @fields)
860
871
  end
861
872
 
862
- def to_form_markup
863
- # Returns the form markup for this response.
864
- return @fields.to_form_markup(
865
- @fields.get_arg(OPENID_NS, 'return_to'))
873
+ # form_tag_attrs is a hash of attributes to be added to the form
874
+ # tag. 'accept-charset' and 'enctype' have defaults that can be
875
+ # overridden. If a value is supplied for 'action' or 'method',
876
+ # it will be replaced.
877
+ # Returns the form markup for this response.
878
+ def to_form_markup(form_tag_attrs=nil)
879
+ return @fields.to_form_markup(@request.return_to, form_tag_attrs)
880
+ end
881
+
882
+ # Wraps the form tag from to_form_markup in a complete HTML document
883
+ # that uses javascript to autosubmit the form.
884
+ def to_html(form_tag_attrs=nil)
885
+ return Util.auto_submit_html(to_form_markup(form_tag_attrs))
866
886
  end
867
887
 
868
888
  def render_as_form
@@ -882,7 +902,7 @@ module OpenID
882
902
  # How should I be encoded?
883
903
  # returns one of ENCODE_URL or ENCODE_KVFORM.
884
904
  if BROWSER_REQUEST_MODES.member?(@request.mode)
885
- if @fields.get_openid_namespace == OPENID2_NS and
905
+ if @fields.is_openid2 and
886
906
  encode_to_url.length > OPENID1_URL_LIMIT
887
907
  return ENCODE_HTML_FORM
888
908
  else
@@ -1044,7 +1064,11 @@ module OpenID
1044
1064
  assoc = create_association(true)
1045
1065
  end
1046
1066
 
1047
- signed_response.fields = assoc.sign_message(signed_response.fields)
1067
+ begin
1068
+ signed_response.fields = assoc.sign_message(signed_response.fields)
1069
+ rescue KVFormError => err
1070
+ raise EncodingError, err
1071
+ end
1048
1072
  return signed_response
1049
1073
  end
1050
1074
 
@@ -1220,7 +1244,14 @@ module OpenID
1220
1244
  return nil
1221
1245
  end
1222
1246
 
1223
- message = Message.from_post_args(query)
1247
+ begin
1248
+ message = Message.from_post_args(query)
1249
+ rescue InvalidOpenIDNamespace => e
1250
+ query = query.dup
1251
+ query['openid.ns'] = OPENID2_NS
1252
+ message = Message.from_post_args(query)
1253
+ raise ProtocolError.new(message, e.to_s)
1254
+ end
1224
1255
 
1225
1256
  mode = message.get_arg(OPENID_NS, 'mode')
1226
1257
  if !mode
@@ -1238,7 +1269,7 @@ module OpenID
1238
1269
  # This implementation always raises ProtocolError.
1239
1270
  def default_decoder(message, server)
1240
1271
  mode = message.get_arg(OPENID_NS, 'mode')
1241
- msg = sprintf("No decoder for mode %s", mode)
1272
+ msg = sprintf("Unrecognized OpenID mode %s", mode)
1242
1273
  raise ProtocolError.new(message, msg)
1243
1274
  end
1244
1275
  end
@@ -1415,6 +1446,10 @@ module OpenID
1415
1446
  return to_message().to_form_markup(get_return_to())
1416
1447
  end
1417
1448
 
1449
+ def to_html
1450
+ return Util.auto_submit_html(to_form_markup)
1451
+ end
1452
+
1418
1453
  # How should I be encoded?
1419
1454
  #
1420
1455
  # Returns one of ENCODE_URL, ENCODE_KVFORM, or None. If None,
@@ -1422,7 +1457,7 @@ module OpenID
1422
1457
  # displayed to the user.
1423
1458
  def which_encoding
1424
1459
  if has_return_to()
1425
- if @openid_message.get_openid_namespace() == OPENID2_NS and
1460
+ if @openid_message.is_openid2 and
1426
1461
  encode_to_url().length > OPENID1_URL_LIMIT
1427
1462
  return ENCODE_HTML_FORM
1428
1463
  else
@@ -18,19 +18,24 @@ module OpenID
18
18
 
19
19
  module TrustRoot
20
20
  TOP_LEVEL_DOMAINS = %w'
21
- com edu gov int mil net org biz info name museum coop aero ac ad
22
- ae af ag ai al am an ao aq ar as at au aw az ba bb bd be bf bg
23
- bh bi bj bm bn bo br bs bt bv bw by bz ca cc cd cf cg ch ci ck
24
- cl cm cn co cr cu cv cx cy cz de dj dk dm do dz ec ee eg eh er
25
- es et eu fi fj fk fm fo fr ga gd ge gf gg gh gi gl gm gn gp gq
26
- gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in io iq ir is
27
- it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk
28
- lr ls lt lu lv ly ma mc md mg mh mk ml mm mn mo mp mq mr ms mt
29
- mu mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe
30
- pf pg ph pk pl pm pn pr ps pt pw py qa re ro ru rw sa sb sc sd
31
- se sg sh si sj sk sl sm sn so sr st sv sy sz tc td tf tg th tj
32
- tk tm tn to tp tr tt tv tw tz ua ug uk um us uy uz va vc ve vg
33
- vi vn vu wf ws ye yt yu za zm zw'
21
+ ac ad ae aero af ag ai al am an ao aq ar arpa as asia at
22
+ au aw ax az ba bb bd be bf bg bh bi biz bj bm bn bo br bs bt
23
+ bv bw by bz ca cat cc cd cf cg ch ci ck cl cm cn co com coop
24
+ cr cu cv cx cy cz de dj dk dm do dz ec edu ee eg er es et eu
25
+ fi fj fk fm fo fr ga gb gd ge gf gg gh gi gl gm gn gov gp gq
26
+ gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in info int
27
+ io iq ir is it je jm jo jobs jp ke kg kh ki km kn kp kr kw
28
+ ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mil
29
+ mk ml mm mn mo mobi mp mq mr ms mt mu museum mv mw mx my mz
30
+ na name nc ne net nf ng ni nl no np nr nu nz om org pa pe pf
31
+ pg ph pk pl pm pn pr pro ps pt pw py qa re ro rs ru rw sa sb
32
+ sc sd se sg sh si sj sk sl sm sn so sr st su sv sy sz tc td
33
+ tel tf tg th tj tk tl tm tn to tp tr travel tt tv tw tz ua
34
+ ug uk us uy uz va vc ve vg vi vn vu wf ws xn--0zwm56d
35
+ xn--11b5bs3a9aj6g xn--80akhbyknj4f xn--9t4b11yi5a
36
+ xn--deba0ad xn--g6w251d xn--hgbk6aj7f53bba
37
+ xn--hlcj6aya9esc7a xn--jxalpdlp xn--kgbechtv xn--zckzah ye
38
+ yt yu za zm zw'
34
39
 
35
40
  ALLOWED_PROTOCOLS = ['http', 'https']
36
41
 
@@ -187,8 +192,6 @@ module OpenID
187
192
  end
188
193
 
189
194
  def TrustRoot.parse(trust_root)
190
- return nil unless trust_root.instance_of?(String)
191
-
192
195
  trust_root = trust_root.dup
193
196
  unparsed = trust_root.dup
194
197
 
@@ -217,8 +220,13 @@ module OpenID
217
220
  return new(unparsed, proto, wildcard, host, port, path)
218
221
  end
219
222
 
220
- def TrustRoot.check_sanity(trust_root)
221
- return TrustRoot.parse(trust_root).sane?
223
+ def TrustRoot.check_sanity(trust_root_string)
224
+ trust_root = TrustRoot.parse(trust_root_string)
225
+ if trust_root.nil?
226
+ return false
227
+ else
228
+ return trust_root.sane?
229
+ end
222
230
  end
223
231
 
224
232
  # quick func for validating a url against a trust root. See the
data/lib/openid/util.rb CHANGED
@@ -4,15 +4,11 @@ require "logger"
4
4
 
5
5
  require "openid/extras"
6
6
 
7
- srand(Time.now.to_f)
8
-
9
7
  # See OpenID::Consumer or OpenID::Server modules, as well as the store classes
10
8
  module OpenID
11
9
  class AssertionError < Exception
12
10
  end
13
11
 
14
- VERSION = "2.0.4"
15
-
16
12
  # Exceptions that are raised by the library are subclasses of this
17
13
  # exception type, so if you want to catch all exceptions raised by
18
14
  # the library, you can catch OpenIDError
@@ -90,6 +86,25 @@ module OpenID
90
86
  def Util.log(message)
91
87
  logger.info(message)
92
88
  end
89
+
90
+ def Util.auto_submit_html(form, title='OpenID transaction in progress')
91
+ return "
92
+ <html>
93
+ <head>
94
+ <title>#{title}</title>
95
+ </head>
96
+ <body onload='document.forms[0].submit();'>
97
+ #{form}
98
+ <script>
99
+ var elements = document.forms[0].elements;
100
+ for (var i = 0; i < elements.length; i++) {
101
+ elements[i].style.display = \"none\";
102
+ }
103
+ </script>
104
+ </body>
105
+ </html>
106
+ "
107
+ end
93
108
  end
94
109
 
95
110
  end