ruby-saml 0.8.11 → 0.8.16

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