entp-ruby-openid 2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. data/CHANGELOG +215 -0
  2. data/INSTALL +47 -0
  3. data/LICENSE +210 -0
  4. data/NOTICE +2 -0
  5. data/README +85 -0
  6. data/UPGRADE +127 -0
  7. data/admin/runtests.rb +45 -0
  8. data/examples/README +32 -0
  9. data/examples/active_record_openid_store/README +58 -0
  10. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
  11. data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
  12. data/examples/active_record_openid_store/init.rb +8 -0
  13. data/examples/active_record_openid_store/lib/association.rb +10 -0
  14. data/examples/active_record_openid_store/lib/nonce.rb +3 -0
  15. data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
  16. data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
  17. data/examples/active_record_openid_store/test/store_test.rb +212 -0
  18. data/examples/discover +49 -0
  19. data/examples/rails_openid/README +153 -0
  20. data/examples/rails_openid/Rakefile +10 -0
  21. data/examples/rails_openid/app/controllers/application.rb +4 -0
  22. data/examples/rails_openid/app/controllers/consumer_controller.rb +125 -0
  23. data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
  24. data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
  25. data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
  26. data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
  27. data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
  28. data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
  29. data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
  30. data/examples/rails_openid/app/views/login/index.rhtml +56 -0
  31. data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
  32. data/examples/rails_openid/config/boot.rb +19 -0
  33. data/examples/rails_openid/config/database.yml +74 -0
  34. data/examples/rails_openid/config/environment.rb +54 -0
  35. data/examples/rails_openid/config/environments/development.rb +19 -0
  36. data/examples/rails_openid/config/environments/production.rb +19 -0
  37. data/examples/rails_openid/config/environments/test.rb +19 -0
  38. data/examples/rails_openid/config/routes.rb +24 -0
  39. data/examples/rails_openid/doc/README_FOR_APP +2 -0
  40. data/examples/rails_openid/public/404.html +8 -0
  41. data/examples/rails_openid/public/500.html +8 -0
  42. data/examples/rails_openid/public/dispatch.cgi +12 -0
  43. data/examples/rails_openid/public/dispatch.fcgi +26 -0
  44. data/examples/rails_openid/public/dispatch.rb +12 -0
  45. data/examples/rails_openid/public/favicon.ico +0 -0
  46. data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
  47. data/examples/rails_openid/public/javascripts/controls.js +750 -0
  48. data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
  49. data/examples/rails_openid/public/javascripts/effects.js +854 -0
  50. data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
  51. data/examples/rails_openid/public/robots.txt +1 -0
  52. data/examples/rails_openid/script/about +3 -0
  53. data/examples/rails_openid/script/breakpointer +3 -0
  54. data/examples/rails_openid/script/console +3 -0
  55. data/examples/rails_openid/script/destroy +3 -0
  56. data/examples/rails_openid/script/generate +3 -0
  57. data/examples/rails_openid/script/performance/benchmarker +3 -0
  58. data/examples/rails_openid/script/performance/profiler +3 -0
  59. data/examples/rails_openid/script/plugin +3 -0
  60. data/examples/rails_openid/script/process/reaper +3 -0
  61. data/examples/rails_openid/script/process/spawner +3 -0
  62. data/examples/rails_openid/script/process/spinner +3 -0
  63. data/examples/rails_openid/script/runner +3 -0
  64. data/examples/rails_openid/script/server +3 -0
  65. data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
  66. data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
  67. data/examples/rails_openid/test/test_helper.rb +28 -0
  68. data/lib/hmac/hmac.rb +112 -0
  69. data/lib/hmac/sha1.rb +11 -0
  70. data/lib/hmac/sha2.rb +25 -0
  71. data/lib/openid.rb +22 -0
  72. data/lib/openid/association.rb +249 -0
  73. data/lib/openid/consumer.rb +395 -0
  74. data/lib/openid/consumer/associationmanager.rb +344 -0
  75. data/lib/openid/consumer/checkid_request.rb +186 -0
  76. data/lib/openid/consumer/discovery.rb +497 -0
  77. data/lib/openid/consumer/discovery_manager.rb +123 -0
  78. data/lib/openid/consumer/html_parse.rb +134 -0
  79. data/lib/openid/consumer/idres.rb +523 -0
  80. data/lib/openid/consumer/responses.rb +150 -0
  81. data/lib/openid/cryptutil.rb +115 -0
  82. data/lib/openid/dh.rb +89 -0
  83. data/lib/openid/extension.rb +39 -0
  84. data/lib/openid/extensions/ax.rb +539 -0
  85. data/lib/openid/extensions/oauth.rb +91 -0
  86. data/lib/openid/extensions/pape.rb +179 -0
  87. data/lib/openid/extensions/sreg.rb +277 -0
  88. data/lib/openid/extras.rb +11 -0
  89. data/lib/openid/fetchers.rb +258 -0
  90. data/lib/openid/kvform.rb +136 -0
  91. data/lib/openid/kvpost.rb +58 -0
  92. data/lib/openid/message.rb +553 -0
  93. data/lib/openid/protocolerror.rb +12 -0
  94. data/lib/openid/server.rb +1544 -0
  95. data/lib/openid/store.rb +10 -0
  96. data/lib/openid/store/filesystem.rb +272 -0
  97. data/lib/openid/store/interface.rb +75 -0
  98. data/lib/openid/store/memcache.rb +109 -0
  99. data/lib/openid/store/memory.rb +84 -0
  100. data/lib/openid/store/nonce.rb +68 -0
  101. data/lib/openid/trustroot.rb +349 -0
  102. data/lib/openid/urinorm.rb +75 -0
  103. data/lib/openid/util.rb +119 -0
  104. data/lib/openid/version.rb +3 -0
  105. data/lib/openid/yadis.rb +15 -0
  106. data/lib/openid/yadis/accept.rb +148 -0
  107. data/lib/openid/yadis/constants.rb +21 -0
  108. data/lib/openid/yadis/discovery.rb +153 -0
  109. data/lib/openid/yadis/filters.rb +205 -0
  110. data/lib/openid/yadis/htmltokenizer.rb +305 -0
  111. data/lib/openid/yadis/parsehtml.rb +45 -0
  112. data/lib/openid/yadis/services.rb +42 -0
  113. data/lib/openid/yadis/xrds.rb +155 -0
  114. data/lib/openid/yadis/xri.rb +90 -0
  115. data/lib/openid/yadis/xrires.rb +91 -0
  116. data/test/data/test_discover/openid_utf8.html +11 -0
  117. data/test/support/test_data_mixin.rb +127 -0
  118. data/test/support/test_util.rb +53 -0
  119. data/test/support/yadis_data.rb +131 -0
  120. data/test/support/yadis_data/accept.txt +124 -0
  121. data/test/support/yadis_data/dh.txt +29 -0
  122. data/test/support/yadis_data/example-xrds.xml +14 -0
  123. data/test/support/yadis_data/linkparse.txt +587 -0
  124. data/test/support/yadis_data/n2b64 +650 -0
  125. data/test/support/yadis_data/test1-discover.txt +137 -0
  126. data/test/support/yadis_data/test1-parsehtml.txt +152 -0
  127. data/test/support/yadis_data/test_discover/malformed_meta_tag.html +19 -0
  128. data/test/support/yadis_data/test_discover/openid.html +11 -0
  129. data/test/support/yadis_data/test_discover/openid2.html +11 -0
  130. data/test/support/yadis_data/test_discover/openid2_xrds.xml +12 -0
  131. data/test/support/yadis_data/test_discover/openid2_xrds_no_local_id.xml +11 -0
  132. data/test/support/yadis_data/test_discover/openid_1_and_2.html +11 -0
  133. data/test/support/yadis_data/test_discover/openid_1_and_2_xrds.xml +16 -0
  134. data/test/support/yadis_data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
  135. data/test/support/yadis_data/test_discover/openid_and_yadis.html +12 -0
  136. data/test/support/yadis_data/test_discover/openid_no_delegate.html +10 -0
  137. data/test/support/yadis_data/test_discover/openid_utf8.html +11 -0
  138. data/test/support/yadis_data/test_discover/yadis_0entries.xml +12 -0
  139. data/test/support/yadis_data/test_discover/yadis_2_bad_local_id.xml +15 -0
  140. data/test/support/yadis_data/test_discover/yadis_2entries_delegate.xml +22 -0
  141. data/test/support/yadis_data/test_discover/yadis_2entries_idp.xml +21 -0
  142. data/test/support/yadis_data/test_discover/yadis_another_delegate.xml +14 -0
  143. data/test/support/yadis_data/test_discover/yadis_idp.xml +12 -0
  144. data/test/support/yadis_data/test_discover/yadis_idp_delegate.xml +13 -0
  145. data/test/support/yadis_data/test_discover/yadis_no_delegate.xml +11 -0
  146. data/test/support/yadis_data/test_xrds/=j3h.2007.11.14.xrds +25 -0
  147. data/test/support/yadis_data/test_xrds/README +12 -0
  148. data/test/support/yadis_data/test_xrds/delegated-20060809-r1.xrds +34 -0
  149. data/test/support/yadis_data/test_xrds/delegated-20060809-r2.xrds +34 -0
  150. data/test/support/yadis_data/test_xrds/delegated-20060809.xrds +34 -0
  151. data/test/support/yadis_data/test_xrds/no-xrd.xml +7 -0
  152. data/test/support/yadis_data/test_xrds/not-xrds.xml +2 -0
  153. data/test/support/yadis_data/test_xrds/prefixsometimes.xrds +34 -0
  154. data/test/support/yadis_data/test_xrds/ref.xrds +109 -0
  155. data/test/support/yadis_data/test_xrds/sometimesprefix.xrds +34 -0
  156. data/test/support/yadis_data/test_xrds/spoof1.xrds +25 -0
  157. data/test/support/yadis_data/test_xrds/spoof2.xrds +25 -0
  158. data/test/support/yadis_data/test_xrds/spoof3.xrds +37 -0
  159. data/test/support/yadis_data/test_xrds/status222.xrds +9 -0
  160. data/test/support/yadis_data/test_xrds/subsegments.xrds +58 -0
  161. data/test/support/yadis_data/test_xrds/valid-populated-xrds.xml +39 -0
  162. data/test/support/yadis_data/trustroot.txt +153 -0
  163. data/test/support/yadis_data/urinorm.txt +79 -0
  164. data/test/test_accept.rb +170 -0
  165. data/test/test_association.rb +268 -0
  166. data/test/test_associationmanager.rb +918 -0
  167. data/test/test_ax.rb +690 -0
  168. data/test/test_checkid_request.rb +293 -0
  169. data/test/test_consumer.rb +260 -0
  170. data/test/test_cryptutil.rb +119 -0
  171. data/test/test_dh.rb +85 -0
  172. data/test/test_discover.rb +848 -0
  173. data/test/test_discovery_manager.rb +259 -0
  174. data/test/test_extension.rb +46 -0
  175. data/test/test_extras.rb +35 -0
  176. data/test/test_fetchers.rb +554 -0
  177. data/test/test_filters.rb +269 -0
  178. data/test/test_helper.rb +4 -0
  179. data/test/test_idres.rb +961 -0
  180. data/test/test_kvform.rb +164 -0
  181. data/test/test_kvpost.rb +64 -0
  182. data/test/test_linkparse.rb +100 -0
  183. data/test/test_message.rb +1115 -0
  184. data/test/test_nonce.rb +89 -0
  185. data/test/test_oauth.rb +176 -0
  186. data/test/test_openid_yadis.rb +177 -0
  187. data/test/test_pape.rb +248 -0
  188. data/test/test_parsehtml.rb +79 -0
  189. data/test/test_responses.rb +63 -0
  190. data/test/test_server.rb +2455 -0
  191. data/test/test_sreg.rb +479 -0
  192. data/test/test_stores.rb +292 -0
  193. data/test/test_trustroot.rb +111 -0
  194. data/test/test_urinorm.rb +34 -0
  195. data/test/test_util.rb +145 -0
  196. data/test/test_xrds.rb +167 -0
  197. data/test/test_xri.rb +48 -0
  198. data/test/test_xrires.rb +67 -0
  199. data/test/test_yadis_discovery.rb +218 -0
  200. metadata +268 -0
@@ -0,0 +1,79 @@
1
+ require "test_helper"
2
+ require "openid/yadis/parsehtml"
3
+
4
+ module OpenID
5
+ class ParseHTMLTestCase < Test::Unit::TestCase
6
+ include OpenID::TestDataMixin
7
+
8
+ def test_parsehtml
9
+ reserved_values = ['None', 'EOF']
10
+ chunks = read_data_file('test1-parsehtml.txt', false).split("\f\n")
11
+ test_num = 1
12
+
13
+ chunks.each{|c|
14
+ expected, html = c.split("\n", 2)
15
+ found = Yadis::html_yadis_location(html)
16
+
17
+ assert(!reserved_values.member?(found))
18
+
19
+ # this case is a little hard to detect and the distinction
20
+ # seems unimportant
21
+ expected = "None" if expected == "EOF"
22
+
23
+ found = "None" if found.nil?
24
+ assert_equal(expected, found, html.split("\n",2)[0])
25
+ }
26
+ end
27
+ end
28
+
29
+ # the HTML tokenizer test
30
+ class TC_TestHTMLTokenizer < Test::Unit::TestCase
31
+ def test_bad_link
32
+ toke = HTMLTokenizer.new("<p><a href=http://bad.com/link>foo</a></p>")
33
+ assert("http://bad.com/link" == toke.getTag("a").attr_hash['href'])
34
+ end
35
+
36
+ def test_namespace
37
+ toke = HTMLTokenizer.new("<f:table xmlns:f=\"http://www.com/foo\">")
38
+ assert("http://www.com/foo" == toke.getTag("f:table").attr_hash['xmlns:f'])
39
+ end
40
+
41
+ def test_comment
42
+ toke = HTMLTokenizer.new("<!-- comment on me -->")
43
+ t = toke.getNextToken
44
+ assert(HTMLComment == t.class)
45
+ assert("comment on me" == t.contents)
46
+ end
47
+
48
+ def test_full
49
+ page = "<HTML>
50
+ <HEAD>
51
+ <TITLE>This is the title</TITLE>
52
+ </HEAD>
53
+ <!-- Here comes the <a href=\"missing.link\">blah</a>
54
+ comment body
55
+ -->
56
+ <BODY>
57
+ <H1>This is the header</H1>
58
+ <P>
59
+ This is the paragraph, it contains
60
+ <a href=\"link.html\">links</a>,
61
+ <img src=\"blah.gif\" optional alt='images
62
+ are
63
+ really cool'>. Ok, here is some more text and
64
+ <A href=\"http://another.link.com/\" target=\"_blank\">another link</A>.
65
+ </P>
66
+ </body>
67
+ </HTML>
68
+ "
69
+ toke = HTMLTokenizer.new(page)
70
+
71
+ assert("<h1>" == toke.getTag("h1", "h2", "h3").to_s.downcase)
72
+ assert(HTMLTag.new("<a href=\"link.html\">") == toke.getTag("IMG", "A"))
73
+ assert("links" == toke.getTrimmedText)
74
+ assert(toke.getTag("IMG", "A").attr_hash['optional'])
75
+ assert("_blank" == toke.getTag("IMG", "A").attr_hash['target'])
76
+ end
77
+ end
78
+ end
79
+
@@ -0,0 +1,63 @@
1
+ require "test_helper"
2
+ require "openid/consumer/discovery"
3
+ require "openid/consumer/responses"
4
+
5
+ module OpenID
6
+ class Consumer
7
+ module TestResponses
8
+ class TestSuccessResponse < Test::Unit::TestCase
9
+ def setup
10
+ @endpoint = OpenIDServiceEndpoint.new
11
+ @endpoint.claimed_id = 'identity_url'
12
+ end
13
+
14
+ def test_extension_response
15
+ q = {
16
+ 'ns.sreg' => 'urn:sreg',
17
+ 'ns.unittest' => 'urn:unittest',
18
+ 'unittest.one' => '1',
19
+ 'unittest.two' => '2',
20
+ 'sreg.nickname' => 'j3h',
21
+ 'return_to' => 'return_to',
22
+ }
23
+ signed_list = q.keys.map { |k| 'openid.' + k }
24
+ msg = Message.from_openid_args(q)
25
+ resp = SuccessResponse.new(@endpoint, msg, signed_list)
26
+ utargs = resp.extension_response('urn:unittest', false)
27
+ assert_equal(utargs, {'one' => '1', 'two' => '2'})
28
+ sregargs = resp.extension_response('urn:sreg', false)
29
+ assert_equal(sregargs, {'nickname' => 'j3h'})
30
+ end
31
+
32
+ def test_extension_response_signed
33
+ args = {
34
+ 'ns.sreg' => 'urn:sreg',
35
+ 'ns.unittest' => 'urn:unittest',
36
+ 'unittest.one' => '1',
37
+ 'unittest.two' => '2',
38
+ 'sreg.nickname' => 'j3h',
39
+ 'sreg.dob' => 'yesterday',
40
+ 'return_to' => 'return_to',
41
+ 'signed' => 'sreg.nickname,unittest.one,sreg.dob',
42
+ }
43
+
44
+ signed_list = ['openid.sreg.nickname',
45
+ 'openid.unittest.one',
46
+ 'openid.sreg.dob',]
47
+
48
+ msg = Message.from_openid_args(args)
49
+ resp = SuccessResponse.new(@endpoint, msg, signed_list)
50
+
51
+ # All args in this NS are signed, so expect all.
52
+ sregargs = resp.extension_response('urn:sreg', true)
53
+ assert_equal(sregargs, {'nickname' => 'j3h', 'dob' => 'yesterday'})
54
+
55
+ # Not all args in this NS are signed, so expect nil when
56
+ # asking for them.
57
+ utargs = resp.extension_response('urn:unittest', true)
58
+ assert_equal(nil, utargs)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,2455 @@
1
+ require "test_helper"
2
+ require 'openid/server'
3
+ require 'openid/cryptutil'
4
+ require 'openid/association'
5
+ require 'openid/util'
6
+ require 'openid/message'
7
+ require 'openid/store/memory'
8
+ require 'openid/dh'
9
+ require 'openid/consumer/associationmanager'
10
+ require 'support/test_util'
11
+ require 'uri'
12
+
13
+ # In general, if you edit or add tests here, try to move in the
14
+ # direction of testing smaller units. For testing the external
15
+ # interfaces, we'll be developing an implementation-agnostic testing
16
+ # suite.
17
+
18
+ # for more, see /etc/ssh/moduli
19
+
20
+ module OpenID
21
+
22
+ ALT_MODULUS = 0xCAADDDEC1667FC68B5FA15D53C4E1532DD24561A1A2D47A12C01ABEA1E00731F6921AAC40742311FDF9E634BB7131BEE1AF240261554389A910425E044E88C8359B010F5AD2B80E29CB1A5B027B19D9E01A6F63A6F45E5D7ED2FF6A2A0085050A7D0CF307C3DB51D2490355907B4427C23A98DF1EB8ABEF2BA209BB7AFFE86A7
23
+ ALT_GEN = 5
24
+
25
+ class CatchLogs
26
+ def catchlogs_setup
27
+ @old_logger = Util.logger
28
+ Util.logger = self.method('got_log_message')
29
+ @messages = []
30
+ end
31
+
32
+ def got_log_message(message)
33
+ @messages << message
34
+ end
35
+
36
+ def teardown
37
+ Util.logger = @old_logger
38
+ end
39
+ end
40
+
41
+ class TestProtocolError < Test::Unit::TestCase
42
+ def test_browserWithReturnTo
43
+ return_to = "http://rp.unittest/consumer"
44
+ # will be a ProtocolError raised by Decode or
45
+ # CheckIDRequest.answer
46
+ args = Message.from_post_args({
47
+ 'openid.mode' => 'monkeydance',
48
+ 'openid.identity' => 'http://wagu.unittest/',
49
+ 'openid.return_to' => return_to,
50
+ })
51
+ e = Server::ProtocolError.new(args, "plucky")
52
+ assert(e.has_return_to)
53
+ expected_args = {
54
+ 'openid.mode' => 'error',
55
+ 'openid.error' => 'plucky',
56
+ }
57
+
58
+ rt_base, result_args = e.encode_to_url.split('?', 2)
59
+ result_args = Util.parse_query(result_args)
60
+ assert_equal(result_args, expected_args)
61
+ end
62
+
63
+ def test_browserWithReturnTo_OpenID2_GET
64
+ return_to = "http://rp.unittest/consumer"
65
+ # will be a ProtocolError raised by Decode or
66
+ # CheckIDRequest.answer
67
+ args = Message.from_post_args({
68
+ 'openid.ns' => OPENID2_NS,
69
+ 'openid.mode' => 'monkeydance',
70
+ 'openid.identity' => 'http://wagu.unittest/',
71
+ 'openid.claimed_id' => 'http://wagu.unittest/',
72
+ 'openid.return_to' => return_to,
73
+ })
74
+ e = Server::ProtocolError.new(args, "plucky")
75
+ assert(e.has_return_to)
76
+ expected_args = {
77
+ 'openid.ns' => OPENID2_NS,
78
+ 'openid.mode' => 'error',
79
+ 'openid.error' => 'plucky',
80
+ }
81
+
82
+ rt_base, result_args = e.encode_to_url.split('?', 2)
83
+ result_args = Util.parse_query(result_args)
84
+ assert_equal(result_args, expected_args)
85
+ end
86
+
87
+ def test_browserWithReturnTo_OpenID2_POST
88
+ return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT)
89
+ # will be a ProtocolError raised by Decode or
90
+ # CheckIDRequest.answer
91
+ args = Message.from_post_args({
92
+ 'openid.ns' => OPENID2_NS,
93
+ 'openid.mode' => 'monkeydance',
94
+ 'openid.identity' => 'http://wagu.unittest/',
95
+ 'openid.claimed_id' => 'http://wagu.unittest/',
96
+ 'openid.return_to' => return_to,
97
+ })
98
+ e = Server::ProtocolError.new(args, "plucky")
99
+ assert(e.has_return_to)
100
+ expected_args = {
101
+ 'openid.ns' => OPENID2_NS,
102
+ 'openid.mode' => 'error',
103
+ 'openid.error' => 'plucky',
104
+ }
105
+
106
+ assert(e.which_encoding == Server::ENCODE_HTML_FORM)
107
+ assert(e.to_form_markup == e.to_message.to_form_markup(
108
+ args.get_arg(OPENID_NS, 'return_to')))
109
+ end
110
+
111
+ def test_browserWithReturnTo_OpenID1_exceeds_limit
112
+ return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT)
113
+ # will be a ProtocolError raised by Decode or
114
+ # CheckIDRequest.answer
115
+ args = Message.from_post_args({
116
+ 'openid.mode' => 'monkeydance',
117
+ 'openid.identity' => 'http://wagu.unittest/',
118
+ 'openid.return_to' => return_to,
119
+ })
120
+ e = Server::ProtocolError.new(args, "plucky")
121
+ assert(e.has_return_to)
122
+ expected_args = {
123
+ 'openid.mode' => 'error',
124
+ 'openid.error' => 'plucky',
125
+ }
126
+
127
+ assert(e.which_encoding == Server::ENCODE_URL)
128
+
129
+ rt_base, result_args = e.encode_to_url.split('?', 2)
130
+ result_args = Util.parse_query(result_args)
131
+ assert_equal(result_args, expected_args)
132
+ end
133
+
134
+ def test_noReturnTo
135
+ # will be a ProtocolError raised by Decode or
136
+ # CheckIDRequest.answer
137
+ args = Message.from_post_args({
138
+ 'openid.mode' => 'zebradance',
139
+ 'openid.identity' => 'http://wagu.unittest/',
140
+ })
141
+ e = Server::ProtocolError.new(args, "waffles")
142
+ assert(!e.has_return_to)
143
+ expected = "error:waffles\nmode:error\n"
144
+ assert_equal(e.encode_to_kvform, expected)
145
+ end
146
+
147
+ def test_no_message
148
+ e = Server::ProtocolError.new(nil, "no message")
149
+ assert(e.get_return_to.nil?)
150
+ assert_equal(e.which_encoding, nil)
151
+ end
152
+
153
+ def test_which_encoding_no_message
154
+ e = Server::ProtocolError.new(nil, "no message")
155
+ assert(e.which_encoding.nil?)
156
+ end
157
+ end
158
+
159
+ class TestDecode < Test::Unit::TestCase
160
+ def setup
161
+ @claimed_id = 'http://de.legating.de.coder.unittest/'
162
+ @id_url = "http://decoder.am.unittest/"
163
+ @rt_url = "http://rp.unittest/foobot/?qux=zam"
164
+ @tr_url = "http://rp.unittest/"
165
+ @assoc_handle = "{assoc}{handle}"
166
+ @op_endpoint = 'http://endpoint.unittest/encode'
167
+ @store = Store::Memory.new()
168
+ @server = Server::Server.new(@store, @op_endpoint)
169
+ @decode = Server::Decoder.new(@server).method('decode')
170
+ end
171
+
172
+ def test_none
173
+ args = {}
174
+ r = @decode.call(args)
175
+ assert_equal(r, nil)
176
+ end
177
+
178
+ def test_irrelevant
179
+ args = {
180
+ 'pony' => 'spotted',
181
+ 'sreg.mutant_power' => 'decaffinator',
182
+ }
183
+ assert_raise(Server::ProtocolError) {
184
+ @decode.call(args)
185
+ }
186
+ end
187
+
188
+ def test_bad
189
+ args = {
190
+ 'openid.mode' => 'twos-compliment',
191
+ 'openid.pants' => 'zippered',
192
+ }
193
+ assert_raise(Server::ProtocolError) {
194
+ @decode.call(args)
195
+ }
196
+ end
197
+
198
+ def test_dictOfLists
199
+ args = {
200
+ 'openid.mode' => ['checkid_setup'],
201
+ 'openid.identity' => @id_url,
202
+ 'openid.assoc_handle' => @assoc_handle,
203
+ 'openid.return_to' => @rt_url,
204
+ 'openid.trust_root' => @tr_url,
205
+ }
206
+ begin
207
+ result = @decode.call(args)
208
+ rescue ArgumentError => err
209
+ assert(!err.to_s.index('values').nil?, err.to_s)
210
+ else
211
+ flunk("Expected ArgumentError, but got result #{result}")
212
+ end
213
+ end
214
+
215
+ def test_checkidImmediate
216
+ args = {
217
+ 'openid.mode' => 'checkid_immediate',
218
+ 'openid.identity' => @id_url,
219
+ 'openid.assoc_handle' => @assoc_handle,
220
+ 'openid.return_to' => @rt_url,
221
+ 'openid.trust_root' => @tr_url,
222
+ # should be ignored
223
+ 'openid.some.extension' => 'junk',
224
+ }
225
+ r = @decode.call(args)
226
+ assert(r.is_a?(Server::CheckIDRequest))
227
+ assert_equal(r.mode, "checkid_immediate")
228
+ assert_equal(r.immediate, true)
229
+ assert_equal(r.identity, @id_url)
230
+ assert_equal(r.trust_root, @tr_url)
231
+ assert_equal(r.return_to, @rt_url)
232
+ assert_equal(r.assoc_handle, @assoc_handle)
233
+ end
234
+
235
+ def test_checkidImmediate_constructor
236
+ r = Server::CheckIDRequest.new(@id_url, @rt_url, nil,
237
+ @rt_url, true, @assoc_handle)
238
+ assert(r.mode == 'checkid_immediate')
239
+ assert(r.immediate)
240
+ end
241
+
242
+ def test_checkid_missing_return_to_and_trust_root
243
+ args = {
244
+ 'openid.ns' => OPENID2_NS,
245
+ 'openid.mode' => 'checkid_setup',
246
+ 'openid.identity' => @id_url,
247
+ 'openid.claimed_id' => @id_url,
248
+ 'openid.assoc_handle' => @assoc_handle,
249
+ }
250
+ assert_raise(Server::ProtocolError) {
251
+ m = Message.from_post_args(args)
252
+ Server::CheckIDRequest.from_message(m, @op_endpoint)
253
+ }
254
+ end
255
+
256
+ def test_checkid_id_select
257
+ args = {
258
+ 'openid.ns' => OPENID2_NS,
259
+ 'openid.mode' => 'checkid_setup',
260
+ 'openid.identity' => IDENTIFIER_SELECT,
261
+ 'openid.claimed_id' => IDENTIFIER_SELECT,
262
+ 'openid.assoc_handle' => @assoc_handle,
263
+ 'openid.return_to' => @rt_url,
264
+ 'openid.realm' => @tr_url,
265
+ }
266
+ m = Message.from_post_args(args)
267
+ req = Server::CheckIDRequest.from_message(m, @op_endpoint)
268
+ assert(req.id_select)
269
+ end
270
+
271
+ def test_checkid_not_id_select
272
+ args = {
273
+ 'openid.ns' => OPENID2_NS,
274
+ 'openid.mode' => 'checkid_setup',
275
+ 'openid.assoc_handle' => @assoc_handle,
276
+ 'openid.return_to' => @rt_url,
277
+ 'openid.realm' => @tr_url,
278
+ }
279
+
280
+ id_args = [
281
+ {'openid.claimed_id' => IDENTIFIER_SELECT,
282
+ 'openid.identity' => 'http://bogus.com/'},
283
+
284
+ {'openid.claimed_id' => 'http://bogus.com/',
285
+ 'openid.identity' => 'http://bogus.com/'},
286
+ ]
287
+
288
+ id_args.each { |id|
289
+ m = Message.from_post_args(args.merge(id))
290
+ req = Server::CheckIDRequest.from_message(m, @op_endpoint)
291
+ assert(!req.id_select)
292
+ }
293
+ end
294
+
295
+ def test_checkidSetup
296
+ args = {
297
+ 'openid.mode' => 'checkid_setup',
298
+ 'openid.identity' => @id_url,
299
+ 'openid.assoc_handle' => @assoc_handle,
300
+ 'openid.return_to' => @rt_url,
301
+ 'openid.trust_root' => @tr_url,
302
+ }
303
+ r = @decode.call(args)
304
+ assert(r.is_a?(Server::CheckIDRequest))
305
+ assert_equal(r.mode, "checkid_setup")
306
+ assert_equal(r.immediate, false)
307
+ assert_equal(r.identity, @id_url)
308
+ assert_equal(r.trust_root, @tr_url)
309
+ assert_equal(r.return_to, @rt_url)
310
+ end
311
+
312
+ def test_checkidSetupOpenID2
313
+ args = {
314
+ 'openid.ns' => OPENID2_NS,
315
+ 'openid.mode' => 'checkid_setup',
316
+ 'openid.identity' => @id_url,
317
+ 'openid.claimed_id' => @claimed_id,
318
+ 'openid.assoc_handle' => @assoc_handle,
319
+ 'openid.return_to' => @rt_url,
320
+ 'openid.realm' => @tr_url,
321
+ }
322
+ r = @decode.call(args)
323
+ assert(r.is_a?(Server::CheckIDRequest))
324
+ assert_equal(r.mode, "checkid_setup")
325
+ assert_equal(r.immediate, false)
326
+ assert_equal(r.identity, @id_url)
327
+ assert_equal(r.claimed_id, @claimed_id)
328
+ assert_equal(r.trust_root, @tr_url)
329
+ assert_equal(r.return_to, @rt_url)
330
+ end
331
+
332
+ def test_checkidSetupNoClaimedIDOpenID2
333
+ args = {
334
+ 'openid.ns' => OPENID2_NS,
335
+ 'openid.mode' => 'checkid_setup',
336
+ 'openid.identity' => @id_url,
337
+ 'openid.assoc_handle' => @assoc_handle,
338
+ 'openid.return_to' => @rt_url,
339
+ 'openid.realm' => @tr_url,
340
+ }
341
+ assert_raise(Server::ProtocolError) {
342
+ @decode.call(args)
343
+ }
344
+ end
345
+
346
+ def test_checkidSetupNoIdentityOpenID2
347
+ args = {
348
+ 'openid.ns' => OPENID2_NS,
349
+ 'openid.mode' => 'checkid_setup',
350
+ 'openid.assoc_handle' => @assoc_handle,
351
+ 'openid.return_to' => @rt_url,
352
+ 'openid.realm' => @tr_url,
353
+ }
354
+ r = @decode.call(args)
355
+ assert(r.is_a?(Server::CheckIDRequest))
356
+ assert_equal(r.mode, "checkid_setup")
357
+ assert_equal(r.immediate, false)
358
+ assert_equal(r.identity, nil)
359
+ assert_equal(r.trust_root, @tr_url)
360
+ assert_equal(r.return_to, @rt_url)
361
+ end
362
+
363
+ def test_checkidSetupNoReturnOpenID1
364
+ # Make sure an OpenID 1 request cannot be decoded if it lacks a
365
+ # return_to.
366
+ args = {
367
+ 'openid.mode' => 'checkid_setup',
368
+ 'openid.identity' => @id_url,
369
+ 'openid.assoc_handle' => @assoc_handle,
370
+ 'openid.trust_root' => @tr_url,
371
+ }
372
+ assert_raise(Server::ProtocolError) {
373
+ @decode.call(args)
374
+ }
375
+ end
376
+
377
+ def test_checkidSetupNoReturnOpenID2
378
+ # Make sure an OpenID 2 request with no return_to can be decoded,
379
+ # and make sure a response to such a request raises
380
+ # NoReturnToError.
381
+ args = {
382
+ 'openid.ns' => OPENID2_NS,
383
+ 'openid.mode' => 'checkid_setup',
384
+ 'openid.identity' => @id_url,
385
+ 'openid.claimed_id' => @id_url,
386
+ 'openid.assoc_handle' => @assoc_handle,
387
+ 'openid.realm' => @tr_url,
388
+ }
389
+
390
+ req = @decode.call(args)
391
+ assert(req.is_a?(Server::CheckIDRequest))
392
+
393
+ assert_raise(Server::NoReturnToError) {
394
+ req.answer(false)
395
+ }
396
+
397
+ assert_raise(Server::NoReturnToError) {
398
+ req.encode_to_url('bogus')
399
+ }
400
+
401
+ assert_raise(Server::NoReturnToError) {
402
+ req.cancel_url
403
+ }
404
+ end
405
+
406
+ def test_checkidSetupRealmRequiredOpenID2
407
+ # Make sure that an OpenID 2 request which lacks return_to cannot
408
+ # be decoded if it lacks a realm. Spec => This value
409
+ # (openid.realm) MUST be sent if openid.return_to is omitted.
410
+ args = {
411
+ 'openid.ns' => OPENID2_NS,
412
+ 'openid.mode' => 'checkid_setup',
413
+ 'openid.identity' => @id_url,
414
+ 'openid.assoc_handle' => @assoc_handle,
415
+ }
416
+ assert_raise(Server::ProtocolError) {
417
+ @decode.call(args)
418
+ }
419
+ end
420
+
421
+ def test_checkidSetupBadReturn
422
+ args = {
423
+ 'openid.mode' => 'checkid_setup',
424
+ 'openid.identity' => @id_url,
425
+ 'openid.assoc_handle' => @assoc_handle,
426
+ 'openid.return_to' => 'not a url',
427
+ }
428
+ begin
429
+ result = @decode.call(args)
430
+ rescue Server::ProtocolError => err
431
+ assert(err.openid_message)
432
+ else
433
+ flunk("Expected ProtocolError, instead returned with #{result}")
434
+ end
435
+ end
436
+
437
+ def test_checkidSetupUntrustedReturn
438
+ args = {
439
+ 'openid.mode' => 'checkid_setup',
440
+ 'openid.identity' => @id_url,
441
+ 'openid.assoc_handle' => @assoc_handle,
442
+ 'openid.return_to' => @rt_url,
443
+ 'openid.trust_root' => 'http://not-the-return-place.unittest/',
444
+ }
445
+ begin
446
+ result = @decode.call(args)
447
+ rescue Server::UntrustedReturnURL => err
448
+ assert(err.openid_message, err.to_s)
449
+ else
450
+ flunk("Expected UntrustedReturnURL, instead returned with #{result}")
451
+ end
452
+ end
453
+
454
+ def test_checkidSetupUntrustedReturn_Constructor
455
+ assert_raise(Server::UntrustedReturnURL) {
456
+ Server::CheckIDRequest.new(@id_url, @rt_url, nil,
457
+ 'http://not-the-return-place.unittest/',
458
+ false, @assoc_handle)
459
+ }
460
+ end
461
+
462
+ def test_checkidSetupMalformedReturnURL_Constructor
463
+ assert_raise(Server::MalformedReturnURL) {
464
+ Server::CheckIDRequest.new(@id_url, 'bogus://return.url', nil,
465
+ 'http://trustroot.com/',
466
+ false, @assoc_handle)
467
+ }
468
+ end
469
+
470
+ def test_checkAuth
471
+ args = {
472
+ 'openid.mode' => 'check_authentication',
473
+ 'openid.assoc_handle' => '{dumb}{handle}',
474
+ 'openid.sig' => 'sigblob',
475
+ 'openid.signed' => 'identity,return_to,response_nonce,mode',
476
+ 'openid.identity' => 'signedval1',
477
+ 'openid.return_to' => 'signedval2',
478
+ 'openid.response_nonce' => 'signedval3',
479
+ 'openid.baz' => 'unsigned',
480
+ }
481
+ r = @decode.call(args)
482
+ assert(r.is_a?(Server::CheckAuthRequest))
483
+ assert_equal(r.mode, 'check_authentication')
484
+ assert_equal(r.sig, 'sigblob')
485
+ end
486
+
487
+ def test_checkAuthMissingSignature
488
+ args = {
489
+ 'openid.mode' => 'check_authentication',
490
+ 'openid.assoc_handle' => '{dumb}{handle}',
491
+ 'openid.signed' => 'foo,bar,mode',
492
+ 'openid.foo' => 'signedval1',
493
+ 'openid.bar' => 'signedval2',
494
+ 'openid.baz' => 'unsigned',
495
+ }
496
+ assert_raise(Server::ProtocolError) {
497
+ @decode.call(args)
498
+ }
499
+ end
500
+
501
+ def test_checkAuthAndInvalidate
502
+ args = {
503
+ 'openid.mode' => 'check_authentication',
504
+ 'openid.assoc_handle' => '{dumb}{handle}',
505
+ 'openid.invalidate_handle' => '[[SMART_handle]]',
506
+ 'openid.sig' => 'sigblob',
507
+ 'openid.signed' => 'identity,return_to,response_nonce,mode',
508
+ 'openid.identity' => 'signedval1',
509
+ 'openid.return_to' => 'signedval2',
510
+ 'openid.response_nonce' => 'signedval3',
511
+ 'openid.baz' => 'unsigned',
512
+ }
513
+ r = @decode.call(args)
514
+ assert(r.is_a?(Server::CheckAuthRequest))
515
+ assert_equal(r.invalidate_handle, '[[SMART_handle]]')
516
+ end
517
+
518
+ def test_associateDH
519
+ args = {
520
+ 'openid.mode' => 'associate',
521
+ 'openid.session_type' => 'DH-SHA1',
522
+ 'openid.dh_consumer_public' => "Rzup9265tw==",
523
+ }
524
+ r = @decode.call(args)
525
+ assert(r.is_a?(Server::AssociateRequest))
526
+ assert_equal(r.mode, "associate")
527
+ assert_equal(r.session.session_type, "DH-SHA1")
528
+ assert_equal(r.assoc_type, "HMAC-SHA1")
529
+ assert(r.session.consumer_pubkey)
530
+ end
531
+
532
+ def test_associateDHMissingKey
533
+ # Trying DH assoc w/o public key
534
+ args = {
535
+ 'openid.mode' => 'associate',
536
+ 'openid.session_type' => 'DH-SHA1',
537
+ }
538
+ # Using DH-SHA1 without supplying dh_consumer_public is an error.
539
+ assert_raise(Server::ProtocolError) {
540
+ @decode.call(args)
541
+ }
542
+ end
543
+
544
+ def test_associateDHpubKeyNotB64
545
+ args = {
546
+ 'openid.mode' => 'associate',
547
+ 'openid.session_type' => 'DH-SHA1',
548
+ 'openid.dh_consumer_public' => "donkeydonkeydonkey",
549
+ }
550
+ assert_raise(Server::ProtocolError) {
551
+ @decode.call(args)
552
+ }
553
+ end
554
+
555
+ def test_associateDHModGen
556
+ # test dh with non-default but valid values for dh_modulus and
557
+ # dh_gen
558
+ args = {
559
+ 'openid.mode' => 'associate',
560
+ 'openid.session_type' => 'DH-SHA1',
561
+ 'openid.dh_consumer_public' => "Rzup9265tw==",
562
+ 'openid.dh_modulus' => CryptUtil.num_to_base64(ALT_MODULUS),
563
+ 'openid.dh_gen' => CryptUtil.num_to_base64(ALT_GEN) ,
564
+ }
565
+ r = @decode.call(args)
566
+ assert(r.is_a?(Server::AssociateRequest))
567
+ assert_equal(r.mode, "associate")
568
+ assert_equal(r.session.session_type, "DH-SHA1")
569
+ assert_equal(r.assoc_type, "HMAC-SHA1")
570
+ assert_equal(r.session.dh.modulus, ALT_MODULUS)
571
+ assert_equal(r.session.dh.generator, ALT_GEN)
572
+ assert(r.session.consumer_pubkey)
573
+ end
574
+
575
+ def test_associateDHCorruptModGen
576
+ # test dh with non-default but valid values for dh_modulus and
577
+ # dh_gen
578
+ args = {
579
+ 'openid.mode' => 'associate',
580
+ 'openid.session_type' => 'DH-SHA1',
581
+ 'openid.dh_consumer_public' => "Rzup9265tw==",
582
+ 'openid.dh_modulus' => 'pizza',
583
+ 'openid.dh_gen' => 'gnocchi',
584
+ }
585
+ assert_raise(Server::ProtocolError) {
586
+ @decode.call(args)
587
+ }
588
+ end
589
+
590
+ def test_associateDHMissingGen
591
+ args = {
592
+ 'openid.mode' => 'associate',
593
+ 'openid.session_type' => 'DH-SHA1',
594
+ 'openid.dh_consumer_public' => "Rzup9265tw==",
595
+ 'openid.dh_modulus' => 'pizza',
596
+ }
597
+ assert_raise(Server::ProtocolError) {
598
+ @decode.call(args)
599
+ }
600
+ end
601
+
602
+ def test_associateDHMissingMod
603
+ args = {
604
+ 'openid.mode' => 'associate',
605
+ 'openid.session_type' => 'DH-SHA1',
606
+ 'openid.dh_consumer_public' => "Rzup9265tw==",
607
+ 'openid.dh_gen' => 'pizza',
608
+ }
609
+ assert_raise(Server::ProtocolError) {
610
+ @decode.call(args)
611
+ }
612
+ end
613
+
614
+ # def test_associateDHInvalidModGen(self):
615
+ # # test dh with properly encoded values that are not a valid
616
+ # # modulus/generator combination.
617
+ # args = {
618
+ # 'openid.mode': 'associate',
619
+ # 'openid.session_type': 'DH-SHA1',
620
+ # 'openid.dh_consumer_public': "Rzup9265tw==",
621
+ # 'openid.dh_modulus': cryptutil.longToBase64(9),
622
+ # 'openid.dh_gen': cryptutil.longToBase64(27) ,
623
+ # }
624
+ # self.failUnlessRaises(server.ProtocolError, self.decode, args)
625
+ # test_associateDHInvalidModGen.todo = "low-priority feature"
626
+
627
+ def test_associateWeirdSession
628
+ args = {
629
+ 'openid.mode' => 'associate',
630
+ 'openid.session_type' => 'FLCL6',
631
+ 'openid.dh_consumer_public' => "YQ==\n",
632
+ }
633
+ assert_raise(Server::ProtocolError) {
634
+ @decode.call(args)
635
+ }
636
+ end
637
+
638
+ def test_associatePlain
639
+ args = {
640
+ 'openid.mode' => 'associate',
641
+ }
642
+ r = @decode.call(args)
643
+ assert(r.is_a?(Server::AssociateRequest))
644
+ assert_equal(r.mode, "associate")
645
+ assert_equal(r.session.session_type, "no-encryption")
646
+ assert_equal(r.assoc_type, "HMAC-SHA1")
647
+ end
648
+
649
+ def test_nomode
650
+ args = {
651
+ 'openid.session_type' => 'DH-SHA1',
652
+ 'openid.dh_consumer_public' => "my public keeey",
653
+ }
654
+ assert_raise(Server::ProtocolError) {
655
+ @decode.call(args)
656
+ }
657
+ end
658
+
659
+ def test_invalidns
660
+ args = {'openid.ns' => 'Vegetables',
661
+ 'openid.mode' => 'associate'}
662
+ begin
663
+ r = @decode.call(args)
664
+ rescue Server::ProtocolError => err
665
+ assert(err.openid_message)
666
+ assert(err.to_s.index('Vegetables'))
667
+ end
668
+ end
669
+ end
670
+
671
+ class BogusEncoder < Server::Encoder
672
+ def encode(response)
673
+ return "BOGUS"
674
+ end
675
+ end
676
+
677
+ class BogusDecoder < Server::Decoder
678
+ def decode(query)
679
+ return "BOGUS"
680
+ end
681
+ end
682
+
683
+ class TestEncode < Test::Unit::TestCase
684
+ def setup
685
+ @encoder = Server::Encoder.new
686
+ @encode = @encoder.method('encode')
687
+ @op_endpoint = 'http://endpoint.unittest/encode'
688
+ @store = Store::Memory.new
689
+ @server = Server::Server.new(@store, @op_endpoint)
690
+ end
691
+
692
+ def test_id_res_OpenID2_GET
693
+ # Check that when an OpenID 2 response does not exceed the OpenID
694
+ # 1 message size, a GET response (i.e., redirect) is issued.
695
+ request = Server::CheckIDRequest.new(
696
+ 'http://bombom.unittest/',
697
+ 'http://burr.unittest/999',
698
+ @server.op_endpoint,
699
+ 'http://burr.unittest/',
700
+ false,
701
+ nil)
702
+ request.message = Message.new(OPENID2_NS)
703
+ response = Server::OpenIDResponse.new(request)
704
+ response.fields = Message.from_openid_args({
705
+ 'ns' => OPENID2_NS,
706
+ 'mode' => 'id_res',
707
+ 'identity' => request.identity,
708
+ 'claimed_id' => request.identity,
709
+ 'return_to' => request.return_to,
710
+ })
711
+
712
+ assert(!response.render_as_form)
713
+ assert(response.which_encoding == Server::ENCODE_URL)
714
+ webresponse = @encode.call(response)
715
+ assert(webresponse.headers.member?('location'))
716
+ end
717
+
718
+ def test_id_res_OpenID2_POST
719
+ # Check that when an OpenID 2 response exceeds the OpenID 1
720
+ # message size, a POST response (i.e., an HTML form) is returned.
721
+ request = Server::CheckIDRequest.new(
722
+ 'http://bombom.unittest/',
723
+ 'http://burr.unittest/999',
724
+ @server.op_endpoint,
725
+ 'http://burr.unittest/',
726
+ false,
727
+ nil)
728
+ request.message = Message.new(OPENID2_NS)
729
+ response = Server::OpenIDResponse.new(request)
730
+ response.fields = Message.from_openid_args({
731
+ 'ns' => OPENID2_NS,
732
+ 'mode' => 'id_res',
733
+ 'identity' => request.identity,
734
+ 'claimed_id' => request.identity,
735
+ 'return_to' => 'x' * OPENID1_URL_LIMIT,
736
+ })
737
+
738
+ assert(response.render_as_form)
739
+ assert(response.encode_to_url.length > OPENID1_URL_LIMIT)
740
+ assert(response.which_encoding == Server::ENCODE_HTML_FORM)
741
+ webresponse = @encode.call(response)
742
+ assert_equal(webresponse.body, response.to_form_markup)
743
+ end
744
+
745
+ def test_to_form_markup
746
+ request = Server::CheckIDRequest.new(
747
+ 'http://bombom.unittest/',
748
+ 'http://burr.unittest/999',
749
+ @server.op_endpoint,
750
+ 'http://burr.unittest/',
751
+ false,
752
+ nil)
753
+ request.message = Message.new(OPENID2_NS)
754
+ response = Server::OpenIDResponse.new(request)
755
+ response.fields = Message.from_openid_args({
756
+ 'ns' => OPENID2_NS,
757
+ 'mode' => 'id_res',
758
+ 'identity' => request.identity,
759
+ 'claimed_id' => request.identity,
760
+ 'return_to' => 'x' * OPENID1_URL_LIMIT,
761
+ })
762
+ form_markup = response.to_form_markup({'foo'=>'bar'})
763
+ assert(/ foo="bar"/ =~ form_markup, form_markup)
764
+ end
765
+
766
+ def test_to_html
767
+ request = Server::CheckIDRequest.new(
768
+ 'http://bombom.unittest/',
769
+ 'http://burr.unittest/999',
770
+ @server.op_endpoint,
771
+ 'http://burr.unittest/',
772
+ false,
773
+ nil)
774
+ request.message = Message.new(OPENID2_NS)
775
+ response = Server::OpenIDResponse.new(request)
776
+ response.fields = Message.from_openid_args({
777
+ 'ns' => OPENID2_NS,
778
+ 'mode' => 'id_res',
779
+ 'identity' => request.identity,
780
+ 'claimed_id' => request.identity,
781
+ 'return_to' => 'x' * OPENID1_URL_LIMIT,
782
+ })
783
+ html = response.to_html
784
+ assert(html)
785
+ end
786
+
787
+ def test_id_res_OpenID1_exceeds_limit
788
+ # Check that when an OpenID 1 response exceeds the OpenID 1
789
+ # message size, a GET response is issued. Technically, this
790
+ # shouldn't be permitted by the library, but this test is in place
791
+ # to preserve the status quo for OpenID 1.
792
+ request = Server::CheckIDRequest.new(
793
+ 'http://bombom.unittest/',
794
+ 'http://burr.unittest/999',
795
+ @server.op_endpoint,
796
+ 'http://burr.unittest/',
797
+ false,
798
+ nil)
799
+ request.message = Message.new(OPENID1_NS)
800
+
801
+ response = Server::OpenIDResponse.new(request)
802
+ response.fields = Message.from_openid_args({
803
+ 'mode' => 'id_res',
804
+ 'identity' => request.identity,
805
+ 'return_to' => 'x' * OPENID1_URL_LIMIT,
806
+ })
807
+
808
+ assert(!response.render_as_form)
809
+ assert(response.encode_to_url.length > OPENID1_URL_LIMIT)
810
+ assert(response.which_encoding == Server::ENCODE_URL)
811
+ webresponse = @encode.call(response)
812
+ assert_equal(webresponse.headers['location'], response.encode_to_url)
813
+ end
814
+
815
+ def test_id_res
816
+ request = Server::CheckIDRequest.new(
817
+ 'http://bombom.unittest/',
818
+ 'http://burr.unittest/999',
819
+ @server.op_endpoint,
820
+ 'http://burr.unittest/',
821
+ false, nil)
822
+ request.message = Message.new(OPENID1_NS)
823
+ response = Server::OpenIDResponse.new(request)
824
+ response.fields = Message.from_openid_args({
825
+ 'mode' => 'id_res',
826
+ 'identity' => request.identity,
827
+ 'return_to' => request.return_to,
828
+ })
829
+ webresponse = @encode.call(response)
830
+ assert_equal(webresponse.code, Server::HTTP_REDIRECT)
831
+ assert(webresponse.headers.member?('location'))
832
+
833
+ location = webresponse.headers['location']
834
+ assert(location.starts_with?(request.return_to),
835
+ sprintf("%s does not start with %s",
836
+ location, request.return_to))
837
+ # argh.
838
+ q2 = Util.parse_query(URI::parse(location).query)
839
+ expected = response.fields.to_post_args
840
+ assert_equal(q2, expected)
841
+ end
842
+
843
+ def test_cancel
844
+ request = Server::CheckIDRequest.new(
845
+ 'http://bombom.unittest/',
846
+ 'http://burr.unittest/999',
847
+ @server.op_endpoint,
848
+ 'http://burr.unittest/',
849
+ false, nil)
850
+ request.message = Message.new(OPENID2_NS)
851
+ response = Server::OpenIDResponse.new(request)
852
+ response.fields = Message.from_openid_args({
853
+ 'mode' => 'cancel',
854
+ })
855
+ webresponse = @encode.call(response)
856
+ assert_equal(webresponse.code, Server::HTTP_REDIRECT)
857
+ assert(webresponse.headers.member?('location'))
858
+ end
859
+
860
+ def test_cancel_to_form
861
+ request = Server::CheckIDRequest.new(
862
+ 'http://bombom.unittest/',
863
+ 'http://burr.unittest/999',
864
+ @server.op_endpoint,
865
+ 'http://burr.unittest/',
866
+ false, nil)
867
+ request.message = Message.new(OPENID2_NS)
868
+ response = Server::OpenIDResponse.new(request)
869
+ response.fields = Message.from_openid_args({
870
+ 'mode' => 'cancel',
871
+ })
872
+ form = response.to_form_markup
873
+ assert(form.index(request.return_to))
874
+ end
875
+
876
+ def test_assocReply
877
+ msg = Message.new(OPENID2_NS)
878
+ msg.set_arg(OPENID2_NS, 'session_type', 'no-encryption')
879
+ request = Server::AssociateRequest.from_message(msg)
880
+ response = Server::OpenIDResponse.new(request)
881
+ response.fields = Message.from_post_args(
882
+ {'openid.assoc_handle' => "every-zig"})
883
+ webresponse = @encode.call(response)
884
+ body = "assoc_handle:every-zig\n"
885
+
886
+ assert_equal(webresponse.code, Server::HTTP_OK)
887
+ assert_equal(webresponse.headers, {})
888
+ assert_equal(webresponse.body, body)
889
+ end
890
+
891
+ def test_checkauthReply
892
+ request = Server::CheckAuthRequest.new('a_sock_monkey',
893
+ 'siggggg',
894
+ [])
895
+ request.message = Message.new(OPENID2_NS)
896
+ response = Server::OpenIDResponse.new(request)
897
+ response.fields = Message.from_openid_args({
898
+ 'is_valid' => 'true',
899
+ 'invalidate_handle' => 'xXxX:xXXx'
900
+ })
901
+ body = "invalidate_handle:xXxX:xXXx\nis_valid:true\n"
902
+
903
+ webresponse = @encode.call(response)
904
+ assert_equal(webresponse.code, Server::HTTP_OK)
905
+ assert_equal(webresponse.headers, {})
906
+ assert_equal(webresponse.body, body)
907
+ end
908
+
909
+ def test_unencodableError
910
+ args = Message.from_post_args({
911
+ 'openid.identity' => 'http://limu.unittest/',
912
+ })
913
+ e = Server::ProtocolError.new(args, "wet paint")
914
+ assert_raise(Server::EncodingError) {
915
+ @encode.call(e)
916
+ }
917
+ end
918
+
919
+ def test_encodableError
920
+ args = Message.from_post_args({
921
+ 'openid.mode' => 'associate',
922
+ 'openid.identity' => 'http://limu.unittest/',
923
+ })
924
+ body="error:snoot\nmode:error\n"
925
+ webresponse = @encode.call(Server::ProtocolError.new(args, "snoot"))
926
+ assert_equal(webresponse.code, Server::HTTP_ERROR)
927
+ assert_equal(webresponse.headers, {})
928
+ assert_equal(webresponse.body, body)
929
+ end
930
+ end
931
+
932
+ class TestSigningEncode < Test::Unit::TestCase
933
+ def setup
934
+ @_dumb_key = Server::Signatory._dumb_key
935
+ @_normal_key = Server::Signatory._normal_key
936
+ @store = Store::Memory.new()
937
+ @server = Server::Server.new(@store, "http://signing.unittest/enc")
938
+ @request = Server::CheckIDRequest.new(
939
+ 'http://bombom.unittest/',
940
+ 'http://burr.unittest/999',
941
+ @server.op_endpoint,
942
+ 'http://burr.unittest/',
943
+ false, nil)
944
+ @request.message = Message.new(OPENID2_NS)
945
+
946
+ @response = Server::OpenIDResponse.new(@request)
947
+ @response.fields = Message.from_openid_args({
948
+ 'mode' => 'id_res',
949
+ 'identity' => @request.identity,
950
+ 'return_to' => @request.return_to,
951
+ })
952
+ @signatory = Server::Signatory.new(@store)
953
+ @encoder = Server::SigningEncoder.new(@signatory)
954
+ @encode = @encoder.method('encode')
955
+ end
956
+
957
+ def test_idres
958
+ assoc_handle = '{bicycle}{shed}'
959
+ @store.store_association(
960
+ @_normal_key,
961
+ Association.from_expires_in(60, assoc_handle,
962
+ 'sekrit', 'HMAC-SHA1'))
963
+ @request.assoc_handle = assoc_handle
964
+ webresponse = @encode.call(@response)
965
+ assert_equal(webresponse.code, Server::HTTP_REDIRECT)
966
+ assert(webresponse.headers.member?('location'))
967
+
968
+ location = webresponse.headers['location']
969
+ query = Util.parse_query(URI::parse(location).query)
970
+ assert(query.member?('openid.sig'))
971
+ assert(query.member?('openid.assoc_handle'))
972
+ assert(query.member?('openid.signed'))
973
+ end
974
+
975
+ def test_idresDumb
976
+ webresponse = @encode.call(@response)
977
+ assert_equal(webresponse.code, Server::HTTP_REDIRECT)
978
+ assert(webresponse.headers.has_key?('location'))
979
+
980
+ location = webresponse.headers['location']
981
+ query = Util.parse_query(URI::parse(location).query)
982
+ assert(query.member?('openid.sig'))
983
+ assert(query.member?('openid.assoc_handle'))
984
+ assert(query.member?('openid.signed'))
985
+ end
986
+
987
+ def test_forgotStore
988
+ @encoder.signatory = nil
989
+ assert_raise(ArgumentError) {
990
+ @encode.call(@response)
991
+ }
992
+ end
993
+
994
+ def test_cancel
995
+ request = Server::CheckIDRequest.new(
996
+ 'http://bombom.unittest/',
997
+ 'http://burr.unittest/999',
998
+ @server.op_endpoint,
999
+ 'http://burr.unittest/',
1000
+ false, nil)
1001
+ request.message = Message.new(OPENID2_NS)
1002
+ response = Server::OpenIDResponse.new(request)
1003
+ response.fields.set_arg(OPENID_NS, 'mode', 'cancel')
1004
+ webresponse = @encode.call(response)
1005
+ assert_equal(webresponse.code, Server::HTTP_REDIRECT)
1006
+ assert(webresponse.headers.has_key?('location'))
1007
+ location = webresponse.headers['location']
1008
+ query = Util.parse_query(URI::parse(location).query)
1009
+ assert(!query.has_key?('openid.sig'))
1010
+ end
1011
+
1012
+ def test_assocReply
1013
+ msg = Message.new(OPENID2_NS)
1014
+ msg.set_arg(OPENID2_NS, 'session_type', 'no-encryption')
1015
+ request = Server::AssociateRequest.from_message(msg)
1016
+ response = Server::OpenIDResponse.new(request)
1017
+ response.fields = Message.from_openid_args({'assoc_handle' => "every-zig"})
1018
+ webresponse = @encode.call(response)
1019
+ body = "assoc_handle:every-zig\n"
1020
+ assert_equal(webresponse.code, Server::HTTP_OK)
1021
+ assert_equal(webresponse.headers, {})
1022
+ assert_equal(webresponse.body, body)
1023
+ end
1024
+
1025
+ def test_alreadySigned
1026
+ @response.fields.set_arg(OPENID_NS, 'sig', 'priorSig==')
1027
+ assert_raise(Server::AlreadySigned) {
1028
+ @encode.call(@response)
1029
+ }
1030
+ end
1031
+ end
1032
+
1033
+ class TestCheckID < Test::Unit::TestCase
1034
+ def setup
1035
+ @op_endpoint = 'http://endpoint.unittest/'
1036
+ @store = Store::Memory.new()
1037
+ @server = Server::Server.new(@store, @op_endpoint)
1038
+ @request = Server::CheckIDRequest.new(
1039
+ 'http://bambam.unittest/',
1040
+ 'http://bar.unittest/999',
1041
+ @server.op_endpoint,
1042
+ 'http://bar.unittest/',
1043
+ false)
1044
+ @request.message = Message.new(OPENID2_NS)
1045
+ end
1046
+
1047
+ def test_trustRootInvalid
1048
+ @request.trust_root = "http://foo.unittest/17"
1049
+ @request.return_to = "http://foo.unittest/39"
1050
+ assert(!@request.trust_root_valid())
1051
+ end
1052
+
1053
+ def test_trustRootInvalid_modified
1054
+ @request.trust_root = "does://not.parse/"
1055
+ @request.message = :sentinel
1056
+ begin
1057
+ result = @request.trust_root_valid
1058
+ rescue Server::MalformedTrustRoot => why
1059
+ assert_equal(:sentinel, why.openid_message)
1060
+ else
1061
+ flunk("Expected MalformedTrustRoot, got #{result.inspect}")
1062
+ end
1063
+ end
1064
+
1065
+ def test_trustRootvalid_absent_trust_root
1066
+ @request.trust_root = nil
1067
+ assert(@request.trust_root_valid())
1068
+ end
1069
+
1070
+ def test_trustRootValid
1071
+ @request.trust_root = "http://foo.unittest/"
1072
+ @request.return_to = "http://foo.unittest/39"
1073
+ assert(@request.trust_root_valid())
1074
+ end
1075
+
1076
+ def test_trustRootValidNoReturnTo
1077
+ request = Server::CheckIDRequest.new(
1078
+ 'http://bambam.unittest/',
1079
+ nil,
1080
+ @server.op_endpoint,
1081
+ 'http://bar.unittest/',
1082
+ false)
1083
+
1084
+ assert(request.trust_root_valid())
1085
+ end
1086
+
1087
+ def test_returnToVerified_callsVerify
1088
+ # Make sure that verifyReturnTo is calling the trustroot
1089
+ # function verifyReturnTo
1090
+ # Ensure that exceptions are passed through
1091
+ sentinel = Exception.new()
1092
+
1093
+ __req = @request
1094
+ tc = self
1095
+
1096
+ vrfyExc = Proc.new { |trust_root, return_to|
1097
+ tc.assert_equal(__req.trust_root, trust_root)
1098
+ tc.assert_equal(__req.return_to, return_to)
1099
+ raise sentinel
1100
+ }
1101
+
1102
+ TrustRoot.extend(OverrideMethodMixin)
1103
+
1104
+ TrustRoot.with_method_overridden(:verify_return_to, vrfyExc) do
1105
+ begin
1106
+ @request.return_to_verified()
1107
+ flunk("Expected sentinel to be raised, got success")
1108
+ rescue Exception => e
1109
+ assert(e.equal?(sentinel), [e, sentinel].inspect)
1110
+ end
1111
+ end
1112
+
1113
+ # Ensure that True and False are passed through unchanged
1114
+ constVerify = Proc.new { |val|
1115
+ verify = Proc.new { |trust_root, return_to|
1116
+ tc.assert_equal(__req.trust_root, trust_root)
1117
+ tc.assert_equal(__req.request.return_to, return_to)
1118
+ return val
1119
+ }
1120
+
1121
+ return verify
1122
+ }
1123
+
1124
+ [true, false].each { |val|
1125
+ verifier = constVerify.call(val)
1126
+
1127
+ TrustRoot.with_method_overridden(:verify_return_to, verifier) do
1128
+ assert_equal(val, @request.return_to_verified())
1129
+ end
1130
+ }
1131
+ end
1132
+
1133
+ def _expectAnswer(answer, identity=nil, claimed_id=nil)
1134
+ expected_list = [
1135
+ ['mode', 'id_res'],
1136
+ ['return_to', @request.return_to],
1137
+ ['op_endpoint', @op_endpoint],
1138
+ ]
1139
+ if identity
1140
+ expected_list << ['identity', identity]
1141
+ if claimed_id
1142
+ expected_list << ['claimed_id', claimed_id]
1143
+ else
1144
+ expected_list << ['claimed_id', identity]
1145
+ end
1146
+ end
1147
+
1148
+ expected_list.each { |k, expected|
1149
+ actual = answer.fields.get_arg(OPENID_NS, k)
1150
+ assert_equal(expected, actual,
1151
+ sprintf("%s: expected %s, got %s",
1152
+ k, expected, actual))
1153
+ }
1154
+
1155
+ assert(answer.fields.has_key?(OPENID_NS, 'response_nonce'))
1156
+ assert(answer.fields.get_openid_namespace() == OPENID2_NS)
1157
+
1158
+ # One for nonce, one for ns
1159
+ assert_equal(answer.fields.to_post_args.length,
1160
+ expected_list.length + 2,
1161
+ answer.fields.to_post_args.inspect)
1162
+ end
1163
+
1164
+ def test_answerAllow
1165
+ # Check the fields specified by "Positive Assertions"
1166
+ #
1167
+ # including mode=id_res, identity, claimed_id, op_endpoint,
1168
+ # return_to
1169
+ answer = @request.answer(true)
1170
+ assert_equal(answer.request, @request)
1171
+ _expectAnswer(answer, @request.identity)
1172
+ end
1173
+
1174
+ def test_answerAllowDelegatedIdentity
1175
+ @request.claimed_id = 'http://delegating.unittest/'
1176
+ answer = @request.answer(true)
1177
+ _expectAnswer(answer, @request.identity,
1178
+ @request.claimed_id)
1179
+ end
1180
+
1181
+ def test_answerAllowWithoutIdentityReally
1182
+ @request.identity = nil
1183
+ answer = @request.answer(true)
1184
+ assert_equal(answer.request, @request)
1185
+ _expectAnswer(answer)
1186
+ end
1187
+
1188
+ def test_answerAllowAnonymousFail
1189
+ @request.identity = nil
1190
+ # XXX - Check on this, I think this behavior is legal in OpenID
1191
+ # 2.0?
1192
+ assert_raise(ArgumentError) {
1193
+ @request.answer(true, nil, "=V")
1194
+ }
1195
+ end
1196
+
1197
+ def test_answerAllowWithIdentity
1198
+ @request.identity = IDENTIFIER_SELECT
1199
+ selected_id = 'http://anon.unittest/9861'
1200
+ answer = @request.answer(true, nil, selected_id)
1201
+ _expectAnswer(answer, selected_id)
1202
+ end
1203
+
1204
+ def test_answerAllowWithNoIdentity
1205
+ @request.identity = IDENTIFIER_SELECT
1206
+ selected_id = 'http://anon.unittest/9861'
1207
+ assert_raise(ArgumentError) {
1208
+ answer = @request.answer(true, nil, nil)
1209
+ }
1210
+ end
1211
+
1212
+ def test_immediate_openid1_no_identity
1213
+ @request.message = Message.new(OPENID1_NS)
1214
+ @request.immediate = true
1215
+ @request.mode = 'checkid_immediate'
1216
+ resp = @request.answer(false)
1217
+ assert(resp.fields.get_arg(OPENID_NS, 'mode') == 'id_res')
1218
+ end
1219
+
1220
+ def test_checkid_setup_openid1_no_identity
1221
+ @request.message = Message.new(OPENID1_NS)
1222
+ @request.immediate = false
1223
+ @request.mode = 'checkid_setup'
1224
+ resp = @request.answer(false)
1225
+ assert(resp.fields.get_arg(OPENID_NS, 'mode') == 'cancel')
1226
+ end
1227
+
1228
+ def test_immediate_openid1_no_server_url
1229
+ @request.message = Message.new(OPENID1_NS)
1230
+ @request.immediate = true
1231
+ @request.mode = 'checkid_immediate'
1232
+ @request.op_endpoint = nil
1233
+
1234
+ assert_raise(ArgumentError) {
1235
+ resp = @request.answer(false)
1236
+ }
1237
+ end
1238
+
1239
+ def test_immediate_encode_to_url
1240
+ @request.message = Message.new(OPENID1_NS)
1241
+ @request.immediate = true
1242
+ @request.mode = 'checkid_immediate'
1243
+ @request.trust_root = "BOGUS"
1244
+ @request.assoc_handle = "ASSOC"
1245
+
1246
+ server_url = "http://server.com/server"
1247
+
1248
+ url = @request.encode_to_url(server_url)
1249
+ assert(url.starts_with?(server_url))
1250
+
1251
+ unused, query = url.split("?", 2)
1252
+ args = Util.parse_query(query)
1253
+
1254
+ m = Message.from_post_args(args)
1255
+ assert(m.get_arg(OPENID_NS, 'trust_root') == "BOGUS")
1256
+ assert(m.get_arg(OPENID_NS, 'assoc_handle') == "ASSOC")
1257
+ assert(m.get_arg(OPENID_NS, 'mode'), "checkid_immediate")
1258
+ assert(m.get_arg(OPENID_NS, 'identity') == @request.identity)
1259
+ assert(m.get_arg(OPENID_NS, 'claimed_id') == @request.claimed_id)
1260
+ assert(m.get_arg(OPENID_NS, 'return_to') == @request.return_to)
1261
+ end
1262
+
1263
+ def test_answerAllowWithDelegatedIdentityOpenID2
1264
+ # Answer an IDENTIFIER_SELECT case with a delegated identifier.
1265
+
1266
+ # claimed_id delegates to selected_id here.
1267
+ @request.identity = IDENTIFIER_SELECT
1268
+ selected_id = 'http://anon.unittest/9861'
1269
+ claimed_id = 'http://monkeyhat.unittest/'
1270
+ answer = @request.answer(true, nil, selected_id, claimed_id)
1271
+ _expectAnswer(answer, selected_id, claimed_id)
1272
+ end
1273
+
1274
+ def test_answerAllowWithDelegatedIdentityOpenID1
1275
+ # claimed_id parameter doesn't exist in OpenID 1.
1276
+ @request.message = Message.new(OPENID1_NS)
1277
+ # claimed_id delegates to selected_id here.
1278
+ @request.identity = IDENTIFIER_SELECT
1279
+ selected_id = 'http://anon.unittest/9861'
1280
+ claimed_id = 'http://monkeyhat.unittest/'
1281
+ assert_raise(Server::VersionError) {
1282
+ @request.answer(true, nil, selected_id, claimed_id)
1283
+ }
1284
+ end
1285
+
1286
+ def test_answerAllowWithAnotherIdentity
1287
+ # XXX - Check on this, I think this behavior is legal in OpenID
1288
+ # 2.0?
1289
+ assert_raise(ArgumentError){
1290
+ @request.answer(true, nil, "http://pebbles.unittest/")
1291
+ }
1292
+ end
1293
+
1294
+ def test_answerAllowNoIdentityOpenID1
1295
+ @request.message = Message.new(OPENID1_NS)
1296
+ @request.identity = nil
1297
+ assert_raise(ArgumentError) {
1298
+ @request.answer(true, nil, nil)
1299
+ }
1300
+ end
1301
+
1302
+ def test_answerAllowForgotEndpoint
1303
+ @request.op_endpoint = nil
1304
+ assert_raise(RuntimeError) {
1305
+ @request.answer(true)
1306
+ }
1307
+ end
1308
+
1309
+ def test_checkIDWithNoIdentityOpenID1
1310
+ msg = Message.new(OPENID1_NS)
1311
+ msg.set_arg(OPENID_NS, 'return_to', 'bogus')
1312
+ msg.set_arg(OPENID_NS, 'trust_root', 'bogus')
1313
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1314
+ msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
1315
+
1316
+ assert_raise(Server::ProtocolError) {
1317
+ Server::CheckIDRequest.from_message(msg, @server)
1318
+ }
1319
+ end
1320
+
1321
+ def test_fromMessageClaimedIDWithoutIdentityOpenID2
1322
+ msg = Message.new(OPENID2_NS)
1323
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1324
+ msg.set_arg(OPENID_NS, 'return_to', 'http://invalid:8000/rt')
1325
+ msg.set_arg(OPENID_NS, 'claimed_id', 'https://example.myopenid.com')
1326
+
1327
+ assert_raise(Server::ProtocolError) {
1328
+ Server::CheckIDRequest.from_message(msg, @server)
1329
+ }
1330
+ end
1331
+
1332
+ def test_fromMessageIdentityWithoutClaimedIDOpenID2
1333
+ msg = Message.new(OPENID2_NS)
1334
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1335
+ msg.set_arg(OPENID_NS, 'return_to', 'http://invalid:8000/rt')
1336
+ msg.set_arg(OPENID_NS, 'identity', 'https://example.myopenid.com')
1337
+
1338
+ assert_raise(Server::ProtocolError) {
1339
+ Server::CheckIDRequest.from_message(msg, @server)
1340
+ }
1341
+ end
1342
+
1343
+ def test_fromMessageWithEmptyTrustRoot
1344
+ return_to = 'http://some.url/foo?bar=baz'
1345
+ msg = Message.from_post_args({
1346
+ 'openid.assoc_handle' => '{blah}{blah}{OZivdQ==}',
1347
+ 'openid.claimed_id' => 'http://delegated.invalid/',
1348
+ 'openid.identity' => 'http://op-local.example.com/',
1349
+ 'openid.mode' => 'checkid_setup',
1350
+ 'openid.ns' => 'http://openid.net/signon/1.0',
1351
+ 'openid.return_to' => return_to,
1352
+ 'openid.trust_root' => ''
1353
+ });
1354
+ result = Server::CheckIDRequest.from_message(msg, @server)
1355
+ assert_equal(return_to, result.trust_root)
1356
+ end
1357
+
1358
+ def test_trustRootOpenID1
1359
+ # Ignore openid.realm in OpenID 1
1360
+ msg = Message.new(OPENID1_NS)
1361
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1362
+ msg.set_arg(OPENID_NS, 'trust_root', 'http://trustroot.com/')
1363
+ msg.set_arg(OPENID_NS, 'realm', 'http://fake_trust_root/')
1364
+ msg.set_arg(OPENID_NS, 'return_to', 'http://trustroot.com/foo')
1365
+ msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
1366
+ msg.set_arg(OPENID_NS, 'identity', 'george')
1367
+
1368
+ result = Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
1369
+
1370
+ assert(result.trust_root == 'http://trustroot.com/')
1371
+ end
1372
+
1373
+ def test_trustRootOpenID2
1374
+ # Ignore openid.trust_root in OpenID 2
1375
+ msg = Message.new(OPENID2_NS)
1376
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1377
+ msg.set_arg(OPENID_NS, 'realm', 'http://trustroot.com/')
1378
+ msg.set_arg(OPENID_NS, 'trust_root', 'http://fake_trust_root/')
1379
+ msg.set_arg(OPENID_NS, 'return_to', 'http://trustroot.com/foo')
1380
+ msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
1381
+ msg.set_arg(OPENID_NS, 'identity', 'george')
1382
+ msg.set_arg(OPENID_NS, 'claimed_id', 'george')
1383
+
1384
+ result = Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
1385
+
1386
+ assert(result.trust_root == 'http://trustroot.com/')
1387
+ end
1388
+
1389
+ def test_answerAllowNoTrustRoot
1390
+ @request.trust_root = nil
1391
+ answer = @request.answer(true)
1392
+ assert_equal(answer.request, @request)
1393
+ _expectAnswer(answer, @request.identity)
1394
+ end
1395
+
1396
+ def test_answerImmediateDenyOpenID2
1397
+ # Look for mode=setup_needed in checkid_immediate negative
1398
+ # response in OpenID 2 case.
1399
+ #
1400
+ # See specification Responding to Authentication Requests /
1401
+ # Negative Assertions / In Response to Immediate Requests.
1402
+ @request.mode = 'checkid_immediate'
1403
+ @request.immediate = true
1404
+
1405
+ server_url = "http://setup-url.unittest/"
1406
+ # crappiting setup_url, you dirty my interface with your presence!
1407
+ answer = @request.answer(false, server_url)
1408
+ assert_equal(answer.request, @request)
1409
+ assert_equal(answer.fields.to_post_args.length, 3, answer.fields)
1410
+ assert_equal(answer.fields.get_openid_namespace, OPENID2_NS)
1411
+ assert_equal(answer.fields.get_arg(OPENID_NS, 'mode'),
1412
+ 'setup_needed')
1413
+ # user_setup_url no longer required.
1414
+ end
1415
+
1416
+ def test_answerImmediateDenyOpenID1
1417
+ # Look for user_setup_url in checkid_immediate negative response
1418
+ # in OpenID 1 case.
1419
+ @request.message = Message.new(OPENID1_NS)
1420
+ @request.mode = 'checkid_immediate'
1421
+ @request.immediate = true
1422
+ @request.claimed_id = 'http://claimed-id.test/'
1423
+ server_url = "http://setup-url.unittest/"
1424
+ # crappiting setup_url, you dirty my interface with your presence!
1425
+ answer = @request.answer(false, server_url)
1426
+ assert_equal(answer.request, @request)
1427
+ assert_equal(2, answer.fields.to_post_args.length, answer.fields)
1428
+ assert_equal(OPENID1_NS, answer.fields.get_openid_namespace)
1429
+ assert_equal('id_res', answer.fields.get_arg(OPENID_NS, 'mode'))
1430
+
1431
+ usu = answer.fields.get_arg(OPENID_NS, 'user_setup_url', '')
1432
+ assert(usu.starts_with?(server_url))
1433
+ expected_substr = 'openid.claimed_id=http%3A%2F%2Fclaimed-id.test%2F'
1434
+ assert(!usu.index(expected_substr).nil?, usu)
1435
+ end
1436
+
1437
+ def test_answerSetupDeny
1438
+ answer = @request.answer(false)
1439
+ assert_equal(answer.fields.get_args(OPENID_NS), {
1440
+ 'mode' => 'cancel',
1441
+ })
1442
+ end
1443
+
1444
+ def test_encodeToURL
1445
+ server_url = 'http://openid-server.unittest/'
1446
+ result = @request.encode_to_url(server_url)
1447
+
1448
+ # How to check? How about a round-trip test.
1449
+ base, result_args = result.split('?', 2)
1450
+ result_args = Util.parse_query(result_args)
1451
+ message = Message.from_post_args(result_args)
1452
+ rebuilt_request = Server::CheckIDRequest.from_message(message,
1453
+ @server.op_endpoint)
1454
+
1455
+ @request.message = message
1456
+
1457
+ @request.instance_variables.each { |var|
1458
+ assert_equal(@request.instance_variable_get(var),
1459
+ rebuilt_request.instance_variable_get(var), var)
1460
+ }
1461
+ end
1462
+
1463
+ def test_getCancelURL
1464
+ url = @request.cancel_url
1465
+ rt, query_string = url.split('?', -1)
1466
+ assert_equal(@request.return_to, rt)
1467
+ query = Util.parse_query(query_string)
1468
+ assert_equal(query, {'openid.mode' => 'cancel',
1469
+ 'openid.ns' => OPENID2_NS})
1470
+ end
1471
+
1472
+ def test_getCancelURLimmed
1473
+ @request.mode = 'checkid_immediate'
1474
+ @request.immediate = true
1475
+ assert_raise(ArgumentError) {
1476
+ @request.cancel_url
1477
+ }
1478
+ end
1479
+
1480
+ def test_fromMessageWithoutTrustRoot
1481
+ msg = Message.new(OPENID2_NS)
1482
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1483
+ msg.set_arg(OPENID_NS, 'return_to', 'http://real.trust.root/foo')
1484
+ msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
1485
+ msg.set_arg(OPENID_NS, 'identity', 'george')
1486
+ msg.set_arg(OPENID_NS, 'claimed_id', 'george')
1487
+
1488
+ result = Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
1489
+
1490
+ assert_equal(result.trust_root, 'http://real.trust.root/foo')
1491
+ end
1492
+
1493
+ def test_fromMessageWithoutTrustRootOrReturnTo
1494
+ msg = Message.new(OPENID2_NS)
1495
+ msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
1496
+ msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
1497
+ msg.set_arg(OPENID_NS, 'identity', 'george')
1498
+ msg.set_arg(OPENID_NS, 'claimed_id', 'george')
1499
+
1500
+ assert_raises(Server::ProtocolError) {
1501
+ Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
1502
+ }
1503
+ end
1504
+ end
1505
+
1506
+ class TestCheckIDExtension < Test::Unit::TestCase
1507
+
1508
+ def setup
1509
+ @op_endpoint = 'http://endpoint.unittest/ext'
1510
+ @store = Store::Memory.new()
1511
+ @server = Server::Server.new(@store, @op_endpoint)
1512
+ @request = Server::CheckIDRequest.new(
1513
+ 'http://bambam.unittest/',
1514
+ 'http://bar.unittest/999',
1515
+ @server.op_endpoint,
1516
+ 'http://bar.unittest/',
1517
+ false)
1518
+ @request.message = Message.new(OPENID2_NS)
1519
+ @response = Server::OpenIDResponse.new(@request)
1520
+ @response.fields.set_arg(OPENID_NS, 'mode', 'id_res')
1521
+ @response.fields.set_arg(OPENID_NS, 'blue', 'star')
1522
+ end
1523
+
1524
+ def test_addField
1525
+ namespace = 'something:'
1526
+ @response.fields.set_arg(namespace, 'bright', 'potato')
1527
+ assert_equal(@response.fields.get_args(OPENID_NS),
1528
+ {'blue' => 'star',
1529
+ 'mode' => 'id_res',
1530
+ })
1531
+
1532
+ assert_equal(@response.fields.get_args(namespace),
1533
+ {'bright' => 'potato'})
1534
+ end
1535
+
1536
+ def test_addFields
1537
+ namespace = 'mi5:'
1538
+ args = {'tangy' => 'suspenders',
1539
+ 'bravo' => 'inclusion'}
1540
+ @response.fields.update_args(namespace, args)
1541
+ assert_equal(@response.fields.get_args(OPENID_NS),
1542
+ {'blue' => 'star',
1543
+ 'mode' => 'id_res',
1544
+ })
1545
+ assert_equal(@response.fields.get_args(namespace), args)
1546
+ end
1547
+ end
1548
+
1549
+ class MockSignatory
1550
+ attr_accessor :isValid, :assocs
1551
+
1552
+ def initialize(assoc)
1553
+ @isValid = true
1554
+ @assocs = [assoc]
1555
+ end
1556
+
1557
+ def verify(assoc_handle, message)
1558
+ Util.assert(message.has_key?(OPENID_NS, "sig"))
1559
+ if self.assocs.member?([true, assoc_handle])
1560
+ return @isValid
1561
+ else
1562
+ return false
1563
+ end
1564
+ end
1565
+
1566
+ def get_association(assoc_handle, dumb)
1567
+ if self.assocs.member?([dumb, assoc_handle])
1568
+ # This isn't a valid implementation for many uses of this
1569
+ # function, mind you.
1570
+ return true
1571
+ else
1572
+ return nil
1573
+ end
1574
+ end
1575
+
1576
+ def invalidate(assoc_handle, dumb)
1577
+ if self.assocs.member?([dumb, assoc_handle])
1578
+ @assocs.delete([dumb, assoc_handle])
1579
+ end
1580
+ end
1581
+ end
1582
+
1583
+ class TestCheckAuth < Test::Unit::TestCase
1584
+ def setup
1585
+ @assoc_handle = 'mooooooooo'
1586
+ @message = Message.from_post_args({
1587
+ 'openid.sig' => 'signarture',
1588
+ 'one' => 'alpha',
1589
+ 'two' => 'beta',
1590
+ })
1591
+ @request = Server::CheckAuthRequest.new(
1592
+ @assoc_handle, @message)
1593
+ @request.message = Message.new(OPENID2_NS)
1594
+
1595
+ @signatory = MockSignatory.new([true, @assoc_handle])
1596
+ end
1597
+
1598
+ def test_to_s
1599
+ @request.to_s
1600
+ end
1601
+
1602
+ def test_valid
1603
+ r = @request.answer(@signatory)
1604
+ assert_equal({'is_valid' => 'true'},
1605
+ r.fields.get_args(OPENID_NS))
1606
+ assert_equal(r.request, @request)
1607
+ end
1608
+
1609
+ def test_invalid
1610
+ @signatory.isValid = false
1611
+ r = @request.answer(@signatory)
1612
+ assert_equal({'is_valid' => 'false'},
1613
+ r.fields.get_args(OPENID_NS))
1614
+
1615
+ end
1616
+
1617
+ def test_replay
1618
+ # Don't validate the same response twice.
1619
+ #
1620
+ # From "Checking the Nonce"::
1621
+ #
1622
+ # When using "check_authentication", the OP MUST ensure that an
1623
+ # assertion has not yet been accepted with the same value for
1624
+ # "openid.response_nonce".
1625
+ #
1626
+ # In this implementation, the assoc_handle is only valid once.
1627
+ # And nonces are a signed component of the message, so they can't
1628
+ # be used with another handle without breaking the sig.
1629
+ r = @request.answer(@signatory)
1630
+ r = @request.answer(@signatory)
1631
+ assert_equal({'is_valid' => 'false'},
1632
+ r.fields.get_args(OPENID_NS))
1633
+ end
1634
+
1635
+ def test_invalidatehandle
1636
+ @request.invalidate_handle = "bogusHandle"
1637
+ r = @request.answer(@signatory)
1638
+ assert_equal(r.fields.get_args(OPENID_NS),
1639
+ {'is_valid' => 'true',
1640
+ 'invalidate_handle' => "bogusHandle"})
1641
+ assert_equal(r.request, @request)
1642
+ end
1643
+
1644
+ def test_invalidatehandleNo
1645
+ assoc_handle = 'goodhandle'
1646
+ @signatory.assocs << [false, 'goodhandle']
1647
+ @request.invalidate_handle = assoc_handle
1648
+ r = @request.answer(@signatory)
1649
+ assert_equal(r.fields.get_args(OPENID_NS), {'is_valid' => 'true'})
1650
+ end
1651
+ end
1652
+
1653
+ class TestAssociate < Test::Unit::TestCase
1654
+ # TODO: test DH with non-default values for modulus and gen.
1655
+ # (important to do because we actually had it broken for a while.)
1656
+
1657
+ def setup
1658
+ @request = Server::AssociateRequest.from_message(Message.from_post_args({}))
1659
+ @store = Store::Memory.new()
1660
+ @signatory = Server::Signatory.new(@store)
1661
+ end
1662
+
1663
+ def test_dhSHA1
1664
+ @assoc = @signatory.create_association(false, 'HMAC-SHA1')
1665
+ consumer_dh = DiffieHellman.from_defaults()
1666
+ cpub = consumer_dh.public
1667
+ server_dh = DiffieHellman.from_defaults()
1668
+ session = Server::DiffieHellmanSHA1ServerSession.new(server_dh, cpub)
1669
+ @request = Server::AssociateRequest.new(session, 'HMAC-SHA1')
1670
+ @request.message = Message.new(OPENID2_NS)
1671
+ response = @request.answer(@assoc)
1672
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1673
+ assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
1674
+ assert_equal(rfg.call("assoc_handle"), @assoc.handle)
1675
+ assert(!rfg.call("mac_key"))
1676
+ assert_equal(rfg.call("session_type"), "DH-SHA1")
1677
+ assert(rfg.call("enc_mac_key"))
1678
+ assert(rfg.call("dh_server_public"))
1679
+
1680
+ enc_key = Util.from_base64(rfg.call("enc_mac_key"))
1681
+ spub = CryptUtil.base64_to_num(rfg.call("dh_server_public"))
1682
+ secret = consumer_dh.xor_secret(CryptUtil.method('sha1'),
1683
+ spub, enc_key)
1684
+ assert_equal(secret, @assoc.secret)
1685
+ end
1686
+
1687
+ def test_dhSHA256
1688
+ @assoc = @signatory.create_association(false, 'HMAC-SHA256')
1689
+ consumer_dh = DiffieHellman.from_defaults()
1690
+ cpub = consumer_dh.public
1691
+ server_dh = DiffieHellman.from_defaults()
1692
+ session = Server::DiffieHellmanSHA256ServerSession.new(server_dh, cpub)
1693
+ @request = Server::AssociateRequest.new(session, 'HMAC-SHA256')
1694
+ @request.message = Message.new(OPENID2_NS)
1695
+ response = @request.answer(@assoc)
1696
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1697
+ assert_equal(rfg.call("assoc_type"), "HMAC-SHA256")
1698
+ assert_equal(rfg.call("assoc_handle"), @assoc.handle)
1699
+ assert(!rfg.call("mac_key"))
1700
+ assert_equal(rfg.call("session_type"), "DH-SHA256")
1701
+ assert(rfg.call("enc_mac_key"))
1702
+ assert(rfg.call("dh_server_public"))
1703
+
1704
+ enc_key = Util.from_base64(rfg.call("enc_mac_key"))
1705
+ spub = CryptUtil.base64_to_num(rfg.call("dh_server_public"))
1706
+ secret = consumer_dh.xor_secret(CryptUtil.method('sha256'),
1707
+ spub, enc_key)
1708
+ assert_equal(secret, @assoc.secret)
1709
+ end
1710
+
1711
+ def test_protoError256
1712
+ s256_session = Consumer::DiffieHellmanSHA256Session.new()
1713
+
1714
+ invalid_s256 = {'openid.assoc_type' => 'HMAC-SHA1',
1715
+ 'openid.session_type' => 'DH-SHA256',}
1716
+ invalid_s256.merge!(s256_session.get_request())
1717
+
1718
+ invalid_s256_2 = {'openid.assoc_type' => 'MONKEY-PIRATE',
1719
+ 'openid.session_type' => 'DH-SHA256',}
1720
+ invalid_s256_2.merge!(s256_session.get_request())
1721
+
1722
+ bad_request_argss = [
1723
+ invalid_s256,
1724
+ invalid_s256_2,
1725
+ ]
1726
+
1727
+ bad_request_argss.each { |request_args|
1728
+ message = Message.from_post_args(request_args)
1729
+ assert_raise(Server::ProtocolError) {
1730
+ Server::AssociateRequest.from_message(message)
1731
+ }
1732
+ }
1733
+ end
1734
+
1735
+ def test_protoError
1736
+ s1_session = Consumer::DiffieHellmanSHA1Session.new()
1737
+
1738
+ invalid_s1 = {'openid.assoc_type' => 'HMAC-SHA256',
1739
+ 'openid.session_type' => 'DH-SHA1',}
1740
+ invalid_s1.merge!(s1_session.get_request())
1741
+
1742
+ invalid_s1_2 = {'openid.assoc_type' => 'ROBOT-NINJA',
1743
+ 'openid.session_type' => 'DH-SHA1',}
1744
+ invalid_s1_2.merge!(s1_session.get_request())
1745
+
1746
+ bad_request_argss = [
1747
+ {'openid.assoc_type' => 'Wha?'},
1748
+ invalid_s1,
1749
+ invalid_s1_2,
1750
+ ]
1751
+
1752
+ bad_request_argss.each { |request_args|
1753
+ message = Message.from_post_args(request_args)
1754
+ assert_raise(Server::ProtocolError) {
1755
+ Server::AssociateRequest.from_message(message)
1756
+ }
1757
+ }
1758
+ end
1759
+
1760
+ def test_protoErrorFields
1761
+
1762
+ contact = 'user@example.invalid'
1763
+ reference = 'Trac ticket number MAX_INT'
1764
+ error = 'poltergeist'
1765
+
1766
+ openid1_args = {
1767
+ 'openid.identitiy' => 'invalid',
1768
+ 'openid.mode' => 'checkid_setup',
1769
+ }
1770
+
1771
+ openid2_args = openid1_args.dup
1772
+ openid2_args.merge!({'openid.ns' => OPENID2_NS})
1773
+
1774
+ # Check presence of optional fields in both protocol versions
1775
+
1776
+ openid1_msg = Message.from_post_args(openid1_args)
1777
+ p = Server::ProtocolError.new(openid1_msg, error,
1778
+ reference, contact)
1779
+ reply = p.to_message()
1780
+
1781
+ assert_equal(reply.get_arg(OPENID_NS, 'reference'), reference)
1782
+ assert_equal(reply.get_arg(OPENID_NS, 'contact'), contact)
1783
+
1784
+ openid2_msg = Message.from_post_args(openid2_args)
1785
+ p = Server::ProtocolError.new(openid2_msg, error,
1786
+ reference, contact)
1787
+ reply = p.to_message()
1788
+
1789
+ assert_equal(reply.get_arg(OPENID_NS, 'reference'), reference)
1790
+ assert_equal(reply.get_arg(OPENID_NS, 'contact'), contact)
1791
+ end
1792
+
1793
+ def failUnlessExpiresInMatches(msg, expected_expires_in)
1794
+ expires_in_str = msg.get_arg(OPENID_NS, 'expires_in', NO_DEFAULT)
1795
+ expires_in = expires_in_str.to_i
1796
+
1797
+ # Slop is necessary because the tests can sometimes get run
1798
+ # right on a second boundary
1799
+ slop = 1 # second
1800
+ difference = expected_expires_in - expires_in
1801
+
1802
+ error_message = sprintf('"expires_in" value not within %s of expected: ' +
1803
+ 'expected=%s, actual=%s', slop, expected_expires_in,
1804
+ expires_in)
1805
+ assert((0 <= difference and difference <= slop), error_message)
1806
+ end
1807
+
1808
+ def test_plaintext
1809
+ @assoc = @signatory.create_association(false, 'HMAC-SHA1')
1810
+ response = @request.answer(@assoc)
1811
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1812
+
1813
+ assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
1814
+ assert_equal(rfg.call("assoc_handle"), @assoc.handle)
1815
+
1816
+ failUnlessExpiresInMatches(response.fields,
1817
+ @signatory.secret_lifetime)
1818
+
1819
+ assert_equal(
1820
+ rfg.call("mac_key"), Util.to_base64(@assoc.secret))
1821
+ assert(!rfg.call("session_type"))
1822
+ assert(!rfg.call("enc_mac_key"))
1823
+ assert(!rfg.call("dh_server_public"))
1824
+ end
1825
+
1826
+ def test_plaintext_v2
1827
+ # The main difference between this and the v1 test is that
1828
+ # session_type is always returned in v2.
1829
+ args = {
1830
+ 'openid.ns' => OPENID2_NS,
1831
+ 'openid.mode' => 'associate',
1832
+ 'openid.assoc_type' => 'HMAC-SHA1',
1833
+ 'openid.session_type' => 'no-encryption',
1834
+ }
1835
+ @request = Server::AssociateRequest.from_message(
1836
+ Message.from_post_args(args))
1837
+
1838
+ assert(!@request.message.is_openid1())
1839
+
1840
+ @assoc = @signatory.create_association(false, 'HMAC-SHA1')
1841
+ response = @request.answer(@assoc)
1842
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1843
+
1844
+ assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
1845
+ assert_equal(rfg.call("assoc_handle"), @assoc.handle)
1846
+
1847
+ failUnlessExpiresInMatches(
1848
+ response.fields, @signatory.secret_lifetime)
1849
+
1850
+ assert_equal(
1851
+ rfg.call("mac_key"), Util.to_base64(@assoc.secret))
1852
+
1853
+ assert_equal(rfg.call("session_type"), "no-encryption")
1854
+ assert(!rfg.call("enc_mac_key"))
1855
+ assert(!rfg.call("dh_server_public"))
1856
+ end
1857
+
1858
+ def test_plaintext256
1859
+ @assoc = @signatory.create_association(false, 'HMAC-SHA256')
1860
+ response = @request.answer(@assoc)
1861
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1862
+
1863
+ assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
1864
+ assert_equal(rfg.call("assoc_handle"), @assoc.handle)
1865
+
1866
+ failUnlessExpiresInMatches(
1867
+ response.fields, @signatory.secret_lifetime)
1868
+
1869
+ assert_equal(
1870
+ rfg.call("mac_key"), Util.to_base64(@assoc.secret))
1871
+ assert(!rfg.call("session_type"))
1872
+ assert(!rfg.call("enc_mac_key"))
1873
+ assert(!rfg.call("dh_server_public"))
1874
+ end
1875
+
1876
+ def test_unsupportedPrefer
1877
+ allowed_assoc = 'COLD-PET-RAT'
1878
+ allowed_sess = 'FROG-BONES'
1879
+ message = 'This is a unit test'
1880
+
1881
+ # Set an OpenID 2 message so answerUnsupported doesn't raise
1882
+ # ProtocolError.
1883
+ @request.message = Message.new(OPENID2_NS)
1884
+
1885
+ response = @request.answer_unsupported(message,
1886
+ allowed_assoc,
1887
+ allowed_sess)
1888
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1889
+ assert_equal(rfg.call('error_code'), 'unsupported-type')
1890
+ assert_equal(rfg.call('assoc_type'), allowed_assoc)
1891
+ assert_equal(rfg.call('error'), message)
1892
+ assert_equal(rfg.call('session_type'), allowed_sess)
1893
+ end
1894
+
1895
+ def test_unsupported
1896
+ message = 'This is a unit test'
1897
+
1898
+ # Set an OpenID 2 message so answerUnsupported doesn't raise
1899
+ # ProtocolError.
1900
+ @request.message = Message.new(OPENID2_NS)
1901
+
1902
+ response = @request.answer_unsupported(message)
1903
+ rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
1904
+ assert_equal(rfg.call('error_code'), 'unsupported-type')
1905
+ assert_equal(rfg.call('assoc_type'), nil)
1906
+ assert_equal(rfg.call('error'), message)
1907
+ assert_equal(rfg.call('session_type'), nil)
1908
+ end
1909
+
1910
+ def test_openid1_unsupported_explode
1911
+ # answer_unsupported on an associate request should explode if
1912
+ # the request was an OpenID 1 request.
1913
+ m = Message.new(OPENID1_NS)
1914
+
1915
+ assert_raise(Server::ProtocolError) {
1916
+ @request.answer_unsupported(m)
1917
+ }
1918
+ end
1919
+ end
1920
+
1921
+ class Counter
1922
+ def initialize
1923
+ @count = 0
1924
+ end
1925
+
1926
+ def inc
1927
+ @count += 1
1928
+ end
1929
+ end
1930
+
1931
+ class UnhandledError < Exception
1932
+ end
1933
+
1934
+ class TestServer < Test::Unit::TestCase
1935
+ include TestUtil
1936
+
1937
+ def setup
1938
+ @store = Store::Memory.new()
1939
+ @server = Server::Server.new(@store, "http://server.unittest/endpt")
1940
+ # catchlogs_setup()
1941
+ end
1942
+
1943
+ def test_failed_dispatch
1944
+ request = Server::OpenIDRequest.new()
1945
+ request.mode = "monkeymode"
1946
+ request.message = Message.new(OPENID1_NS)
1947
+ assert_raise(RuntimeError) {
1948
+ webresult = @server.handle_request(request)
1949
+ }
1950
+ end
1951
+
1952
+ def test_decode_request
1953
+ @server.decoder = BogusDecoder.new(@server)
1954
+ assert(@server.decode_request({}) == "BOGUS")
1955
+ end
1956
+
1957
+ def test_encode_response
1958
+ @server.encoder = BogusEncoder.new
1959
+ assert(@server.encode_response(nil) == "BOGUS")
1960
+ end
1961
+
1962
+ def test_dispatch
1963
+ monkeycalled = Counter.new()
1964
+
1965
+ @server.extend(InstanceDefExtension)
1966
+ @server.instance_def(:openid_monkeymode) do |request|
1967
+ raise UnhandledError
1968
+ end
1969
+
1970
+ request = Server::OpenIDRequest.new()
1971
+ request.mode = "monkeymode"
1972
+ request.message = Message.new(OPENID1_NS)
1973
+ assert_raise(UnhandledError) {
1974
+ webresult = @server.handle_request(request)
1975
+ }
1976
+ end
1977
+
1978
+ def test_associate
1979
+ request = Server::AssociateRequest.from_message(Message.from_post_args({}))
1980
+ response = @server.openid_associate(request)
1981
+ assert(response.fields.has_key?(OPENID_NS, "assoc_handle"),
1982
+ sprintf("No assoc_handle here: %s", response.fields.inspect))
1983
+ end
1984
+
1985
+ def test_associate2
1986
+ # Associate when the server has no allowed association types
1987
+ #
1988
+ # Gives back an error with error_code and no fallback session or
1989
+ # assoc types.
1990
+ @server.negotiator.allowed_types = []
1991
+
1992
+ # Set an OpenID 2 message so answerUnsupported doesn't raise
1993
+ # ProtocolError.
1994
+ msg = Message.from_post_args({
1995
+ 'openid.ns' => OPENID2_NS,
1996
+ 'openid.session_type' => 'no-encryption',
1997
+ })
1998
+
1999
+ request = Server::AssociateRequest.from_message(msg)
2000
+
2001
+ response = @server.openid_associate(request)
2002
+ assert(response.fields.has_key?(OPENID_NS, "error"))
2003
+ assert(response.fields.has_key?(OPENID_NS, "error_code"))
2004
+ assert(!response.fields.has_key?(OPENID_NS, "assoc_handle"))
2005
+ assert(!response.fields.has_key?(OPENID_NS, "assoc_type"))
2006
+ assert(!response.fields.has_key?(OPENID_NS, "session_type"))
2007
+ end
2008
+
2009
+ def test_associate3
2010
+ # Request an assoc type that is not supported when there are
2011
+ # supported types.
2012
+ #
2013
+ # Should give back an error message with a fallback type.
2014
+ @server.negotiator.allowed_types = [['HMAC-SHA256', 'DH-SHA256']]
2015
+
2016
+ msg = Message.from_post_args({
2017
+ 'openid.ns' => OPENID2_NS,
2018
+ 'openid.session_type' => 'no-encryption',
2019
+ })
2020
+
2021
+ request = Server::AssociateRequest.from_message(msg)
2022
+ response = @server.openid_associate(request)
2023
+
2024
+ assert(response.fields.has_key?(OPENID_NS, "error"))
2025
+ assert(response.fields.has_key?(OPENID_NS, "error_code"))
2026
+ assert(!response.fields.has_key?(OPENID_NS, "assoc_handle"))
2027
+
2028
+ assert_equal(response.fields.get_arg(OPENID_NS, "assoc_type"),
2029
+ 'HMAC-SHA256')
2030
+ assert_equal(response.fields.get_arg(OPENID_NS, "session_type"),
2031
+ 'DH-SHA256')
2032
+ end
2033
+
2034
+ def test_associate4
2035
+ # DH-SHA256 association session
2036
+ @server.negotiator.allowed_types = [['HMAC-SHA256', 'DH-SHA256']]
2037
+
2038
+ query = {
2039
+ 'openid.dh_consumer_public' =>
2040
+ 'ALZgnx8N5Lgd7pCj8K86T/DDMFjJXSss1SKoLmxE72kJTzOtG6I2PaYrHX' +
2041
+ 'xku4jMQWSsGfLJxwCZ6280uYjUST/9NWmuAfcrBfmDHIBc3H8xh6RBnlXJ' +
2042
+ '1WxJY3jHd5k1/ZReyRZOxZTKdF/dnIqwF8ZXUwI6peV0TyS/K1fOfF/s',
2043
+
2044
+ 'openid.assoc_type' => 'HMAC-SHA256',
2045
+ 'openid.session_type' => 'DH-SHA256',
2046
+ }
2047
+
2048
+ message = Message.from_post_args(query)
2049
+ request = Server::AssociateRequest.from_message(message)
2050
+ response = @server.openid_associate(request)
2051
+ assert(response.fields.has_key?(OPENID_NS, "assoc_handle"))
2052
+ end
2053
+
2054
+ def test_no_encryption_openid1
2055
+ # Make sure no-encryption associate requests for OpenID 1 are
2056
+ # logged.
2057
+ assert_log_matches(/Continuing anyway./) {
2058
+ m = Message.from_openid_args({
2059
+ 'session_type' => 'no-encryption',
2060
+ })
2061
+
2062
+ req = Server::AssociateRequest.from_message(m)
2063
+ }
2064
+ end
2065
+
2066
+ def test_missingSessionTypeOpenID2
2067
+ # Make sure session_type is required in OpenID 2
2068
+ msg = Message.from_post_args({
2069
+ 'openid.ns' => OPENID2_NS,
2070
+ })
2071
+
2072
+ assert_raises(Server::ProtocolError) {
2073
+ Server::AssociateRequest.from_message(msg)
2074
+ }
2075
+ end
2076
+
2077
+ def test_checkAuth
2078
+ request = Server::CheckAuthRequest.new('arrrrrf', '0x3999', [])
2079
+ request.message = Message.new(OPENID2_NS)
2080
+ response = nil
2081
+ silence_logging {
2082
+ response = @server.openid_check_authentication(request)
2083
+ }
2084
+ assert(response.fields.has_key?(OPENID_NS, "is_valid"))
2085
+ end
2086
+ end
2087
+
2088
+ class TestingRequest < Server::OpenIDRequest
2089
+ attr_accessor :assoc_handle, :namespace
2090
+ end
2091
+
2092
+ class TestSignatory < Test::Unit::TestCase
2093
+ include TestUtil
2094
+
2095
+ def setup
2096
+ @store = Store::Memory.new()
2097
+ @signatory = Server::Signatory.new(@store)
2098
+ @_dumb_key = @signatory.class._dumb_key
2099
+ @_normal_key = @signatory.class._normal_key
2100
+ # CatchLogs.setUp(self)
2101
+ end
2102
+
2103
+ def test_get_association_nil
2104
+ assert_raises(ArgumentError) {
2105
+ @signatory.get_association(nil, false)
2106
+ }
2107
+ end
2108
+
2109
+ def test_sign
2110
+ request = TestingRequest.new()
2111
+ assoc_handle = '{assoc}{lookatme}'
2112
+ @store.store_association(
2113
+ @_normal_key,
2114
+ Association.from_expires_in(60, assoc_handle,
2115
+ 'sekrit', 'HMAC-SHA1'))
2116
+ request.assoc_handle = assoc_handle
2117
+ request.namespace = OPENID1_NS
2118
+ response = Server::OpenIDResponse.new(request)
2119
+ response.fields = Message.from_openid_args({
2120
+ 'foo' => 'amsigned',
2121
+ 'bar' => 'notsigned',
2122
+ 'azu' => 'alsosigned',
2123
+ })
2124
+ sresponse = @signatory.sign(response)
2125
+ assert_equal(
2126
+ sresponse.fields.get_arg(OPENID_NS, 'assoc_handle'),
2127
+ assoc_handle)
2128
+ assert_equal(sresponse.fields.get_arg(OPENID_NS, 'signed'),
2129
+ 'assoc_handle,azu,bar,foo,signed')
2130
+ assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
2131
+ # assert(!@messages, @messages)
2132
+ end
2133
+
2134
+ def test_signDumb
2135
+ request = TestingRequest.new()
2136
+ request.assoc_handle = nil
2137
+ request.namespace = OPENID2_NS
2138
+ response = Server::OpenIDResponse.new(request)
2139
+ response.fields = Message.from_openid_args({
2140
+ 'foo' => 'amsigned',
2141
+ 'bar' => 'notsigned',
2142
+ 'azu' => 'alsosigned',
2143
+ 'ns' => OPENID2_NS,
2144
+ })
2145
+ sresponse = @signatory.sign(response)
2146
+ assoc_handle = sresponse.fields.get_arg(OPENID_NS, 'assoc_handle')
2147
+ assert(assoc_handle)
2148
+ assoc = @signatory.get_association(assoc_handle, true)
2149
+ assert(assoc)
2150
+ assert_equal(sresponse.fields.get_arg(OPENID_NS, 'signed'),
2151
+ 'assoc_handle,azu,bar,foo,ns,signed')
2152
+ assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
2153
+ # assert(!@messages, @messages)
2154
+ end
2155
+
2156
+ def test_signExpired
2157
+ # Sign a response to a message with an expired handle (using
2158
+ # invalidate_handle).
2159
+ #
2160
+ # From "Verifying with an Association":
2161
+ #
2162
+ # If an authentication request included an association handle
2163
+ # for an association between the OP and the Relying party, and
2164
+ # the OP no longer wishes to use that handle (because it has
2165
+ # expired or the secret has been compromised, for instance),
2166
+ # the OP will send a response that must be verified directly
2167
+ # with the OP, as specified in Section 11.3.2. In that
2168
+ # instance, the OP will include the field
2169
+ # "openid.invalidate_handle" set to the association handle
2170
+ # that the Relying Party included with the original request.
2171
+ request = TestingRequest.new()
2172
+ request.namespace = OPENID2_NS
2173
+ assoc_handle = '{assoc}{lookatme}'
2174
+ @store.store_association(
2175
+ @_normal_key,
2176
+ Association.from_expires_in(-10, assoc_handle,
2177
+ 'sekrit', 'HMAC-SHA1'))
2178
+ assert(@store.get_association(@_normal_key, assoc_handle))
2179
+
2180
+ request.assoc_handle = assoc_handle
2181
+ response = Server::OpenIDResponse.new(request)
2182
+ response.fields = Message.from_openid_args({
2183
+ 'foo' => 'amsigned',
2184
+ 'bar' => 'notsigned',
2185
+ 'azu' => 'alsosigned',
2186
+ })
2187
+ sresponse = nil
2188
+ silence_logging {
2189
+ sresponse = @signatory.sign(response)
2190
+ }
2191
+
2192
+ new_assoc_handle = sresponse.fields.get_arg(OPENID_NS, 'assoc_handle')
2193
+ assert(new_assoc_handle)
2194
+ assert(new_assoc_handle != assoc_handle)
2195
+
2196
+ assert_equal(
2197
+ sresponse.fields.get_arg(OPENID_NS, 'invalidate_handle'),
2198
+ assoc_handle)
2199
+
2200
+ assert_equal(sresponse.fields.get_arg(OPENID_NS, 'signed'),
2201
+ 'assoc_handle,azu,bar,foo,invalidate_handle,signed')
2202
+ assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
2203
+
2204
+ # make sure the expired association is gone
2205
+ assert(!@store.get_association(@_normal_key, assoc_handle),
2206
+ "expired association is still retrievable.")
2207
+
2208
+ # make sure the new key is a dumb mode association
2209
+ assert(@store.get_association(@_dumb_key, new_assoc_handle))
2210
+ assert(!@store.get_association(@_normal_key, new_assoc_handle))
2211
+ # assert(@messages)
2212
+ end
2213
+
2214
+ def test_signInvalidHandle
2215
+ request = TestingRequest.new()
2216
+ request.namespace = OPENID2_NS
2217
+ assoc_handle = '{bogus-assoc}{notvalid}'
2218
+
2219
+ request.assoc_handle = assoc_handle
2220
+ response = Server::OpenIDResponse.new(request)
2221
+ response.fields = Message.from_openid_args({
2222
+ 'foo' => 'amsigned',
2223
+ 'bar' => 'notsigned',
2224
+ 'azu' => 'alsosigned',
2225
+ })
2226
+ sresponse = @signatory.sign(response)
2227
+
2228
+ new_assoc_handle = sresponse.fields.get_arg(OPENID_NS, 'assoc_handle')
2229
+ assert(new_assoc_handle)
2230
+ assert(new_assoc_handle != assoc_handle)
2231
+
2232
+ assert_equal(
2233
+ sresponse.fields.get_arg(OPENID_NS, 'invalidate_handle'),
2234
+ assoc_handle)
2235
+
2236
+ assert_equal(
2237
+ sresponse.fields.get_arg(OPENID_NS, 'signed'),
2238
+ 'assoc_handle,azu,bar,foo,invalidate_handle,signed')
2239
+ assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
2240
+
2241
+ # make sure the new key is a dumb mode association
2242
+ assert(@store.get_association(@_dumb_key, new_assoc_handle))
2243
+ assert(!@store.get_association(@_normal_key, new_assoc_handle))
2244
+ # @failIf(@messages, @messages)
2245
+ end
2246
+
2247
+ def test_verify
2248
+ assoc_handle = '{vroom}{zoom}'
2249
+ assoc = Association.from_expires_in(
2250
+ 60, assoc_handle, 'sekrit', 'HMAC-SHA1')
2251
+
2252
+ @store.store_association(@_dumb_key, assoc)
2253
+
2254
+ signed = Message.from_post_args({
2255
+ 'openid.foo' => 'bar',
2256
+ 'openid.apple' => 'orange',
2257
+ 'openid.assoc_handle' => assoc_handle,
2258
+ 'openid.signed' => 'apple,assoc_handle,foo,signed',
2259
+ 'openid.sig' => 'uXoT1qm62/BB09Xbj98TQ8mlBco=',
2260
+ })
2261
+
2262
+ verified = @signatory.verify(assoc_handle, signed)
2263
+ assert(verified)
2264
+ # assert(!@messages, @messages)
2265
+ end
2266
+
2267
+ def test_verifyBadSig
2268
+ assoc_handle = '{vroom}{zoom}'
2269
+ assoc = Association.from_expires_in(
2270
+ 60, assoc_handle, 'sekrit', 'HMAC-SHA1')
2271
+
2272
+ @store.store_association(@_dumb_key, assoc)
2273
+
2274
+ signed = Message.from_post_args({
2275
+ 'openid.foo' => 'bar',
2276
+ 'openid.apple' => 'orange',
2277
+ 'openid.assoc_handle' => assoc_handle,
2278
+ 'openid.signed' => 'apple,assoc_handle,foo,signed',
2279
+ 'openid.sig' => 'uXoT1qm62/BB09Xbj98TQ8mlBco=BOGUS'
2280
+ })
2281
+
2282
+ verified = @signatory.verify(assoc_handle, signed)
2283
+ # @failIf(@messages, @messages)
2284
+ assert(!verified)
2285
+ end
2286
+
2287
+ def test_verifyBadHandle
2288
+ assoc_handle = '{vroom}{zoom}'
2289
+ signed = Message.from_post_args({
2290
+ 'foo' => 'bar',
2291
+ 'apple' => 'orange',
2292
+ 'openid.sig' => "Ylu0KcIR7PvNegB/K41KpnRgJl0=",
2293
+ })
2294
+
2295
+ verified = nil
2296
+ silence_logging {
2297
+ verified = @signatory.verify(assoc_handle, signed)
2298
+ }
2299
+
2300
+ assert(!verified)
2301
+ #assert(@messages)
2302
+ end
2303
+
2304
+ def test_verifyAssocMismatch
2305
+ # Attempt to validate sign-all message with a signed-list assoc.
2306
+ assoc_handle = '{vroom}{zoom}'
2307
+ assoc = Association.from_expires_in(
2308
+ 60, assoc_handle, 'sekrit', 'HMAC-SHA1')
2309
+
2310
+ @store.store_association(@_dumb_key, assoc)
2311
+
2312
+ signed = Message.from_post_args({
2313
+ 'foo' => 'bar',
2314
+ 'apple' => 'orange',
2315
+ 'openid.sig' => "d71xlHtqnq98DonoSgoK/nD+QRM=",
2316
+ })
2317
+
2318
+ verified = nil
2319
+ silence_logging {
2320
+ verified = @signatory.verify(assoc_handle, signed)
2321
+ }
2322
+
2323
+ assert(!verified)
2324
+ #assert(@messages)
2325
+ end
2326
+
2327
+ def test_getAssoc
2328
+ assoc_handle = makeAssoc(true)
2329
+ assoc = @signatory.get_association(assoc_handle, true)
2330
+ assert(assoc)
2331
+ assert_equal(assoc.handle, assoc_handle)
2332
+ # @failIf(@messages, @messages)
2333
+ end
2334
+
2335
+ def test_getAssocExpired
2336
+ assoc_handle = makeAssoc(true, -10)
2337
+ assoc = nil
2338
+ silence_logging {
2339
+ assoc = @signatory.get_association(assoc_handle, true)
2340
+ }
2341
+ assert(!assoc)
2342
+ # assert(@messages)
2343
+ end
2344
+
2345
+ def test_getAssocInvalid
2346
+ ah = 'no-such-handle'
2347
+ silence_logging {
2348
+ assert_equal(
2349
+ @signatory.get_association(ah, false), nil)
2350
+ }
2351
+ # assert(!@messages, @messages)
2352
+ end
2353
+
2354
+ def test_getAssocDumbVsNormal
2355
+ # getAssociation(dumb=False) cannot get a dumb assoc
2356
+ assoc_handle = makeAssoc(true)
2357
+ silence_logging {
2358
+ assert_equal(
2359
+ @signatory.get_association(assoc_handle, false), nil)
2360
+ }
2361
+ # @failIf(@messages, @messages)
2362
+ end
2363
+
2364
+ def test_getAssocNormalVsDumb
2365
+ # getAssociation(dumb=True) cannot get a shared assoc
2366
+ #
2367
+ # From "Verifying Directly with the OpenID Provider"::
2368
+ #
2369
+ # An OP MUST NOT verify signatures for associations that have shared
2370
+ # MAC keys.
2371
+ assoc_handle = makeAssoc(false)
2372
+ silence_logging {
2373
+ assert_equal(
2374
+ @signatory.get_association(assoc_handle, true), nil)
2375
+ }
2376
+ # @failIf(@messages, @messages)
2377
+ end
2378
+
2379
+ def test_createAssociation
2380
+ assoc = @signatory.create_association(false)
2381
+ silence_logging {
2382
+ assert(@signatory.get_association(assoc.handle, false))
2383
+ }
2384
+ # @failIf(@messages, @messages)
2385
+ end
2386
+
2387
+ def makeAssoc(dumb, lifetime=60)
2388
+ assoc_handle = '{bling}'
2389
+ assoc = Association.from_expires_in(lifetime, assoc_handle,
2390
+ 'sekrit', 'HMAC-SHA1')
2391
+
2392
+ silence_logging {
2393
+ @store.store_association(((dumb and @_dumb_key) or @_normal_key), assoc)
2394
+ }
2395
+
2396
+ return assoc_handle
2397
+ end
2398
+
2399
+ def test_invalidate
2400
+ assoc_handle = '-squash-'
2401
+ assoc = Association.from_expires_in(60, assoc_handle,
2402
+ 'sekrit', 'HMAC-SHA1')
2403
+
2404
+ silence_logging {
2405
+ @store.store_association(@_dumb_key, assoc)
2406
+ assoc = @signatory.get_association(assoc_handle, true)
2407
+ assert(assoc)
2408
+ assoc = @signatory.get_association(assoc_handle, true)
2409
+ assert(assoc)
2410
+ @signatory.invalidate(assoc_handle, true)
2411
+ assoc = @signatory.get_association(assoc_handle, true)
2412
+ assert(!assoc)
2413
+ }
2414
+ # @failIf(@messages, @messages)
2415
+ end
2416
+ end
2417
+
2418
+ class RunthroughTestCase < Test::Unit::TestCase
2419
+ def setup
2420
+ @store = Store::Memory.new
2421
+ @server = Server::Server.new(@store, "http://example.com/openid/server")
2422
+ end
2423
+
2424
+ def test_openid1_assoc_checkid
2425
+ assoc_args = {'openid.mode' => 'associate',
2426
+ 'openid.assoc_type' => 'HMAC-SHA1'}
2427
+ areq = @server.decode_request(assoc_args)
2428
+ aresp = @server.handle_request(areq)
2429
+
2430
+ amess = aresp.fields
2431
+ assert(amess.is_openid1)
2432
+ ahandle = amess.get_arg(OPENID_NS, 'assoc_handle')
2433
+ assert(ahandle)
2434
+ assoc = @store.get_association('http://localhost/|normal', ahandle)
2435
+ assert(assoc.is_a?(Association))
2436
+
2437
+
2438
+ checkid_args = {'openid.mode' => 'checkid_setup',
2439
+ 'openid.return_to' => 'http://example.com/openid/consumer',
2440
+ 'openid.assoc_handle' => ahandle,
2441
+ 'openid.identity' => 'http://foo.com/'}
2442
+
2443
+ cireq = @server.decode_request(checkid_args)
2444
+ ciresp = cireq.answer(true)
2445
+
2446
+ signed_resp = @server.signatory.sign(ciresp)
2447
+
2448
+ assert_equal(assoc.get_message_signature(signed_resp.fields),
2449
+ signed_resp.fields.get_arg(OPENID_NS, 'sig'))
2450
+
2451
+ assert(assoc.check_message_signature(signed_resp.fields))
2452
+ end
2453
+
2454
+ end
2455
+ end