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.
- data/COPYING +21 -0
- data/INSTALL +34 -0
- data/README +67 -0
- data/TODO +9 -0
- data/examples/README +54 -0
- data/examples/cacert.pem +7815 -0
- data/examples/consumer.rb +285 -0
- data/examples/openid-store/associations/http-localhost_3A3000_2Fserver-EMQbAy3NnHVzA.s0u5KAcplKGzo +6 -0
- data/examples/openid-store/auth_key +1 -0
- data/examples/rails_active_record_store/README +59 -0
- data/examples/rails_active_record_store/XX_add_openidstore.rb +30 -0
- data/examples/rails_active_record_store/models/openid_association.rb +12 -0
- data/examples/rails_active_record_store/models/openid_nonce.rb +3 -0
- data/examples/rails_active_record_store/models/openid_setting.rb +2 -0
- data/examples/rails_active_record_store/openid_helper.rb +91 -0
- data/examples/rails_active_record_store/openidstore_test.rb +15 -0
- data/examples/rails_active_record_store/schema.mysql.sql +22 -0
- data/examples/rails_active_record_store/schema.postgresql.sql +21 -0
- data/examples/rails_active_record_store/schema.sqlite.sql +21 -0
- data/examples/rails_openid_login_generator/USAGE +23 -0
- data/examples/rails_openid_login_generator/openid_login_generator.rb +36 -0
- data/examples/rails_openid_login_generator/templates/README +116 -0
- data/examples/rails_openid_login_generator/templates/controller.rb +116 -0
- data/examples/rails_openid_login_generator/templates/controller_test.rb +0 -0
- data/examples/rails_openid_login_generator/templates/helper.rb +2 -0
- data/examples/rails_openid_login_generator/templates/openid_login_system.rb +87 -0
- data/examples/rails_openid_login_generator/templates/user.rb +14 -0
- data/examples/rails_openid_login_generator/templates/user_test.rb +0 -0
- data/examples/rails_openid_login_generator/templates/users.yml +0 -0
- data/examples/rails_openid_login_generator/templates/view_login.rhtml +15 -0
- data/examples/rails_openid_login_generator/templates/view_logout.rhtml +10 -0
- data/examples/rails_openid_login_generator/templates/view_welcome.rhtml +9 -0
- data/examples/rails_server/README +153 -0
- data/examples/rails_server/Rakefile +10 -0
- data/examples/rails_server/app/controllers/application.rb +4 -0
- data/examples/rails_server/app/controllers/login_controller.rb +35 -0
- data/examples/rails_server/app/controllers/server_controller.rb +185 -0
- data/examples/rails_server/app/helpers/application_helper.rb +3 -0
- data/examples/rails_server/app/helpers/login_helper.rb +2 -0
- data/examples/rails_server/app/helpers/server_helper.rb +9 -0
- data/examples/rails_server/app/views/layouts/server.rhtml +61 -0
- data/examples/rails_server/app/views/login/index.rhtml +32 -0
- data/examples/rails_server/app/views/server/decide.rhtml +11 -0
- data/examples/rails_server/config/boot.rb +19 -0
- data/examples/rails_server/config/database.yml +85 -0
- data/examples/rails_server/config/environment.rb +53 -0
- data/examples/rails_server/config/environments/development.rb +19 -0
- data/examples/rails_server/config/environments/production.rb +19 -0
- data/examples/rails_server/config/environments/test.rb +19 -0
- data/examples/rails_server/config/routes.rb +23 -0
- data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-YU.tkND1J4fEZhnuAoT5Zc0yCA0 +6 -0
- data/examples/rails_server/doc/README_FOR_APP +2 -0
- data/examples/rails_server/log/development.log +6059 -0
- data/examples/rails_server/log/production.log +0 -0
- data/examples/rails_server/log/server.log +0 -0
- data/examples/rails_server/log/test.log +0 -0
- data/examples/rails_server/public/404.html +8 -0
- data/examples/rails_server/public/500.html +8 -0
- data/examples/rails_server/public/dispatch.cgi +12 -0
- data/examples/rails_server/public/dispatch.fcgi +26 -0
- data/examples/rails_server/public/dispatch.rb +12 -0
- data/examples/rails_server/public/favicon.ico +0 -0
- data/examples/rails_server/public/images/rails.png +0 -0
- data/examples/rails_server/public/javascripts/controls.js +750 -0
- data/examples/rails_server/public/javascripts/dragdrop.js +584 -0
- data/examples/rails_server/public/javascripts/effects.js +854 -0
- data/examples/rails_server/public/javascripts/prototype.js +1785 -0
- data/examples/rails_server/public/robots.txt +1 -0
- data/examples/rails_server/script/about +3 -0
- data/examples/rails_server/script/breakpointer +3 -0
- data/examples/rails_server/script/console +3 -0
- data/examples/rails_server/script/destroy +3 -0
- data/examples/rails_server/script/generate +3 -0
- data/examples/rails_server/script/performance/benchmarker +3 -0
- data/examples/rails_server/script/performance/profiler +3 -0
- data/examples/rails_server/script/plugin +3 -0
- data/examples/rails_server/script/process/reaper +3 -0
- data/examples/rails_server/script/process/spawner +3 -0
- data/examples/rails_server/script/process/spinner +3 -0
- data/examples/rails_server/script/runner +3 -0
- data/examples/rails_server/script/server +3 -0
- data/examples/rails_server/test/functional/login_controller_test.rb +18 -0
- data/examples/rails_server/test/functional/server_controller_test.rb +18 -0
- data/examples/rails_server/test/test_helper.rb +28 -0
- data/lib/hmac-md5.rb +11 -0
- data/lib/hmac-rmd160.rb +11 -0
- data/lib/hmac-sha1.rb +11 -0
- data/lib/hmac-sha2.rb +25 -0
- data/lib/hmac.rb +112 -0
- data/lib/openid/association.rb +109 -0
- data/lib/openid/consumer.rb +928 -0
- data/lib/openid/dh.rb +48 -0
- data/lib/openid/discovery.rb +89 -0
- data/lib/openid/fetchers.rb +119 -0
- data/lib/openid/filestore.rb +315 -0
- data/lib/openid/htmltokenizer.rb +355 -0
- data/lib/openid/parse.rb +23 -0
- data/lib/openid/server.rb +951 -0
- data/lib/openid/service.rb +135 -0
- data/lib/openid/stores.rb +178 -0
- data/lib/openid/trustroot.rb +100 -0
- data/lib/openid/util.rb +273 -0
- data/test/assoc.rb +38 -0
- data/test/consumer.rb +384 -0
- data/test/dh.rb +20 -0
- data/test/extensions.rb +30 -0
- data/test/linkparse.rb +305 -0
- data/test/runtests.rb +11 -0
- data/test/server2.rb +1053 -0
- data/test/storetestcase.rb +172 -0
- data/test/teststore.rb +23 -0
- data/test/trustroot.rb +113 -0
- data/test/util.rb +56 -0
- 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
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
|
+
|