entp-ruby-openid 2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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