kl-ruby-saml 0.0.1
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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +14 -0
- data/.travis.yml +17 -0
- data/Gemfile +9 -0
- data/LICENSE +19 -0
- data/README.md +575 -0
- data/Rakefile +41 -0
- data/changelog.md +75 -0
- data/gemfiles/nokogiri-1.5.gemfile +5 -0
- data/lib/onelogin/ruby-saml.rb +17 -0
- data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
- data/lib/onelogin/ruby-saml/attributes.rb +128 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +156 -0
- data/lib/onelogin/ruby-saml/http_error.rb +7 -0
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
- data/lib/onelogin/ruby-saml/logging.rb +30 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
- data/lib/onelogin/ruby-saml/metadata.rb +123 -0
- data/lib/onelogin/ruby-saml/response.rb +722 -0
- data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
- data/lib/onelogin/ruby-saml/settings.rb +165 -0
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
- data/lib/onelogin/ruby-saml/utils.rb +172 -0
- data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
- data/lib/onelogin/ruby-saml/version.rb +5 -0
- data/lib/ruby-saml.rb +1 -0
- data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
- data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/xenc-schema.xsd +136 -0
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/xmldsig-core-schema.xsd +309 -0
- data/lib/xml_security.rb +358 -0
- data/ruby-saml.gemspec +57 -0
- data/test/certificates/certificate1 +12 -0
- data/test/certificates/certificate_without_head_foot +1 -0
- data/test/certificates/formatted_certificate +14 -0
- data/test/certificates/formatted_private_key +12 -0
- data/test/certificates/formatted_rsa_private_key +12 -0
- data/test/certificates/invalid_certificate1 +1 -0
- data/test/certificates/invalid_certificate2 +1 -0
- data/test/certificates/invalid_certificate3 +12 -0
- data/test/certificates/invalid_private_key1 +1 -0
- data/test/certificates/invalid_private_key2 +1 -0
- data/test/certificates/invalid_private_key3 +10 -0
- data/test/certificates/invalid_rsa_private_key1 +1 -0
- data/test/certificates/invalid_rsa_private_key2 +1 -0
- data/test/certificates/invalid_rsa_private_key3 +10 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/idp_metadata_parser_test.rb +95 -0
- data/test/logging_test.rb +62 -0
- data/test/logout_requests/invalid_slo_request.xml +6 -0
- data/test/logout_requests/slo_request.xml +4 -0
- data/test/logout_requests/slo_request.xml.base64 +1 -0
- data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
- data/test/logout_requests/slo_request_with_session_index.xml +5 -0
- data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
- data/test/logoutrequest_test.rb +211 -0
- data/test/logoutresponse_test.rb +258 -0
- data/test/metadata_test.rb +203 -0
- data/test/request_test.rb +282 -0
- data/test/response_test.rb +1094 -0
- data/test/responses/adfs_response_sha1.xml +46 -0
- data/test/responses/adfs_response_sha256.xml +46 -0
- data/test/responses/adfs_response_sha384.xml +46 -0
- data/test/responses/adfs_response_sha512.xml +46 -0
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/attackxee.xml +13 -0
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
- data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
- data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_id.xml.base64 +1 -0
- data/test/responses/invalids/no_saml2.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/no_status.xml.base64 +1 -0
- data/test/responses/invalids/no_status_code.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
- data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
- data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
- data/test/responses/no_signature_ns.xml +48 -0
- data/test/responses/open_saml_response.xml +56 -0
- data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
- data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
- data/test/responses/response_eval.xml +7 -0
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_unsigned_xml_base64 +1 -0
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -0
- data/test/responses/response_with_multiple_attribute_values.xml +67 -0
- data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
- data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
- data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_without_attributes.xml.base64 +79 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/simple_saml_php.xml +71 -0
- data/test/responses/starfield_response.xml.base64 +1 -0
- data/test/responses/test_sign.xml +43 -0
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/saml_message_test.rb +56 -0
- data/test/settings_test.rb +218 -0
- data/test/slo_logoutrequest_test.rb +275 -0
- data/test/slo_logoutresponse_test.rb +185 -0
- data/test/test_helper.rb +252 -0
- data/test/utils_test.rb +145 -0
- data/test/xml_security_test.rb +329 -0
- metadata +415 -0
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
|
+
|
|
3
|
+
require 'onelogin/ruby-saml/response'
|
|
4
|
+
|
|
5
|
+
class RubySamlTest < Minitest::Test
|
|
6
|
+
|
|
7
|
+
describe "Response" do
|
|
8
|
+
|
|
9
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
|
10
|
+
let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
|
|
11
|
+
let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
|
|
12
|
+
let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
|
|
13
|
+
let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
|
|
14
|
+
let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
|
|
15
|
+
let(:response_multiple_attr_values) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) }
|
|
16
|
+
let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
|
|
17
|
+
let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) }
|
|
18
|
+
let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
|
|
19
|
+
let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
|
|
20
|
+
let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) }
|
|
21
|
+
let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) }
|
|
22
|
+
let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) }
|
|
23
|
+
let(:response_statuscode_responder_and_msg) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responer_and_msg.xml.base64")) }
|
|
24
|
+
let(:response_encrypted_attrs) { OneLogin::RubySaml::Response.new(read_invalid_response("response_encrypted_attrs.xml.base64")) }
|
|
25
|
+
let(:response_no_signed_elements) { OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) }
|
|
26
|
+
let(:response_multiple_signed) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) }
|
|
27
|
+
let(:response_invalid_audience) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64")) }
|
|
28
|
+
let(:response_invalid_signed_element) { OneLogin::RubySaml::Response.new(read_invalid_response("response_invalid_signed_element.xml.base64")) }
|
|
29
|
+
let(:response_invalid_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64")) }
|
|
30
|
+
let(:response_invalid_issuer_message) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64")) }
|
|
31
|
+
let(:response_no_subjectconfirmation_data) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_data.xml.base64")) }
|
|
32
|
+
let(:response_no_subjectconfirmation_method) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_method.xml.base64")) }
|
|
33
|
+
let(:response_invalid_subjectconfirmation_inresponse) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_inresponse.xml.base64")) }
|
|
34
|
+
let(:response_invalid_subjectconfirmation_recipient) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_recipient.xml.base64")) }
|
|
35
|
+
let(:response_invalid_subjectconfirmation_nb) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_nb.xml.base64")) }
|
|
36
|
+
let(:response_invalid_subjectconfirmation_noa) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64")) }
|
|
37
|
+
let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) }
|
|
38
|
+
let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) }
|
|
39
|
+
|
|
40
|
+
it "raise an exception when response is initialized with nil" do
|
|
41
|
+
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "be able to parse a document which contains ampersands" do
|
|
45
|
+
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
|
46
|
+
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
|
47
|
+
|
|
48
|
+
ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document)
|
|
49
|
+
ampersands_response.settings = settings
|
|
50
|
+
ampersands_response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
|
|
51
|
+
|
|
52
|
+
assert !ampersands_response.is_valid?
|
|
53
|
+
assert_includes ampersands_response.errors, "SAML Response must contain 1 assertion"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "Prevent XEE attack" do
|
|
57
|
+
before do
|
|
58
|
+
@response = OneLogin::RubySaml::Response.new(fixture(:attackxee))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "false when evil attack vector is present, soft = true" do
|
|
62
|
+
@response.soft = true
|
|
63
|
+
assert !@response.send(:validate_structure)
|
|
64
|
+
assert_includes @response.errors, "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "raise when evil attack vector is present, soft = false " do
|
|
68
|
+
@response.soft = false
|
|
69
|
+
|
|
70
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
71
|
+
@response.send(:validate_structure)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "adapt namespace" do
|
|
77
|
+
refute_nil response.nameid
|
|
78
|
+
refute_nil response_without_attributes.nameid
|
|
79
|
+
refute_nil response_with_signed_assertion.nameid
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "default to raw input when a response is not Base64 encoded" do
|
|
83
|
+
decoded = Base64.decode64(response_document_without_attributes)
|
|
84
|
+
response_from_raw = OneLogin::RubySaml::Response.new(decoded)
|
|
85
|
+
assert response_from_raw.document
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe "Assertion" do
|
|
89
|
+
it "only retreive an assertion with an ID that matches the signature's reference URI" do
|
|
90
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
|
91
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
92
|
+
response_wrapped.settings = settings
|
|
93
|
+
assert_nil response_wrapped.nameid
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "#is_valid?" do
|
|
98
|
+
describe "soft = false" do
|
|
99
|
+
|
|
100
|
+
before do
|
|
101
|
+
response.soft = false
|
|
102
|
+
response_valid_signed.soft = false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "raise when response is initialized with blank data" do
|
|
106
|
+
blank_response = OneLogin::RubySaml::Response.new('')
|
|
107
|
+
blank_response.soft = false
|
|
108
|
+
error_msg = "Blank response"
|
|
109
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
110
|
+
blank_response.is_valid?
|
|
111
|
+
end
|
|
112
|
+
assert_includes blank_response.errors, error_msg
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "raise when settings have not been set" do
|
|
116
|
+
error_msg = "No settings on response"
|
|
117
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
118
|
+
response.is_valid?
|
|
119
|
+
end
|
|
120
|
+
assert_includes response.errors, error_msg
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "raise when No fingerprint or certificate on settings" do
|
|
124
|
+
settings.idp_cert_fingerprint = nil
|
|
125
|
+
settings.idp_cert = nil
|
|
126
|
+
response.settings = settings
|
|
127
|
+
error_msg = "No fingerprint or certificate on settings"
|
|
128
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
129
|
+
response.is_valid?
|
|
130
|
+
end
|
|
131
|
+
assert_includes response.errors, error_msg
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "raise when signature wrapping attack" do
|
|
135
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
|
136
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
137
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
138
|
+
response_wrapped.settings = settings
|
|
139
|
+
assert !response_wrapped.is_valid?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "validate SAML 2.0 XML structure" do
|
|
143
|
+
resp_xml = Base64.decode64(response_document_unsigned).gsub(/emailAddress/,'test')
|
|
144
|
+
response_unsigned_mod = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
145
|
+
response_unsigned_mod.stubs(:conditions).returns(nil)
|
|
146
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
147
|
+
response_unsigned_mod.settings = settings
|
|
148
|
+
response_unsigned_mod.soft = false
|
|
149
|
+
assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch') do
|
|
150
|
+
response_unsigned_mod.is_valid?
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "raise when encountering a condition that prevents the document from being valid" do
|
|
155
|
+
settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
156
|
+
response.settings = settings
|
|
157
|
+
response.soft = false
|
|
158
|
+
error_msg = "Current time is on or after NotOnOrAfter condition"
|
|
159
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
160
|
+
response.is_valid?
|
|
161
|
+
end
|
|
162
|
+
assert_includes response.errors[0], error_msg
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "raise when encountering a SAML Response with bad formatted" do
|
|
166
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
167
|
+
response_without_attributes.settings = settings
|
|
168
|
+
response_without_attributes.soft = false
|
|
169
|
+
assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
170
|
+
response_without_attributes.is_valid?
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "raise when the inResponseTo value does not match the Request ID" do
|
|
175
|
+
settings.soft = false
|
|
176
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
177
|
+
opts = {}
|
|
178
|
+
opts[:settings] = settings
|
|
179
|
+
opts[:matches_request_id] = "invalid_request_id"
|
|
180
|
+
response_valid_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
|
|
181
|
+
error_msg = "The InResponseTo of the Response: _fc4a34b0-7efb-012e-caae-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid_request_id"
|
|
182
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
183
|
+
response_valid_signed.is_valid?
|
|
184
|
+
end
|
|
185
|
+
assert_includes response_valid_signed.errors, error_msg
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "raise when the assertion contains encrypted attributes" do
|
|
189
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
190
|
+
response_encrypted_attrs.settings = settings
|
|
191
|
+
response_encrypted_attrs.soft = false
|
|
192
|
+
error_msg = "There is an EncryptedAttribute in the Response and this SP not support them"
|
|
193
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
194
|
+
response_encrypted_attrs.is_valid?
|
|
195
|
+
end
|
|
196
|
+
assert_includes response_encrypted_attrs.errors, error_msg
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "raise when there is no valid audience" do
|
|
200
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
201
|
+
settings.issuer = 'invalid'
|
|
202
|
+
response_valid_signed.settings = settings
|
|
203
|
+
response_valid_signed.soft = false
|
|
204
|
+
error_msg = "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
|
|
205
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
206
|
+
response_valid_signed.is_valid?
|
|
207
|
+
end
|
|
208
|
+
assert_includes response_valid_signed.errors, error_msg
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "raise when no ID present in the SAML Response" do
|
|
212
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
213
|
+
response_no_id.settings = settings
|
|
214
|
+
response_no_id.soft = false
|
|
215
|
+
error_msg = "Missing ID attribute on SAML Response"
|
|
216
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
217
|
+
response_no_id.is_valid?
|
|
218
|
+
end
|
|
219
|
+
assert_includes response_no_id.errors, error_msg
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "raise when no 2.0 Version present in the SAML Response" do
|
|
223
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
224
|
+
response_no_version.settings = settings
|
|
225
|
+
response_no_version.soft = false
|
|
226
|
+
error_msg = "Unsupported SAML version"
|
|
227
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
228
|
+
response_no_version.is_valid?
|
|
229
|
+
end
|
|
230
|
+
assert_includes response_no_version.errors, error_msg
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe "soft = true" do
|
|
235
|
+
before do
|
|
236
|
+
response.soft = true
|
|
237
|
+
response_valid_signed.soft = true
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "return true when the response is initialized with valid data" do
|
|
241
|
+
response_valid_signed.stubs(:conditions).returns(nil)
|
|
242
|
+
response_valid_signed.settings = settings
|
|
243
|
+
response_valid_signed.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
244
|
+
assert response_valid_signed.is_valid?
|
|
245
|
+
assert_empty response_valid_signed.errors
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it "return true when the response is initialized with valid data and using certificate instead of fingerprint" do
|
|
249
|
+
response_valid_signed.stubs(:conditions).returns(nil)
|
|
250
|
+
response_valid_signed.settings = settings
|
|
251
|
+
response_valid_signed.settings.idp_cert = ruby_saml_cert_text
|
|
252
|
+
assert response_valid_signed.is_valid?
|
|
253
|
+
assert_empty response_valid_signed.errors
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "return false when response is initialized with blank data" do
|
|
257
|
+
blank_response = OneLogin::RubySaml::Response.new('')
|
|
258
|
+
blank_response.soft = true
|
|
259
|
+
assert !blank_response.is_valid?
|
|
260
|
+
assert_includes blank_response.errors, "Blank response"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it "return false if settings have not been set" do
|
|
264
|
+
assert !response.is_valid?
|
|
265
|
+
assert_includes response.errors, "No settings on response"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it "return false if fingerprint or certificate not been set on settings" do
|
|
269
|
+
response.settings = settings
|
|
270
|
+
assert !response.is_valid?
|
|
271
|
+
assert_includes response.errors, "No fingerprint or certificate on settings"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it "should be idempotent when the response is initialized with invalid data" do
|
|
275
|
+
response_unsigned.stubs(:conditions).returns(nil)
|
|
276
|
+
response_unsigned.settings = settings
|
|
277
|
+
assert !response_unsigned.is_valid?
|
|
278
|
+
assert !response_unsigned.is_valid?
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "should be idempotent when the response is initialized with valid data" do
|
|
282
|
+
response_valid_signed.stubs(:conditions).returns(nil)
|
|
283
|
+
response_valid_signed.settings = settings
|
|
284
|
+
response_valid_signed.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
285
|
+
assert response_valid_signed.is_valid?
|
|
286
|
+
assert response_valid_signed.is_valid?
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "not allow signature wrapping attack" do
|
|
290
|
+
response_wrapped.stubs(:conditions).returns(nil)
|
|
291
|
+
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
292
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
293
|
+
response_wrapped.settings = settings
|
|
294
|
+
assert !response_wrapped.is_valid?
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "support dynamic namespace resolution on signature elements" do
|
|
298
|
+
no_signature_response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
|
|
299
|
+
no_signature_response.stubs(:conditions).returns(nil)
|
|
300
|
+
no_signature_response.stubs(:validate_subject_confirmation).returns(true)
|
|
301
|
+
no_signature_response.settings = settings
|
|
302
|
+
no_signature_response.settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
303
|
+
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
|
304
|
+
assert no_signature_response.is_valid?
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "validate ADFS assertions" do
|
|
308
|
+
adfs_response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
|
309
|
+
adfs_response.stubs(:conditions).returns(nil)
|
|
310
|
+
adfs_response.stubs(:validate_subject_confirmation).returns(true)
|
|
311
|
+
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
312
|
+
adfs_response.settings = settings
|
|
313
|
+
adfs_response.soft = true
|
|
314
|
+
assert adfs_response.is_valid?
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it "validate SAML 2.0 XML structure" do
|
|
318
|
+
resp_xml = Base64.decode64(response_document_unsigned).gsub(/emailAddress/,'test')
|
|
319
|
+
response_unsigned_mod = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
320
|
+
response_unsigned_mod.stubs(:conditions).returns(nil)
|
|
321
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
322
|
+
response_unsigned_mod.settings = settings
|
|
323
|
+
response_unsigned_mod.soft = true
|
|
324
|
+
assert !response_unsigned_mod.is_valid?
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it "return false when encountering a condition that prevents the document from being valid" do
|
|
328
|
+
settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
329
|
+
response.settings = settings
|
|
330
|
+
error_msg = "Current time is on or after NotOnOrAfter condition"
|
|
331
|
+
assert !response.is_valid?
|
|
332
|
+
assert_includes response.errors[0], "Current time is on or after NotOnOrAfter condition"
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "return false when encountering a SAML Response with bad formatted" do
|
|
336
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
337
|
+
response_without_attributes.settings = settings
|
|
338
|
+
response_without_attributes.soft = true
|
|
339
|
+
error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
340
|
+
response_without_attributes.is_valid?
|
|
341
|
+
assert_includes response_without_attributes.errors, error_msg
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it "return false when the inResponseTo value does not match the Request ID" do
|
|
345
|
+
settings.soft = true
|
|
346
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
347
|
+
opts = {}
|
|
348
|
+
opts[:settings] = settings
|
|
349
|
+
opts[:matches_request_id] = "invalid_request_id"
|
|
350
|
+
response_valid_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
|
|
351
|
+
response_valid_signed.is_valid?
|
|
352
|
+
assert_includes response_valid_signed.errors, "The InResponseTo of the Response: _fc4a34b0-7efb-012e-caae-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid_request_id"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "return false when the assertion contains encrypted attributes" do
|
|
356
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
357
|
+
response_encrypted_attrs.settings = settings
|
|
358
|
+
response_encrypted_attrs.soft = true
|
|
359
|
+
response_encrypted_attrs.is_valid?
|
|
360
|
+
assert_includes response_encrypted_attrs.errors, "There is an EncryptedAttribute in the Response and this SP not support them"
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "return false when there is no valid audience" do
|
|
364
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
365
|
+
settings.issuer = 'invalid'
|
|
366
|
+
response_valid_signed.settings = settings
|
|
367
|
+
response_valid_signed.is_valid?
|
|
368
|
+
assert_includes response_valid_signed.errors, "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it "return false when no ID present in the SAML Response" do
|
|
372
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
373
|
+
response_no_id.settings = settings
|
|
374
|
+
response_no_id.soft = true
|
|
375
|
+
response_no_id.is_valid?
|
|
376
|
+
assert_includes response_no_id.errors, "Missing ID attribute on SAML Response"
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "return false when no 2.0 Version present in the SAML Response" do
|
|
380
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
381
|
+
response_no_version.settings = settings
|
|
382
|
+
response_no_version.soft = true
|
|
383
|
+
error_msg = "Unsupported SAML version"
|
|
384
|
+
response_no_version.is_valid?
|
|
385
|
+
assert_includes response_no_version.errors, "Unsupported SAML version"
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
describe "#validate_audience" do
|
|
391
|
+
it "return true when the audience is valid" do
|
|
392
|
+
response.settings = settings
|
|
393
|
+
response.settings.issuer = '{audience}'
|
|
394
|
+
assert response.send(:validate_audience)
|
|
395
|
+
assert_empty response.errors
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
it "return false when the audience is valid" do
|
|
399
|
+
response.settings = settings
|
|
400
|
+
response.settings.issuer = 'invalid_audience'
|
|
401
|
+
assert !response.send(:validate_audience)
|
|
402
|
+
assert_includes response.errors, "#{response.settings.issuer} is not a valid audience for this Response"
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
describe "#validate_issuer" do
|
|
407
|
+
it "return true when the issuer of the Message/Assertion matches the IdP entityId" do
|
|
408
|
+
response_valid_signed.settings = settings
|
|
409
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
410
|
+
|
|
411
|
+
response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
|
|
412
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it "return false when the issuer of the Message does not match the IdP entityId" do
|
|
416
|
+
response_invalid_issuer_message.settings = settings
|
|
417
|
+
response_invalid_issuer_message.settings.idp_entity_id = 'http://idp.example.com/'
|
|
418
|
+
assert !response_invalid_issuer_message.send(:validate_issuer)
|
|
419
|
+
assert_includes response_invalid_issuer_message.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_message.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it "return false when the issuer of the Assertion does not match the IdP entityId" do
|
|
423
|
+
response_invalid_issuer_assertion.settings = settings
|
|
424
|
+
response_invalid_issuer_assertion.settings.idp_entity_id = 'http://idp.example.com/'
|
|
425
|
+
assert !response_invalid_issuer_assertion.send(:validate_issuer)
|
|
426
|
+
assert_includes response_invalid_issuer_assertion.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_assertion.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
describe "#validate_num_assertion" do
|
|
431
|
+
it "return true when SAML Response contains 1 assertion" do
|
|
432
|
+
assert response.send(:validate_num_assertion)
|
|
433
|
+
assert_empty response.errors
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
it "return false when no 2.0 Version present in the SAML Response" do
|
|
437
|
+
assert !response_multi_assertion.send(:validate_num_assertion)
|
|
438
|
+
assert_includes response_multi_assertion.errors, "SAML Response must contain 1 assertion"
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
describe "validate_success_status" do
|
|
443
|
+
it "return true when the status is 'Success'" do
|
|
444
|
+
assert response.send(:validate_success_status)
|
|
445
|
+
assert_empty response.errors
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
it "return false when the status if no Status provided" do
|
|
449
|
+
assert !response_no_status.send(:validate_success_status)
|
|
450
|
+
assert_includes response_no_status.errors, "The status code of the Response was not Success"
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it "return false when the status if no StatusCode provided" do
|
|
454
|
+
assert !response_no_statuscode.send(:validate_success_status)
|
|
455
|
+
assert_includes response_no_statuscode.errors, "The status code of the Response was not Success"
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
it "return false when the status is not 'Success'" do
|
|
459
|
+
assert !response_statuscode_responder.send(:validate_success_status)
|
|
460
|
+
assert_includes response_statuscode_responder.errors, "The status code of the Response was not Success, was Responder"
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
it "return false when the status is not 'Success', and shows the StatusMessage" do
|
|
464
|
+
assert !response_statuscode_responder_and_msg.send(:validate_success_status)
|
|
465
|
+
assert_includes response_statuscode_responder_and_msg.errors, "The status code of the Response was not Success, was Responder -> something_is_wrong"
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
describe "#validate_structure" do
|
|
470
|
+
it "return true when encountering a wellformed SAML Response" do
|
|
471
|
+
assert response.send(:validate_structure)
|
|
472
|
+
assert_empty response.errors
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
it "return false when encountering a mailformed element that prevents the document from being valid" do
|
|
476
|
+
response_without_attributes.soft = true
|
|
477
|
+
response_without_attributes.send(:validate_structure)
|
|
478
|
+
assert response_without_attributes.errors.include? "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
it "raise when encountering a mailformed element that prevents the document from being valid" do
|
|
482
|
+
response_without_attributes.soft = false
|
|
483
|
+
assert_raises(OneLogin::RubySaml::ValidationError) {
|
|
484
|
+
response_without_attributes.send(:validate_structure)
|
|
485
|
+
}
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
describe "#validate_in_response_to" do
|
|
490
|
+
it "return true when the inResponseTo value matches the Request ID" do
|
|
491
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "_fc4a34b0-7efb-012e-caae-782bcb13bb38")
|
|
492
|
+
assert response.send(:validate_in_response_to)
|
|
493
|
+
assert_empty response.errors
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it "return true when no Request ID is provided for checking" do
|
|
497
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings)
|
|
498
|
+
assert response.send(:validate_in_response_to)
|
|
499
|
+
assert_empty response.errors
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "return false when the inResponseTo value does not match the Request ID" do
|
|
503
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "invalid_request_id")
|
|
504
|
+
assert !response.send(:validate_in_response_to)
|
|
505
|
+
assert_includes response.errors, "The InResponseTo of the Response: _fc4a34b0-7efb-012e-caae-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid_request_id"
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
describe "#validate_no_encrypted_attributes" do
|
|
510
|
+
it "return true when the assertion does not contain encrypted attributes" do
|
|
511
|
+
response_valid_signed.settings = settings
|
|
512
|
+
assert response_valid_signed.send(:validate_no_encrypted_attributes)
|
|
513
|
+
assert_empty response_valid_signed.errors
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
it "return false when the assertion contains encrypted attributes" do
|
|
517
|
+
response_encrypted_attrs.settings = settings
|
|
518
|
+
assert !response_encrypted_attrs.send(:validate_no_encrypted_attributes)
|
|
519
|
+
assert_includes response_encrypted_attrs.errors, "There is an EncryptedAttribute in the Response and this SP not support them"
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
describe "#validate_audience" do
|
|
524
|
+
it "return true when the audience is valid" do
|
|
525
|
+
response_valid_signed.settings = settings
|
|
526
|
+
response_valid_signed.settings.issuer = "https://someone.example.com/audience"
|
|
527
|
+
assert response_valid_signed.send(:validate_audience)
|
|
528
|
+
assert_empty response_valid_signed.errors
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
it "return true when there is not issuer defined" do
|
|
532
|
+
response_valid_signed.settings = settings
|
|
533
|
+
response_valid_signed.settings.issuer = nil
|
|
534
|
+
assert response_valid_signed.send(:validate_audience)
|
|
535
|
+
assert_empty response_valid_signed.errors
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
it "return false when there is no valid audience" do
|
|
539
|
+
response_invalid_audience.settings = settings
|
|
540
|
+
response_invalid_audience.settings.issuer = "https://invalid.example.com/audience"
|
|
541
|
+
assert !response_invalid_audience.send(:validate_audience)
|
|
542
|
+
assert_includes response_invalid_audience.errors, "#{response_invalid_audience.settings.issuer} is not a valid audience for this Response"
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
describe "#validate_issuer" do
|
|
547
|
+
it "return true when the issuer of the Message/Assertion matches the IdP entityId or it was empty" do
|
|
548
|
+
response_valid_signed.settings = settings
|
|
549
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
550
|
+
assert_empty response_valid_signed.errors
|
|
551
|
+
|
|
552
|
+
response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
|
|
553
|
+
assert response_valid_signed.send(:validate_issuer)
|
|
554
|
+
assert_empty response_valid_signed.errors
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
it "return false when the issuer of the Message does not match the IdP entityId" do
|
|
558
|
+
response_invalid_issuer_message.settings = settings
|
|
559
|
+
response_invalid_issuer_message.settings.idp_entity_id = 'http://idp.example.com/'
|
|
560
|
+
assert !response_invalid_issuer_message.send(:validate_issuer)
|
|
561
|
+
assert_includes response_invalid_issuer_message.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_message.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
it "return false when the issuer of the Assertion does not match the IdP entityId" do
|
|
565
|
+
response_invalid_issuer_assertion.settings = settings
|
|
566
|
+
response_invalid_issuer_assertion.settings.idp_entity_id = 'http://idp.example.com/'
|
|
567
|
+
assert !response_invalid_issuer_assertion.send(:validate_issuer)
|
|
568
|
+
assert_includes response_invalid_issuer_assertion.errors, "Doesn't match the issuer, expected: <#{response_invalid_issuer_assertion.settings.idp_entity_id}>, but was: <http://invalid.issuer.example.com/>"
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
describe "#validate_subject_confirmation" do
|
|
573
|
+
it "return true when valid subject confirmation" do
|
|
574
|
+
response_valid_signed.settings = settings
|
|
575
|
+
response_valid_signed.settings.assertion_consumer_service_url = 'recipient'
|
|
576
|
+
assert response_valid_signed.send(:validate_subject_confirmation)
|
|
577
|
+
assert_empty response_valid_signed.errors
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
it "return false when no subject confirmation data" do
|
|
581
|
+
response_no_subjectconfirmation_data.settings = settings
|
|
582
|
+
assert !response_no_subjectconfirmation_data.send(:validate_subject_confirmation)
|
|
583
|
+
assert_includes response_no_subjectconfirmation_data.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
it "return false when no valid subject confirmation method" do
|
|
587
|
+
response_no_subjectconfirmation_method.settings = settings
|
|
588
|
+
assert !response_no_subjectconfirmation_method.send(:validate_subject_confirmation)
|
|
589
|
+
assert_includes response_no_subjectconfirmation_method.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
it "return false when invalid inresponse" do
|
|
593
|
+
response_invalid_subjectconfirmation_inresponse.settings = settings
|
|
594
|
+
assert !response_invalid_subjectconfirmation_inresponse.send(:validate_subject_confirmation)
|
|
595
|
+
assert_includes response_invalid_subjectconfirmation_inresponse.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
it "return false when invalid NotBefore" do
|
|
599
|
+
response_invalid_subjectconfirmation_nb.settings = settings
|
|
600
|
+
assert !response_invalid_subjectconfirmation_nb.send(:validate_subject_confirmation)
|
|
601
|
+
assert_includes response_invalid_subjectconfirmation_nb.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
it "return false when invalid NotOnOrAfter" do
|
|
605
|
+
response_invalid_subjectconfirmation_noa.settings = settings
|
|
606
|
+
assert !response_invalid_subjectconfirmation_noa.send(:validate_subject_confirmation)
|
|
607
|
+
assert_includes response_invalid_subjectconfirmation_noa.errors, "A valid SubjectConfirmation was not found on this Response"
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
describe "#validate_session_expiration" do
|
|
612
|
+
it "return true when the session has not expired" do
|
|
613
|
+
response_valid_signed.settings = settings
|
|
614
|
+
assert response_valid_signed.send(:validate_session_expiration)
|
|
615
|
+
assert_empty response_valid_signed.errors
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
it "return false when the session has expired" do
|
|
619
|
+
response.settings = settings
|
|
620
|
+
assert !response.send(:validate_session_expiration)
|
|
621
|
+
assert_includes response.errors, "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response"
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
describe "#validate_signature" do
|
|
626
|
+
it "return true when the signature is valid" do
|
|
627
|
+
settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
|
628
|
+
response_valid_signed.settings = settings
|
|
629
|
+
assert response_valid_signed.send(:validate_signature)
|
|
630
|
+
assert_empty response_valid_signed.errors
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it "return false when no fingerprint" do
|
|
634
|
+
settings.idp_cert_fingerprint = nil
|
|
635
|
+
settings.idp_cert = nil
|
|
636
|
+
response.settings = settings
|
|
637
|
+
assert !response.send(:validate_signature)
|
|
638
|
+
assert_includes response.errors, "Invalid Signature on SAML Response"
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
it "return false when the signature is invalid" do
|
|
642
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
643
|
+
response.settings = settings
|
|
644
|
+
assert !response.send(:validate_signature)
|
|
645
|
+
assert_includes response.errors, "Invalid Signature on SAML Response"
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
describe "#nameid" do
|
|
650
|
+
it "extract the value of the name id element" do
|
|
651
|
+
assert_equal "support@onelogin.com", response.nameid
|
|
652
|
+
assert_equal "someone@example.com", response_with_signed_assertion.nameid
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
it "be extractable from an OpenSAML response" do
|
|
656
|
+
response_open_saml = OneLogin::RubySaml::Response.new(fixture(:open_saml))
|
|
657
|
+
assert_equal "someone@example.org", response_open_saml.nameid
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
it "be extractable from a Simple SAML PHP response" do
|
|
661
|
+
response_ssp = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
|
662
|
+
assert_equal "someone@example.com", response_ssp.nameid
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
describe "#sessionindex" do
|
|
667
|
+
it "extract the value of the sessionindex element" do
|
|
668
|
+
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
|
669
|
+
assert_equal "_51be37965feb5579d803141076936dc2e9d1d98ebf", response.sessionindex
|
|
670
|
+
end
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
describe "#check_conditions" do
|
|
674
|
+
it "check time conditions" do
|
|
675
|
+
response.soft = true
|
|
676
|
+
assert !response.send(:validate_conditions)
|
|
677
|
+
response_time_updated = OneLogin::RubySaml::Response.new(response_document_without_recipient_with_time_updated)
|
|
678
|
+
response_time_updated.soft = true
|
|
679
|
+
assert response_time_updated.send(:validate_conditions)
|
|
680
|
+
Timecop.freeze(Time.parse("2011-06-14T18:25:01.516Z")) do
|
|
681
|
+
response_with_saml2_namespace = OneLogin::RubySaml::Response.new(response_document_with_saml2_namespace)
|
|
682
|
+
response_with_saml2_namespace.soft = true
|
|
683
|
+
assert response_with_saml2_namespace.send(:validate_conditions)
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
it "optionally allows for clock drift" do
|
|
688
|
+
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
|
|
689
|
+
Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do
|
|
690
|
+
settings.soft = true
|
|
691
|
+
special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new(
|
|
692
|
+
response_document_with_saml2_namespace,
|
|
693
|
+
:allowed_clock_drift => 0.515,
|
|
694
|
+
:settings => settings
|
|
695
|
+
)
|
|
696
|
+
assert !special_response_with_saml2_namespace.send(:validate_conditions)
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do
|
|
700
|
+
special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new(
|
|
701
|
+
response_document_with_saml2_namespace,
|
|
702
|
+
:allowed_clock_drift => 0.516
|
|
703
|
+
)
|
|
704
|
+
assert special_response_with_saml2_namespace.send(:validate_conditions)
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
describe "#attributes" do
|
|
710
|
+
it "extract the first attribute in a hash accessed via its symbol" do
|
|
711
|
+
assert_equal "demo", response.attributes[:uid]
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
it "extract the first attribute in a hash accessed via its name" do
|
|
715
|
+
assert_equal "demo", response.attributes["uid"]
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
it "extract all attributes" do
|
|
719
|
+
assert_equal "demo", response.attributes[:uid]
|
|
720
|
+
assert_equal "value", response.attributes[:another_value]
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
it "work for implicit namespaces" do
|
|
724
|
+
assert_equal "someone@example.com", response_with_signed_assertion.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
it "not raise errors about nil/empty attributes for EncryptedAttributes" do
|
|
728
|
+
response_no_cert_and_encrypted_attrs = OneLogin::RubySaml::Response.new(response_document_no_cert_and_encrypted_attrs)
|
|
729
|
+
assert_equal 'Demo', response_no_cert_and_encrypted_attrs.attributes["first_name"]
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
it "not raise on responses without attributes" do
|
|
733
|
+
assert_equal OneLogin::RubySaml::Attributes.new, response_unsigned.attributes
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
describe "#multiple values" do
|
|
737
|
+
it "extract single value as string" do
|
|
738
|
+
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
it "extract single value as string in compatibility mode off" do
|
|
742
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
743
|
+
assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
|
|
744
|
+
# classes are not reloaded between tests so restore default
|
|
745
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
it "extract first of multiple values as string for b/w compatibility" do
|
|
749
|
+
assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
|
753
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
754
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
|
|
755
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
it "return array with all attributes when asked in XML order" do
|
|
759
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
it "return array with all attributes when asked in XML order in compatibility mode off" do
|
|
763
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
764
|
+
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
|
765
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
it "return first of multiple values when multiple Attribute tags in XML" do
|
|
769
|
+
assert_equal 'role1', response_multiple_attr_values.attributes[:role]
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
|
773
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
774
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
|
|
775
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
|
779
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
|
783
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
784
|
+
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
|
785
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
it "return nil value correctly" do
|
|
789
|
+
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
it "return nil value correctly when not in compatibility mode off" do
|
|
793
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
794
|
+
assert_equal [nil], response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
|
795
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
it "return multiple values including nil and empty string" do
|
|
799
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
800
|
+
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
it "return multiple values from [] when not in compatibility mode off" do
|
|
804
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
805
|
+
assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
|
|
806
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
it "check what happens when trying retrieve attribute that does not exists" do
|
|
810
|
+
assert_equal nil, response_multiple_attr_values.attributes[:attribute_not_exists]
|
|
811
|
+
assert_equal nil, response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
|
812
|
+
assert_equal nil, response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
|
813
|
+
|
|
814
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
815
|
+
assert_equal nil, response_multiple_attr_values.attributes[:attribute_not_exists]
|
|
816
|
+
assert_equal nil, response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
|
817
|
+
assert_equal nil, response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
|
818
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
end
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
describe "#session_expires_at" do
|
|
825
|
+
it "extract the value of the SessionNotOnOrAfter attribute" do
|
|
826
|
+
assert response.session_expires_at.is_a?(Time)
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
it "return nil when the value of the SessionNotOnOrAfter is not set" do
|
|
830
|
+
assert_nil response_without_attributes.session_expires_at
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
describe "#issuers" do
|
|
835
|
+
it "return the issuer inside the response assertion" do
|
|
836
|
+
assert_includes response.issuers, "https://app.onelogin.com/saml/metadata/13590"
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
it "return the issuer inside the response" do
|
|
840
|
+
assert_includes response_without_attributes.issuers, "wibble"
|
|
841
|
+
end
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
describe "#success" do
|
|
845
|
+
it "find a status code that says success" do
|
|
846
|
+
response.success?
|
|
847
|
+
end
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
describe '#xpath_first_from_signed_assertion' do
|
|
851
|
+
it 'not allow arbitrary code execution' do
|
|
852
|
+
malicious_response_document = fixture('response_eval', false)
|
|
853
|
+
malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document)
|
|
854
|
+
malicious_response.send(:xpath_first_from_signed_assertion)
|
|
855
|
+
assert_nil $evalled
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
describe '#sign_document' do
|
|
860
|
+
it 'Sign an unsigned SAML Response XML and initiate the SAML object with it' do
|
|
861
|
+
xml = Base64.decode64(fixture("test_sign.xml"))
|
|
862
|
+
|
|
863
|
+
document = XMLSecurity::Document.new(xml)
|
|
864
|
+
|
|
865
|
+
formated_cert = OneLogin::RubySaml::Utils.format_cert(ruby_saml_cert_text)
|
|
866
|
+
cert = OpenSSL::X509::Certificate.new(formated_cert)
|
|
867
|
+
|
|
868
|
+
formated_private_key = OneLogin::RubySaml::Utils.format_private_key(ruby_saml_key_text)
|
|
869
|
+
private_key = OpenSSL::PKey::RSA.new(formated_private_key)
|
|
870
|
+
document.sign_document(private_key, cert)
|
|
871
|
+
|
|
872
|
+
signed_response = OneLogin::RubySaml::Response.new(document.to_s)
|
|
873
|
+
settings.idp_cert = ruby_saml_cert_text
|
|
874
|
+
signed_response.settings = settings
|
|
875
|
+
Timecop.freeze(Time.parse("2015-03-18T04:50:24Z")) do
|
|
876
|
+
assert signed_response.is_valid?
|
|
877
|
+
end
|
|
878
|
+
assert_empty signed_response.errors
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
describe "retrieve nameID" do
|
|
883
|
+
it 'is possible when nameID inside the assertion' do
|
|
884
|
+
response_valid_signed.settings = settings
|
|
885
|
+
assert_equal "test@onelogin.com", response_valid_signed.nameid
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
it 'is not possible when encryptID inside the assertion but no private key' do
|
|
889
|
+
response_encrypted_nameid.settings = settings
|
|
890
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
|
|
891
|
+
assert_equal "test@onelogin.com", response_encrypted_nameid.nameid
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
it 'is possible when encryptID inside the assertion and settings has the private key' do
|
|
896
|
+
settings.private_key = ruby_saml_key_text
|
|
897
|
+
response_encrypted_nameid.settings = settings
|
|
898
|
+
assert_equal "test@onelogin.com", response_encrypted_nameid.nameid
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
describe 'try to initialize an encrypted response' do
|
|
906
|
+
it 'raise if an encrypted assertion is found and no sp private key to decrypt it' do
|
|
907
|
+
error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method"
|
|
908
|
+
|
|
909
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
910
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
914
|
+
response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
settings.certificate = ruby_saml_cert_text
|
|
918
|
+
settings.private_key = ruby_saml_key_text
|
|
919
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
920
|
+
response3 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
|
|
921
|
+
response3.settings
|
|
922
|
+
end
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
it 'raise if an encrypted assertion is found and the sp private key is wrong' do
|
|
926
|
+
settings.certificate = ruby_saml_cert_text
|
|
927
|
+
wrong_private_key = ruby_saml_key_text.sub!('A', 'B')
|
|
928
|
+
settings.private_key = wrong_private_key
|
|
929
|
+
|
|
930
|
+
error_msg = "Neither PUB key nor PRIV key: nested asn1 error"
|
|
931
|
+
assert_raises(OpenSSL::PKey::RSAError, error_msg) do
|
|
932
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
it 'return true if an encrypted assertion is found and settings initialized with private_key' do
|
|
937
|
+
settings.certificate = ruby_saml_cert_text
|
|
938
|
+
settings.private_key = ruby_saml_key_text
|
|
939
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
940
|
+
assert response.decrypted_document
|
|
941
|
+
|
|
942
|
+
response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_signed_assertion, :settings => settings)
|
|
943
|
+
assert response2.decrypted_document
|
|
944
|
+
|
|
945
|
+
response3 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_signed_assertion, :settings => settings)
|
|
946
|
+
assert response3.decrypted_document
|
|
947
|
+
|
|
948
|
+
response4 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
|
|
949
|
+
assert response4.decrypted_document
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
describe "retrieve nameID and attributes from encrypted assertion" do
|
|
954
|
+
|
|
955
|
+
before do
|
|
956
|
+
settings.idp_cert_fingerprint = 'EE:17:4E:FB:A8:81:71:12:0D:2A:78:43:BC:E7:0C:07:58:79:F4:F4'
|
|
957
|
+
settings.issuer = 'http://rubysaml.com:3000/saml/metadata'
|
|
958
|
+
settings.assertion_consumer_service_url = 'http://rubysaml.com:3000/saml/acs'
|
|
959
|
+
settings.certificate = ruby_saml_cert_text
|
|
960
|
+
settings.private_key = ruby_saml_key_text
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
it 'is possible when signed_message_encrypted_unsigned_assertion' do
|
|
964
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
965
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
966
|
+
assert response.is_valid?
|
|
967
|
+
assert_empty response.errors
|
|
968
|
+
assert_equal "test", response.attributes[:uid]
|
|
969
|
+
assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
|
|
970
|
+
end
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
it 'is possible when signed_message_encrypted_signed_assertion' do
|
|
974
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_signed_assertion, :settings => settings)
|
|
975
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
976
|
+
assert response.is_valid?
|
|
977
|
+
assert_empty response.errors
|
|
978
|
+
assert_equal "test", response.attributes[:uid]
|
|
979
|
+
assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
|
|
980
|
+
end
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
it 'is possible when unsigned_message_encrypted_signed_assertion' do
|
|
984
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_signed_assertion, :settings => settings)
|
|
985
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
986
|
+
assert response.is_valid?
|
|
987
|
+
assert_empty response.errors
|
|
988
|
+
assert_equal "test", response.attributes[:uid]
|
|
989
|
+
assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
|
|
990
|
+
end
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
it 'is not possible when unsigned_message_encrypted_unsigned_assertion' do
|
|
994
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
|
|
995
|
+
Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
|
|
996
|
+
assert !response.is_valid?
|
|
997
|
+
assert_includes response.errors, "Found an unexpected number of Signature Element. SAML Response rejected"
|
|
998
|
+
end
|
|
999
|
+
end
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
describe "#decrypt_assertion" do
|
|
1003
|
+
before do
|
|
1004
|
+
settings.private_key = ruby_saml_key_text
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
describe "check right settings" do
|
|
1008
|
+
|
|
1009
|
+
it "is not possible to decrypt the assertion if no private key" do
|
|
1010
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
1011
|
+
|
|
1012
|
+
encrypted_assertion_node = REXML::XPath.first(
|
|
1013
|
+
response.document,
|
|
1014
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1015
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1016
|
+
)
|
|
1017
|
+
response.settings.private_key = nil
|
|
1018
|
+
|
|
1019
|
+
error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it"
|
|
1020
|
+
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
|
1021
|
+
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
it "is possible to decrypt the assertion if private key" do
|
|
1026
|
+
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
|
|
1027
|
+
|
|
1028
|
+
encrypted_assertion_node = REXML::XPath.first(
|
|
1029
|
+
response.document,
|
|
1030
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1031
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1032
|
+
)
|
|
1033
|
+
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
|
|
1034
|
+
|
|
1035
|
+
encrypted_assertion_node2 = REXML::XPath.first(
|
|
1036
|
+
decrypted,
|
|
1037
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1038
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1039
|
+
)
|
|
1040
|
+
assert_nil encrypted_assertion_node2
|
|
1041
|
+
assert decrypted.name, "Assertion"
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
it "is possible to decrypt the assertion if private key but no saml namespace on the Assertion Element that is inside the EncryptedAssertion" do
|
|
1045
|
+
unsigned_message_encrypted_assertion_without_saml_namespace = read_response('unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64')
|
|
1046
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_assertion_without_saml_namespace, :settings => settings)
|
|
1047
|
+
encrypted_assertion_node = REXML::XPath.first(
|
|
1048
|
+
response.document,
|
|
1049
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1050
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1051
|
+
)
|
|
1052
|
+
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
|
|
1053
|
+
|
|
1054
|
+
encrypted_assertion_node2 = REXML::XPath.first(
|
|
1055
|
+
decrypted,
|
|
1056
|
+
"(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
|
|
1057
|
+
{ "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
|
1058
|
+
)
|
|
1059
|
+
assert_nil encrypted_assertion_node2
|
|
1060
|
+
assert decrypted.name, "Assertion"
|
|
1061
|
+
end
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
describe "check different encrypt methods supported" do
|
|
1065
|
+
it "EncryptionMethod DES-192 && Key Encryption Algorithm RSA-1_5" do
|
|
1066
|
+
unsigned_message_des192_encrypted_signed_assertion = read_response('unsigned_message_des192_encrypted_signed_assertion.xml.base64')
|
|
1067
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_des192_encrypted_signed_assertion, :settings => settings)
|
|
1068
|
+
assert_equal "test", response.attributes[:uid]
|
|
1069
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
it "EncryptionMethod AES-128 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
|
|
1073
|
+
unsigned_message_aes128_encrypted_signed_assertion = read_response('unsigned_message_aes128_encrypted_signed_assertion.xml.base64')
|
|
1074
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_aes128_encrypted_signed_assertion, :settings => settings)
|
|
1075
|
+
assert_equal "test", response.attributes[:uid]
|
|
1076
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
it "EncryptionMethod AES-192 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
|
|
1080
|
+
unsigned_message_aes192_encrypted_signed_assertion = read_response('unsigned_message_aes192_encrypted_signed_assertion.xml.base64')
|
|
1081
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_aes192_encrypted_signed_assertion, :settings => settings)
|
|
1082
|
+
assert_equal "test", response.attributes[:uid]
|
|
1083
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
it "EncryptionMethod AES-256 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
|
|
1087
|
+
unsigned_message_aes256_encrypted_signed_assertion = read_response('unsigned_message_aes256_encrypted_signed_assertion.xml.base64')
|
|
1088
|
+
response = OneLogin::RubySaml::Response.new(unsigned_message_aes256_encrypted_signed_assertion, :settings => settings)
|
|
1089
|
+
assert_equal "test", response.attributes[:uid]
|
|
1090
|
+
assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
|
|
1091
|
+
end
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
end
|