ruby-saml 0.8.11 → 0.8.16

Sign up to get free protection for your applications and to get access to all the features.

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