ruby-saml 0.8.9 → 0.8.14

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.

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