ruby-openid 1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-openid might be problematic. Click here for more details.

Files changed (114) hide show
  1. data/COPYING +21 -0
  2. data/INSTALL +34 -0
  3. data/README +67 -0
  4. data/TODO +9 -0
  5. data/examples/README +54 -0
  6. data/examples/cacert.pem +7815 -0
  7. data/examples/consumer.rb +285 -0
  8. data/examples/openid-store/associations/http-localhost_3A3000_2Fserver-EMQbAy3NnHVzA.s0u5KAcplKGzo +6 -0
  9. data/examples/openid-store/auth_key +1 -0
  10. data/examples/rails_active_record_store/README +59 -0
  11. data/examples/rails_active_record_store/XX_add_openidstore.rb +30 -0
  12. data/examples/rails_active_record_store/models/openid_association.rb +12 -0
  13. data/examples/rails_active_record_store/models/openid_nonce.rb +3 -0
  14. data/examples/rails_active_record_store/models/openid_setting.rb +2 -0
  15. data/examples/rails_active_record_store/openid_helper.rb +91 -0
  16. data/examples/rails_active_record_store/openidstore_test.rb +15 -0
  17. data/examples/rails_active_record_store/schema.mysql.sql +22 -0
  18. data/examples/rails_active_record_store/schema.postgresql.sql +21 -0
  19. data/examples/rails_active_record_store/schema.sqlite.sql +21 -0
  20. data/examples/rails_openid_login_generator/USAGE +23 -0
  21. data/examples/rails_openid_login_generator/openid_login_generator.rb +36 -0
  22. data/examples/rails_openid_login_generator/templates/README +116 -0
  23. data/examples/rails_openid_login_generator/templates/controller.rb +116 -0
  24. data/examples/rails_openid_login_generator/templates/controller_test.rb +0 -0
  25. data/examples/rails_openid_login_generator/templates/helper.rb +2 -0
  26. data/examples/rails_openid_login_generator/templates/openid_login_system.rb +87 -0
  27. data/examples/rails_openid_login_generator/templates/user.rb +14 -0
  28. data/examples/rails_openid_login_generator/templates/user_test.rb +0 -0
  29. data/examples/rails_openid_login_generator/templates/users.yml +0 -0
  30. data/examples/rails_openid_login_generator/templates/view_login.rhtml +15 -0
  31. data/examples/rails_openid_login_generator/templates/view_logout.rhtml +10 -0
  32. data/examples/rails_openid_login_generator/templates/view_welcome.rhtml +9 -0
  33. data/examples/rails_server/README +153 -0
  34. data/examples/rails_server/Rakefile +10 -0
  35. data/examples/rails_server/app/controllers/application.rb +4 -0
  36. data/examples/rails_server/app/controllers/login_controller.rb +35 -0
  37. data/examples/rails_server/app/controllers/server_controller.rb +185 -0
  38. data/examples/rails_server/app/helpers/application_helper.rb +3 -0
  39. data/examples/rails_server/app/helpers/login_helper.rb +2 -0
  40. data/examples/rails_server/app/helpers/server_helper.rb +9 -0
  41. data/examples/rails_server/app/views/layouts/server.rhtml +61 -0
  42. data/examples/rails_server/app/views/login/index.rhtml +32 -0
  43. data/examples/rails_server/app/views/server/decide.rhtml +11 -0
  44. data/examples/rails_server/config/boot.rb +19 -0
  45. data/examples/rails_server/config/database.yml +85 -0
  46. data/examples/rails_server/config/environment.rb +53 -0
  47. data/examples/rails_server/config/environments/development.rb +19 -0
  48. data/examples/rails_server/config/environments/production.rb +19 -0
  49. data/examples/rails_server/config/environments/test.rb +19 -0
  50. data/examples/rails_server/config/routes.rb +23 -0
  51. data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-YU.tkND1J4fEZhnuAoT5Zc0yCA0 +6 -0
  52. data/examples/rails_server/doc/README_FOR_APP +2 -0
  53. data/examples/rails_server/log/development.log +6059 -0
  54. data/examples/rails_server/log/production.log +0 -0
  55. data/examples/rails_server/log/server.log +0 -0
  56. data/examples/rails_server/log/test.log +0 -0
  57. data/examples/rails_server/public/404.html +8 -0
  58. data/examples/rails_server/public/500.html +8 -0
  59. data/examples/rails_server/public/dispatch.cgi +12 -0
  60. data/examples/rails_server/public/dispatch.fcgi +26 -0
  61. data/examples/rails_server/public/dispatch.rb +12 -0
  62. data/examples/rails_server/public/favicon.ico +0 -0
  63. data/examples/rails_server/public/images/rails.png +0 -0
  64. data/examples/rails_server/public/javascripts/controls.js +750 -0
  65. data/examples/rails_server/public/javascripts/dragdrop.js +584 -0
  66. data/examples/rails_server/public/javascripts/effects.js +854 -0
  67. data/examples/rails_server/public/javascripts/prototype.js +1785 -0
  68. data/examples/rails_server/public/robots.txt +1 -0
  69. data/examples/rails_server/script/about +3 -0
  70. data/examples/rails_server/script/breakpointer +3 -0
  71. data/examples/rails_server/script/console +3 -0
  72. data/examples/rails_server/script/destroy +3 -0
  73. data/examples/rails_server/script/generate +3 -0
  74. data/examples/rails_server/script/performance/benchmarker +3 -0
  75. data/examples/rails_server/script/performance/profiler +3 -0
  76. data/examples/rails_server/script/plugin +3 -0
  77. data/examples/rails_server/script/process/reaper +3 -0
  78. data/examples/rails_server/script/process/spawner +3 -0
  79. data/examples/rails_server/script/process/spinner +3 -0
  80. data/examples/rails_server/script/runner +3 -0
  81. data/examples/rails_server/script/server +3 -0
  82. data/examples/rails_server/test/functional/login_controller_test.rb +18 -0
  83. data/examples/rails_server/test/functional/server_controller_test.rb +18 -0
  84. data/examples/rails_server/test/test_helper.rb +28 -0
  85. data/lib/hmac-md5.rb +11 -0
  86. data/lib/hmac-rmd160.rb +11 -0
  87. data/lib/hmac-sha1.rb +11 -0
  88. data/lib/hmac-sha2.rb +25 -0
  89. data/lib/hmac.rb +112 -0
  90. data/lib/openid/association.rb +109 -0
  91. data/lib/openid/consumer.rb +928 -0
  92. data/lib/openid/dh.rb +48 -0
  93. data/lib/openid/discovery.rb +89 -0
  94. data/lib/openid/fetchers.rb +119 -0
  95. data/lib/openid/filestore.rb +315 -0
  96. data/lib/openid/htmltokenizer.rb +355 -0
  97. data/lib/openid/parse.rb +23 -0
  98. data/lib/openid/server.rb +951 -0
  99. data/lib/openid/service.rb +135 -0
  100. data/lib/openid/stores.rb +178 -0
  101. data/lib/openid/trustroot.rb +100 -0
  102. data/lib/openid/util.rb +273 -0
  103. data/test/assoc.rb +38 -0
  104. data/test/consumer.rb +384 -0
  105. data/test/dh.rb +20 -0
  106. data/test/extensions.rb +30 -0
  107. data/test/linkparse.rb +305 -0
  108. data/test/runtests.rb +11 -0
  109. data/test/server2.rb +1053 -0
  110. data/test/storetestcase.rb +172 -0
  111. data/test/teststore.rb +23 -0
  112. data/test/trustroot.rb +113 -0
  113. data/test/util.rb +56 -0
  114. metadata +218 -0
data/test/linkparse.rb ADDED
@@ -0,0 +1,305 @@
1
+ require 'test/unit'
2
+
3
+ require "openid/parse"
4
+
5
+ class LinkParseTestCase < Test::Unit::TestCase
6
+
7
+ def test_bad
8
+ cases = <<EOF
9
+ <foo>
10
+
11
+ <><barf
12
+
13
+ not even html
14
+
15
+ <link>
16
+
17
+ <html>
18
+ <link>
19
+
20
+ <head>
21
+ <link>
22
+
23
+ <html>
24
+ <head>
25
+ </head>
26
+ <link>
27
+
28
+ <html>
29
+ <link>
30
+ <head>
31
+
32
+ <link>
33
+ <html>
34
+ <head>
35
+
36
+ <html>
37
+ <head>
38
+ </head>
39
+ </html>
40
+ <link>
41
+
42
+ <html>
43
+ <head>
44
+ <html>
45
+ <link>
46
+
47
+ <head>
48
+ <html>
49
+ <link>
50
+
51
+ <html>
52
+ <head>
53
+ <body>
54
+ <link>
55
+
56
+ <html>
57
+ <head>
58
+ <head>
59
+ <link>
60
+
61
+ <html>
62
+ <head>
63
+ <script>
64
+ <link>
65
+ </script>
66
+
67
+ <html>
68
+ <head>
69
+ <!--
70
+ <link>
71
+ -->
72
+
73
+ <html>
74
+ <head>
75
+ <![CDATA[
76
+ <link>
77
+ ]]>
78
+
79
+ <html>
80
+ <head>
81
+ <![cDaTa[
82
+ <link>
83
+ ]]>
84
+
85
+ <htmlx>
86
+ <head>
87
+ <link>
88
+
89
+ <html:summer>
90
+ <head>
91
+ <link>
92
+
93
+ <html>
94
+ <head:zucchini>
95
+ <link>
96
+
97
+ <html/>
98
+ <head>
99
+ <link>
100
+
101
+ <html/>
102
+ <html>
103
+ <head>
104
+ <link>
105
+
106
+ <html>
107
+ <head/>
108
+ <link>
109
+
110
+ <html>
111
+ <head/>
112
+ <head>
113
+ <link>
114
+
115
+ <!-- Plain vanilla -->
116
+ <html>
117
+ <head>
118
+ <link>
119
+
120
+ <!-- Ignore tags in the <script:... > namespace -->
121
+ <html>
122
+ <head>
123
+ <script:paddypan>
124
+ <link>
125
+ </script:paddypan>
126
+
127
+ <!-- Short link tag -->
128
+ <html>
129
+ <head>
130
+ <link/>
131
+
132
+ <!-- Spaces in the HTML tag -->
133
+ <html >
134
+ <head>
135
+ <link>
136
+
137
+ <!-- Spaces in the head tag -->
138
+ <html>
139
+ <head >
140
+ <link>
141
+
142
+ <html>
143
+ <head>
144
+ <link >
145
+
146
+ <html><head><link>
147
+
148
+ <html>
149
+ <head>
150
+ <link>
151
+ </head>
152
+
153
+ <html>
154
+ <head>
155
+ <link>
156
+ </head>
157
+ <link>
158
+
159
+ <html>
160
+ <head>
161
+ <link>
162
+ <body>
163
+ <link>
164
+
165
+ <html>
166
+ <head>
167
+ <link>
168
+ </html>
169
+
170
+ <html>
171
+ <head>
172
+ <link>
173
+ </html>
174
+ <link>
175
+
176
+ <html>
177
+ <delicata>
178
+ <head>
179
+ <title>
180
+ <link>
181
+
182
+ <HtMl>
183
+ <hEaD>
184
+ <LiNk>
185
+
186
+ <butternut>
187
+ <html>
188
+ <summer>
189
+ <head>
190
+ <turban>
191
+ <link>
192
+
193
+ <html>
194
+ <head>
195
+ <script>
196
+ <link>
197
+
198
+ <html><head><script><link>
199
+
200
+ <html>
201
+ <head>
202
+ <!--
203
+ <link>
204
+
205
+ <html>
206
+ <head>
207
+ <![CDATA[
208
+ <link>
209
+
210
+ <html>
211
+ <head>
212
+ <![ACORN[
213
+ <link>
214
+ ]]>
215
+
216
+ <html>
217
+ <head>
218
+ <link>
219
+
220
+ <html>
221
+ <head>
222
+ <link>
223
+ <link>
224
+
225
+ <html>
226
+ <gold nugget>
227
+ <head>
228
+ <link>
229
+ <link>
230
+
231
+ <html>
232
+ <head>
233
+ <link>
234
+ <LiNk>
235
+ <body>
236
+ <link>
237
+ EOF
238
+
239
+
240
+ results = []
241
+ cases.split("\n\n").each do |c|
242
+ parse_link_attrs(c){ |x| results << x }
243
+ end
244
+
245
+ results.each {|r| assert(r == {}, r.to_s)}
246
+ end
247
+
248
+ def test_good_rel
249
+ cases = [
250
+ "<html><head><link rel=openid.server>",
251
+ "<html><head><link rel=openid.server />",
252
+ "<html><head><link hubbard rel=openid.server>",
253
+ "<html><head><link hubbard rel=openid.server></link>",
254
+ "<html><head><link hubbard rel=openid.server />",
255
+ "<html><head><link / rel=openid.server>",
256
+ "<html><head><link rel='openid.server'>"
257
+ ]
258
+
259
+ results = []
260
+ cases.each do |c|
261
+ parse_link_attrs(c) { |x| results << x }
262
+ end
263
+
264
+ assert_equal(cases.length, results.length)
265
+ results.each { |x| assert(x["rel"] == "openid.server", x["rel"]) }
266
+ end
267
+
268
+ def test_good
269
+ lj_test = <<EOF
270
+ <!DOCTYPE html
271
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
272
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
273
+ <html xmlns="http://www.w3.org/1999/xhtml">
274
+ <head>
275
+ <link rel="stylesheet" href="http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711" type="text/css" />
276
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
277
+ <meta name="foaf:maker" content="foaf:mbox_sha1sum '12f8abdacb5b1a806711e23249da592c0d316260'" />
278
+ <meta name="robots" content="noindex, nofollow, noarchive" />
279
+ <meta name="googlebot" content="nosnippet" />
280
+ <link rel="openid.server" href="http://www.livejournal.com/openid/server.bml" />
281
+ <title>Brian</title>
282
+ </head>
283
+ EOF
284
+ cases = [
285
+ ["<html><head><link rel=\"openid.server\" href=\"http://www.myopenid.com/server\" /></head></html>", [{"rel"=>"openid.server","href"=>"http://www.myopenid.com/server"}]],
286
+ ###
287
+ ["<html><head><link rel='openid.server' href='http://www.myopenid.com/server' /><link rel='openid.delegate' href='http://example.myopenid.com/' /></head></html>",
288
+ [{"rel"=>"openid.server","href"=>"http://www.myopenid.com/server"},
289
+ {"rel"=>"openid.delegate","href"=>"http://example.myopenid.com/"}]],
290
+ ###
291
+ [lj_test,
292
+ [{"rel"=>"stylesheet","type"=>"text/css","href"=>"http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711"},
293
+ {"rel"=>"openid.server","href"=>"http://www.livejournal.com/openid/server.bml"}]]
294
+
295
+ ]
296
+
297
+ cases.each do |unparsed, expected|
298
+ actual = []
299
+ parse_link_attrs(unparsed) {|x| actual << x}
300
+ assert_equal(expected, actual)
301
+ end
302
+
303
+ end
304
+
305
+ end
data/test/runtests.rb ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "teststore"
4
+ require "assoc"
5
+ require "dh"
6
+ require "util"
7
+ require "linkparse"
8
+ require "trustroot"
9
+ require "assoc"
10
+ require "server2"
11
+ require "consumer"
data/test/server2.rb ADDED
@@ -0,0 +1,1053 @@
1
+ require 'test/unit'
2
+ require 'uri'
3
+
4
+ require 'openid/server'
5
+ require 'openid/stores'
6
+
7
+ include OpenID::Server
8
+
9
+ ALT_MODULUS = 0xCAADDDEC1667FC68B5FA15D53C4E1532DD24561A1A2D47A12C01ABEA1E00731F6921AAC40742311FDF9E634BB7131BEE1AF240261554389A910425E044E88C8359B010F5AD2B80E29CB1A5B027B19D9E01A6F63A6F45E5D7ED2FF6A2A0085050A7D0CF307C3DB51D2490355907B4427C23A98DF1EB8ABEF2BA209BB7AFFE86A7
10
+ ALT_GEN = 5
11
+
12
+ class TestProtocolError < Test::Unit::TestCase
13
+
14
+ def test_browser_with_return_to
15
+ return_to = 'http://rp.unittest/consumer'
16
+ # will be a ProtocolError raised by Decode or CheckAuthRequest.answer
17
+ args = {
18
+ 'openid.mode' => 'monkeydance',
19
+ 'openid.identity' => 'http://wagu.unittest/',
20
+ 'openid.return_to' => return_to
21
+ }
22
+ e = ProtocolError.new(args, 'plucky')
23
+
24
+ assert_equal(true, e.has_return_to?)
25
+
26
+ expected_args = {
27
+ 'openid.mode' => 'error',
28
+ 'openid.error' => 'plucky'
29
+ }
30
+
31
+ actual_args = OpenID::Util.parse_query(URI.parse(e.encode_to_url).query)
32
+ assert_equal(expected_args, actual_args)
33
+ assert_equal(ENCODE_URL, e.which_encoding?)
34
+ end
35
+
36
+ def test_no_return_to
37
+ args = {
38
+ 'openid.mode' => 'zebradance',
39
+ 'openid.identity' => 'http://wagu.unittest/',
40
+ }
41
+
42
+ e = ProtocolError.new(args, "waffles")
43
+ assert_equal(false, e.has_return_to?)
44
+
45
+ expected = {
46
+ 'error' => 'waffles',
47
+ 'mode' => 'error'
48
+ }
49
+
50
+ actual = OpenID::Util.parsekv(e.encode_to_kvform)
51
+ assert_equal(expected, actual)
52
+ assert_equal(ENCODE_KVFORM, e.which_encoding?)
53
+ end
54
+
55
+ end
56
+
57
+ class TestDecode < Test::Unit::TestCase
58
+
59
+ def setup
60
+ @id_url = "http://decoder.am.unittest/"
61
+ @rt_url = "http://rp.unittest/foobot/?qux=zam"
62
+ @tr_url = "http://rp.unittest/"
63
+ @assoc_handle = "{assoc}{handle}"
64
+ end
65
+
66
+ def decode(args)
67
+ Decoder.new.decode(args)
68
+ end
69
+
70
+ def test_nil
71
+ args = {}
72
+ r = self.decode(args)
73
+ assert_equal(nil, r)
74
+ end
75
+
76
+ def test_irrelevant
77
+ args = {
78
+ 'pony' => 'spotted',
79
+ 'sreg.mutant_power' => 'decaffinator',
80
+ }
81
+ r = self.decode(args)
82
+ assert_equal(nil, r)
83
+ end
84
+
85
+ def test_bad
86
+ args = {
87
+ 'openid.mode' => 'twos-compliment',
88
+ 'openid.pants' => 'zippered'
89
+ }
90
+
91
+ begin
92
+ self.decode(args)
93
+ rescue ProtocolError
94
+ assert true
95
+ else
96
+ assert false, 'failed to raise ProtocolError'
97
+ end
98
+ end
99
+
100
+ def test_dict_of_lists
101
+ args = {
102
+ 'openid.mode' => ['checkid_setup'],
103
+ 'openid.identity' => @id_url,
104
+ 'openid.assoc_handle' => @assoc_handle,
105
+ 'openid.return_to' => @rt_url,
106
+ 'openid.trust_root' => @tr_url
107
+ }
108
+
109
+ # this should raise an argument error
110
+ begin
111
+ result = self.decode(args)
112
+ rescue ArgumentError
113
+ assert true
114
+ end
115
+ end
116
+
117
+
118
+ def test_checkid_immediate
119
+ args = {
120
+ 'openid.mode' => 'checkid_immediate',
121
+ 'openid.identity' => @id_url,
122
+ 'openid.assoc_handle' => @assoc_handle,
123
+ 'openid.return_to' => @rt_url,
124
+ 'openid.trust_root' => @tr_url,
125
+ # should be ignored
126
+ 'openid.some.extension' => 'junk'
127
+ }
128
+
129
+ r = self.decode(args)
130
+
131
+ assert_equal(CheckIDRequest, r.class)
132
+ assert_equal('checkid_immediate', r.mode)
133
+ assert_equal(true, r.immediate)
134
+ assert_equal(@id_url, r.identity)
135
+ assert_equal(@tr_url, r.trust_root)
136
+ assert_equal(@rt_url, r.return_to)
137
+ assert_equal(@assoc_handle, r.assoc_handle)
138
+ end
139
+
140
+ def test_checkid_setup_no_identity
141
+ args = {
142
+ 'openid.mode' => 'checkid_setup',
143
+ 'openid.assoc_handle' => @assoc_handle,
144
+ 'openid.return_to' => @rt_url,
145
+ 'openid.trust_root' => @tr_url,
146
+ }
147
+
148
+ begin
149
+ result = self.decode(args)
150
+ rescue ProtocolError => e
151
+ assert e.query == args
152
+ else
153
+ flunk('Expected a ProtocolError, but did not get one')
154
+ end
155
+ end
156
+
157
+
158
+ def test_checkid_setup_no_return_to
159
+ args = {
160
+ 'openid.mode' => 'checkid_setup',
161
+ 'openid.identity' => @id_url,
162
+ 'openid.assoc_handle' => @assoc_handle,
163
+ 'openid.trust_root' => @tr_url,
164
+ }
165
+ begin
166
+ result = self.decode(args)
167
+ rescue ProtocolError => e
168
+ assert e.query == args
169
+ else
170
+ flunk('Expected a ProtocolError, but did not get one')
171
+ end
172
+ end
173
+
174
+ def test_checkid_setup_bad_return_to
175
+ args = {
176
+ 'openid.mode' => 'checkid_setup',
177
+ 'openid.identity' => @id_url,
178
+ 'openid.assoc_handle' => @assoc_handle,
179
+ 'openid.return_to' => 'not a url',
180
+ 'openid.trust_root' => @tr_url,
181
+ }
182
+ begin
183
+ result = self.decode(args)
184
+ rescue MalformedReturnURL => e
185
+ assert e.query == args
186
+ else
187
+ flunk('Expected a MalformedReturnURL, but did not get one')
188
+ end
189
+ end
190
+
191
+ def test_checkid_setup_untrusted_return_to
192
+ args = {
193
+ 'openid.mode' => 'checkid_setup',
194
+ 'openid.identity' => @id_url,
195
+ 'openid.assoc_handle' => @assoc_handle,
196
+ 'openid.return_to' => 'http://incorrect.example.com/',
197
+ 'openid.trust_root' => @tr_url,
198
+ }
199
+ begin
200
+ result = self.decode(args)
201
+ rescue UntrustedReturnURL => e
202
+ assert true
203
+ else
204
+ flunk('Expected a UntrustedReturnURL, but did not get one')
205
+ end
206
+ end
207
+
208
+
209
+ def test_check_auth
210
+ args = {
211
+ 'openid.mode' => 'check_authentication',
212
+ 'openid.assoc_handle' => '{dumb}{handle}',
213
+ 'openid.sig' => 'sigblob',
214
+ 'openid.signed' => 'foo,bar,mode',
215
+ 'openid.foo' => 'signedval1',
216
+ 'openid.bar' => 'signedval2',
217
+ 'openid.baz' => 'unsigned',
218
+ }
219
+ r = self.decode(args)
220
+ assert_equal(r.class, CheckAuthRequest)
221
+ assert_equal('check_authentication', r.mode)
222
+ assert_equal('sigblob', r.sig)
223
+ assert_equal([['foo', 'signedval1'],
224
+ ['bar', 'signedval2'],
225
+ ['mode', 'id_res']], r.signed)
226
+
227
+ end
228
+
229
+ def test_check_auth_missing_signed_field
230
+ args = {
231
+ 'openid.mode' => 'check_authentication',
232
+ 'openid.assoc_handle' => '{dumb}{handle}',
233
+ 'openid.sig' => 'sigblob',
234
+ 'openid.signed' => 'foo,bar,mode',
235
+ 'openid.foo' => 'signedval1',
236
+ 'openid.baz' => 'unsigned'
237
+ }
238
+ begin
239
+ r = self.decode(args)
240
+ rescue ProtocolError => e
241
+ assert e.query == args
242
+ else
243
+ flunk('Expected a ProtocolError, but did not get one')
244
+ end
245
+ end
246
+
247
+ def test_check_auth_missing_signature
248
+ args = {
249
+ 'openid.mode' => 'check_authentication',
250
+ 'openid.assoc_handle' => '{dumb}{handle}',
251
+ 'openid.signed' => 'foo,bar,mode',
252
+ 'openid.foo' => 'signedval1',
253
+ 'openid.bar' => 'signedval2',
254
+ 'openid.baz' => 'unsigned'
255
+ }
256
+ begin
257
+ r = self.decode(args)
258
+ rescue ProtocolError => e
259
+ assert e.query == args
260
+ else
261
+ flunk('Expected a ProtocolError, but did not get one')
262
+ end
263
+ end
264
+
265
+
266
+ def test_check_auth_and_invalidate
267
+ args = {
268
+ 'openid.mode' => 'check_authentication',
269
+ 'openid.assoc_handle' => '{dumb}{handle}',
270
+ 'openid.invalidate_handle' => '[[SMART_handle]]',
271
+ 'openid.sig' => 'sigblob',
272
+ 'openid.signed' => 'foo,bar,mode',
273
+ 'openid.foo' => 'signedval1',
274
+ 'openid.bar' => 'signedval2',
275
+ 'openid.baz' => 'unsigned'
276
+ }
277
+ r = self.decode(args)
278
+ assert_equal(CheckAuthRequest, r.class)
279
+ assert_equal('[[SMART_handle]]', r.invalidate_handle)
280
+ end
281
+
282
+
283
+ def test_associate_DH
284
+ args = {
285
+ 'openid.mode' => 'associate',
286
+ 'openid.session_type' => 'DH-SHA1',
287
+ 'openid.dh_consumer_public' => "Rzup9265tw=="
288
+ }
289
+ r = self.decode(args)
290
+ assert_equal(AssociateRequest, r.class)
291
+ assert_equal("associate", r.mode)
292
+ assert_equal("DH-SHA1", r.session.session_type)
293
+ assert_equal("HMAC-SHA1", r.assoc_type)
294
+ assert(r.session.consumer_pubkey)
295
+ end
296
+
297
+ # Trying DH assoc w/o public key
298
+ def test_associate_DH_missing_key
299
+ args = {
300
+ 'openid.mode' => 'associate',
301
+ 'openid.session_type' => 'DH-SHA1'
302
+ }
303
+ # Using DH-SHA1 without supplying dh_consumer_public is an error.
304
+ begin
305
+ self.decode(args)
306
+ rescue ProtocolError => e
307
+ assert e.query == args
308
+ else
309
+ flunk('Wanted a ProtocolError')
310
+ end
311
+ end
312
+
313
+ # test associate w/ non default mod and gen
314
+ def test_associate_DH_mod_gen
315
+ args = {
316
+ 'openid.mode' => 'associate',
317
+ 'openid.session_type' => 'DH-SHA1',
318
+ 'openid.dh_consumer_public' => "Rzup9265tw==",
319
+ 'openid.dh_modulus' => OpenID::Util.num_to_base64(ALT_MODULUS),
320
+ 'openid.dh_gen' => OpenID::Util.num_to_base64(ALT_GEN)
321
+ }
322
+ r = self.decode(args)
323
+ assert_equal(AssociateRequest, r.class)
324
+ assert_equal('DH-SHA1', r.session.session_type)
325
+ assert_equal('HMAC-SHA1', r.assoc_type)
326
+ assert_equal(ALT_MODULUS, r.session.dh.p)
327
+ assert_equal(ALT_GEN, r.session.dh.g)
328
+ assert r.session.consumer_pubkey
329
+ end
330
+
331
+ def test_associate_weird_session
332
+ args = {
333
+ 'openid.mode' => 'associate',
334
+ 'openid.session_type' => 'FLCL6',
335
+ 'openid.dh_consumer_public' => "YQ==\n"
336
+ }
337
+ begin
338
+ self.decode(args)
339
+ rescue ProtocolError => e
340
+ assert e.query == args
341
+ else
342
+ flunk('Wanted a ProtocolError')
343
+ end
344
+ end
345
+
346
+ def test_associate_plain_session
347
+ args = {'openid.mode' => 'associate'}
348
+ r = self.decode(args)
349
+ assert_equal(AssociateRequest, r.class)
350
+ assert_equal('associate', r.mode)
351
+ assert_equal('plaintext', r.session.session_type)
352
+ assert_equal('HMAC-SHA1', r.assoc_type)
353
+ end
354
+
355
+ end
356
+
357
+ class TestEncode < Test::Unit::TestCase
358
+
359
+ def setup
360
+ @encoder = Encoder.new
361
+ end
362
+
363
+ def test_id_res
364
+ request = CheckIDRequest.new('checkid_setup',
365
+ 'http://bombom.unittest/',
366
+ 'http://burr.unittest/999',
367
+ 'http://burr.unittest/')
368
+ response = OpenIDResponse.new(request)
369
+ response.fields = {
370
+ 'mode' => 'id_res',
371
+ 'identity' => request.identity,
372
+ 'return_to' => request.return_to
373
+ }
374
+ webresponse = @encoder.encode(response)
375
+ assert_equal(HTTP_REDIRECT, webresponse.code)
376
+ assert webresponse.headers.has_key?('location')
377
+ loc = webresponse.headers['location']
378
+ assert loc.starts_with?(request.return_to)
379
+
380
+ query = OpenID::Util.parse_query(URI.parse(loc).query)
381
+ expected = {}
382
+ response.fields.each {|k,v| expected['openid.'+k] = v}
383
+ assert_equal(expected, query)
384
+ end
385
+
386
+ def test_cancel
387
+ request = CheckIDRequest.new('checkid_setup',
388
+ 'http://bombom.unittest/',
389
+ 'http://burr.unittest/999',
390
+ 'http://burr.unittest/')
391
+ response = OpenIDResponse.new(request)
392
+ response.fields = {'mode' => 'cancel'}
393
+ wr = @encoder.encode(response)
394
+ assert_equal(HTTP_REDIRECT, wr.code)
395
+ assert wr.headers.has_key?('location')
396
+ assert wr.headers['location'].starts_with?(request.return_to)
397
+ end
398
+
399
+ def test_assoc_reply
400
+ req = AssociateRequest.from_query({})
401
+ resp = OpenIDResponse.new(req)
402
+ resp.fields = {'assoc_handle' => 'every-zig'}
403
+ wr = @encoder.encode(resp)
404
+ assert_equal(HTTP_OK, wr.code)
405
+ assert_equal({}, wr.headers)
406
+ assert_equal("assoc_handle:every-zig\n", wr.body)
407
+ end
408
+
409
+ def test_check_auth_reply
410
+ req = CheckAuthRequest.new('sock', 'sigg', [])
411
+ res = OpenIDResponse.new(req)
412
+ res.fields = {'is_valid' => 'true', 'invalidate_handle' => 'xxx:xxx'}
413
+ wr = @encoder.encode(res)
414
+
415
+ expected = "invalidate_handle:xxx:xxx\nis_valid:true\n"
416
+ expected = OpenID::Util.parsekv(expected)
417
+
418
+ assert_equal(HTTP_OK, wr.code)
419
+ assert_equal({}, wr.headers)
420
+ assert_equal(expected, OpenID::Util.parsekv(wr.body))
421
+ end
422
+
423
+ def test_unencodable_error
424
+ args = {'openid.identity' => 'http://limu.unittest/'}
425
+ e = ProtocolError.new(args, 'foo')
426
+ begin
427
+ @encoder.encode(e)
428
+ rescue EncodingError => e
429
+ assert true
430
+ else
431
+ flunk('Expected an EncodingError')
432
+ end
433
+ end
434
+
435
+ def test_encodable_error
436
+ args = {
437
+ 'openid.mode' => 'associate',
438
+ 'openid.identity' => 'http://limu.unittest/'
439
+ }
440
+ e = ProtocolError.new(args, 'snoot')
441
+
442
+ expected_body = "error:snoot\nmode:error\n"
443
+ expected_body = OpenID::Util.parsekv(expected_body)
444
+
445
+ wr = @encoder.encode(e)
446
+ assert_equal(HTTP_ERROR, wr.code)
447
+ assert_equal({}, wr.headers)
448
+ assert_equal(expected_body, OpenID::Util.parsekv(wr.body))
449
+ end
450
+
451
+ end
452
+
453
+ class TestSigningEncode < Test::Unit::TestCase
454
+
455
+ def setup
456
+ @dumb_key = 'http://localhost/|dumb'
457
+ @normal_key = 'http://localhost/|normal'
458
+ @store = OpenID::MemoryStore.new
459
+ @request = CheckIDRequest.new('checkid_setup',
460
+ 'http://bombom.unittest/',
461
+ 'http://burr.unittest/999',
462
+ 'http://burr.unittest/')
463
+ @response = OpenIDResponse.new(@request)
464
+ @response.fields = {
465
+ 'mode' => 'id_res',
466
+ 'identity' => @request.identity,
467
+ 'return_to' => @request.return_to
468
+ }
469
+ @response.signed += ['mode','identity','return_to']
470
+ @signatory = Signatory.new(@store)
471
+ @encoder = SigningEncoder.new(@signatory)
472
+ end
473
+
474
+ def test_id_res
475
+ assoc_handle = '{bicycle}{shed}'
476
+ assoc = OpenID::Association.from_expires_in(60, assoc_handle,
477
+ 'sekrit','HMAC-SHA1')
478
+ @store.store_association(@normal_key, assoc)
479
+ @request.assoc_handle = assoc_handle
480
+ wr = @encoder.encode(@response)
481
+
482
+ assert_equal(HTTP_REDIRECT, wr.code)
483
+ assert wr.headers.has_key?('location')
484
+
485
+ loc = wr.headers['location']
486
+ query = OpenID::Util.parse_query(URI.parse(loc).query)
487
+
488
+ assert query.has_key?('openid.sig')
489
+ assert query.has_key?('openid.signed')
490
+ assert query.has_key?('openid.assoc_handle')
491
+ end
492
+
493
+ def test_id_res_dumb
494
+ wr = @encoder.encode(@response)
495
+
496
+ assert_equal(HTTP_REDIRECT, wr.code)
497
+ assert wr.headers.has_key?('location')
498
+
499
+ loc = wr.headers['location']
500
+ query = OpenID::Util.parse_query(URI.parse(loc).query)
501
+
502
+ assert query.has_key?('openid.sig')
503
+ assert query.has_key?('openid.signed')
504
+ assert query.has_key?('openid.assoc_handle')
505
+ end
506
+
507
+ def test_cancel
508
+ response = OpenIDResponse.new(@request)
509
+ response.fields['mode'] = 'cancel'
510
+ response.signed.clear
511
+ wr = @encoder.encode(response)
512
+ assert_equal(HTTP_REDIRECT, wr.code)
513
+ assert wr.headers.has_key?('location')
514
+
515
+ loc = wr.headers['location']
516
+ query = OpenID::Util.parse_query(URI.parse(loc).query)
517
+ assert(!query.has_key?('openid.sig'))
518
+ end
519
+
520
+ def test_assoc_reply
521
+ req = AssociateRequest.from_query({})
522
+ res = OpenIDResponse.new(req)
523
+ res.fields = {'assoc_handle' => 'every-zig'}
524
+ wr = @encoder.encode(res)
525
+ assert_equal(HTTP_OK, wr.code)
526
+ assert_equal({}, wr.headers)
527
+ assert_equal("assoc_handle:every-zig\n", wr.body)
528
+ end
529
+
530
+ def test_already_signed
531
+ @response.fields['sig'] = 'priorSig=='
532
+ begin
533
+ @encoder.encode(@response)
534
+ rescue AlreadySigned => e
535
+ assert true
536
+ else
537
+ flunk('Wanted an AlreadySigned exception')
538
+ end
539
+ end
540
+
541
+ end
542
+
543
+ class TestCheckID < Test::Unit::TestCase
544
+
545
+ def setup
546
+ @request = CheckIDRequest.new('checkid_setup',
547
+ 'http://bombom.unittest/',
548
+ 'http://burr.unittest/999',
549
+ 'http://burr.unittest/')
550
+ end
551
+
552
+ def test_trust_root_invalid
553
+ @request.trust_root = 'http://foo.un/17'
554
+ @request.return_to = 'http://foo.un/39'
555
+ assert !@request.trust_root_valid
556
+ end
557
+
558
+ def test_trust_root_hvalid
559
+ @request.trust_root = 'http://foo.un/'
560
+ @request.return_to = 'http://foo.un/39'
561
+ assert @request.trust_root_valid
562
+ end
563
+
564
+ def test_answer_allow
565
+ answer = @request.answer(true)
566
+ assert_equal(@request, answer.request)
567
+ assert_equal({'mode'=>'id_res','identity'=>@request.identity,
568
+ 'return_to'=>@request.return_to}, answer.fields)
569
+ signed = answer.signed.dup.sort
570
+ assert_equal(['identity','mode','return_to'], signed)
571
+ end
572
+
573
+ def test_answer_allow_no_trust_root
574
+ @request.trust_root = nil
575
+ answer = @request.answer(true)
576
+ assert_equal(@request, answer.request)
577
+ assert_equal({'mode'=>'id_res','identity'=>@request.identity,
578
+ 'return_to'=>@request.return_to}, answer.fields)
579
+ signed = answer.signed.dup.sort
580
+ assert_equal(['identity','mode','return_to'], signed)
581
+ end
582
+
583
+ def test_answer_immediate_deny
584
+ @request.mode = 'checkid_immediate'
585
+ @request.immediate = true
586
+ server_url = 'http://server-url.unittest/'
587
+ answer = @request.answer(false, server_url)
588
+ assert_equal(@request, answer.request)
589
+ assert_equal(2, answer.fields.length)
590
+ assert_equal('id_res', answer.fields['mode'])
591
+ assert answer.fields['user_setup_url'].starts_with?(server_url)
592
+ assert_equal([], answer.signed)
593
+ end
594
+
595
+ def test_answer_setup_deny
596
+ answer = @request.answer(false)
597
+ assert_equal({'mode' => 'cancel'}, answer.fields)
598
+ assert_equal([], answer.signed)
599
+ end
600
+
601
+ def test_encode_to_url
602
+ server_url = 'http://openid-server.un/'
603
+ url = @request.encode_to_url(server_url)
604
+ query = OpenID::Util.parse_query(URI.parse(url).query)
605
+
606
+ req = CheckIDRequest.from_query(query)
607
+ assert_equal(@request.mode, req.mode)
608
+ assert_equal(@request.identity, req.identity)
609
+ assert_equal(@request.return_to, req.return_to)
610
+ assert_equal(@request.trust_root, req.trust_root)
611
+ assert_equal(@request.immediate, req.immediate)
612
+ assert_equal(@request.assoc_handle, req.assoc_handle)
613
+ end
614
+
615
+ def test_cancel_url
616
+ url = @request.cancel_url
617
+ expected = OpenID::Util.append_args(@request.return_to,
618
+ {'openid.mode'=>'cancel'})
619
+ assert_equal(expected, url)
620
+ end
621
+
622
+ def test_cancel_url_immediate
623
+ @request.immediate = true
624
+ begin
625
+ url = @request.cancel_url
626
+ rescue ProtocolError => e
627
+ assert true
628
+ else
629
+ flunk('Wanted a ProtoclError')
630
+ end
631
+ end
632
+
633
+ end
634
+
635
+
636
+ class TestCheckIDExtension < Test::Unit::TestCase
637
+
638
+ def setup
639
+ @request = CheckIDRequest.new('checkid_setup',
640
+ 'http://bombom.unittest/',
641
+ 'http://burr.unittest/999',
642
+ 'http://burr.unittest/')
643
+ @response = OpenIDResponse.new(@request)
644
+ @response.fields['mode'] = 'id_res'
645
+ @response.fields['blue'] = 'star'
646
+ @response.signed += ['mode','identity','return_to']
647
+ end
648
+
649
+ def test_add_field
650
+ ns = 'mj12'
651
+ @response.add_field(ns, 'bright', 'potato')
652
+ assert_equal({'blue'=>'star',
653
+ 'mode'=>'id_res',
654
+ 'mj12.bright'=>'potato'}, @response.fields)
655
+ assert_equal(['mode','identity','return_to','mj12.bright'], @response.signed)
656
+ end
657
+
658
+ def test_add_field_unsigned
659
+ ns = 'mj12'
660
+ @response.add_field(ns, 'bright', 'potato', false)
661
+ assert_equal({'blue'=>'star',
662
+ 'mode'=>'id_res',
663
+ 'mj12.bright'=>'potato'}, @response.fields)
664
+ assert_equal(['mode','identity','return_to'], @response.signed)
665
+ end
666
+
667
+ def test_add_fields
668
+ ns = 'mj12'
669
+ @response.add_fields(ns, {'bright'=>'potato', 'xxx'=>'yyy'})
670
+ assert_equal({'blue'=>'star',
671
+ 'mode'=>'id_res',
672
+ 'mj12.bright'=>'potato',
673
+ 'mj12.xxx'=>'yyy'}, @response.fields)
674
+ assert_equal(['mode','identity','return_to','mj12.bright','mj12.xxx'].sort, @response.signed.sort)
675
+ end
676
+
677
+ def test_add_fields_unsigned
678
+ ns = 'mj12'
679
+ @response.add_fields(ns, {'bright'=>'potato', 'xxx'=>'yyy'}, false)
680
+ assert_equal({'blue'=>'star',
681
+ 'mode'=>'id_res',
682
+ 'mj12.bright'=>'potato',
683
+ 'mj12.xxx'=>'yyy'}, @response.fields)
684
+ assert_equal(['mode','identity','return_to'].sort, @response.signed.sort)
685
+ end
686
+
687
+ def test_update
688
+ eres = OpenIDResponse.new(nil)
689
+ eres.fields.update({'a'=>'b','c'=>'d'})
690
+ eres.signed = ['c']
691
+
692
+ @response.update('ns', eres)
693
+ assert_equal({'blue'=>'star','mode'=>'id_res','ns.a'=>'b','ns.c'=>'d'},
694
+ @response.fields)
695
+ assert_equal(['mode', 'identity', 'return_to', 'ns.c'], @response.signed)
696
+ end
697
+
698
+ def test_update_no_namespace
699
+ eres = OpenIDResponse.new(nil)
700
+ eres.fields.update({'a'=>'b','c'=>'d'})
701
+ eres.signed = ['c']
702
+
703
+ @response.update(nil, eres)
704
+ assert_equal({'blue'=>'star','mode'=>'id_res','a'=>'b','c'=>'d'},
705
+ @response.fields)
706
+ assert_equal(['mode', 'identity', 'return_to', 'c'], @response.signed)
707
+ end
708
+
709
+ end
710
+
711
+ class MockSignatory
712
+
713
+ attr_accessor :is_valid, :assocs
714
+
715
+ def initialize(assoc)
716
+ @assocs = [assoc]
717
+ @is_valid = true
718
+ end
719
+
720
+ def is_valid?
721
+ @is_valid
722
+ end
723
+
724
+ def verify(assoc_handle, sig, signed_pairs)
725
+ if @assocs.member?([true, assoc_handle])
726
+ return self.is_valid?
727
+ else
728
+ return false
729
+ end
730
+ end
731
+
732
+ def get_association(assoc_handle, dumb)
733
+ if @assocs.member?([dumb, assoc_handle])
734
+ return true
735
+ else
736
+ return nil
737
+ end
738
+ end
739
+
740
+ def invalidate(assoc_handle, dumb)
741
+ @assocs.delete([dumb, assoc_handle])
742
+ end
743
+
744
+ end
745
+
746
+ class TestCheckAuthServer < Test::Unit::TestCase
747
+
748
+ def setup
749
+ @assoc_handle = 'moo'
750
+ @request = CheckAuthRequest.new(@assoc_handle, 'signature',
751
+ [['one','alpha'],['two','beta']])
752
+ @signatory = MockSignatory.new([true, @assoc_handle])
753
+ end
754
+
755
+ def test_valid
756
+ r = @request.answer(@signatory)
757
+ assert_equal({'is_valid'=>'true'}, r.fields)
758
+ assert_equal(r.request, @request)
759
+ end
760
+
761
+ def test_invalid
762
+ assert_not_nil(@signatory)
763
+
764
+ @signatory.is_valid = false
765
+ r = @request.answer(@signatory)
766
+ assert_equal({'is_valid'=>'false'}, r.fields)
767
+ assert_equal(r.request, @request)
768
+ end
769
+
770
+ def test_replay
771
+ r = @request.answer(@signatory)
772
+ r = @request.answer(@signatory)
773
+ assert_equal({'is_valid'=>'false'}, r.fields)
774
+ end
775
+
776
+ def test_invalidate_handle
777
+ @request.invalidate_handle = 'bogushandle'
778
+ r = @request.answer(@signatory)
779
+ assert_equal({'is_valid'=>'true',
780
+ 'invalidate_handle'=>'bogushandle'}, r.fields)
781
+ assert_equal(r.request, @request)
782
+ end
783
+
784
+ def test_invalidate_handle_no
785
+ assoc_handle = 'goodhandle'
786
+ @signatory.assocs << [false, assoc_handle]
787
+ @request.invalidate_handle = assoc_handle
788
+ r = @request.answer(@signatory)
789
+ assert_equal({'is_valid'=>'true'}, r.fields)
790
+ end
791
+
792
+ end
793
+
794
+
795
+ class TestAssociate < Test::Unit::TestCase
796
+
797
+ def setup
798
+ @request = AssociateRequest.from_query({})
799
+ @store = OpenID::MemoryStore.new
800
+ @signatory = Signatory.new(@store)
801
+ @assoc = @signatory.create_association(false)
802
+ end
803
+
804
+ def test_dh
805
+ consumer_dh = OpenID::DiffieHellman.new
806
+ cpub = consumer_dh.public
807
+ session = DiffieHellmanServerSession.new(OpenID::DiffieHellman.new, cpub)
808
+ @request = AssociateRequest.new(session)
809
+ response = @request.answer(@assoc)
810
+ rf = response.fields
811
+ assert_equal('HMAC-SHA1', rf['assoc_type'])
812
+ assert_equal('DH-SHA1', rf['session_type'])
813
+ assert_equal(@assoc.handle, rf['assoc_handle'])
814
+ assert_nil(rf['mac_key'])
815
+ assert_not_nil(rf['enc_mac_key'])
816
+ assert_not_nil(rf['dh_server_public'])
817
+
818
+ enc_key = OpenID::Util.from_base64(rf['enc_mac_key'])
819
+ spub = OpenID::Util.base64_to_num(rf['dh_server_public'])
820
+ secret = consumer_dh.xor_secrect(spub, enc_key)
821
+ assert_equal(@assoc.secret, secret)
822
+ end
823
+
824
+ def test_plaintext
825
+ response = @request.answer(@assoc)
826
+ rf = response.fields
827
+
828
+ assert_equal('HMAC-SHA1', rf['assoc_type'])
829
+ assert_equal(@assoc.handle, rf['assoc_handle'])
830
+ assert_equal(OpenID::Util.to_base64(@assoc.secret), rf['mac_key'])
831
+ assert_equal("#{14 * 24 * 60 * 6}", rf['expires_in'])
832
+ assert_nil(rf['session_type'])
833
+ assert_nil(rf['enc_mac_key'])
834
+ assert_nil(rf['dh_server_public'])
835
+ end
836
+
837
+ end
838
+
839
+ class Counter
840
+
841
+ attr_accessor :count
842
+
843
+ def initialize
844
+ @count = 0
845
+ end
846
+
847
+ def inc
848
+ @count += 1
849
+ end
850
+
851
+ end
852
+
853
+ class TestServer < Test::Unit::TestCase
854
+
855
+ def setup
856
+ @store = OpenID::MemoryStore.new
857
+ @server = Server.new(@store)
858
+ end
859
+
860
+ def test_associate
861
+ req = AssociateRequest.from_query({})
862
+ res = @server.openid_associate(req)
863
+ assert res.fields.has_key?('assoc_handle')
864
+ end
865
+
866
+ def test_check_auth
867
+ req = CheckAuthRequest.new('arrrrg', '0x3999', [])
868
+ res = @server.openid_check_authentication(req)
869
+ assert res.fields.has_key?('is_valid')
870
+ end
871
+
872
+ end
873
+
874
+ class TestSignatory < Test::Unit::TestCase
875
+
876
+ def setup
877
+ @store = OpenID::MemoryStore.new
878
+ @signatory = Signatory.new(@store)
879
+ @dumb_key = 'http://localhost/|dumb'
880
+ @normal_key = 'http://localhost/|normal'
881
+ end
882
+
883
+ def test_sign
884
+ request = CheckIDRequest.new('checkid_setup','foo','bar','baz')
885
+ assoc_handle = '{assoc}{lookatme}'
886
+ @store.store_association(@normal_key,
887
+ OpenID::Association.from_expires_in(60,
888
+ assoc_handle,
889
+ 'sekrit',
890
+ 'HMAC-SHA1'))
891
+ request.assoc_handle = assoc_handle
892
+ response = OpenIDResponse.new(request)
893
+ response.fields = {
894
+ 'foo' => 'amsigned',
895
+ 'bar' => 'notsigned',
896
+ 'azu' => 'alsosigned'
897
+ }
898
+ response.signed = ['foo','azu']
899
+ sresponse = @signatory.sign(response)
900
+
901
+ assert_equal(assoc_handle, sresponse.fields['assoc_handle'])
902
+ assert_equal('foo,azu', sresponse.fields['signed'])
903
+ assert_not_nil(sresponse.fields['sig'])
904
+ end
905
+
906
+ def test_sign_dumb
907
+ request = CheckIDRequest.new('checkid_setup','foo','bar','baz')
908
+ request.assoc_handle = nil
909
+ response = OpenIDResponse.new(request)
910
+ response.fields = {
911
+ 'foo' => 'amsigned',
912
+ 'bar' => 'notsigned',
913
+ 'azu' => 'alsosigned'
914
+ }
915
+ response.signed = ['foo','azu']
916
+ sresponse = @signatory.sign(response)
917
+ assoc_handle = sresponse.fields['assoc_handle']
918
+ assert_not_nil(assoc_handle)
919
+ assoc = @signatory.get_association(assoc_handle, true)
920
+ assert_not_nil(assoc)
921
+ assert_equal('foo,azu', sresponse.fields['signed'])
922
+ assert_not_nil(sresponse.fields['sig'])
923
+ end
924
+
925
+ def test_sign_expired
926
+ request = CheckIDRequest.new('checkid_setup','foo','bar','baz')
927
+ assoc_handle = '{assoc}{lookatme}'
928
+ @store.store_association(@normal_key,
929
+ OpenID::Association.from_expires_in(-10,
930
+ assoc_handle,
931
+ 'sekrit',
932
+ 'HMAC-SHA1'))
933
+ assert_not_nil(@store.get_association(@normal_key, assoc_handle))
934
+
935
+ request.assoc_handle = assoc_handle
936
+ response = OpenIDResponse.new(request)
937
+ response.fields = {
938
+ 'foo' => 'amsigned',
939
+ 'bar' => 'notsigned',
940
+ 'azu' => 'alsosigned'
941
+ }
942
+ response.signed = ['foo','azu']
943
+ sresponse = @signatory.sign(response)
944
+
945
+ new_assoc_handle = sresponse.fields['assoc_handle']
946
+ assert_not_nil(new_assoc_handle)
947
+ assert new_assoc_handle != assoc_handle
948
+
949
+ assert_equal(assoc_handle, sresponse.fields['invalidate_handle'])
950
+ assert_equal('foo,azu', sresponse.fields['signed'])
951
+ assert_not_nil(sresponse.fields['sig'])
952
+
953
+ assert_nil(@store.get_association(@normal_key, assoc_handle))
954
+ assert_not_nil(@store.get_association(@dumb_key, new_assoc_handle))
955
+ assert_nil(@store.get_association(@normal_key, new_assoc_handle))
956
+ end
957
+
958
+ def test_verify
959
+ assoc_handle = '{vroom}{zoom}'
960
+ assoc = OpenID::Association.from_expires_in(60, assoc_handle,
961
+ 'sekrit', 'HMAC-SHA1')
962
+
963
+ @store.store_association(@dumb_key, assoc)
964
+
965
+ signed_pairs = [['foo', 'bar'],['apple', 'orange']]
966
+
967
+ sig = "Ylu0KcIR7PvNegB/K41KpnRgJl0="
968
+ verified = @signatory.verify(assoc_handle, sig, signed_pairs)
969
+ assert_not_nil(verified)
970
+ end
971
+
972
+ def test_verify_bad_sig
973
+ assoc_handle = '{vroom}{zoom}'
974
+ assoc = OpenID::Association.from_expires_in(60, assoc_handle,
975
+ 'sekrit', 'HMAC-SHA1')
976
+
977
+ @store.store_association(@dumb_key, assoc)
978
+
979
+ signed_pairs = [['foo', 'bar'],['apple', 'orange']]
980
+
981
+ sig = "Ylu0KcIR7PvNegB/K41KpnRgXXX="
982
+ verified = @signatory.verify(assoc_handle, sig, signed_pairs)
983
+ assert(!verified)
984
+ end
985
+
986
+ def test_verify_bad_handle
987
+ assoc_handle = '{vroom}{zoom}'
988
+ signed_pairs = [['foo', 'bar'],['apple', 'orange']]
989
+
990
+ sig = "Ylu0KcIR7PvNegB/K41KpnRgJl0="
991
+ verified = @signatory.verify(assoc_handle, sig, signed_pairs)
992
+ assert(!verified)
993
+ end
994
+
995
+ def make_assoc(dumb, lifetime=60)
996
+ assoc_handle = '{bling}'
997
+ assoc = OpenID::Association.from_expires_in(lifetime, assoc_handle,
998
+ 'sekrit', 'HMAC-SHA1')
999
+ key = dumb ? @dumb_key : @normal_key
1000
+ @store.store_association(key, assoc)
1001
+ return assoc_handle
1002
+ end
1003
+
1004
+ def test_get_assoc
1005
+ assoc_handle = self.make_assoc(true)
1006
+ assoc = @signatory.get_association(assoc_handle, true)
1007
+ assert_not_nil(assoc)
1008
+ assert_equal(assoc_handle, assoc.handle)
1009
+ end
1010
+
1011
+ def test_get_assoc_expired
1012
+ assoc_handle = self.make_assoc(true, -10)
1013
+ assoc = @signatory.get_association(assoc_handle, true)
1014
+ assert_nil(assoc)
1015
+ end
1016
+
1017
+ def test_get_assoc_invalid
1018
+ assoc_handle = 'no-such-handle'
1019
+ assoc = @signatory.get_association(assoc_handle, false)
1020
+ assert_nil(assoc)
1021
+ end
1022
+
1023
+ def test_get_assoc_dumb_vs_normal
1024
+ assoc_handle = self.make_assoc(true)
1025
+ assoc = @signatory.get_association(assoc_handle, false)
1026
+ assert_nil(assoc)
1027
+ end
1028
+
1029
+ def test_create_assoc
1030
+ assoc = @signatory.create_association(false)
1031
+ assoc2 = @signatory.get_association(assoc.handle, false)
1032
+ assert_not_nil(assoc2)
1033
+ assert_equal(assoc, assoc2)
1034
+ end
1035
+
1036
+ def test_invalidate
1037
+ assoc_handle = '-squash-'
1038
+ assoc = OpenID::Association.from_expires_in(60, assoc_handle,
1039
+ 'sekrit', 'HMAC-SHA1')
1040
+ @store.store_association(@dumb_key, assoc)
1041
+ assoc = @signatory.get_association(assoc_handle, true)
1042
+ assert_not_nil(assoc)
1043
+
1044
+ assoc = @signatory.get_association(assoc_handle, true)
1045
+ assert_not_nil(assoc)
1046
+
1047
+ @signatory.invalidate(assoc_handle, true)
1048
+ assoc = @signatory.get_association(assoc_handle, true)
1049
+ assert_nil(assoc)
1050
+ end
1051
+
1052
+ end
1053
+