ruby-saml 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 -0
- data/.document +5 -0
- data/.gitignore +12 -0
- data/.travis.yml +11 -0
- data/Gemfile +37 -0
- data/LICENSE +19 -0
- data/README.md +160 -0
- data/Rakefile +27 -0
- data/changelog.md +24 -0
- data/lib/onelogin/ruby-saml/attributes.rb +147 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +168 -0
- data/lib/onelogin/ruby-saml/logging.rb +26 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +161 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +153 -0
- data/lib/onelogin/ruby-saml/metadata.rb +66 -0
- data/lib/onelogin/ruby-saml/response.rb +426 -0
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +166 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +158 -0
- data/lib/onelogin/ruby-saml/utils.rb +119 -0
- data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
- data/lib/onelogin/ruby-saml/version.rb +5 -0
- data/lib/ruby-saml.rb +12 -0
- data/lib/schemas/saml20assertion_schema.xsd +283 -0
- data/lib/schemas/saml20protocol_schema.xsd +302 -0
- data/lib/schemas/xenc_schema.xsd +146 -0
- data/lib/schemas/xmldsig_schema.xsd +318 -0
- data/lib/xml_security.rb +292 -0
- data/ruby-saml.gemspec +28 -0
- data/test/certificates/certificate1 +12 -0
- data/test/certificates/r1_certificate2_base64 +1 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/logoutrequest_test.rb +244 -0
- data/test/logoutresponse_test.rb +112 -0
- data/test/request_test.rb +229 -0
- data/test/response_test.rb +475 -0
- data/test/responses/adfs_response_sha1.xml +46 -0
- data/test/responses/adfs_response_sha256.xml +46 -0
- data/test/responses/adfs_response_sha384.xml +46 -0
- data/test/responses/adfs_response_sha512.xml +46 -0
- data/test/responses/encrypted_new_attack.xml.base64 +1 -0
- data/test/responses/logoutresponse_fixtures.rb +67 -0
- data/test/responses/no_signature_ns.xml +48 -0
- data/test/responses/open_saml_response.xml +56 -0
- data/test/responses/r1_response6.xml.base64 +1 -0
- data/test/responses/response1.xml.base64 +1 -0
- data/test/responses/response2.xml.base64 +79 -0
- data/test/responses/response3.xml.base64 +66 -0
- data/test/responses/response4.xml.base64 +93 -0
- data/test/responses/response5.xml.base64 +102 -0
- data/test/responses/response_eval.xml +7 -0
- data/test/responses/response_node_text_attack.xml.base64 +1 -0
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -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_multiple_attribute_statements.xml +72 -0
- data/test/responses/response_with_multiple_attribute_values.xml +67 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/simple_saml_php.xml +71 -0
- data/test/responses/starfield_response.xml.base64 +1 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/responses/wrapped_response_2.xml.base64 +150 -0
- data/test/settings_test.rb +47 -0
- data/test/slo_logoutresponse_test.rb +226 -0
- data/test/test_helper.rb +155 -0
- data/test/utils_test.rb +41 -0
- data/test/xml_security_test.rb +158 -0
- metadata +178 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "responses/logoutresponse_fixtures"))
|
3
|
+
|
4
|
+
class LogoutResponseTest < Minitest::Test
|
5
|
+
|
6
|
+
describe "Logoutresponse" do
|
7
|
+
|
8
|
+
describe "#new" do
|
9
|
+
it "raise an exception when response is initialized with nil" do
|
10
|
+
assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
|
11
|
+
end
|
12
|
+
it "default to empty settings" do
|
13
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new( valid_response)
|
14
|
+
assert logoutresponse.settings.nil?
|
15
|
+
end
|
16
|
+
it "accept constructor-injected settings" do
|
17
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings)
|
18
|
+
assert !logoutresponse.settings.nil?
|
19
|
+
end
|
20
|
+
it "accept constructor-injected options" do
|
21
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} )
|
22
|
+
assert !logoutresponse.options.empty?
|
23
|
+
end
|
24
|
+
it "support base64 encoded responses" do
|
25
|
+
expected_response = valid_response
|
26
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings)
|
27
|
+
|
28
|
+
assert_equal expected_response, logoutresponse.response
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#validate" do
|
33
|
+
it "validate the response" do
|
34
|
+
in_relation_to_request_id = random_id
|
35
|
+
|
36
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
|
37
|
+
|
38
|
+
assert logoutresponse.validate
|
39
|
+
|
40
|
+
assert_equal settings.sp_entity_id, logoutresponse.issuer
|
41
|
+
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
42
|
+
|
43
|
+
assert logoutresponse.success?
|
44
|
+
end
|
45
|
+
|
46
|
+
it "invalidate responses with wrong id when given option :matches_uuid" do
|
47
|
+
|
48
|
+
expected_request_id = "_some_other_expected_uuid"
|
49
|
+
opts = { :matches_request_id => expected_request_id}
|
50
|
+
|
51
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
|
52
|
+
|
53
|
+
assert !logoutresponse.validate
|
54
|
+
assert expected_request_id != logoutresponse.in_response_to
|
55
|
+
end
|
56
|
+
|
57
|
+
it "invalidate responses with wrong request status" do
|
58
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
|
59
|
+
|
60
|
+
assert !logoutresponse.validate
|
61
|
+
assert !logoutresponse.success?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#validate!" do
|
66
|
+
it "validates good responses" do
|
67
|
+
in_relation_to_request_id = random_id
|
68
|
+
|
69
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
|
70
|
+
|
71
|
+
logoutresponse.validate!
|
72
|
+
end
|
73
|
+
|
74
|
+
it "raises validation error when matching for wrong request id" do
|
75
|
+
|
76
|
+
expected_request_id = "_some_other_expected_id"
|
77
|
+
opts = { :matches_request_id => expected_request_id}
|
78
|
+
|
79
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
|
80
|
+
|
81
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
82
|
+
end
|
83
|
+
|
84
|
+
it "raise validation error for wrong request status" do
|
85
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
|
86
|
+
|
87
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
88
|
+
end
|
89
|
+
|
90
|
+
it "raise validation error when in bad state" do
|
91
|
+
# no settings
|
92
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response)
|
93
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
94
|
+
end
|
95
|
+
|
96
|
+
it "raise validation error when in lack of sp_entity_id setting" do
|
97
|
+
bad_settings = settings
|
98
|
+
bad_settings.issuer = nil
|
99
|
+
bad_settings.sp_entity_id = nil
|
100
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, bad_settings)
|
101
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
102
|
+
end
|
103
|
+
|
104
|
+
it "raise error for invalid xml" do
|
105
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings)
|
106
|
+
|
107
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
|
3
|
+
class RequestTest < Minitest::Test
|
4
|
+
|
5
|
+
describe "Authrequest" do
|
6
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
7
|
+
|
8
|
+
before do
|
9
|
+
settings.idp_sso_target_url = "http://example.com"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "create the deflated SAMLRequest URL parameter" do
|
13
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
14
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
15
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
16
|
+
decoded = Base64.decode64(payload)
|
17
|
+
|
18
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
19
|
+
inflated = zstream.inflate(decoded)
|
20
|
+
zstream.finish
|
21
|
+
zstream.close
|
22
|
+
|
23
|
+
assert_match /^<samlp:AuthnRequest/, inflated
|
24
|
+
end
|
25
|
+
|
26
|
+
it "create the deflated SAMLRequest URL parameter including the Destination" do
|
27
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
28
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
29
|
+
decoded = Base64.decode64(payload)
|
30
|
+
|
31
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
32
|
+
inflated = zstream.inflate(decoded)
|
33
|
+
zstream.finish
|
34
|
+
zstream.close
|
35
|
+
|
36
|
+
assert_match /<samlp:AuthnRequest[^<]* Destination='http:\/\/example.com'/, inflated
|
37
|
+
end
|
38
|
+
|
39
|
+
it "create the SAMLRequest URL parameter without deflating" do
|
40
|
+
settings.compress_request = false
|
41
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
42
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
43
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
44
|
+
decoded = Base64.decode64(payload)
|
45
|
+
|
46
|
+
assert_match /^<samlp:AuthnRequest/, decoded
|
47
|
+
end
|
48
|
+
|
49
|
+
it "create the SAMLRequest URL parameter with IsPassive" do
|
50
|
+
settings.passive = true
|
51
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
52
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
53
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
54
|
+
decoded = Base64.decode64(payload)
|
55
|
+
|
56
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
57
|
+
inflated = zstream.inflate(decoded)
|
58
|
+
zstream.finish
|
59
|
+
zstream.close
|
60
|
+
|
61
|
+
assert_match /<samlp:AuthnRequest[^<]* IsPassive='true'/, inflated
|
62
|
+
end
|
63
|
+
|
64
|
+
it "create the SAMLRequest URL parameter with ProtocolBinding" do
|
65
|
+
settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
66
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
67
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
68
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
69
|
+
decoded = Base64.decode64(payload)
|
70
|
+
|
71
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
72
|
+
inflated = zstream.inflate(decoded)
|
73
|
+
zstream.finish
|
74
|
+
zstream.close
|
75
|
+
|
76
|
+
assert_match /<samlp:AuthnRequest[^<]* ProtocolBinding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'/, inflated
|
77
|
+
end
|
78
|
+
|
79
|
+
it "create the SAMLRequest URL parameter with ForceAuthn" do
|
80
|
+
settings.force_authn = true
|
81
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
82
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
83
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
84
|
+
decoded = Base64.decode64(payload)
|
85
|
+
|
86
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
87
|
+
inflated = zstream.inflate(decoded)
|
88
|
+
zstream.finish
|
89
|
+
zstream.close
|
90
|
+
assert_match /<samlp:AuthnRequest[^<]* ForceAuthn='true'/, inflated
|
91
|
+
end
|
92
|
+
|
93
|
+
it "create the SAMLRequest URL parameter with NameID Format" do
|
94
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
95
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
96
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
97
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
98
|
+
decoded = Base64.decode64(payload)
|
99
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
100
|
+
inflated = zstream.inflate(decoded)
|
101
|
+
zstream.finish
|
102
|
+
zstream.close
|
103
|
+
|
104
|
+
assert_match /<samlp:NameIDPolicy[^<]* AllowCreate='true'/, inflated
|
105
|
+
assert_match /<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated
|
106
|
+
end
|
107
|
+
|
108
|
+
it "create the SAMLRequest URL parameter with Subject" do
|
109
|
+
settings.name_identifier_value_requested = "testuser@example.com"
|
110
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
111
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
112
|
+
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
113
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
114
|
+
decoded = Base64.decode64(payload)
|
115
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
116
|
+
inflated = zstream.inflate(decoded)
|
117
|
+
zstream.finish
|
118
|
+
zstream.close
|
119
|
+
|
120
|
+
assert inflated.include?('<saml:Subject>')
|
121
|
+
assert inflated.include?("<saml:NameID Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'>testuser@example.com</saml:NameID>")
|
122
|
+
assert inflated.include?("<saml:SubjectConfirmation Method='urn:oasis:names:tc:SAML:2.0:cm:bearer'/>")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "accept extra parameters" do
|
126
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" })
|
127
|
+
assert auth_url =~ /&hello=there$/
|
128
|
+
|
129
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => nil })
|
130
|
+
assert auth_url =~ /&hello=$/
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "when the target url doesn't contain a query string" do
|
134
|
+
it "create the SAMLRequest parameter correctly" do
|
135
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
136
|
+
assert auth_url =~ /^http:\/\/example.com\?SAMLRequest/
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "when the target url contains a query string" do
|
141
|
+
it "create the SAMLRequest parameter correctly" do
|
142
|
+
settings.idp_sso_target_url = "http://example.com?field=value"
|
143
|
+
|
144
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
145
|
+
assert auth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#create_params when the settings indicate to sign (embebed) the request" do
|
150
|
+
before do
|
151
|
+
settings.compress_request = false
|
152
|
+
settings.idp_sso_target_url = "http://example.com?field=value"
|
153
|
+
settings.security[:authn_requests_signed] = true
|
154
|
+
settings.security[:embed_sign] = true
|
155
|
+
settings.certificate = ruby_saml_cert_text
|
156
|
+
settings.private_key = ruby_saml_key_text
|
157
|
+
end
|
158
|
+
|
159
|
+
it "create a signed request" do
|
160
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
161
|
+
request_xml = Base64.decode64(params["SAMLRequest"])
|
162
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
163
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], request_xml
|
164
|
+
end
|
165
|
+
|
166
|
+
it "create a signed request with 256 digest and signature methods" do
|
167
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
168
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
169
|
+
|
170
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
171
|
+
|
172
|
+
request_xml = Base64.decode64(params["SAMLRequest"])
|
173
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
174
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
|
175
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha512'/>], request_xml
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "#create_params when the settings indicate to sign the request" do
|
180
|
+
let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
181
|
+
|
182
|
+
before do
|
183
|
+
settings.compress_request = false
|
184
|
+
settings.idp_sso_target_url = "http://example.com?field=value"
|
185
|
+
settings.security[:authn_requests_signed] = true
|
186
|
+
settings.security[:embed_sign] = false
|
187
|
+
settings.certificate = ruby_saml_cert_text
|
188
|
+
settings.private_key = ruby_saml_key_text
|
189
|
+
end
|
190
|
+
|
191
|
+
it "create a signature parameter with RSA_SHA1 and validate it" do
|
192
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
193
|
+
|
194
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
195
|
+
assert params['SAMLRequest']
|
196
|
+
assert params[:RelayState]
|
197
|
+
assert params['Signature']
|
198
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
|
199
|
+
|
200
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
201
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
202
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
203
|
+
|
204
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
205
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
206
|
+
|
207
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "create a signature parameter with RSA_SHA256 and validate it" do
|
211
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
212
|
+
|
213
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
214
|
+
assert params['Signature']
|
215
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
|
216
|
+
|
217
|
+
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
218
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
219
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
220
|
+
|
221
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
222
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
223
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
@@ -0,0 +1,475 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
|
3
|
+
class ResponseTest < Minitest::Test
|
4
|
+
|
5
|
+
describe "Response" do
|
6
|
+
it "raise an exception when response is initialized with nil" do
|
7
|
+
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
|
8
|
+
end
|
9
|
+
|
10
|
+
it "be able to parse a document which contains ampersands" do
|
11
|
+
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
12
|
+
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
13
|
+
|
14
|
+
response = OneLogin::RubySaml::Response.new(ampersands_response)
|
15
|
+
settings = OneLogin::RubySaml::Settings.new
|
16
|
+
settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
|
17
|
+
response.settings = settings
|
18
|
+
response.validate!
|
19
|
+
end
|
20
|
+
|
21
|
+
it "adapt namespace" do
|
22
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
23
|
+
assert !response.name_id.nil?
|
24
|
+
response = OneLogin::RubySaml::Response.new(response_document_2)
|
25
|
+
assert !response.name_id.nil?
|
26
|
+
response = OneLogin::RubySaml::Response.new(response_document_3)
|
27
|
+
assert !response.name_id.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
it "default to raw input when a response is not Base64 encoded" do
|
31
|
+
decoded = Base64.decode64(response_document_2)
|
32
|
+
response = OneLogin::RubySaml::Response.new(decoded)
|
33
|
+
assert response.document
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "Assertion" do
|
37
|
+
it "only retreive an assertion with an ID that matches the signature's reference URI" do
|
38
|
+
response = OneLogin::RubySaml::Response.new(wrapped_response_2)
|
39
|
+
response.stubs(:conditions).returns(nil)
|
40
|
+
settings = OneLogin::RubySaml::Settings.new
|
41
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
42
|
+
response.settings = settings
|
43
|
+
assert response.name_id.nil?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#validate!" do
|
48
|
+
it "raise when encountering a condition that prevents the document from being valid" do
|
49
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
50
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
51
|
+
response.validate!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#is_valid?" do
|
57
|
+
it "return false when response is initialized with blank data" do
|
58
|
+
response = OneLogin::RubySaml::Response.new('')
|
59
|
+
assert !response.is_valid?
|
60
|
+
end
|
61
|
+
|
62
|
+
it "return false if settings have not been set" do
|
63
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
64
|
+
assert !response.is_valid?
|
65
|
+
end
|
66
|
+
|
67
|
+
it "return true when the response is initialized with valid data" do
|
68
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
69
|
+
response.stubs(:conditions).returns(nil)
|
70
|
+
assert !response.is_valid?
|
71
|
+
settings = OneLogin::RubySaml::Settings.new
|
72
|
+
assert !response.is_valid?
|
73
|
+
response.settings = settings
|
74
|
+
assert !response.is_valid?
|
75
|
+
response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
76
|
+
response.validate!
|
77
|
+
end
|
78
|
+
|
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
|
+
response.stubs(:conditions).returns(nil)
|
82
|
+
settings = OneLogin::RubySaml::Settings.new
|
83
|
+
response.settings = settings
|
84
|
+
assert !response.is_valid?
|
85
|
+
assert !response.is_valid?
|
86
|
+
end
|
87
|
+
|
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
|
+
response.stubs(:conditions).returns(nil)
|
91
|
+
settings = OneLogin::RubySaml::Settings.new
|
92
|
+
response.settings = settings
|
93
|
+
response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
94
|
+
assert response.is_valid?
|
95
|
+
assert response.is_valid?
|
96
|
+
end
|
97
|
+
|
98
|
+
it "return true when using certificate instead of fingerprint" do
|
99
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
100
|
+
response.stubs(:conditions).returns(nil)
|
101
|
+
settings = OneLogin::RubySaml::Settings.new
|
102
|
+
response.settings = settings
|
103
|
+
settings.idp_cert = valid_cert
|
104
|
+
assert response.is_valid?
|
105
|
+
end
|
106
|
+
|
107
|
+
it "not allow signature wrapping attack" do
|
108
|
+
response = OneLogin::RubySaml::Response.new(response_document_4)
|
109
|
+
response.stubs(:conditions).returns(nil)
|
110
|
+
settings = OneLogin::RubySaml::Settings.new
|
111
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
112
|
+
response.settings = settings
|
113
|
+
assert !response.is_valid?
|
114
|
+
assert response.name_id == "test@onelogin.com"
|
115
|
+
end
|
116
|
+
|
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
|
130
|
+
response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
|
131
|
+
response.stubs(:conditions).returns(nil)
|
132
|
+
settings = OneLogin::RubySaml::Settings.new
|
133
|
+
response.settings = settings
|
134
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
135
|
+
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
136
|
+
assert response.validate!
|
137
|
+
end
|
138
|
+
|
139
|
+
it "validate ADFS assertions" do
|
140
|
+
response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
141
|
+
response.stubs(:conditions).returns(nil)
|
142
|
+
settings = OneLogin::RubySaml::Settings.new
|
143
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
144
|
+
response.settings = settings
|
145
|
+
assert response.validate!
|
146
|
+
end
|
147
|
+
|
148
|
+
it "validate the digest" do
|
149
|
+
response = OneLogin::RubySaml::Response.new(r1_response_document_6)
|
150
|
+
response.stubs(:conditions).returns(nil)
|
151
|
+
settings = OneLogin::RubySaml::Settings.new
|
152
|
+
settings.idp_cert = Base64.decode64(r1_signature_2)
|
153
|
+
response.settings = settings
|
154
|
+
assert response.validate!
|
155
|
+
end
|
156
|
+
|
157
|
+
it "validate SAML 2.0 XML structure" do
|
158
|
+
resp_xml = Base64.decode64(response_document_valid_signed).gsub(/emailAddress/,'test')
|
159
|
+
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
160
|
+
response.stubs(:conditions).returns(nil)
|
161
|
+
settings = OneLogin::RubySaml::Settings.new
|
162
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
163
|
+
response.settings = settings
|
164
|
+
assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response.validate! }
|
165
|
+
end
|
166
|
+
|
167
|
+
it "Prevent node text with comment (VU#475445) attack" do
|
168
|
+
response_doc = File.read(File.join(File.dirname(__FILE__), "responses", 'response_node_text_attack.xml.base64'))
|
169
|
+
response = OneLogin::RubySaml::Response.new(response_doc)
|
170
|
+
|
171
|
+
assert_equal "support@onelogin.com", response.name_id
|
172
|
+
assert_equal "smith", response.attributes["surname"]
|
173
|
+
end
|
174
|
+
|
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)
|
178
|
+
response.stubs(:conditions).returns(nil)
|
179
|
+
settings = OneLogin::RubySaml::Settings.new
|
180
|
+
response.settings = settings
|
181
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
182
|
+
assert response.is_valid?
|
183
|
+
settings.sp_entity_id = ''
|
184
|
+
assert response.is_valid?
|
185
|
+
end
|
186
|
+
|
187
|
+
it "return false when sp_entity_id set to incorrectly" do
|
188
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
189
|
+
response.stubs(:conditions).returns(nil)
|
190
|
+
settings = OneLogin::RubySaml::Settings.new
|
191
|
+
response.settings = settings
|
192
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
193
|
+
settings.sp_entity_id = 'wrong_audience'
|
194
|
+
assert !response.is_valid?
|
195
|
+
end
|
196
|
+
|
197
|
+
it "return true when sp_entity_id set to correctly" do
|
198
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
199
|
+
response.stubs(:conditions).returns(nil)
|
200
|
+
settings = OneLogin::RubySaml::Settings.new
|
201
|
+
response.settings = settings
|
202
|
+
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
203
|
+
settings.sp_entity_id = 'https://someone.example.com/audience'
|
204
|
+
assert response.is_valid?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "#name_id" do
|
210
|
+
it "extract the value of the name id element" do
|
211
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
212
|
+
assert_equal "support@onelogin.com", response.name_id
|
213
|
+
|
214
|
+
response = OneLogin::RubySaml::Response.new(response_document_3)
|
215
|
+
assert_equal "someone@example.com", response.name_id
|
216
|
+
end
|
217
|
+
|
218
|
+
it "be extractable from an OpenSAML response" do
|
219
|
+
response = OneLogin::RubySaml::Response.new(fixture(:open_saml))
|
220
|
+
assert_equal "someone@example.org", response.name_id
|
221
|
+
end
|
222
|
+
|
223
|
+
it "be extractable from a Simple SAML PHP response" do
|
224
|
+
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
225
|
+
assert_equal "someone@example.com", response.name_id
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#check_conditions" do
|
230
|
+
it "check time conditions" do
|
231
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
232
|
+
assert !response.send(:validate_conditions, true)
|
233
|
+
response = OneLogin::RubySaml::Response.new(response_document_6)
|
234
|
+
assert response.send(:validate_conditions, true)
|
235
|
+
time = Time.parse("2011-06-14T18:25:01.516Z")
|
236
|
+
Time.stubs(:now).returns(time)
|
237
|
+
response = OneLogin::RubySaml::Response.new(response_document_5)
|
238
|
+
assert response.send(:validate_conditions, true)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "optionally allow for clock drift" do
|
242
|
+
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
|
243
|
+
expected_time = Time.parse("2011-06-14T18:21:01Z")
|
244
|
+
Time.stubs(:now).returns(expected_time)
|
245
|
+
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.515)
|
246
|
+
assert !response.send(:validate_conditions, true)
|
247
|
+
|
248
|
+
expected_time = Time.parse("2011-06-14T18:21:01Z")
|
249
|
+
Time.stubs(:now).returns(expected_time)
|
250
|
+
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.516)
|
251
|
+
assert response.send(:validate_conditions, true)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "#attributes" do
|
256
|
+
before do
|
257
|
+
@response = OneLogin::RubySaml::Response.new(response_document)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "extract the first attribute in a hash accessed via its symbol" do
|
261
|
+
assert_equal "demo", @response.attributes[:uid]
|
262
|
+
end
|
263
|
+
|
264
|
+
it "extract the first attribute in a hash accessed via its name" do
|
265
|
+
assert_equal "demo", @response.attributes["uid"]
|
266
|
+
end
|
267
|
+
|
268
|
+
it "extract all attributes" do
|
269
|
+
assert_equal "demo", @response.attributes[:uid]
|
270
|
+
assert_equal "value", @response.attributes[:another_value]
|
271
|
+
end
|
272
|
+
|
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"]
|
276
|
+
end
|
277
|
+
|
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
|
284
|
+
assert_equal "smith", response_with_multiple_attribute_statements.attributes[:surname]
|
285
|
+
assert_equal "bob", response_with_multiple_attribute_statements.attributes[:firstname]
|
286
|
+
end
|
287
|
+
|
288
|
+
it "be manipulable by hash methods such as #merge and not raise an exception" do
|
289
|
+
@response.attributes.merge({ :testing_attribute => "test" })
|
290
|
+
end
|
291
|
+
|
292
|
+
it "be manipulable by hash methods such as #shift and not raise an exception" do
|
293
|
+
@response.attributes.shift
|
294
|
+
end
|
295
|
+
|
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]
|
299
|
+
end
|
300
|
+
|
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]]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe "#session_expires_at" do
|
308
|
+
it "extract the value of the SessionNotOnOrAfter attribute" do
|
309
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
310
|
+
assert response.session_expires_at.is_a?(Time)
|
311
|
+
|
312
|
+
response = OneLogin::RubySaml::Response.new(response_document_2)
|
313
|
+
assert response.session_expires_at.nil?
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
describe "#issuer" do
|
318
|
+
it "return the issuer inside the response assertion" do
|
319
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
320
|
+
assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
|
321
|
+
end
|
322
|
+
|
323
|
+
it "return the issuer inside the response" do
|
324
|
+
response = OneLogin::RubySaml::Response.new(response_document_2)
|
325
|
+
assert_equal "wibble", response.issuer
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
describe "#success" do
|
330
|
+
it "find a status code that says success" do
|
331
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
332
|
+
assert response.send(:success?)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe '#xpath_first_from_signed_assertion' do
|
337
|
+
it 'not allow arbitrary code execution' do
|
338
|
+
malicious_response_document = fixture('response_eval', false)
|
339
|
+
response = OneLogin::RubySaml::Response.new(malicious_response_document)
|
340
|
+
response.send(:xpath_first_from_signed_assertion)
|
341
|
+
assert_nil $evalled
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe "#multiple values" do
|
346
|
+
it "extract single value as string" do
|
347
|
+
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
|
348
|
+
end
|
349
|
+
|
350
|
+
it "extract single value as string in compatibility mode off" do
|
351
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
352
|
+
assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
|
353
|
+
# classes are not reloaded between tests so restore default
|
354
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
355
|
+
end
|
356
|
+
|
357
|
+
it "extract first of multiple values as string for b/w compatibility" do
|
358
|
+
assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
|
359
|
+
end
|
360
|
+
|
361
|
+
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
362
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
363
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
|
364
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
365
|
+
end
|
366
|
+
|
367
|
+
it "return array with all attributes when asked in XML order" do
|
368
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
369
|
+
end
|
370
|
+
|
371
|
+
it "return array with all attributes when asked in XML order in compatibility mode off" do
|
372
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
373
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
374
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
375
|
+
end
|
376
|
+
|
377
|
+
it "return first of multiple values when multiple Attribute tags in XML" do
|
378
|
+
assert_equal 'role1', response_multiple_attr_values.attributes[:role]
|
379
|
+
end
|
380
|
+
|
381
|
+
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
382
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
383
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
|
384
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
385
|
+
end
|
386
|
+
|
387
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
388
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
389
|
+
end
|
390
|
+
|
391
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
392
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
393
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
394
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
395
|
+
end
|
396
|
+
|
397
|
+
it "return all of multiple values when multiple Attribute tags in multiple AttributeStatement tags" do
|
398
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
399
|
+
assert_equal ['role1', 'role2', 'role3'], response_with_multiple_attribute_statements.attributes.multi(:role)
|
400
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
401
|
+
end
|
402
|
+
|
403
|
+
it "return nil value correctly" do
|
404
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
405
|
+
end
|
406
|
+
|
407
|
+
it "return nil value correctly when not in compatibility mode off" do
|
408
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
409
|
+
assert [nil] == response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
410
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
411
|
+
end
|
412
|
+
|
413
|
+
it "return multiple values including nil and empty string" do
|
414
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
415
|
+
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
|
416
|
+
end
|
417
|
+
|
418
|
+
it "return multiple values from [] when not in compatibility mode off" do
|
419
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
420
|
+
assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
|
421
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
422
|
+
end
|
423
|
+
|
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)
|
428
|
+
|
429
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
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)
|
433
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
434
|
+
end
|
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
|
474
|
+
end
|
475
|
+
end
|