ruby-saml 0.8.11 → 0.8.12
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 +7 -7
- data/Gemfile +3 -1
- data/Rakefile +0 -14
- data/lib/onelogin/ruby-saml/response.rb +24 -13
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +1 -2
- data/test/logoutrequest_test.rb +124 -126
- data/test/logoutresponse_test.rb +22 -28
- data/test/response_test.rb +171 -122
- data/test/responses/encrypted_new_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_wrapped.xml.base64 +150 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/settings_test.rb +5 -5
- data/test/test_helper.rb +48 -11
- data/test/utils_test.rb +10 -10
- data/test/xml_security_test.rb +34 -36
- metadata +55 -48
data/test/logoutresponse_test.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
-
require
|
3
|
-
require 'responses/logoutresponse_fixtures'
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "responses/logoutresponse_fixtures"))
|
4
3
|
|
5
|
-
class LogoutResponseTest < Test
|
4
|
+
class LogoutResponseTest < Minitest::Test
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
describe "Logoutresponse" do
|
7
|
+
|
8
|
+
describe "#new" do
|
9
|
+
it "raise an exception when response is initialized with nil" do
|
10
10
|
assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
|
11
11
|
end
|
12
|
-
|
12
|
+
it "default to empty settings" do
|
13
13
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new( valid_response)
|
14
14
|
assert logoutresponse.settings.nil?
|
15
15
|
end
|
16
|
-
|
16
|
+
it "accept constructor-injected settings" do
|
17
17
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings)
|
18
18
|
assert !logoutresponse.settings.nil?
|
19
19
|
end
|
20
|
-
|
20
|
+
it "accept constructor-injected options" do
|
21
21
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} )
|
22
22
|
assert !logoutresponse.options.empty?
|
23
23
|
end
|
24
|
-
|
24
|
+
it "support base64 encoded responses" do
|
25
25
|
expected_response = valid_response
|
26
26
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings)
|
27
27
|
|
@@ -29,8 +29,8 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
describe "#validate" do
|
33
|
+
it "validate the response" do
|
34
34
|
in_relation_to_request_id = random_id
|
35
35
|
|
36
36
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
|
@@ -43,7 +43,7 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
43
43
|
assert logoutresponse.success?
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
it "invalidate responses with wrong id when given option :matches_uuid" do
|
47
47
|
|
48
48
|
expected_request_id = "_some_other_expected_uuid"
|
49
49
|
opts = { :matches_request_id => expected_request_id}
|
@@ -51,10 +51,10 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
51
51
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
|
52
52
|
|
53
53
|
assert !logoutresponse.validate
|
54
|
-
|
54
|
+
assert expected_request_id != logoutresponse.in_response_to
|
55
55
|
end
|
56
56
|
|
57
|
-
|
57
|
+
it "invalidate responses with wrong request status" do
|
58
58
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
|
59
59
|
|
60
60
|
assert !logoutresponse.validate
|
@@ -62,8 +62,8 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
|
65
|
+
describe "#validate!" do
|
66
|
+
it "validates good responses" do
|
67
67
|
in_relation_to_request_id = random_id
|
68
68
|
|
69
69
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
|
@@ -71,7 +71,7 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
71
71
|
logoutresponse.validate!
|
72
72
|
end
|
73
73
|
|
74
|
-
|
74
|
+
it "raises validation error when matching for wrong request id" do
|
75
75
|
|
76
76
|
expected_request_id = "_some_other_expected_id"
|
77
77
|
opts = { :matches_request_id => expected_request_id}
|
@@ -81,19 +81,19 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
81
81
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
82
82
|
end
|
83
83
|
|
84
|
-
|
84
|
+
it "raise validation error for wrong request status" do
|
85
85
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
|
86
86
|
|
87
87
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
88
88
|
end
|
89
89
|
|
90
|
-
|
90
|
+
it "raise validation error when in bad state" do
|
91
91
|
# no settings
|
92
92
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response)
|
93
93
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
94
94
|
end
|
95
95
|
|
96
|
-
|
96
|
+
it "raise validation error when in lack of sp_entity_id setting" do
|
97
97
|
bad_settings = settings
|
98
98
|
bad_settings.issuer = nil
|
99
99
|
bad_settings.sp_entity_id = nil
|
@@ -101,7 +101,7 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
101
101
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
102
102
|
end
|
103
103
|
|
104
|
-
|
104
|
+
it "raise error for invalid xml" do
|
105
105
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings)
|
106
106
|
|
107
107
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
@@ -109,10 +109,4 @@ class LogoutResponseTest < Test::Unit::TestCase
|
|
109
109
|
end
|
110
110
|
|
111
111
|
end
|
112
|
-
|
113
|
-
# logoutresponse fixtures
|
114
|
-
def random_id
|
115
|
-
"_#{UUID.new.generate}"
|
116
|
-
end
|
117
|
-
|
118
112
|
end
|
data/test/response_test.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
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
|
-
|
5
|
+
describe "Response" do
|
6
|
+
it "raise an exception when response is initialized with nil" do
|
7
7
|
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
it "be able to parse a document which contains ampersands" do
|
11
11
|
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
12
12
|
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
13
13
|
|
@@ -18,7 +18,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
18
18
|
response.validate!
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
it "adapt namespace" do
|
22
22
|
response = OneLogin::RubySaml::Response.new(response_document)
|
23
23
|
assert !response.name_id.nil?
|
24
24
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
@@ -27,14 +27,14 @@ class ResponseTest < Test::Unit::TestCase
|
|
27
27
|
assert !response.name_id.nil?
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
it "default to raw input when a response is not Base64 encoded" do
|
31
31
|
decoded = Base64.decode64(response_document_2)
|
32
32
|
response = OneLogin::RubySaml::Response.new(decoded)
|
33
33
|
assert response.document
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
describe "Assertion" do
|
37
|
+
it "only retreive an assertion with an ID that matches the signature's reference URI" do
|
38
38
|
response = OneLogin::RubySaml::Response.new(wrapped_response_2)
|
39
39
|
response.stubs(:conditions).returns(nil)
|
40
40
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -44,77 +44,89 @@ class ResponseTest < Test::Unit::TestCase
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
47
|
+
describe "#validate!" do
|
48
|
+
it "raise when encountering a condition that prevents the document from being valid" do
|
49
49
|
response = OneLogin::RubySaml::Response.new(response_document)
|
50
|
-
|
50
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
51
51
|
response.validate!
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
|
-
|
56
|
+
describe "#is_valid?" do
|
57
|
+
it "return false when response is initialized with blank data" do
|
58
58
|
response = OneLogin::RubySaml::Response.new('')
|
59
59
|
assert !response.is_valid?
|
60
60
|
end
|
61
61
|
|
62
|
-
|
62
|
+
it "return false if settings have not been set" do
|
63
63
|
response = OneLogin::RubySaml::Response.new(response_document)
|
64
64
|
assert !response.is_valid?
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
|
-
response = OneLogin::RubySaml::Response.new(
|
67
|
+
it "return true when the response is initialized with valid data" do
|
68
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
69
69
|
response.stubs(:conditions).returns(nil)
|
70
70
|
assert !response.is_valid?
|
71
71
|
settings = OneLogin::RubySaml::Settings.new
|
72
72
|
assert !response.is_valid?
|
73
73
|
response.settings = settings
|
74
74
|
assert !response.is_valid?
|
75
|
-
settings.idp_cert_fingerprint =
|
76
|
-
|
75
|
+
response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
76
|
+
response.validate!
|
77
77
|
end
|
78
78
|
|
79
|
-
|
80
|
-
response = OneLogin::RubySaml::Response.new(
|
79
|
+
it "should be idempotent when the response is initialized with invalid data" do
|
80
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
81
81
|
response.stubs(:conditions).returns(nil)
|
82
82
|
settings = OneLogin::RubySaml::Settings.new
|
83
|
-
response.settings = settings
|
83
|
+
response.settings = settings
|
84
84
|
assert !response.is_valid?
|
85
85
|
assert !response.is_valid?
|
86
86
|
end
|
87
87
|
|
88
|
-
|
89
|
-
response = OneLogin::RubySaml::Response.new(
|
88
|
+
it "should be idempotent when the response is initialized with valid data" do
|
89
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
90
90
|
response.stubs(:conditions).returns(nil)
|
91
91
|
settings = OneLogin::RubySaml::Settings.new
|
92
92
|
response.settings = settings
|
93
|
-
settings.idp_cert_fingerprint =
|
93
|
+
response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
94
94
|
assert response.is_valid?
|
95
95
|
assert response.is_valid?
|
96
96
|
end
|
97
97
|
|
98
|
-
|
99
|
-
response = OneLogin::RubySaml::Response.new(
|
98
|
+
it "return true when using certificate instead of fingerprint" do
|
99
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
100
100
|
response.stubs(:conditions).returns(nil)
|
101
101
|
settings = OneLogin::RubySaml::Settings.new
|
102
102
|
response.settings = settings
|
103
|
-
settings.idp_cert =
|
103
|
+
settings.idp_cert = valid_cert
|
104
104
|
assert response.is_valid?
|
105
105
|
end
|
106
106
|
|
107
|
-
|
107
|
+
it "not allow signature wrapping attack" do
|
108
108
|
response = OneLogin::RubySaml::Response.new(response_document_4)
|
109
109
|
response.stubs(:conditions).returns(nil)
|
110
110
|
settings = OneLogin::RubySaml::Settings.new
|
111
111
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
112
112
|
response.settings = settings
|
113
|
-
assert response.is_valid?
|
113
|
+
assert !response.is_valid?
|
114
114
|
assert response.name_id == "test@onelogin.com"
|
115
115
|
end
|
116
116
|
|
117
|
-
|
117
|
+
it "not allow element wrapping attack" do
|
118
|
+
response_wrapped = OneLogin::RubySaml::Response.new(response_document_wrapped)
|
119
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
120
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
121
|
+
settings = OneLogin::RubySaml::Settings.new
|
122
|
+
response_wrapped.settings = settings
|
123
|
+
response_wrapped.settings.idp_cert_fingerprint = signature_fingerprint_1
|
124
|
+
|
125
|
+
assert !response_wrapped.is_valid?
|
126
|
+
assert_nil response_wrapped.name_id
|
127
|
+
end
|
128
|
+
|
129
|
+
it "support dynamic namespace resolution on signature elements" do
|
118
130
|
response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
|
119
131
|
response.stubs(:conditions).returns(nil)
|
120
132
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -124,7 +136,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
124
136
|
assert response.validate!
|
125
137
|
end
|
126
138
|
|
127
|
-
|
139
|
+
it "validate ADFS assertions" do
|
128
140
|
response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
129
141
|
response.stubs(:conditions).returns(nil)
|
130
142
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -133,7 +145,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
133
145
|
assert response.validate!
|
134
146
|
end
|
135
147
|
|
136
|
-
|
148
|
+
it "validate the digest" do
|
137
149
|
response = OneLogin::RubySaml::Response.new(r1_response_document_6)
|
138
150
|
response.stubs(:conditions).returns(nil)
|
139
151
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -142,8 +154,8 @@ class ResponseTest < Test::Unit::TestCase
|
|
142
154
|
assert response.validate!
|
143
155
|
end
|
144
156
|
|
145
|
-
|
146
|
-
resp_xml = Base64.decode64(
|
157
|
+
it "validate SAML 2.0 XML structure" do
|
158
|
+
resp_xml = Base64.decode64(response_document_valid_signed).gsub(/emailAddress/,'test')
|
147
159
|
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
148
160
|
response.stubs(:conditions).returns(nil)
|
149
161
|
settings = OneLogin::RubySaml::Settings.new
|
@@ -152,7 +164,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
152
164
|
assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response.validate! }
|
153
165
|
end
|
154
166
|
|
155
|
-
|
167
|
+
it "Prevent node text with comment (VU#475445) attack" do
|
156
168
|
response_doc = File.read(File.join(File.dirname(__FILE__), "responses", 'response_node_text_attack.xml.base64'))
|
157
169
|
response = OneLogin::RubySaml::Response.new(response_doc)
|
158
170
|
|
@@ -160,42 +172,42 @@ class ResponseTest < Test::Unit::TestCase
|
|
160
172
|
assert_equal "smith", response.attributes["surname"]
|
161
173
|
end
|
162
174
|
|
163
|
-
|
164
|
-
|
165
|
-
response = OneLogin::RubySaml::Response.new(
|
175
|
+
describe '#validate_audience' do
|
176
|
+
it "return true when sp_entity_id not set or empty" do
|
177
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
166
178
|
response.stubs(:conditions).returns(nil)
|
167
179
|
settings = OneLogin::RubySaml::Settings.new
|
168
180
|
response.settings = settings
|
169
|
-
settings.idp_cert_fingerprint =
|
181
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
170
182
|
assert response.is_valid?
|
171
183
|
settings.sp_entity_id = ''
|
172
184
|
assert response.is_valid?
|
173
185
|
end
|
174
186
|
|
175
|
-
|
176
|
-
response = OneLogin::RubySaml::Response.new(
|
187
|
+
it "return false when sp_entity_id set to incorrectly" do
|
188
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
177
189
|
response.stubs(:conditions).returns(nil)
|
178
190
|
settings = OneLogin::RubySaml::Settings.new
|
179
191
|
response.settings = settings
|
180
|
-
settings.idp_cert_fingerprint =
|
192
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
181
193
|
settings.sp_entity_id = 'wrong_audience'
|
182
194
|
assert !response.is_valid?
|
183
195
|
end
|
184
196
|
|
185
|
-
|
186
|
-
response = OneLogin::RubySaml::Response.new(
|
197
|
+
it "return true when sp_entity_id set to correctly" do
|
198
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
187
199
|
response.stubs(:conditions).returns(nil)
|
188
200
|
settings = OneLogin::RubySaml::Settings.new
|
189
201
|
response.settings = settings
|
190
|
-
settings.idp_cert_fingerprint =
|
191
|
-
settings.sp_entity_id = 'audience'
|
202
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
203
|
+
settings.sp_entity_id = 'https://someone.example.com/audience'
|
192
204
|
assert response.is_valid?
|
193
205
|
end
|
194
206
|
end
|
195
207
|
end
|
196
208
|
|
197
|
-
|
198
|
-
|
209
|
+
describe "#name_id" do
|
210
|
+
it "extract the value of the name id element" do
|
199
211
|
response = OneLogin::RubySaml::Response.new(response_document)
|
200
212
|
assert_equal "support@onelogin.com", response.name_id
|
201
213
|
|
@@ -203,19 +215,19 @@ class ResponseTest < Test::Unit::TestCase
|
|
203
215
|
assert_equal "someone@example.com", response.name_id
|
204
216
|
end
|
205
217
|
|
206
|
-
|
218
|
+
it "be extractable from an OpenSAML response" do
|
207
219
|
response = OneLogin::RubySaml::Response.new(fixture(:open_saml))
|
208
220
|
assert_equal "someone@example.org", response.name_id
|
209
221
|
end
|
210
222
|
|
211
|
-
|
223
|
+
it "be extractable from a Simple SAML PHP response" do
|
212
224
|
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
213
225
|
assert_equal "someone@example.com", response.name_id
|
214
226
|
end
|
215
227
|
end
|
216
228
|
|
217
|
-
|
218
|
-
|
229
|
+
describe "#check_conditions" do
|
230
|
+
it "check time conditions" do
|
219
231
|
response = OneLogin::RubySaml::Response.new(response_document)
|
220
232
|
assert !response.send(:validate_conditions, true)
|
221
233
|
response = OneLogin::RubySaml::Response.new(response_document_6)
|
@@ -226,75 +238,74 @@ class ResponseTest < Test::Unit::TestCase
|
|
226
238
|
assert response.send(:validate_conditions, true)
|
227
239
|
end
|
228
240
|
|
229
|
-
|
241
|
+
it "optionally allow for clock drift" do
|
230
242
|
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
|
231
|
-
Time.
|
243
|
+
expected_time = Time.parse("2011-06-14T18:21:01Z")
|
244
|
+
Time.stubs(:now).returns(expected_time)
|
232
245
|
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.515)
|
233
246
|
assert !response.send(:validate_conditions, true)
|
234
247
|
|
235
|
-
Time.
|
248
|
+
expected_time = Time.parse("2011-06-14T18:21:01Z")
|
249
|
+
Time.stubs(:now).returns(expected_time)
|
236
250
|
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.516)
|
237
251
|
assert response.send(:validate_conditions, true)
|
238
252
|
end
|
239
253
|
end
|
240
254
|
|
241
|
-
|
242
|
-
|
243
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
244
|
-
assert_equal "demo", response.attributes[:uid]
|
255
|
+
describe "#attributes" do
|
256
|
+
before do
|
257
|
+
@response = OneLogin::RubySaml::Response.new(response_document)
|
245
258
|
end
|
246
259
|
|
247
|
-
|
248
|
-
|
249
|
-
assert_equal "demo", response.attributes["uid"]
|
260
|
+
it "extract the first attribute in a hash accessed via its symbol" do
|
261
|
+
assert_equal "demo", @response.attributes[:uid]
|
250
262
|
end
|
251
263
|
|
252
|
-
|
253
|
-
|
254
|
-
assert_equal "demo", response.attributes[:uid]
|
255
|
-
assert_equal "value", response.attributes[:another_value]
|
264
|
+
it "extract the first attribute in a hash accessed via its name" do
|
265
|
+
assert_equal "demo", @response.attributes["uid"]
|
256
266
|
end
|
257
267
|
|
258
|
-
|
259
|
-
|
260
|
-
assert_equal "
|
268
|
+
it "extract all attributes" do
|
269
|
+
assert_equal "demo", @response.attributes[:uid]
|
270
|
+
assert_equal "value", @response.attributes[:another_value]
|
261
271
|
end
|
262
272
|
|
263
|
-
|
264
|
-
|
265
|
-
assert_equal
|
273
|
+
it "work for implicit namespaces" do
|
274
|
+
response_3 = OneLogin::RubySaml::Response.new(response_document_3)
|
275
|
+
assert_equal "someone@example.com", response_3.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
266
276
|
end
|
267
277
|
|
268
|
-
|
278
|
+
it "not raise on responses without attributes" do
|
279
|
+
response_4 = OneLogin::RubySaml::Response.new(response_document_4)
|
280
|
+
assert_equal OneLogin::RubySaml::Attributes.new, response_4.attributes
|
281
|
+
end
|
282
|
+
|
283
|
+
it "extract attributes from all AttributeStatement tags" do
|
269
284
|
assert_equal "smith", response_with_multiple_attribute_statements.attributes[:surname]
|
270
285
|
assert_equal "bob", response_with_multiple_attribute_statements.attributes[:firstname]
|
271
286
|
end
|
272
287
|
|
273
|
-
|
274
|
-
response
|
275
|
-
response.attributes.merge({ :testing_attribute => "test" })
|
288
|
+
it "be manipulable by hash methods such as #merge and not raise an exception" do
|
289
|
+
@response.attributes.merge({ :testing_attribute => "test" })
|
276
290
|
end
|
277
291
|
|
278
|
-
|
279
|
-
response
|
280
|
-
response.attributes.shift
|
292
|
+
it "be manipulable by hash methods such as #shift and not raise an exception" do
|
293
|
+
@response.attributes.shift
|
281
294
|
end
|
282
295
|
|
283
|
-
|
284
|
-
response
|
285
|
-
response.attributes
|
286
|
-
assert response.attributes[:testing_attribute]
|
296
|
+
it "be manipulable by hash methods such as #merge! and actually contain the value" do
|
297
|
+
@response.attributes.merge!({ :testing_attribute => "test" })
|
298
|
+
assert @response.attributes[:testing_attribute]
|
287
299
|
end
|
288
300
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
assert_nil response.attributes[removed_value[0]]
|
301
|
+
it "be manipulable by hash methods such as #shift and actually remove the value" do
|
302
|
+
removed_value = @response.attributes.shift
|
303
|
+
assert_nil @response.attributes[removed_value[0]]
|
293
304
|
end
|
294
305
|
end
|
295
306
|
|
296
|
-
|
297
|
-
|
307
|
+
describe "#session_expires_at" do
|
308
|
+
it "extract the value of the SessionNotOnOrAfter attribute" do
|
298
309
|
response = OneLogin::RubySaml::Response.new(response_document)
|
299
310
|
assert response.session_expires_at.is_a?(Time)
|
300
311
|
|
@@ -303,124 +314,162 @@ class ResponseTest < Test::Unit::TestCase
|
|
303
314
|
end
|
304
315
|
end
|
305
316
|
|
306
|
-
|
307
|
-
|
317
|
+
describe "#issuer" do
|
318
|
+
it "return the issuer inside the response assertion" do
|
308
319
|
response = OneLogin::RubySaml::Response.new(response_document)
|
309
320
|
assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
|
310
321
|
end
|
311
322
|
|
312
|
-
|
323
|
+
it "return the issuer inside the response" do
|
313
324
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
314
325
|
assert_equal "wibble", response.issuer
|
315
326
|
end
|
316
327
|
end
|
317
328
|
|
318
|
-
|
319
|
-
|
329
|
+
describe "#success" do
|
330
|
+
it "find a status code that says success" do
|
320
331
|
response = OneLogin::RubySaml::Response.new(response_document)
|
321
|
-
response.success?
|
332
|
+
assert response.send(:success?)
|
322
333
|
end
|
323
334
|
end
|
324
335
|
|
325
|
-
|
326
|
-
|
336
|
+
describe '#xpath_first_from_signed_assertion' do
|
337
|
+
it 'not allow arbitrary code execution' do
|
327
338
|
malicious_response_document = fixture('response_eval', false)
|
328
339
|
response = OneLogin::RubySaml::Response.new(malicious_response_document)
|
329
340
|
response.send(:xpath_first_from_signed_assertion)
|
330
|
-
|
341
|
+
assert_nil $evalled
|
331
342
|
end
|
332
343
|
end
|
333
344
|
|
334
|
-
|
335
|
-
|
345
|
+
describe "#multiple values" do
|
346
|
+
it "extract single value as string" do
|
336
347
|
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
|
337
348
|
end
|
338
349
|
|
339
|
-
|
350
|
+
it "extract single value as string in compatibility mode off" do
|
340
351
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
341
352
|
assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
|
342
353
|
# classes are not reloaded between tests so restore default
|
343
354
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
344
355
|
end
|
345
356
|
|
346
|
-
|
357
|
+
it "extract first of multiple values as string for b/w compatibility" do
|
347
358
|
assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
|
348
359
|
end
|
349
360
|
|
350
|
-
|
361
|
+
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
351
362
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
352
363
|
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
|
353
364
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
354
365
|
end
|
355
366
|
|
356
|
-
|
367
|
+
it "return array with all attributes when asked in XML order" do
|
357
368
|
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
358
369
|
end
|
359
370
|
|
360
|
-
|
371
|
+
it "return array with all attributes when asked in XML order in compatibility mode off" do
|
361
372
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
362
373
|
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
363
374
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
364
375
|
end
|
365
376
|
|
366
|
-
|
377
|
+
it "return first of multiple values when multiple Attribute tags in XML" do
|
367
378
|
assert_equal 'role1', response_multiple_attr_values.attributes[:role]
|
368
379
|
end
|
369
380
|
|
370
|
-
|
381
|
+
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
371
382
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
372
383
|
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
|
373
384
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
374
385
|
end
|
375
386
|
|
376
|
-
|
387
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
377
388
|
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
378
389
|
end
|
379
390
|
|
380
|
-
|
391
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
381
392
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
382
393
|
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
383
394
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
384
395
|
end
|
385
396
|
|
386
|
-
|
397
|
+
it "return all of multiple values when multiple Attribute tags in multiple AttributeStatement tags" do
|
387
398
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
388
399
|
assert_equal ['role1', 'role2', 'role3'], response_with_multiple_attribute_statements.attributes.multi(:role)
|
389
400
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
390
401
|
end
|
391
402
|
|
392
|
-
|
403
|
+
it "return nil value correctly" do
|
393
404
|
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
394
405
|
end
|
395
406
|
|
396
|
-
|
407
|
+
it "return nil value correctly when not in compatibility mode off" do
|
397
408
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
398
|
-
|
409
|
+
assert [nil] == response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
399
410
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
400
411
|
end
|
401
412
|
|
402
|
-
|
413
|
+
it "return multiple values including nil and empty string" do
|
403
414
|
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
404
415
|
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
|
405
416
|
end
|
406
417
|
|
407
|
-
|
418
|
+
it "return multiple values from [] when not in compatibility mode off" do
|
408
419
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
409
420
|
assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
|
410
421
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
411
422
|
end
|
412
423
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
424
|
+
it "check what happens when trying retrieve attribute that does not exists" do
|
425
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_not_exists]
|
426
|
+
assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
427
|
+
assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
417
428
|
|
418
429
|
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
419
|
-
|
420
|
-
|
421
|
-
|
430
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_not_exists]
|
431
|
+
assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
432
|
+
assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
422
433
|
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
423
434
|
end
|
424
435
|
end
|
436
|
+
|
437
|
+
describe "signature wrapping attack with encrypted assertion" do
|
438
|
+
it "should not be valid" do
|
439
|
+
settings = OneLogin::RubySaml::Settings.new
|
440
|
+
settings.private_key = valid_key
|
441
|
+
signature_wrapping_attack = read_response("encrypted_new_attack.xml.base64")
|
442
|
+
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
443
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
444
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
445
|
+
settings.idp_cert_fingerprint = "385b1eec71143f00db6af936e2ea12a28771d72c"
|
446
|
+
assert !response_wrapped.is_valid?
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
describe "signature wrapping attack - concealed SAML response body" do
|
451
|
+
it "should not be valid" do
|
452
|
+
settings = OneLogin::RubySaml::Settings.new
|
453
|
+
signature_wrapping_attack = read_response("response_with_concealed_signed_assertion.xml")
|
454
|
+
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
455
|
+
settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
|
456
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
457
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
458
|
+
assert !response_wrapped.is_valid?
|
459
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "SAML Response must contain 1 assertion"){ response_wrapped.validate! }
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe "signature wrapping attack - doubled signed assertion SAML response" do
|
464
|
+
it "should not be valid" do
|
465
|
+
settings = OneLogin::RubySaml::Settings.new
|
466
|
+
signature_wrapping_attack = read_response("response_with_doubled_signed_assertion.xml")
|
467
|
+
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
468
|
+
settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
|
469
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
470
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
471
|
+
assert !response_wrapped.is_valid?
|
472
|
+
end
|
473
|
+
end
|
425
474
|
end
|
426
475
|
end
|