ruby-openid 1.1.4 → 2.0.1

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