ruby-openid 1.0

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 (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
+