entp-ruby-openid 2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +215 -0
- data/INSTALL +47 -0
- data/LICENSE +210 -0
- data/NOTICE +2 -0
- data/README +85 -0
- data/UPGRADE +127 -0
- data/admin/runtests.rb +45 -0
- data/examples/README +32 -0
- data/examples/active_record_openid_store/README +58 -0
- data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
- data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
- data/examples/active_record_openid_store/init.rb +8 -0
- data/examples/active_record_openid_store/lib/association.rb +10 -0
- data/examples/active_record_openid_store/lib/nonce.rb +3 -0
- data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
- data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
- data/examples/active_record_openid_store/test/store_test.rb +212 -0
- data/examples/discover +49 -0
- data/examples/rails_openid/README +153 -0
- data/examples/rails_openid/Rakefile +10 -0
- data/examples/rails_openid/app/controllers/application.rb +4 -0
- data/examples/rails_openid/app/controllers/consumer_controller.rb +125 -0
- data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
- data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
- data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
- data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
- data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
- data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
- data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
- data/examples/rails_openid/app/views/login/index.rhtml +56 -0
- data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
- data/examples/rails_openid/config/boot.rb +19 -0
- data/examples/rails_openid/config/database.yml +74 -0
- data/examples/rails_openid/config/environment.rb +54 -0
- data/examples/rails_openid/config/environments/development.rb +19 -0
- data/examples/rails_openid/config/environments/production.rb +19 -0
- data/examples/rails_openid/config/environments/test.rb +19 -0
- data/examples/rails_openid/config/routes.rb +24 -0
- data/examples/rails_openid/doc/README_FOR_APP +2 -0
- data/examples/rails_openid/public/404.html +8 -0
- data/examples/rails_openid/public/500.html +8 -0
- data/examples/rails_openid/public/dispatch.cgi +12 -0
- data/examples/rails_openid/public/dispatch.fcgi +26 -0
- data/examples/rails_openid/public/dispatch.rb +12 -0
- data/examples/rails_openid/public/favicon.ico +0 -0
- data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
- data/examples/rails_openid/public/javascripts/controls.js +750 -0
- data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
- data/examples/rails_openid/public/javascripts/effects.js +854 -0
- data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
- data/examples/rails_openid/public/robots.txt +1 -0
- data/examples/rails_openid/script/about +3 -0
- data/examples/rails_openid/script/breakpointer +3 -0
- data/examples/rails_openid/script/console +3 -0
- data/examples/rails_openid/script/destroy +3 -0
- data/examples/rails_openid/script/generate +3 -0
- data/examples/rails_openid/script/performance/benchmarker +3 -0
- data/examples/rails_openid/script/performance/profiler +3 -0
- data/examples/rails_openid/script/plugin +3 -0
- data/examples/rails_openid/script/process/reaper +3 -0
- data/examples/rails_openid/script/process/spawner +3 -0
- data/examples/rails_openid/script/process/spinner +3 -0
- data/examples/rails_openid/script/runner +3 -0
- data/examples/rails_openid/script/server +3 -0
- data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
- data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
- data/examples/rails_openid/test/test_helper.rb +28 -0
- data/lib/hmac/hmac.rb +112 -0
- data/lib/hmac/sha1.rb +11 -0
- data/lib/hmac/sha2.rb +25 -0
- data/lib/openid.rb +22 -0
- data/lib/openid/association.rb +249 -0
- data/lib/openid/consumer.rb +395 -0
- data/lib/openid/consumer/associationmanager.rb +344 -0
- data/lib/openid/consumer/checkid_request.rb +186 -0
- data/lib/openid/consumer/discovery.rb +497 -0
- data/lib/openid/consumer/discovery_manager.rb +123 -0
- data/lib/openid/consumer/html_parse.rb +134 -0
- data/lib/openid/consumer/idres.rb +523 -0
- data/lib/openid/consumer/responses.rb +150 -0
- data/lib/openid/cryptutil.rb +115 -0
- data/lib/openid/dh.rb +89 -0
- data/lib/openid/extension.rb +39 -0
- data/lib/openid/extensions/ax.rb +539 -0
- data/lib/openid/extensions/oauth.rb +91 -0
- data/lib/openid/extensions/pape.rb +179 -0
- data/lib/openid/extensions/sreg.rb +277 -0
- data/lib/openid/extras.rb +11 -0
- data/lib/openid/fetchers.rb +258 -0
- data/lib/openid/kvform.rb +136 -0
- data/lib/openid/kvpost.rb +58 -0
- data/lib/openid/message.rb +553 -0
- data/lib/openid/protocolerror.rb +12 -0
- data/lib/openid/server.rb +1544 -0
- data/lib/openid/store.rb +10 -0
- data/lib/openid/store/filesystem.rb +272 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memcache.rb +109 -0
- data/lib/openid/store/memory.rb +84 -0
- data/lib/openid/store/nonce.rb +68 -0
- data/lib/openid/trustroot.rb +349 -0
- data/lib/openid/urinorm.rb +75 -0
- data/lib/openid/util.rb +119 -0
- data/lib/openid/version.rb +3 -0
- data/lib/openid/yadis.rb +15 -0
- data/lib/openid/yadis/accept.rb +148 -0
- data/lib/openid/yadis/constants.rb +21 -0
- data/lib/openid/yadis/discovery.rb +153 -0
- data/lib/openid/yadis/filters.rb +205 -0
- data/lib/openid/yadis/htmltokenizer.rb +305 -0
- data/lib/openid/yadis/parsehtml.rb +45 -0
- data/lib/openid/yadis/services.rb +42 -0
- data/lib/openid/yadis/xrds.rb +155 -0
- data/lib/openid/yadis/xri.rb +90 -0
- data/lib/openid/yadis/xrires.rb +91 -0
- data/test/data/test_discover/openid_utf8.html +11 -0
- data/test/support/test_data_mixin.rb +127 -0
- data/test/support/test_util.rb +53 -0
- data/test/support/yadis_data.rb +131 -0
- data/test/support/yadis_data/accept.txt +124 -0
- data/test/support/yadis_data/dh.txt +29 -0
- data/test/support/yadis_data/example-xrds.xml +14 -0
- data/test/support/yadis_data/linkparse.txt +587 -0
- data/test/support/yadis_data/n2b64 +650 -0
- data/test/support/yadis_data/test1-discover.txt +137 -0
- data/test/support/yadis_data/test1-parsehtml.txt +152 -0
- data/test/support/yadis_data/test_discover/malformed_meta_tag.html +19 -0
- data/test/support/yadis_data/test_discover/openid.html +11 -0
- data/test/support/yadis_data/test_discover/openid2.html +11 -0
- data/test/support/yadis_data/test_discover/openid2_xrds.xml +12 -0
- data/test/support/yadis_data/test_discover/openid2_xrds_no_local_id.xml +11 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2.html +11 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2_xrds.xml +16 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
- data/test/support/yadis_data/test_discover/openid_and_yadis.html +12 -0
- data/test/support/yadis_data/test_discover/openid_no_delegate.html +10 -0
- data/test/support/yadis_data/test_discover/openid_utf8.html +11 -0
- data/test/support/yadis_data/test_discover/yadis_0entries.xml +12 -0
- data/test/support/yadis_data/test_discover/yadis_2_bad_local_id.xml +15 -0
- data/test/support/yadis_data/test_discover/yadis_2entries_delegate.xml +22 -0
- data/test/support/yadis_data/test_discover/yadis_2entries_idp.xml +21 -0
- data/test/support/yadis_data/test_discover/yadis_another_delegate.xml +14 -0
- data/test/support/yadis_data/test_discover/yadis_idp.xml +12 -0
- data/test/support/yadis_data/test_discover/yadis_idp_delegate.xml +13 -0
- data/test/support/yadis_data/test_discover/yadis_no_delegate.xml +11 -0
- data/test/support/yadis_data/test_xrds/=j3h.2007.11.14.xrds +25 -0
- data/test/support/yadis_data/test_xrds/README +12 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809-r1.xrds +34 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809-r2.xrds +34 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809.xrds +34 -0
- data/test/support/yadis_data/test_xrds/no-xrd.xml +7 -0
- data/test/support/yadis_data/test_xrds/not-xrds.xml +2 -0
- data/test/support/yadis_data/test_xrds/prefixsometimes.xrds +34 -0
- data/test/support/yadis_data/test_xrds/ref.xrds +109 -0
- data/test/support/yadis_data/test_xrds/sometimesprefix.xrds +34 -0
- data/test/support/yadis_data/test_xrds/spoof1.xrds +25 -0
- data/test/support/yadis_data/test_xrds/spoof2.xrds +25 -0
- data/test/support/yadis_data/test_xrds/spoof3.xrds +37 -0
- data/test/support/yadis_data/test_xrds/status222.xrds +9 -0
- data/test/support/yadis_data/test_xrds/subsegments.xrds +58 -0
- data/test/support/yadis_data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/support/yadis_data/trustroot.txt +153 -0
- data/test/support/yadis_data/urinorm.txt +79 -0
- data/test/test_accept.rb +170 -0
- data/test/test_association.rb +268 -0
- data/test/test_associationmanager.rb +918 -0
- data/test/test_ax.rb +690 -0
- data/test/test_checkid_request.rb +293 -0
- data/test/test_consumer.rb +260 -0
- data/test/test_cryptutil.rb +119 -0
- data/test/test_dh.rb +85 -0
- data/test/test_discover.rb +848 -0
- data/test/test_discovery_manager.rb +259 -0
- data/test/test_extension.rb +46 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +554 -0
- data/test/test_filters.rb +269 -0
- data/test/test_helper.rb +4 -0
- data/test/test_idres.rb +961 -0
- data/test/test_kvform.rb +164 -0
- data/test/test_kvpost.rb +64 -0
- data/test/test_linkparse.rb +100 -0
- data/test/test_message.rb +1115 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_oauth.rb +176 -0
- data/test/test_openid_yadis.rb +177 -0
- data/test/test_pape.rb +248 -0
- data/test/test_parsehtml.rb +79 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2455 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +292 -0
- data/test/test_trustroot.rb +111 -0
- data/test/test_urinorm.rb +34 -0
- data/test/test_util.rb +145 -0
- data/test/test_xrds.rb +167 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +67 -0
- data/test/test_yadis_discovery.rb +218 -0
- metadata +268 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'support/test_util'
|
|
3
|
+
require "openid/association"
|
|
4
|
+
require 'test_util'
|
|
5
|
+
|
|
6
|
+
module OpenID
|
|
7
|
+
class AssociationTestCase < Test::Unit::TestCase
|
|
8
|
+
def setup
|
|
9
|
+
# Use this funny way of getting a time so that it does not have
|
|
10
|
+
# fractional seconds, and so can be serialized exactly using our
|
|
11
|
+
# standard code.
|
|
12
|
+
issued = Time.at(Time.now.to_i)
|
|
13
|
+
lifetime = 600
|
|
14
|
+
|
|
15
|
+
@assoc = Association.new('handle', 'secret', issued,
|
|
16
|
+
lifetime, 'HMAC-SHA1')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_round_trip
|
|
20
|
+
assoc2 = Association.deserialize(@assoc.serialize())
|
|
21
|
+
[:handle, :secret, :lifetime, :assoc_type].each do |attr|
|
|
22
|
+
assert_equal(@assoc.send(attr), assoc2.send(attr))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_deserialize_failure
|
|
27
|
+
field_list = Util.kv_to_seq(@assoc.serialize)
|
|
28
|
+
kv = Util.seq_to_kv(field_list + [['monkeys', 'funny']])
|
|
29
|
+
assert_raises(ProtocolError) {
|
|
30
|
+
Association.deserialize(kv)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bad_version_list = field_list.dup
|
|
34
|
+
bad_version_list[0] = ['version', 'moon']
|
|
35
|
+
bad_version_kv = Util.seq_to_kv(bad_version_list)
|
|
36
|
+
assert_raises(ProtocolError) {
|
|
37
|
+
Association.deserialize(bad_version_kv)
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_serialization_identity
|
|
42
|
+
assoc2 = Association.deserialize(@assoc.serialize)
|
|
43
|
+
assert_equal(@assoc, assoc2)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_expires_in
|
|
47
|
+
# Allow one second of slop
|
|
48
|
+
assert(@assoc.expires_in.between?(599,600))
|
|
49
|
+
assert(@assoc.expires_in(Time.now.to_i).between?(599,600))
|
|
50
|
+
assert_equal(0,@assoc.expires_in(Time.now.to_i + 10000),"negative expires_in")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_from_expires_in
|
|
54
|
+
start_time = Time.now
|
|
55
|
+
expires_in = @assoc.expires_in
|
|
56
|
+
assoc = Association.from_expires_in(expires_in,
|
|
57
|
+
@assoc.handle,
|
|
58
|
+
@assoc.secret,
|
|
59
|
+
@assoc.assoc_type)
|
|
60
|
+
|
|
61
|
+
# Allow one second of slop here for code execution time
|
|
62
|
+
assert_in_delta(1, assoc.expires_in, @assoc.expires_in)
|
|
63
|
+
[:handle, :secret, :assoc_type].each do |attr|
|
|
64
|
+
assert_equal(@assoc.send(attr), assoc.send(attr))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Make sure the issued time is near the start
|
|
68
|
+
assert(assoc.issued >= start_time)
|
|
69
|
+
assert_in_delta(1, assoc.issued.to_f, start_time.to_f)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_sign_sha1
|
|
73
|
+
pairs = [['key1', 'value1'],
|
|
74
|
+
['key2', 'value2']]
|
|
75
|
+
|
|
76
|
+
[['HMAC-SHA256', "\xfd\xaa\xfe;\xac\xfc*\x988\xad\x05d6-"\
|
|
77
|
+
"\xeaVy\xd5\xa5Z.<\xa9\xed\x18\x82\\$"\
|
|
78
|
+
"\x95x\x1c&"],
|
|
79
|
+
['HMAC-SHA1', "\xe0\x1bv\x04\xf1G\xc0\xbb\x7f\x9a\x8b"\
|
|
80
|
+
"\xe9\xbc\xee}\\\xe5\xbb7*"],
|
|
81
|
+
].each do |assoc_type, expected|
|
|
82
|
+
assoc = Association.from_expires_in(3600, "handle", 'very_secret',
|
|
83
|
+
assoc_type)
|
|
84
|
+
sig = assoc.sign(pairs)
|
|
85
|
+
assert_equal(sig, expected)
|
|
86
|
+
|
|
87
|
+
m = Message.new(OPENID2_NS)
|
|
88
|
+
pairs.each { |k, v|
|
|
89
|
+
m.set_arg(OPENID_NS, k, v)
|
|
90
|
+
}
|
|
91
|
+
m.set_arg(BARE_NS, "not_an_openid_arg", "bogus")
|
|
92
|
+
|
|
93
|
+
signed_m = assoc.sign_message(m)
|
|
94
|
+
assert(signed_m.has_key?(OPENID_NS, 'sig'))
|
|
95
|
+
assert_equal(signed_m.get_arg(OPENID_NS, 'signed'),
|
|
96
|
+
'assoc_handle,key1,key2,ns,signed')
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_sign_message_with_sig
|
|
101
|
+
assoc = Association.from_expires_in(3600, "handle", "very_secret",
|
|
102
|
+
"HMAC-SHA1")
|
|
103
|
+
m = Message.new(OPENID2_NS)
|
|
104
|
+
m.set_arg(OPENID_NS, 'sig', 'noise')
|
|
105
|
+
assert_raises(ArgumentError) {
|
|
106
|
+
assoc.sign_message(m)
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_sign_message_with_signed
|
|
111
|
+
assoc = Association.from_expires_in(3600, "handle", "very_secret",
|
|
112
|
+
"HMAC-SHA1")
|
|
113
|
+
m = Message.new(OPENID2_NS)
|
|
114
|
+
m.set_arg(OPENID_NS, 'signed', 'fields')
|
|
115
|
+
assert_raises(ArgumentError) {
|
|
116
|
+
assoc.sign_message(m)
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_sign_different_assoc_handle
|
|
121
|
+
assoc = Association.from_expires_in(3600, "handle", "very_secret",
|
|
122
|
+
"HMAC-SHA1")
|
|
123
|
+
m = Message.new(OPENID2_NS)
|
|
124
|
+
m.set_arg(OPENID_NS, 'assoc_handle', 'different')
|
|
125
|
+
assert_raises(ArgumentError) {
|
|
126
|
+
assoc.sign_message(m)
|
|
127
|
+
}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def test_sign_bad_assoc_type
|
|
131
|
+
@assoc.instance_eval { @assoc_type = 'Cookies' }
|
|
132
|
+
assert_raises(ProtocolError) {
|
|
133
|
+
@assoc.sign([])
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_make_pairs
|
|
138
|
+
msg = Message.new(OPENID2_NS)
|
|
139
|
+
msg.update_args(OPENID2_NS, {
|
|
140
|
+
'mode' => 'id_res',
|
|
141
|
+
'identifier' => '=example',
|
|
142
|
+
'signed' => 'identifier,mode',
|
|
143
|
+
'sig' => 'cephalopod',
|
|
144
|
+
})
|
|
145
|
+
msg.update_args(BARE_NS, {'xey' => 'value'})
|
|
146
|
+
assoc = Association.from_expires_in(3600, '{sha1}', 'very_secret',
|
|
147
|
+
"HMAC-SHA1")
|
|
148
|
+
pairs = assoc.make_pairs(msg)
|
|
149
|
+
assert_equal([['identifier', '=example'],
|
|
150
|
+
['mode', 'id_res']], pairs)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_check_message_signature_no_signed
|
|
154
|
+
m = Message.new(OPENID2_NS)
|
|
155
|
+
m.update_args(OPENID2_NS, {'mode' => 'id_res',
|
|
156
|
+
'identifier' => '=example',
|
|
157
|
+
'sig' => 'coyote',
|
|
158
|
+
})
|
|
159
|
+
assoc = Association.from_expires_in(3600, '{sha1}', 'very_secret',
|
|
160
|
+
"HMAC-SHA1")
|
|
161
|
+
assert_raises(ProtocolError) {
|
|
162
|
+
assoc.check_message_signature(m)
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_check_message_signature_no_sig
|
|
167
|
+
m = Message.new(OPENID2_NS)
|
|
168
|
+
m.update_args(OPENID2_NS, {'mode' => 'id_res',
|
|
169
|
+
'identifier' => '=example',
|
|
170
|
+
'signed' => 'mode',
|
|
171
|
+
})
|
|
172
|
+
assoc = Association.from_expires_in(3600, '{sha1}', 'very_secret',
|
|
173
|
+
"HMAC-SHA1")
|
|
174
|
+
assert_raises(ProtocolError) {
|
|
175
|
+
assoc.check_message_signature(m)
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def test_check_message_signature_bad_sig
|
|
180
|
+
m = Message.new(OPENID2_NS)
|
|
181
|
+
m.update_args(OPENID2_NS, {'mode' => 'id_res',
|
|
182
|
+
'identifier' => '=example',
|
|
183
|
+
'signed' => 'mode',
|
|
184
|
+
'sig' => Util.to_base64('coyote'),
|
|
185
|
+
})
|
|
186
|
+
assoc = Association.from_expires_in(3600, '{sha1}', 'very_secret',
|
|
187
|
+
"HMAC-SHA1")
|
|
188
|
+
assert(!assoc.check_message_signature(m))
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_check_message_signature_good_sig
|
|
192
|
+
m = Message.new(OPENID2_NS)
|
|
193
|
+
m.update_args(OPENID2_NS, {'mode' => 'id_res',
|
|
194
|
+
'identifier' => '=example',
|
|
195
|
+
'signed' => 'mode',
|
|
196
|
+
'sig' => Util.to_base64('coyote'),
|
|
197
|
+
})
|
|
198
|
+
assoc = Association.from_expires_in(3600, '{sha1}', 'very_secret',
|
|
199
|
+
"HMAC-SHA1")
|
|
200
|
+
class << assoc
|
|
201
|
+
# Override sign, because it's already tested elsewhere
|
|
202
|
+
def sign(pairs)
|
|
203
|
+
"coyote"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
assert(assoc.check_message_signature(m))
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
class AssociationNegotiatorTestCase < Test::Unit::TestCase
|
|
212
|
+
def assert_equal_under(item1, item2)
|
|
213
|
+
val1 = yield(item1)
|
|
214
|
+
val2 = yield(item2)
|
|
215
|
+
assert_equal(val1, val2)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def test_copy
|
|
219
|
+
neg = AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1']])
|
|
220
|
+
neg2 = neg.copy
|
|
221
|
+
assert_equal_under(neg, neg2) {|n| n.instance_eval{@allowed_types} }
|
|
222
|
+
assert(neg.object_id != neg2.object_id)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def test_add_allowed
|
|
226
|
+
neg = AssociationNegotiator.new([])
|
|
227
|
+
assert(!neg.allowed?('HMAC-SHA1', 'DH-SHA1'))
|
|
228
|
+
assert(!neg.allowed?('HMAC-SHA1', 'no-encryption'))
|
|
229
|
+
assert(!neg.allowed?('HMAC-SHA256', 'DH-SHA256'))
|
|
230
|
+
assert(!neg.allowed?('HMAC-SHA256', 'no-encryption'))
|
|
231
|
+
neg.add_allowed_type('HMAC-SHA1')
|
|
232
|
+
assert(neg.allowed?('HMAC-SHA1', 'DH-SHA1'))
|
|
233
|
+
assert(neg.allowed?('HMAC-SHA1', 'no-encryption'))
|
|
234
|
+
assert(!neg.allowed?('HMAC-SHA256', 'DH-SHA256'))
|
|
235
|
+
assert(!neg.allowed?('HMAC-SHA256', 'no-encryption'))
|
|
236
|
+
neg.add_allowed_type('HMAC-SHA256', 'DH-SHA256')
|
|
237
|
+
assert(neg.allowed?('HMAC-SHA1', 'DH-SHA1'))
|
|
238
|
+
assert(neg.allowed?('HMAC-SHA1', 'no-encryption'))
|
|
239
|
+
assert(neg.allowed?('HMAC-SHA256', 'DH-SHA256'))
|
|
240
|
+
assert(!neg.allowed?('HMAC-SHA256', 'no-encryption'))
|
|
241
|
+
assert_equal(neg.get_allowed_type, ['HMAC-SHA1', 'DH-SHA1'])
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def test_bad_assoc_type
|
|
245
|
+
assert_raises(ProtocolError) {
|
|
246
|
+
AssociationNegotiator.new([['OMG', 'Ponies']])
|
|
247
|
+
}
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def test_bad_session_type
|
|
251
|
+
assert_raises(ProtocolError) {
|
|
252
|
+
AssociationNegotiator.new([['HMAC-SHA1', 'OMG-Ponies']])
|
|
253
|
+
}
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def test_default_negotiator
|
|
257
|
+
assert_equal(DefaultNegotiator.get_allowed_type,
|
|
258
|
+
['HMAC-SHA1', 'DH-SHA1'])
|
|
259
|
+
assert(DefaultNegotiator.allowed?('HMAC-SHA256', 'no-encryption'))
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def test_encrypted_negotiator
|
|
263
|
+
assert_equal(EncryptedNegotiator.get_allowed_type,
|
|
264
|
+
['HMAC-SHA1', 'DH-SHA1'])
|
|
265
|
+
assert(!EncryptedNegotiator.allowed?('HMAC-SHA256', 'no-encryption'))
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'support/test_util'
|
|
3
|
+
require "openid/consumer/associationmanager"
|
|
4
|
+
require "openid/association"
|
|
5
|
+
require "openid/dh"
|
|
6
|
+
require "openid/util"
|
|
7
|
+
require "openid/cryptutil"
|
|
8
|
+
require "openid/message"
|
|
9
|
+
require "openid/store/memory"
|
|
10
|
+
require "time"
|
|
11
|
+
require 'test_util'
|
|
12
|
+
|
|
13
|
+
module OpenID
|
|
14
|
+
class DHAssocSessionTest < Test::Unit::TestCase
|
|
15
|
+
def test_sha1_get_request
|
|
16
|
+
# Initialized without an explicit DH gets defaults
|
|
17
|
+
sess = Consumer::DiffieHellmanSHA1Session.new
|
|
18
|
+
assert_equal(['dh_consumer_public'], sess.get_request.keys)
|
|
19
|
+
assert_nothing_raised do
|
|
20
|
+
Util::from_base64(sess.get_request['dh_consumer_public'])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_sha1_get_request_custom_dh
|
|
25
|
+
dh = DiffieHellman.new(1299721, 2)
|
|
26
|
+
sess = Consumer::DiffieHellmanSHA1Session.new(dh)
|
|
27
|
+
req = sess.get_request
|
|
28
|
+
assert_equal(['dh_consumer_public', 'dh_modulus', 'dh_gen'].sort,
|
|
29
|
+
req.keys.sort)
|
|
30
|
+
assert_equal(dh.modulus, CryptUtil.base64_to_num(req['dh_modulus']))
|
|
31
|
+
assert_equal(dh.generator, CryptUtil.base64_to_num(req['dh_gen']))
|
|
32
|
+
assert_nothing_raised do
|
|
33
|
+
Util::from_base64(req['dh_consumer_public'])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module TestDiffieHellmanResponseParametersMixin
|
|
39
|
+
def setup
|
|
40
|
+
session_cls = self.class.session_cls
|
|
41
|
+
|
|
42
|
+
# Pre-compute DH with small prime so tests run quickly.
|
|
43
|
+
@server_dh = DiffieHellman.new(100389557, 2)
|
|
44
|
+
@consumer_dh = DiffieHellman.new(100389557, 2)
|
|
45
|
+
|
|
46
|
+
# base64(btwoc(g ^ xb mod p))
|
|
47
|
+
@dh_server_public = CryptUtil.num_to_base64(@server_dh.public)
|
|
48
|
+
|
|
49
|
+
@secret = CryptUtil.random_string(session_cls.secret_size)
|
|
50
|
+
|
|
51
|
+
enc_mac_key_unencoded =
|
|
52
|
+
@server_dh.xor_secret(session_cls.hashfunc,
|
|
53
|
+
@consumer_dh.public,
|
|
54
|
+
@secret)
|
|
55
|
+
|
|
56
|
+
@enc_mac_key = Util.to_base64(enc_mac_key_unencoded)
|
|
57
|
+
|
|
58
|
+
@consumer_session = session_cls.new(@consumer_dh)
|
|
59
|
+
|
|
60
|
+
@msg = Message.new(self.class.message_namespace)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_extract_secret
|
|
64
|
+
@msg.set_arg(OPENID_NS, 'dh_server_public', @dh_server_public)
|
|
65
|
+
@msg.set_arg(OPENID_NS, 'enc_mac_key', @enc_mac_key)
|
|
66
|
+
|
|
67
|
+
extracted = @consumer_session.extract_secret(@msg)
|
|
68
|
+
assert_equal(extracted, @secret)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_absent_serve_public
|
|
72
|
+
@msg.set_arg(OPENID_NS, 'enc_mac_key', @enc_mac_key)
|
|
73
|
+
|
|
74
|
+
assert_raises(Message::KeyNotFound) {
|
|
75
|
+
@consumer_session.extract_secret(@msg)
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_absent_mac_key
|
|
80
|
+
@msg.set_arg(OPENID_NS, 'dh_server_public', @dh_server_public)
|
|
81
|
+
|
|
82
|
+
assert_raises(Message::KeyNotFound) {
|
|
83
|
+
@consumer_session.extract_secret(@msg)
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_invalid_base64_public
|
|
88
|
+
@msg.set_arg(OPENID_NS, 'dh_server_public', 'n o t b a s e 6 4.')
|
|
89
|
+
@msg.set_arg(OPENID_NS, 'enc_mac_key', @enc_mac_key)
|
|
90
|
+
|
|
91
|
+
assert_raises(ArgumentError) {
|
|
92
|
+
@consumer_session.extract_secret(@msg)
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_invalid_base64_mac_key
|
|
97
|
+
@msg.set_arg(OPENID_NS, 'dh_server_public', @dh_server_public)
|
|
98
|
+
@msg.set_arg(OPENID_NS, 'enc_mac_key', 'n o t base 64')
|
|
99
|
+
|
|
100
|
+
assert_raises(ArgumentError) {
|
|
101
|
+
@consumer_session.extract_secret(@msg)
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class TestConsumerOpenID1DHSHA1 < Test::Unit::TestCase
|
|
107
|
+
include TestDiffieHellmanResponseParametersMixin
|
|
108
|
+
class << self
|
|
109
|
+
attr_reader :session_cls, :message_namespace
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
@session_cls = Consumer::DiffieHellmanSHA1Session
|
|
113
|
+
@message_namespace = OPENID1_NS
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class TestConsumerOpenID2DHSHA1 < Test::Unit::TestCase
|
|
117
|
+
include TestDiffieHellmanResponseParametersMixin
|
|
118
|
+
class << self
|
|
119
|
+
attr_reader :session_cls, :message_namespace
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
@session_cls = Consumer::DiffieHellmanSHA1Session
|
|
123
|
+
@message_namespace = OPENID2_NS
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class TestConsumerOpenID2DHSHA256 < Test::Unit::TestCase
|
|
127
|
+
include TestDiffieHellmanResponseParametersMixin
|
|
128
|
+
class << self
|
|
129
|
+
attr_reader :session_cls, :message_namespace
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
@session_cls = Consumer::DiffieHellmanSHA256Session
|
|
133
|
+
@message_namespace = OPENID2_NS
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
class TestConsumerNoEncryptionSession < Test::Unit::TestCase
|
|
137
|
+
def setup
|
|
138
|
+
@sess = Consumer::NoEncryptionSession.new
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def test_empty_request
|
|
142
|
+
assert_equal(@sess.get_request, {})
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_get_secret
|
|
146
|
+
secret = 'shhh!' * 4
|
|
147
|
+
mac_key = Util.to_base64(secret)
|
|
148
|
+
msg = Message.from_openid_args({'mac_key' => mac_key})
|
|
149
|
+
assert_equal(secret, @sess.extract_secret(msg))
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class TestCreateAssociationRequest < Test::Unit::TestCase
|
|
154
|
+
def setup
|
|
155
|
+
@server_url = 'http://invalid/'
|
|
156
|
+
@assoc_manager = Consumer::AssociationManager.new(nil, @server_url)
|
|
157
|
+
class << @assoc_manager
|
|
158
|
+
def compatibility_mode=(val)
|
|
159
|
+
@compatibility_mode = val
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
@assoc_type = 'HMAC-SHA1'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def test_no_encryption_sends_type
|
|
166
|
+
session_type = 'no-encryption'
|
|
167
|
+
session, args = @assoc_manager.send(:create_associate_request,
|
|
168
|
+
@assoc_type,
|
|
169
|
+
session_type)
|
|
170
|
+
|
|
171
|
+
assert(session.is_a?(Consumer::NoEncryptionSession))
|
|
172
|
+
expected = Message.from_openid_args(
|
|
173
|
+
{'ns' => OPENID2_NS,
|
|
174
|
+
'session_type' => session_type,
|
|
175
|
+
'mode' => 'associate',
|
|
176
|
+
'assoc_type' => @assoc_type,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
assert_equal(expected, args)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def test_no_encryption_compatibility
|
|
183
|
+
@assoc_manager.compatibility_mode = true
|
|
184
|
+
session_type = 'no-encryption'
|
|
185
|
+
session, args = @assoc_manager.send(:create_associate_request,
|
|
186
|
+
@assoc_type,
|
|
187
|
+
session_type)
|
|
188
|
+
|
|
189
|
+
assert(session.is_a?(Consumer::NoEncryptionSession))
|
|
190
|
+
assert_equal(Message.from_openid_args({'mode' => 'associate',
|
|
191
|
+
'assoc_type' => @assoc_type,
|
|
192
|
+
}), args)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def test_dh_sha1_compatibility
|
|
196
|
+
@assoc_manager.compatibility_mode = true
|
|
197
|
+
session_type = 'DH-SHA1'
|
|
198
|
+
session, args = @assoc_manager.send(:create_associate_request,
|
|
199
|
+
@assoc_type,
|
|
200
|
+
session_type)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
assert(session.is_a?(Consumer::DiffieHellmanSHA1Session))
|
|
204
|
+
|
|
205
|
+
# This is a random base-64 value, so just check that it's
|
|
206
|
+
# present.
|
|
207
|
+
assert_not_nil(args.get_arg(OPENID1_NS, 'dh_consumer_public'))
|
|
208
|
+
args.del_arg(OPENID1_NS, 'dh_consumer_public')
|
|
209
|
+
|
|
210
|
+
# OK, session_type is set here and not for no-encryption
|
|
211
|
+
# compatibility
|
|
212
|
+
expected = Message.from_openid_args({'mode' => 'associate',
|
|
213
|
+
'session_type' => 'DH-SHA1',
|
|
214
|
+
'assoc_type' => @assoc_type,
|
|
215
|
+
})
|
|
216
|
+
assert_equal(expected, args)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
class TestAssociationManagerExpiresIn < Test::Unit::TestCase
|
|
221
|
+
def expires_in_msg(val)
|
|
222
|
+
msg = Message.from_openid_args({'expires_in' => val})
|
|
223
|
+
Consumer::AssociationManager.extract_expires_in(msg)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def test_parse_fail
|
|
227
|
+
['',
|
|
228
|
+
'-2',
|
|
229
|
+
' 1',
|
|
230
|
+
' ',
|
|
231
|
+
'0x00',
|
|
232
|
+
'foosball',
|
|
233
|
+
'1\n',
|
|
234
|
+
'100,000,000,000',
|
|
235
|
+
].each do |x|
|
|
236
|
+
assert_raises(ProtocolError) {expires_in_msg(x)}
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def test_parse
|
|
241
|
+
['0',
|
|
242
|
+
'1',
|
|
243
|
+
'1000',
|
|
244
|
+
'9999999',
|
|
245
|
+
'01',
|
|
246
|
+
].each do |n|
|
|
247
|
+
assert_equal(n.to_i, expires_in_msg(n))
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
class TestAssociationManagerCreateSession < Test::Unit::TestCase
|
|
253
|
+
def test_invalid
|
|
254
|
+
assert_raises(ArgumentError) {
|
|
255
|
+
Consumer::AssociationManager.create_session('monkeys')
|
|
256
|
+
}
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def test_sha256
|
|
260
|
+
sess = Consumer::AssociationManager.create_session('DH-SHA256')
|
|
261
|
+
assert(sess.is_a?(Consumer::DiffieHellmanSHA256Session))
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
module NegotiationTestMixin
|
|
266
|
+
include TestUtil
|
|
267
|
+
def mk_message(args)
|
|
268
|
+
args['ns'] = @openid_ns
|
|
269
|
+
Message.from_openid_args(args)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def call_negotiate(responses, negotiator=nil)
|
|
273
|
+
store = nil
|
|
274
|
+
compat = self.class::Compat
|
|
275
|
+
assoc_manager = Consumer::AssociationManager.new(store, @server_url,
|
|
276
|
+
compat, negotiator)
|
|
277
|
+
class << assoc_manager
|
|
278
|
+
attr_accessor :responses
|
|
279
|
+
|
|
280
|
+
def request_association(assoc_type, session_type)
|
|
281
|
+
m = @responses.shift
|
|
282
|
+
if m.is_a?(Message)
|
|
283
|
+
raise ServerError.from_message(m)
|
|
284
|
+
else
|
|
285
|
+
return m
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
assoc_manager.responses = responses
|
|
290
|
+
assoc_manager.negotiate_association
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Test the session type negotiation behavior of an OpenID 2
|
|
295
|
+
# consumer.
|
|
296
|
+
class TestOpenID2SessionNegotiation < Test::Unit::TestCase
|
|
297
|
+
include NegotiationTestMixin
|
|
298
|
+
|
|
299
|
+
Compat = false
|
|
300
|
+
|
|
301
|
+
def setup
|
|
302
|
+
@server_url = 'http://invalid/'
|
|
303
|
+
@openid_ns = OPENID2_NS
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Test the case where the response to an associate request is a
|
|
307
|
+
# server error or is otherwise undecipherable.
|
|
308
|
+
def test_bad_response
|
|
309
|
+
assert_log_matches('Server error when requesting an association') {
|
|
310
|
+
assert_equal(call_negotiate([mk_message({})]), nil)
|
|
311
|
+
}
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Test the case where the association type (assoc_type) returned
|
|
315
|
+
# in an unsupported-type response is absent.
|
|
316
|
+
def test_empty_assoc_type
|
|
317
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
318
|
+
'error_code' => 'unsupported-type',
|
|
319
|
+
'session_type' => 'new-session-type',
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
assert_log_matches('Unsupported association type',
|
|
323
|
+
"Server #{@server_url} responded with unsupported "\
|
|
324
|
+
"association session but did not supply a fallback."
|
|
325
|
+
) {
|
|
326
|
+
assert_equal(call_negotiate([msg]), nil)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Test the case where the session type (session_type) returned
|
|
332
|
+
# in an unsupported-type response is absent.
|
|
333
|
+
def test_empty_session_type
|
|
334
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
335
|
+
'error_code' => 'unsupported-type',
|
|
336
|
+
'assoc_type' => 'new-assoc-type',
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
assert_log_matches('Unsupported association type',
|
|
340
|
+
"Server #{@server_url} responded with unsupported "\
|
|
341
|
+
"association session but did not supply a fallback."
|
|
342
|
+
) {
|
|
343
|
+
assert_equal(call_negotiate([msg]), nil)
|
|
344
|
+
}
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Test the case where an unsupported-type response specifies a
|
|
348
|
+
# preferred (assoc_type, session_type) combination that is not
|
|
349
|
+
# allowed by the consumer's SessionNegotiator.
|
|
350
|
+
def test_not_allowed
|
|
351
|
+
negotiator = AssociationNegotiator.new([])
|
|
352
|
+
negotiator.instance_eval{
|
|
353
|
+
@allowed_types = [['assoc_bogus', 'session_bogus']]
|
|
354
|
+
}
|
|
355
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
356
|
+
'error_code' => 'unsupported-type',
|
|
357
|
+
'assoc_type' => 'not-allowed',
|
|
358
|
+
'session_type' => 'not-allowed',
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
assert_log_matches('Unsupported association type',
|
|
362
|
+
'Server sent unsupported session/association type:') {
|
|
363
|
+
assert_equal(call_negotiate([msg], negotiator), nil)
|
|
364
|
+
}
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Test the case where an unsupported-type response triggers a
|
|
368
|
+
# retry to get an association with the new preferred type.
|
|
369
|
+
def test_unsupported_with_retry
|
|
370
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
371
|
+
'error_code' => 'unsupported-type',
|
|
372
|
+
'assoc_type' => 'HMAC-SHA1',
|
|
373
|
+
'session_type' => 'DH-SHA1',
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
assoc = Association.new('handle', 'secret', Time.now, 10000, 'HMAC-SHA1')
|
|
377
|
+
|
|
378
|
+
assert_log_matches('Unsupported association type') {
|
|
379
|
+
assert_equal(assoc, call_negotiate([msg, assoc]))
|
|
380
|
+
}
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Test the case where an unsupported-typ response triggers a
|
|
384
|
+
# retry, but the retry fails and nil is returned instead.
|
|
385
|
+
def test_unsupported_with_retry_and_fail
|
|
386
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
387
|
+
'error_code' => 'unsupported-type',
|
|
388
|
+
'assoc_type' => 'HMAC-SHA1',
|
|
389
|
+
'session_type' => 'DH-SHA1',
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
assert_log_matches('Unsupported association type',
|
|
393
|
+
"Server #{@server_url} refused") {
|
|
394
|
+
assert_equal(call_negotiate([msg, msg]), nil)
|
|
395
|
+
}
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Test the valid case, wherein an association is returned on the
|
|
399
|
+
# first attempt to get one.
|
|
400
|
+
def test_valid
|
|
401
|
+
assoc = Association.new('handle', 'secret', Time.now, 10000, 'HMAC-SHA1')
|
|
402
|
+
|
|
403
|
+
assert_log_matches() {
|
|
404
|
+
assert_equal(call_negotiate([assoc]), assoc)
|
|
405
|
+
}
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# Tests for the OpenID 1 consumer association session behavior. See
|
|
411
|
+
# the docs for TestOpenID2SessionNegotiation. Notice that this
|
|
412
|
+
# class is not a subclass of the OpenID 2 tests. Instead, it uses
|
|
413
|
+
# many of the same inputs but inspects the log messages logged with
|
|
414
|
+
# oidutil.log. See the calls to self.failUnlessLogMatches. Some of
|
|
415
|
+
# these tests pass openid2-style messages to the openid 1
|
|
416
|
+
# association processing logic to be sure it ignores the extra data.
|
|
417
|
+
class TestOpenID1SessionNegotiation < Test::Unit::TestCase
|
|
418
|
+
include NegotiationTestMixin
|
|
419
|
+
|
|
420
|
+
Compat = true
|
|
421
|
+
|
|
422
|
+
def setup
|
|
423
|
+
@server_url = 'http://invalid/'
|
|
424
|
+
@openid_ns = OPENID1_NS
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def test_bad_response
|
|
428
|
+
assert_log_matches('Server error when requesting an association') {
|
|
429
|
+
response = call_negotiate([mk_message({})])
|
|
430
|
+
assert_equal(nil, response)
|
|
431
|
+
}
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def test_empty_assoc_type
|
|
435
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
436
|
+
'error_code' => 'unsupported-type',
|
|
437
|
+
'session_type' => 'new-session-type',
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
assert_log_matches('Server error when requesting an association') {
|
|
441
|
+
response = call_negotiate([msg])
|
|
442
|
+
assert_equal(nil, response)
|
|
443
|
+
}
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def test_empty_session_type
|
|
447
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
448
|
+
'error_code' => 'unsupported-type',
|
|
449
|
+
'assoc_type' => 'new-assoc-type',
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
assert_log_matches('Server error when requesting an association') {
|
|
453
|
+
response = call_negotiate([msg])
|
|
454
|
+
assert_equal(nil, response)
|
|
455
|
+
}
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def test_not_allowed
|
|
459
|
+
negotiator = AssociationNegotiator.new([])
|
|
460
|
+
negotiator.instance_eval{
|
|
461
|
+
@allowed_types = [['assoc_bogus', 'session_bogus']]
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
465
|
+
'error_code' => 'unsupported-type',
|
|
466
|
+
'assoc_type' => 'not-allowed',
|
|
467
|
+
'session_type' => 'not-allowed',
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
assert_log_matches('Server error when requesting an association') {
|
|
471
|
+
response = call_negotiate([msg])
|
|
472
|
+
assert_equal(nil, response)
|
|
473
|
+
}
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def test_unsupported_with_retry
|
|
477
|
+
msg = mk_message({'error' => 'Unsupported type',
|
|
478
|
+
'error_code' => 'unsupported-type',
|
|
479
|
+
'assoc_type' => 'HMAC-SHA1',
|
|
480
|
+
'session_type' => 'DH-SHA1',
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
assoc = Association.new('handle', 'secret', Time.now, 10000, 'HMAC-SHA1')
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
assert_log_matches('Server error when requesting an association') {
|
|
487
|
+
response = call_negotiate([msg, assoc])
|
|
488
|
+
assert_equal(nil, response)
|
|
489
|
+
}
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def test_valid
|
|
493
|
+
assoc = Association.new('handle', 'secret', Time.now, 10000, 'HMAC-SHA1')
|
|
494
|
+
assert_log_matches() {
|
|
495
|
+
response = call_negotiate([assoc])
|
|
496
|
+
assert_equal(assoc, response)
|
|
497
|
+
}
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
class TestExtractAssociation < Test::Unit::TestCase
|
|
503
|
+
include ProtocolErrorMixin
|
|
504
|
+
|
|
505
|
+
# An OpenID associate response (without the namespace)
|
|
506
|
+
DEFAULTS = {
|
|
507
|
+
'expires_in' => '1000',
|
|
508
|
+
'assoc_handle' => 'a handle',
|
|
509
|
+
'assoc_type' => 'a type',
|
|
510
|
+
'session_type' => 'a session type',
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
def setup
|
|
514
|
+
@assoc_manager = Consumer::AssociationManager.new(nil, nil)
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Make tests that ensure that an association response that is
|
|
518
|
+
# missing required fields will raise an Message::KeyNotFound.
|
|
519
|
+
#
|
|
520
|
+
# According to 'Association Session Response' subsection 'Common
|
|
521
|
+
# Response Parameters', the following fields are required for
|
|
522
|
+
# OpenID 2.0:
|
|
523
|
+
#
|
|
524
|
+
# * ns
|
|
525
|
+
# * session_type
|
|
526
|
+
# * assoc_handle
|
|
527
|
+
# * assoc_type
|
|
528
|
+
# * expires_in
|
|
529
|
+
#
|
|
530
|
+
# In OpenID 1, everything except 'session_type' and 'ns' are
|
|
531
|
+
# required.
|
|
532
|
+
MISSING_FIELD_SETS = ([["no_fields", []]] +
|
|
533
|
+
(DEFAULTS.keys.map do |f|
|
|
534
|
+
fields = DEFAULTS.keys
|
|
535
|
+
fields.delete(f)
|
|
536
|
+
["missing_#{f}", fields]
|
|
537
|
+
end)
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
[OPENID1_NS, OPENID2_NS].each do |ns|
|
|
541
|
+
MISSING_FIELD_SETS.each do |name, fields|
|
|
542
|
+
# OpenID 1 is allowed to be missing session_type
|
|
543
|
+
if ns != OPENID1_NS and name != 'missing_session_type'
|
|
544
|
+
test = lambda do
|
|
545
|
+
msg = Message.new(ns)
|
|
546
|
+
fields.each do |field|
|
|
547
|
+
msg.set_arg(ns, field, DEFAULTS[field])
|
|
548
|
+
end
|
|
549
|
+
assert_raises(Message::KeyNotFound) do
|
|
550
|
+
@assoc_manager.send(:extract_association, msg, nil)
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
define_method("test_#{name}", test)
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
# assert that extracting a response that contains the given
|
|
559
|
+
# response session type when the request was made for the given
|
|
560
|
+
# request session type will raise a ProtocolError indicating
|
|
561
|
+
# session type mismatch
|
|
562
|
+
def assert_session_mismatch(req_type, resp_type, ns)
|
|
563
|
+
# Create an association session that has "req_type" as its
|
|
564
|
+
# session_type and no allowed_assoc_types
|
|
565
|
+
assoc_session_class = Class.new do
|
|
566
|
+
@session_type = req_type
|
|
567
|
+
def self.session_type
|
|
568
|
+
@session_type
|
|
569
|
+
end
|
|
570
|
+
def self.allowed_assoc_types
|
|
571
|
+
[]
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
assoc_session = assoc_session_class.new
|
|
575
|
+
|
|
576
|
+
# Build an OpenID 1 or 2 association response message that has
|
|
577
|
+
# the specified association session type
|
|
578
|
+
msg = Message.new(ns)
|
|
579
|
+
msg.update_args(ns, DEFAULTS)
|
|
580
|
+
msg.set_arg(ns, 'session_type', resp_type)
|
|
581
|
+
|
|
582
|
+
# The request type and response type have been chosen to produce
|
|
583
|
+
# a session type mismatch.
|
|
584
|
+
assert_protocol_error('Session type mismatch') {
|
|
585
|
+
@assoc_manager.send(:extract_association, msg, assoc_session)
|
|
586
|
+
}
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
[['no-encryption', '', OPENID2_NS],
|
|
590
|
+
['DH-SHA1', 'no-encryption', OPENID2_NS],
|
|
591
|
+
['DH-SHA256', 'no-encryption', OPENID2_NS],
|
|
592
|
+
['no-encryption', 'DH-SHA1', OPENID2_NS],
|
|
593
|
+
['DH-SHA1', 'DH-SHA256', OPENID1_NS],
|
|
594
|
+
['DH-SHA256', 'DH-SHA1', OPENID1_NS],
|
|
595
|
+
['no-encryption', 'DH-SHA1', OPENID1_NS],
|
|
596
|
+
].each do |req_type, resp_type, ns|
|
|
597
|
+
test = lambda { assert_session_mismatch(req_type, resp_type, ns) }
|
|
598
|
+
name = "test_mismatch_req_#{req_type}_resp_#{resp_type}_#{ns}"
|
|
599
|
+
define_method(name, test)
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
def test_openid1_no_encryption_fallback
|
|
603
|
+
# A DH-SHA1 session
|
|
604
|
+
assoc_session = Consumer::DiffieHellmanSHA1Session.new
|
|
605
|
+
|
|
606
|
+
# An OpenID 1 no-encryption association response
|
|
607
|
+
msg = Message.from_openid_args({
|
|
608
|
+
'expires_in' => '1000',
|
|
609
|
+
'assoc_handle' => 'a handle',
|
|
610
|
+
'assoc_type' => 'HMAC-SHA1',
|
|
611
|
+
'mac_key' => 'X' * 20,
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
# Should succeed
|
|
615
|
+
assoc = @assoc_manager.send(:extract_association, msg, assoc_session)
|
|
616
|
+
assert_equal('a handle', assoc.handle)
|
|
617
|
+
assert_equal('HMAC-SHA1', assoc.assoc_type)
|
|
618
|
+
assert(assoc.expires_in.between?(999, 1000))
|
|
619
|
+
assert('X' * 20, assoc.secret)
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
class GetOpenIDSessionTypeTest < Test::Unit::TestCase
|
|
624
|
+
include TestUtil
|
|
625
|
+
|
|
626
|
+
SERVER_URL = 'http://invalid/'
|
|
627
|
+
|
|
628
|
+
def do_test(expected_session_type, session_type_value)
|
|
629
|
+
# Create a Message with just 'session_type' in it, since
|
|
630
|
+
# that's all this function will use. 'session_type' may be
|
|
631
|
+
# absent if it's set to None.
|
|
632
|
+
args = {}
|
|
633
|
+
if !session_type_value.nil?
|
|
634
|
+
args['session_type'] = session_type_value
|
|
635
|
+
end
|
|
636
|
+
message = Message.from_openid_args(args)
|
|
637
|
+
assert(message.is_openid1)
|
|
638
|
+
|
|
639
|
+
assoc_manager = Consumer::AssociationManager.new(nil, SERVER_URL)
|
|
640
|
+
actual_session_type = assoc_manager.send(:get_openid1_session_type,
|
|
641
|
+
message)
|
|
642
|
+
error_message = ("Returned session type parameter #{session_type_value}"\
|
|
643
|
+
"was expected to yield session type "\
|
|
644
|
+
"#{expected_session_type}, but yielded "\
|
|
645
|
+
"#{actual_session_type}")
|
|
646
|
+
assert_equal(expected_session_type, actual_session_type, error_message)
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
[['nil', 'no-encryption', nil],
|
|
651
|
+
['empty', 'no-encryption', ''],
|
|
652
|
+
['dh_sha1', 'DH-SHA1', 'DH-SHA1'],
|
|
653
|
+
['dh_sha256', 'DH-SHA256', 'DH-SHA256'],
|
|
654
|
+
].each {|name, expected, input|
|
|
655
|
+
# Define a test method that will check what session type will be
|
|
656
|
+
# used if the OpenID 1 response to an associate call sets the
|
|
657
|
+
# 'session_type' field to `session_type_value`
|
|
658
|
+
test = lambda {assert_log_matches() { do_test(expected, input) } }
|
|
659
|
+
define_method("test_#{name}", &test)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
# This one's different because it expects log messages
|
|
663
|
+
def test_explicit_no_encryption
|
|
664
|
+
assert_log_matches("WARNING: #{SERVER_URL} sent 'no-encryption'"){
|
|
665
|
+
do_test('no-encryption', 'no-encryption')
|
|
666
|
+
}
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
class ExtractAssociationTest < Test::Unit::TestCase
|
|
671
|
+
include ProtocolErrorMixin
|
|
672
|
+
|
|
673
|
+
SERVER_URL = 'http://invalid/'
|
|
674
|
+
|
|
675
|
+
def setup
|
|
676
|
+
@session_type = 'testing-session'
|
|
677
|
+
|
|
678
|
+
# This must something that works for Association::from_expires_in
|
|
679
|
+
@assoc_type = 'HMAC-SHA1'
|
|
680
|
+
|
|
681
|
+
@assoc_handle = 'testing-assoc-handle'
|
|
682
|
+
|
|
683
|
+
# These arguments should all be valid
|
|
684
|
+
@assoc_response =
|
|
685
|
+
Message.from_openid_args({
|
|
686
|
+
'expires_in' => '1000',
|
|
687
|
+
'assoc_handle' => @assoc_handle,
|
|
688
|
+
'assoc_type' => @assoc_type,
|
|
689
|
+
'session_type' => @session_type,
|
|
690
|
+
'ns' => OPENID2_NS,
|
|
691
|
+
})
|
|
692
|
+
assoc_session_cls = Class.new do
|
|
693
|
+
class << self
|
|
694
|
+
attr_accessor :allowed_assoc_types, :session_type
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
attr_reader :extract_secret_called, :secret
|
|
698
|
+
def initialize
|
|
699
|
+
@extract_secret_called = false
|
|
700
|
+
@secret = 'shhhhh!'
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
def extract_secret(_)
|
|
704
|
+
@extract_secret_called = true
|
|
705
|
+
@secret
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
@assoc_session = assoc_session_cls.new
|
|
709
|
+
@assoc_session.class.allowed_assoc_types = [@assoc_type]
|
|
710
|
+
@assoc_session.class.session_type = @session_type
|
|
711
|
+
|
|
712
|
+
@assoc_manager = Consumer::AssociationManager.new(nil, SERVER_URL)
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
def call_extract
|
|
716
|
+
@assoc_manager.send(:extract_association,
|
|
717
|
+
@assoc_response, @assoc_session)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
# Handle a full successful association response
|
|
721
|
+
def test_works_with_good_fields
|
|
722
|
+
assoc = call_extract
|
|
723
|
+
assert(@assoc_session.extract_secret_called)
|
|
724
|
+
assert_equal(@assoc_session.secret, assoc.secret)
|
|
725
|
+
assert_equal(1000, assoc.lifetime)
|
|
726
|
+
assert_equal(@assoc_handle, assoc.handle)
|
|
727
|
+
assert_equal(@assoc_type, assoc.assoc_type)
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
def test_bad_assoc_type
|
|
731
|
+
# Make sure that the assoc type in the response is not valid
|
|
732
|
+
# for the given session.
|
|
733
|
+
@assoc_session.class.allowed_assoc_types = []
|
|
734
|
+
assert_protocol_error('Unsupported assoc_type for sess') {call_extract}
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def test_bad_expires_in
|
|
738
|
+
# Invalid value for expires_in should cause failure
|
|
739
|
+
@assoc_response.set_arg(OPENID_NS, 'expires_in', 'forever')
|
|
740
|
+
assert_protocol_error('Invalid expires_in') {call_extract}
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
class TestExtractAssociationDiffieHellman < Test::Unit::TestCase
|
|
745
|
+
include ProtocolErrorMixin
|
|
746
|
+
|
|
747
|
+
SECRET = 'x' * 20
|
|
748
|
+
|
|
749
|
+
def setup
|
|
750
|
+
@assoc_manager = Consumer::AssociationManager.new(nil, nil)
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def setup_dh
|
|
754
|
+
sess, message = @assoc_manager.send(:create_associate_request,
|
|
755
|
+
'HMAC-SHA1', 'DH-SHA1')
|
|
756
|
+
|
|
757
|
+
server_dh = DiffieHellman.new
|
|
758
|
+
cons_dh = sess.instance_variable_get('@dh')
|
|
759
|
+
|
|
760
|
+
enc_mac_key = server_dh.xor_secret(CryptUtil.method(:sha1),
|
|
761
|
+
cons_dh.public, SECRET)
|
|
762
|
+
|
|
763
|
+
server_resp = {
|
|
764
|
+
'dh_server_public' => CryptUtil.num_to_base64(server_dh.public),
|
|
765
|
+
'enc_mac_key' => Util.to_base64(enc_mac_key),
|
|
766
|
+
'assoc_type' => 'HMAC-SHA1',
|
|
767
|
+
'assoc_handle' => 'handle',
|
|
768
|
+
'expires_in' => '1000',
|
|
769
|
+
'session_type' => 'DH-SHA1',
|
|
770
|
+
}
|
|
771
|
+
if @assoc_manager.instance_variable_get(:@compatibility_mode)
|
|
772
|
+
server_resp['ns'] = OPENID2_NS
|
|
773
|
+
end
|
|
774
|
+
return [sess, Message.from_openid_args(server_resp)]
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def test_success
|
|
778
|
+
sess, server_resp = setup_dh
|
|
779
|
+
ret = @assoc_manager.send(:extract_association, server_resp, sess)
|
|
780
|
+
assert(!ret.nil?)
|
|
781
|
+
assert_equal(ret.assoc_type, 'HMAC-SHA1')
|
|
782
|
+
assert_equal(ret.secret, SECRET)
|
|
783
|
+
assert_equal(ret.handle, 'handle')
|
|
784
|
+
assert_equal(ret.lifetime, 1000)
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def test_openid2success
|
|
788
|
+
# Use openid 1 type in endpoint so _setUpDH checks
|
|
789
|
+
# compatibility mode state properly
|
|
790
|
+
@assoc_manager.instance_variable_set('@compatibility_mode', true)
|
|
791
|
+
test_success()
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
def test_bad_dh_values
|
|
795
|
+
sess, server_resp = setup_dh
|
|
796
|
+
server_resp.set_arg(OPENID_NS, 'enc_mac_key', '\x00\x00\x00')
|
|
797
|
+
assert_protocol_error('Malformed response for') {
|
|
798
|
+
@assoc_manager.send(:extract_association, server_resp, sess)
|
|
799
|
+
}
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
class TestAssocManagerGetAssociation < Test::Unit::TestCase
|
|
804
|
+
include FetcherMixin
|
|
805
|
+
include TestUtil
|
|
806
|
+
|
|
807
|
+
attr_reader :negotiate_association
|
|
808
|
+
|
|
809
|
+
def setup
|
|
810
|
+
@server_url = 'http://invalid/'
|
|
811
|
+
@store = Store::Memory.new
|
|
812
|
+
@assoc_manager = Consumer::AssociationManager.new(@store, @server_url)
|
|
813
|
+
@assoc_manager.extend(Const)
|
|
814
|
+
@assoc = Association.new('handle', 'secret', Time.now, 10000,
|
|
815
|
+
'HMAC-SHA1')
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
def set_negotiate_response(assoc)
|
|
819
|
+
@assoc_manager.const(:negotiate_association, assoc)
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def test_not_in_store_no_response
|
|
823
|
+
set_negotiate_response(nil)
|
|
824
|
+
assert_equal(nil, @assoc_manager.get_association)
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
def test_not_in_store_negotiate_assoc
|
|
828
|
+
# Not stored beforehand:
|
|
829
|
+
stored_assoc = @store.get_association(@server_url, @assoc.handle)
|
|
830
|
+
assert_equal(nil, stored_assoc)
|
|
831
|
+
|
|
832
|
+
# Returned from associate call:
|
|
833
|
+
set_negotiate_response(@assoc)
|
|
834
|
+
assert_equal(@assoc, @assoc_manager.get_association)
|
|
835
|
+
|
|
836
|
+
# It should have been stored:
|
|
837
|
+
stored_assoc = @store.get_association(@server_url, @assoc.handle)
|
|
838
|
+
assert_equal(@assoc, stored_assoc)
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def test_in_store_no_response
|
|
842
|
+
set_negotiate_response(nil)
|
|
843
|
+
@store.store_association(@server_url, @assoc)
|
|
844
|
+
assert_equal(@assoc, @assoc_manager.get_association)
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
def test_request_assoc_with_status_error
|
|
848
|
+
fetcher_class = Class.new do
|
|
849
|
+
define_method(:fetch) do |*args|
|
|
850
|
+
MockResponse.new(500, '')
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
with_fetcher(fetcher_class.new) do
|
|
854
|
+
assert_log_matches('Got HTTP status error when requesting') {
|
|
855
|
+
result = @assoc_manager.send(:request_association, 'HMAC-SHA1',
|
|
856
|
+
'no-encryption')
|
|
857
|
+
assert(result.nil?)
|
|
858
|
+
}
|
|
859
|
+
end
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
class TestAssocManagerRequestAssociation < Test::Unit::TestCase
|
|
864
|
+
include FetcherMixin
|
|
865
|
+
include TestUtil
|
|
866
|
+
|
|
867
|
+
def setup
|
|
868
|
+
@assoc_manager = Consumer::AssociationManager.new(nil, 'http://invalid/')
|
|
869
|
+
@assoc_type = 'HMAC-SHA1'
|
|
870
|
+
@session_type = 'no-encryption'
|
|
871
|
+
@message = Message.new(OPENID2_NS)
|
|
872
|
+
@message.update_args(OPENID_NS, {
|
|
873
|
+
'assoc_type' => @assoc_type,
|
|
874
|
+
'session_type' => @session_type,
|
|
875
|
+
'assoc_handle' => 'kaboodle',
|
|
876
|
+
'expires_in' => '1000',
|
|
877
|
+
'mac_key' => 'X' * 20,
|
|
878
|
+
})
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def make_request
|
|
882
|
+
kv = @message.to_kvform
|
|
883
|
+
fetcher_class = Class.new do
|
|
884
|
+
define_method(:fetch) do |*args|
|
|
885
|
+
MockResponse.new(200, kv)
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
with_fetcher(fetcher_class.new) do
|
|
889
|
+
@assoc_manager.send(:request_association, @assoc_type, @session_type)
|
|
890
|
+
end
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
# The association we get is from valid processing of our result,
|
|
894
|
+
# and that no errors are raised
|
|
895
|
+
def test_success
|
|
896
|
+
assert_equal('kaboodle', make_request.handle)
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
# A missing parameter gets translated into a log message and
|
|
900
|
+
# causes the method to return nil
|
|
901
|
+
def test_missing_fields
|
|
902
|
+
@message.del_arg(OPENID_NS, 'assoc_type')
|
|
903
|
+
assert_log_matches('Missing required par') {
|
|
904
|
+
assert_equal(nil, make_request)
|
|
905
|
+
}
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
# A bad value results in a log message and causes the method to
|
|
909
|
+
# return nil
|
|
910
|
+
def test_protocol_error
|
|
911
|
+
@message.set_arg(OPENID_NS, 'expires_in', 'goats')
|
|
912
|
+
assert_log_matches('Protocol error processing') {
|
|
913
|
+
assert_equal(nil, make_request)
|
|
914
|
+
}
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
end
|