ruby-saml 0.8.10 → 0.8.15
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.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Rakefile +0 -14
- data/lib/onelogin/ruby-saml/authrequest.rb +3 -2
- data/lib/onelogin/ruby-saml/logoutrequest.rb +3 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +1 -24
- data/lib/onelogin/ruby-saml/response.rb +206 -20
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +26 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +3 -2
- data/lib/onelogin/ruby-saml/utils.rb +90 -0
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +222 -87
- data/test/certificates/ruby-saml-2.crt +15 -0
- data/test/logoutrequest_test.rb +124 -126
- data/test/logoutresponse_test.rb +22 -28
- data/test/response_test.rb +348 -129
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/encrypted_new_attack.xml.base64 +1 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +1 -0
- data/test/responses/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/response_with_signed_assertion_3.xml +30 -0
- data/test/responses/response_with_signed_message_and_assertion.xml +34 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/responses/valid_response_without_x509certificate.xml.base64 +1 -0
- data/test/settings_test.rb +5 -5
- data/test/test_helper.rb +110 -41
- data/test/utils_test.rb +10 -10
- data/test/xml_security_test.rb +359 -68
- metadata +38 -5
data/test/response_test.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
2
|
|
3
|
-
class ResponseTest <
|
3
|
+
class ResponseTest < Minitest::Test
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
assert_raises(ArgumentError)
|
5
|
+
describe "Response" do
|
6
|
+
it "raise an exception when response is initialized with nil" do
|
7
|
+
err = assert_raises(ArgumentError) do
|
8
|
+
OneLogin::RubySaml::Response.new(nil)
|
9
|
+
end
|
10
|
+
assert_equal "Response cannot be nil", err.message
|
8
11
|
end
|
9
12
|
|
10
|
-
|
13
|
+
it "be able to parse a document which contains ampersands" do
|
11
14
|
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
12
15
|
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
13
16
|
|
@@ -18,7 +21,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
18
21
|
response.validate!
|
19
22
|
end
|
20
23
|
|
21
|
-
|
24
|
+
it "adapt namespace" do
|
22
25
|
response = OneLogin::RubySaml::Response.new(response_document)
|
23
26
|
assert !response.name_id.nil?
|
24
27
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
@@ -27,14 +30,14 @@ class ResponseTest < Test::Unit::TestCase
|
|
27
30
|
assert !response.name_id.nil?
|
28
31
|
end
|
29
32
|
|
30
|
-
|
33
|
+
it "default to raw input when a response is not Base64 encoded" do
|
31
34
|
decoded = Base64.decode64(response_document_2)
|
32
35
|
response = OneLogin::RubySaml::Response.new(decoded)
|
33
36
|
assert response.document
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
39
|
+
describe "Assertion" do
|
40
|
+
it "only retreive an assertion with an ID that matches the signature's reference URI" do
|
38
41
|
response = OneLogin::RubySaml::Response.new(wrapped_response_2)
|
39
42
|
response.stubs(:conditions).returns(nil)
|
40
43
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -44,40 +47,109 @@ class ResponseTest < Test::Unit::TestCase
|
|
44
47
|
end
|
45
48
|
end
|
46
49
|
|
47
|
-
|
48
|
-
|
50
|
+
describe "#validate!" do
|
51
|
+
it "raise when settings not initialized" do
|
49
52
|
response = OneLogin::RubySaml::Response.new(response_document)
|
50
|
-
|
53
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
54
|
+
response.validate!
|
55
|
+
end
|
56
|
+
assert_equal "No settings on response", err.message
|
57
|
+
end
|
58
|
+
|
59
|
+
it "raise when encountering a condition that prevents the document from being valid" do
|
60
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
61
|
+
response.settings = settings
|
62
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
63
|
+
response.validate!
|
64
|
+
end
|
65
|
+
assert_equal "Current time is on or after NotOnOrAfter condition", err.message
|
66
|
+
end
|
67
|
+
|
68
|
+
it "raises an exception when no cert or fingerprint provided" do
|
69
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
70
|
+
response.stubs(:conditions).returns(nil)
|
71
|
+
settings = OneLogin::RubySaml::Settings.new
|
72
|
+
response.settings = settings
|
73
|
+
settings.idp_cert = nil
|
74
|
+
settings.idp_cert_fingerprint = nil
|
75
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
51
76
|
response.validate!
|
52
77
|
end
|
78
|
+
assert_equal "No fingerprint or certificate on settings", err.message
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raise when no signature" do
|
82
|
+
response_no_signed_elements = OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64"))
|
83
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
84
|
+
response_no_signed_elements.settings = settings
|
85
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
86
|
+
response_no_signed_elements.validate!
|
87
|
+
end
|
88
|
+
assert_equal "Found an unexpected number of Signature Element. SAML Response rejected", err.message
|
53
89
|
end
|
90
|
+
|
91
|
+
it "raise when multiple signatures" do
|
92
|
+
response_multiple_signed = OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64"))
|
93
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
94
|
+
response_multiple_signed.settings = settings
|
95
|
+
response_multiple_signed.stubs(:validate_structure).returns(true)
|
96
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
97
|
+
response_multiple_signed.validate!
|
98
|
+
end
|
99
|
+
assert_equal "Duplicated ID. SAML Response rejected", err.message
|
100
|
+
end
|
101
|
+
|
102
|
+
it "raise when fingerprint missmatch" do
|
103
|
+
resp_xml = Base64.decode64(response_document_valid_signed)
|
104
|
+
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
105
|
+
response.stubs(:conditions).returns(nil)
|
106
|
+
settings = OneLogin::RubySaml::Settings.new
|
107
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
108
|
+
response.settings = settings
|
109
|
+
|
110
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
111
|
+
response.validate!
|
112
|
+
end
|
113
|
+
assert_equal 'Fingerprint mismatch', err.message
|
114
|
+
end
|
115
|
+
|
54
116
|
end
|
55
117
|
|
56
|
-
|
57
|
-
|
118
|
+
describe "#is_valid?" do
|
119
|
+
it "return false when response is initialized with blank data" do
|
58
120
|
response = OneLogin::RubySaml::Response.new('')
|
59
121
|
assert !response.is_valid?
|
60
122
|
end
|
61
123
|
|
62
|
-
|
124
|
+
it "return false if settings have not been set" do
|
63
125
|
response = OneLogin::RubySaml::Response.new(response_document)
|
64
126
|
assert !response.is_valid?
|
65
127
|
end
|
66
128
|
|
67
|
-
|
68
|
-
response = OneLogin::RubySaml::Response.new(
|
129
|
+
it "return false when no cert or fingerprint provided" do
|
130
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
131
|
+
response.stubs(:conditions).returns(nil)
|
132
|
+
settings = OneLogin::RubySaml::Settings.new
|
133
|
+
response.settings = settings
|
134
|
+
settings.idp_cert = nil
|
135
|
+
settings.idp_cert_fingerprint = nil
|
136
|
+
assert !response.is_valid?
|
137
|
+
end
|
138
|
+
|
139
|
+
it "return true when the response is initialized with valid data" do
|
140
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
69
141
|
response.stubs(:conditions).returns(nil)
|
70
142
|
assert !response.is_valid?
|
71
143
|
settings = OneLogin::RubySaml::Settings.new
|
72
144
|
assert !response.is_valid?
|
73
145
|
response.settings = settings
|
74
146
|
assert !response.is_valid?
|
75
|
-
settings.idp_cert_fingerprint =
|
76
|
-
|
147
|
+
response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
148
|
+
response.validate!
|
77
149
|
end
|
78
150
|
|
79
|
-
|
80
|
-
response = OneLogin::RubySaml::Response.new(
|
151
|
+
it "should be idempotent when the response is initialized with invalid data" do
|
152
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
81
153
|
response.stubs(:conditions).returns(nil)
|
82
154
|
settings = OneLogin::RubySaml::Settings.new
|
83
155
|
response.settings = settings
|
@@ -85,36 +157,58 @@ class ResponseTest < Test::Unit::TestCase
|
|
85
157
|
assert !response.is_valid?
|
86
158
|
end
|
87
159
|
|
88
|
-
|
89
|
-
response = OneLogin::RubySaml::Response.new(
|
160
|
+
it "should be idempotent when the response is initialized with valid data" do
|
161
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
90
162
|
response.stubs(:conditions).returns(nil)
|
91
163
|
settings = OneLogin::RubySaml::Settings.new
|
92
164
|
response.settings = settings
|
93
|
-
settings.idp_cert_fingerprint =
|
165
|
+
response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
94
166
|
assert response.is_valid?
|
95
167
|
assert response.is_valid?
|
96
168
|
end
|
97
169
|
|
98
|
-
|
99
|
-
response = OneLogin::RubySaml::Response.new(
|
170
|
+
it "return true when valid response and using fingerprint" do
|
171
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
172
|
+
response.stubs(:conditions).returns(nil)
|
173
|
+
settings = OneLogin::RubySaml::Settings.new
|
174
|
+
response.settings = settings
|
175
|
+
settings.idp_cert = nil
|
176
|
+
settings.idp_cert_fingerprint = "4B:68:C4:53:C7:D9:94:AA:D9:02:5C:99:D5:EF:CF:56:62:87:FE:8D"
|
177
|
+
assert response.is_valid?
|
178
|
+
end
|
179
|
+
|
180
|
+
it "return true when valid response using certificate" do
|
181
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
100
182
|
response.stubs(:conditions).returns(nil)
|
101
183
|
settings = OneLogin::RubySaml::Settings.new
|
102
184
|
response.settings = settings
|
103
|
-
settings.idp_cert =
|
185
|
+
settings.idp_cert = valid_cert
|
104
186
|
assert response.is_valid?
|
105
187
|
end
|
106
188
|
|
107
|
-
|
189
|
+
it "not allow signature wrapping attack" do
|
108
190
|
response = OneLogin::RubySaml::Response.new(response_document_4)
|
109
191
|
response.stubs(:conditions).returns(nil)
|
110
192
|
settings = OneLogin::RubySaml::Settings.new
|
111
193
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
112
194
|
response.settings = settings
|
113
|
-
assert response.is_valid?
|
195
|
+
assert !response.is_valid?
|
114
196
|
assert response.name_id == "test@onelogin.com"
|
115
197
|
end
|
116
198
|
|
117
|
-
|
199
|
+
it "not allow element wrapping attack" do
|
200
|
+
response_wrapped = OneLogin::RubySaml::Response.new(response_document_wrapped)
|
201
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
202
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
203
|
+
settings = OneLogin::RubySaml::Settings.new
|
204
|
+
response_wrapped.settings = settings
|
205
|
+
response_wrapped.settings.idp_cert_fingerprint = signature_fingerprint_1
|
206
|
+
|
207
|
+
assert !response_wrapped.is_valid?
|
208
|
+
assert_nil response_wrapped.name_id
|
209
|
+
end
|
210
|
+
|
211
|
+
it "support dynamic namespace resolution on signature elements" do
|
118
212
|
response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
|
119
213
|
response.stubs(:conditions).returns(nil)
|
120
214
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -124,35 +218,70 @@ class ResponseTest < Test::Unit::TestCase
|
|
124
218
|
assert response.validate!
|
125
219
|
end
|
126
220
|
|
127
|
-
|
128
|
-
response = OneLogin::RubySaml::Response.new(
|
221
|
+
it "support signature elements with no KeyInfo if cert provided" do
|
222
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
129
223
|
response.stubs(:conditions).returns(nil)
|
130
224
|
settings = OneLogin::RubySaml::Settings.new
|
131
|
-
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
132
225
|
response.settings = settings
|
226
|
+
settings.idp_cert = ruby_saml_cert
|
227
|
+
settings.idp_cert_fingerprint = nil
|
228
|
+
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
133
229
|
assert response.validate!
|
134
230
|
end
|
135
231
|
|
136
|
-
|
137
|
-
response = OneLogin::RubySaml::Response.new(
|
232
|
+
it "support signature elements with no KeyInfo if cert provided as text" do
|
233
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
138
234
|
response.stubs(:conditions).returns(nil)
|
139
235
|
settings = OneLogin::RubySaml::Settings.new
|
140
|
-
settings.idp_cert = Base64.decode64(r1_signature_2)
|
141
236
|
response.settings = settings
|
237
|
+
settings.idp_cert = ruby_saml_cert_text
|
238
|
+
settings.idp_cert_fingerprint = nil
|
239
|
+
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
142
240
|
assert response.validate!
|
143
241
|
end
|
144
242
|
|
145
|
-
|
146
|
-
|
147
|
-
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
243
|
+
it "returns an error if the signature contains no KeyInfo, cert is not provided and soft" do
|
244
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
148
245
|
response.stubs(:conditions).returns(nil)
|
149
246
|
settings = OneLogin::RubySaml::Settings.new
|
150
|
-
settings.idp_cert_fingerprint = signature_fingerprint_1
|
151
247
|
response.settings = settings
|
152
|
-
|
248
|
+
settings.idp_cert = nil
|
249
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
250
|
+
assert !response.is_valid?
|
153
251
|
end
|
154
252
|
|
155
|
-
|
253
|
+
it "raises an exception if the signature contains no KeyInfo, cert is not provided and no soft" do
|
254
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
255
|
+
response.stubs(:conditions).returns(nil)
|
256
|
+
settings = OneLogin::RubySaml::Settings.new
|
257
|
+
response.settings = settings
|
258
|
+
settings.idp_cert = nil
|
259
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
260
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
261
|
+
response.validate!
|
262
|
+
end
|
263
|
+
assert_equal "Certificate element missing in response (ds:X509Certificate) and not cert provided at settings", err.message
|
264
|
+
end
|
265
|
+
|
266
|
+
it "validate ADFS assertions" do
|
267
|
+
response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
268
|
+
response.stubs(:conditions).returns(nil)
|
269
|
+
settings = OneLogin::RubySaml::Settings.new
|
270
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
271
|
+
response.settings = settings
|
272
|
+
assert response.validate!
|
273
|
+
end
|
274
|
+
|
275
|
+
it "validate the digest" do
|
276
|
+
response = OneLogin::RubySaml::Response.new(r1_response_document_6)
|
277
|
+
response.stubs(:conditions).returns(nil)
|
278
|
+
settings = OneLogin::RubySaml::Settings.new
|
279
|
+
settings.idp_cert = Base64.decode64(r1_signature_2)
|
280
|
+
response.settings = settings
|
281
|
+
assert response.validate!
|
282
|
+
end
|
283
|
+
|
284
|
+
it "Prevent node text with comment (VU#475445) attack" do
|
156
285
|
response_doc = File.read(File.join(File.dirname(__FILE__), "responses", 'response_node_text_attack.xml.base64'))
|
157
286
|
response = OneLogin::RubySaml::Response.new(response_doc)
|
158
287
|
|
@@ -160,42 +289,42 @@ class ResponseTest < Test::Unit::TestCase
|
|
160
289
|
assert_equal "smith", response.attributes["surname"]
|
161
290
|
end
|
162
291
|
|
163
|
-
|
164
|
-
|
165
|
-
response = OneLogin::RubySaml::Response.new(
|
292
|
+
describe '#validate_audience' do
|
293
|
+
it "return true when sp_entity_id not set or empty" do
|
294
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
166
295
|
response.stubs(:conditions).returns(nil)
|
167
296
|
settings = OneLogin::RubySaml::Settings.new
|
168
297
|
response.settings = settings
|
169
|
-
settings.idp_cert_fingerprint =
|
298
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
170
299
|
assert response.is_valid?
|
171
300
|
settings.sp_entity_id = ''
|
172
301
|
assert response.is_valid?
|
173
302
|
end
|
174
303
|
|
175
|
-
|
176
|
-
response = OneLogin::RubySaml::Response.new(
|
304
|
+
it "return false when sp_entity_id set to incorrectly" do
|
305
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
177
306
|
response.stubs(:conditions).returns(nil)
|
178
307
|
settings = OneLogin::RubySaml::Settings.new
|
179
308
|
response.settings = settings
|
180
|
-
settings.idp_cert_fingerprint =
|
309
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
181
310
|
settings.sp_entity_id = 'wrong_audience'
|
182
311
|
assert !response.is_valid?
|
183
312
|
end
|
184
313
|
|
185
|
-
|
186
|
-
response = OneLogin::RubySaml::Response.new(
|
314
|
+
it "return true when sp_entity_id set to correctly" do
|
315
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
187
316
|
response.stubs(:conditions).returns(nil)
|
188
317
|
settings = OneLogin::RubySaml::Settings.new
|
189
318
|
response.settings = settings
|
190
|
-
settings.idp_cert_fingerprint =
|
191
|
-
settings.sp_entity_id = 'audience'
|
319
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
320
|
+
settings.sp_entity_id = 'https://someone.example.com/audience'
|
192
321
|
assert response.is_valid?
|
193
322
|
end
|
194
323
|
end
|
195
324
|
end
|
196
325
|
|
197
|
-
|
198
|
-
|
326
|
+
describe "#name_id" do
|
327
|
+
it "extract the value of the name id element" do
|
199
328
|
response = OneLogin::RubySaml::Response.new(response_document)
|
200
329
|
assert_equal "support@onelogin.com", response.name_id
|
201
330
|
|
@@ -203,19 +332,19 @@ class ResponseTest < Test::Unit::TestCase
|
|
203
332
|
assert_equal "someone@example.com", response.name_id
|
204
333
|
end
|
205
334
|
|
206
|
-
|
335
|
+
it "be extractable from an OpenSAML response" do
|
207
336
|
response = OneLogin::RubySaml::Response.new(fixture(:open_saml))
|
208
337
|
assert_equal "someone@example.org", response.name_id
|
209
338
|
end
|
210
339
|
|
211
|
-
|
340
|
+
it "be extractable from a Simple SAML PHP response" do
|
212
341
|
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
213
342
|
assert_equal "someone@example.com", response.name_id
|
214
343
|
end
|
215
344
|
end
|
216
345
|
|
217
|
-
|
218
|
-
|
346
|
+
describe "#check_conditions" do
|
347
|
+
it "check time conditions" do
|
219
348
|
response = OneLogin::RubySaml::Response.new(response_document)
|
220
349
|
assert !response.send(:validate_conditions, true)
|
221
350
|
response = OneLogin::RubySaml::Response.new(response_document_6)
|
@@ -226,75 +355,122 @@ class ResponseTest < Test::Unit::TestCase
|
|
226
355
|
assert response.send(:validate_conditions, true)
|
227
356
|
end
|
228
357
|
|
229
|
-
|
358
|
+
it "optionally allow for clock drift" do
|
230
359
|
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
|
231
|
-
Time.
|
360
|
+
expected_time = Time.parse("2011-06-14T18:21:01Z")
|
361
|
+
Time.stubs(:now).returns(expected_time)
|
232
362
|
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.515)
|
233
363
|
assert !response.send(:validate_conditions, true)
|
234
364
|
|
235
|
-
Time.
|
365
|
+
expected_time = Time.parse("2011-06-14T18:21:01Z")
|
366
|
+
Time.stubs(:now).returns(expected_time)
|
236
367
|
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.516)
|
237
368
|
assert response.send(:validate_conditions, true)
|
238
369
|
end
|
239
370
|
end
|
240
371
|
|
241
|
-
|
242
|
-
|
243
|
-
response = OneLogin::RubySaml::Response.new(
|
244
|
-
|
372
|
+
describe "validate_signature" do
|
373
|
+
it "raises an exception when no cert or fingerprint provided" do
|
374
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
375
|
+
settings = OneLogin::RubySaml::Settings.new
|
376
|
+
response.settings = settings
|
377
|
+
settings.idp_cert = nil
|
378
|
+
settings.idp_cert_fingerprint = nil
|
379
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
380
|
+
response.send(:validate_signature, false)
|
381
|
+
end
|
382
|
+
assert_equal "No fingerprint or certificate on settings", err.message
|
245
383
|
end
|
246
384
|
|
247
|
-
|
248
|
-
response = OneLogin::RubySaml::Response.new(
|
249
|
-
|
385
|
+
it "raises an exception when wrong cert provided" do
|
386
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
387
|
+
settings = OneLogin::RubySaml::Settings.new
|
388
|
+
response.settings = settings
|
389
|
+
settings.idp_cert = ruby_saml_cert2
|
390
|
+
settings.idp_cert_fingerprint = nil
|
391
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
392
|
+
response.send(:validate_signature, false)
|
393
|
+
end
|
394
|
+
assert_equal "Fingerprint mismatch", err.message
|
250
395
|
end
|
251
396
|
|
252
|
-
|
253
|
-
response = OneLogin::RubySaml::Response.new(
|
254
|
-
|
255
|
-
|
397
|
+
it "raises an exception when wrong fingerprint provided" do
|
398
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
399
|
+
settings = OneLogin::RubySaml::Settings.new
|
400
|
+
response.settings = settings
|
401
|
+
settings.idp_cert = nil
|
402
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
403
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
404
|
+
response.send(:validate_signature, false)
|
405
|
+
end
|
406
|
+
assert_equal "Fingerprint mismatch", err.message
|
256
407
|
end
|
257
408
|
|
258
|
-
|
259
|
-
|
260
|
-
|
409
|
+
it "raises an exception when no signature" do
|
410
|
+
response_no_signed_elements = OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64"))
|
411
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
412
|
+
response_no_signed_elements.settings = settings
|
413
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
414
|
+
response_no_signed_elements.validate!
|
415
|
+
end
|
416
|
+
assert_equal "Found an unexpected number of Signature Element. SAML Response rejected", err.message
|
261
417
|
end
|
418
|
+
end
|
262
419
|
|
263
|
-
|
264
|
-
|
265
|
-
|
420
|
+
describe "#attributes" do
|
421
|
+
before do
|
422
|
+
@response = OneLogin::RubySaml::Response.new(response_document)
|
423
|
+
end
|
424
|
+
|
425
|
+
it "extract the first attribute in a hash accessed via its symbol" do
|
426
|
+
assert_equal "demo", @response.attributes[:uid]
|
427
|
+
end
|
428
|
+
|
429
|
+
it "extract the first attribute in a hash accessed via its name" do
|
430
|
+
assert_equal "demo", @response.attributes["uid"]
|
431
|
+
end
|
432
|
+
|
433
|
+
it "extract all attributes" do
|
434
|
+
assert_equal "demo", @response.attributes[:uid]
|
435
|
+
assert_equal "value", @response.attributes[:another_value]
|
436
|
+
end
|
437
|
+
|
438
|
+
it "work for implicit namespaces" do
|
439
|
+
response_3 = OneLogin::RubySaml::Response.new(response_document_3)
|
440
|
+
assert_equal "someone@example.com", response_3.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
441
|
+
end
|
442
|
+
|
443
|
+
it "not raise on responses without attributes" do
|
444
|
+
response_4 = OneLogin::RubySaml::Response.new(response_document_4)
|
445
|
+
assert_equal OneLogin::RubySaml::Attributes.new, response_4.attributes
|
266
446
|
end
|
267
447
|
|
268
|
-
|
448
|
+
it "extract attributes from all AttributeStatement tags" do
|
269
449
|
assert_equal "smith", response_with_multiple_attribute_statements.attributes[:surname]
|
270
450
|
assert_equal "bob", response_with_multiple_attribute_statements.attributes[:firstname]
|
271
451
|
end
|
272
452
|
|
273
|
-
|
274
|
-
response
|
275
|
-
response.attributes.merge({ :testing_attribute => "test" })
|
453
|
+
it "be manipulable by hash methods such as #merge and not raise an exception" do
|
454
|
+
@response.attributes.merge({ :testing_attribute => "test" })
|
276
455
|
end
|
277
456
|
|
278
|
-
|
279
|
-
response
|
280
|
-
response.attributes.shift
|
457
|
+
it "be manipulable by hash methods such as #shift and not raise an exception" do
|
458
|
+
@response.attributes.shift
|
281
459
|
end
|
282
460
|
|
283
|
-
|
284
|
-
response
|
285
|
-
response.attributes
|
286
|
-
assert response.attributes[:testing_attribute]
|
461
|
+
it "be manipulable by hash methods such as #merge! and actually contain the value" do
|
462
|
+
@response.attributes.merge!({ :testing_attribute => "test" })
|
463
|
+
assert @response.attributes[:testing_attribute]
|
287
464
|
end
|
288
465
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
assert_nil response.attributes[removed_value[0]]
|
466
|
+
it "be manipulable by hash methods such as #shift and actually remove the value" do
|
467
|
+
removed_value = @response.attributes.shift
|
468
|
+
assert_nil @response.attributes[removed_value[0]]
|
293
469
|
end
|
294
470
|
end
|
295
471
|
|
296
|
-
|
297
|
-
|
472
|
+
describe "#session_expires_at" do
|
473
|
+
it "extract the value of the SessionNotOnOrAfter attribute" do
|
298
474
|
response = OneLogin::RubySaml::Response.new(response_document)
|
299
475
|
assert response.session_expires_at.is_a?(Time)
|
300
476
|
|
@@ -303,124 +479,167 @@ class ResponseTest < Test::Unit::TestCase
|
|
303
479
|
end
|
304
480
|
end
|
305
481
|
|
306
|
-
|
307
|
-
|
482
|
+
describe "#issuer" do
|
483
|
+
it "return the issuer inside the response assertion" do
|
308
484
|
response = OneLogin::RubySaml::Response.new(response_document)
|
309
485
|
assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
|
310
486
|
end
|
311
487
|
|
312
|
-
|
488
|
+
it "return the issuer inside the response" do
|
313
489
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
314
490
|
assert_equal "wibble", response.issuer
|
315
491
|
end
|
316
492
|
end
|
317
493
|
|
318
|
-
|
319
|
-
|
494
|
+
describe "#success" do
|
495
|
+
it "find a status code that says success" do
|
320
496
|
response = OneLogin::RubySaml::Response.new(response_document)
|
321
|
-
response.success?
|
497
|
+
assert response.send(:success?)
|
322
498
|
end
|
323
499
|
end
|
324
500
|
|
325
|
-
|
326
|
-
|
501
|
+
describe '#xpath_first_from_signed_assertion' do
|
502
|
+
it 'not allow arbitrary code execution' do
|
327
503
|
malicious_response_document = fixture('response_eval', false)
|
328
504
|
response = OneLogin::RubySaml::Response.new(malicious_response_document)
|
329
505
|
response.send(:xpath_first_from_signed_assertion)
|
330
|
-
|
506
|
+
assert_nil $evalled
|
331
507
|
end
|
332
508
|
end
|
333
509
|
|
334
|
-
|
335
|
-
|
510
|
+
describe "#multiple values" do
|
511
|
+
it "extract single value as string" do
|
336
512
|
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
|
337
513
|
end
|
338
514
|
|
339
|
-
|
515
|
+
it "extract single value as string in compatibility mode off" do
|
340
516
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
341
517
|
assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
|
342
518
|
# classes are not reloaded between tests so restore default
|
343
519
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
344
520
|
end
|
345
521
|
|
346
|
-
|
522
|
+
it "extract first of multiple values as string for b/w compatibility" do
|
347
523
|
assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
|
348
524
|
end
|
349
525
|
|
350
|
-
|
526
|
+
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
351
527
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
352
528
|
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
|
353
529
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
354
530
|
end
|
355
531
|
|
356
|
-
|
532
|
+
it "return array with all attributes when asked in XML order" do
|
357
533
|
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
358
534
|
end
|
359
535
|
|
360
|
-
|
536
|
+
it "return array with all attributes when asked in XML order in compatibility mode off" do
|
361
537
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
362
538
|
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
363
539
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
364
540
|
end
|
365
541
|
|
366
|
-
|
542
|
+
it "return first of multiple values when multiple Attribute tags in XML" do
|
367
543
|
assert_equal 'role1', response_multiple_attr_values.attributes[:role]
|
368
544
|
end
|
369
545
|
|
370
|
-
|
546
|
+
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
371
547
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
372
548
|
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
|
373
549
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
374
550
|
end
|
375
551
|
|
376
|
-
|
552
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
377
553
|
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
378
554
|
end
|
379
555
|
|
380
|
-
|
556
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
381
557
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
382
558
|
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
383
559
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
384
560
|
end
|
385
561
|
|
386
|
-
|
562
|
+
it "return all of multiple values when multiple Attribute tags in multiple AttributeStatement tags" do
|
387
563
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
388
564
|
assert_equal ['role1', 'role2', 'role3'], response_with_multiple_attribute_statements.attributes.multi(:role)
|
389
565
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
390
566
|
end
|
391
567
|
|
392
|
-
|
568
|
+
it "return nil value correctly" do
|
393
569
|
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
394
570
|
end
|
395
571
|
|
396
|
-
|
572
|
+
it "return nil value correctly when not in compatibility mode off" do
|
397
573
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
398
|
-
|
574
|
+
assert [nil] == response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
399
575
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
400
576
|
end
|
401
577
|
|
402
|
-
|
578
|
+
it "return multiple values including nil and empty string" do
|
403
579
|
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
404
580
|
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
|
405
581
|
end
|
406
582
|
|
407
|
-
|
583
|
+
it "return multiple values from [] when not in compatibility mode off" do
|
408
584
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
409
585
|
assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
|
410
586
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
411
587
|
end
|
412
588
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
589
|
+
it "check what happens when trying retrieve attribute that does not exists" do
|
590
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_not_exists]
|
591
|
+
assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
592
|
+
assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
417
593
|
|
418
594
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
419
|
-
|
420
|
-
|
421
|
-
|
595
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_not_exists]
|
596
|
+
assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
597
|
+
assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
422
598
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
423
599
|
end
|
424
600
|
end
|
601
|
+
|
602
|
+
describe "signature wrapping attack with encrypted assertion" do
|
603
|
+
it "should not be valid" do
|
604
|
+
settings = OneLogin::RubySaml::Settings.new
|
605
|
+
settings.private_key = valid_key
|
606
|
+
signature_wrapping_attack = read_response("encrypted_new_attack.xml.base64")
|
607
|
+
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
608
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
609
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
610
|
+
settings.idp_cert_fingerprint = "385b1eec71143f00db6af936e2ea12a28771d72c"
|
611
|
+
assert !response_wrapped.is_valid?
|
612
|
+
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
613
|
+
response_wrapped.validate!
|
614
|
+
end
|
615
|
+
assert_equal "Found an invalid Signed Element. SAML Response rejected", err.message
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
describe "signature wrapping attack - concealed SAML response body" do
|
620
|
+
it "should not be valid" do
|
621
|
+
settings = OneLogin::RubySaml::Settings.new
|
622
|
+
signature_wrapping_attack = read_response("response_with_concealed_signed_assertion.xml")
|
623
|
+
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
624
|
+
settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
|
625
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
626
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
627
|
+
response_wrapped.stubs(:validate_structure).returns(true)
|
628
|
+
assert !response_wrapped.is_valid?
|
629
|
+
assert !response_wrapped.validate!
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
describe "signature wrapping attack - doubled signed assertion SAML response" do
|
634
|
+
it "should not be valid" do
|
635
|
+
settings = OneLogin::RubySaml::Settings.new
|
636
|
+
signature_wrapping_attack = read_response("response_with_doubled_signed_assertion.xml")
|
637
|
+
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
638
|
+
settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
|
639
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
640
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
641
|
+
assert !response_wrapped.is_valid?
|
642
|
+
end
|
643
|
+
end
|
425
644
|
end
|
426
645
|
end
|