test/response_test.rb in ruby-saml-1.3.1 vs test/response_test.rb in ruby-saml-1.4.0
- old
+ new
@@ -7,10 +7,11 @@
describe "Response" do
let(:settings) { OneLogin::RubySaml::Settings.new }
let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
+ let(:response_with_multiple_attribute_statements) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_statements)) }
let(:response_without_reference_uri) { OneLogin::RubySaml::Response.new(response_document_without_reference_uri) }
let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
let(:response_with_ds_namespace_at_the_root) { OneLogin::RubySaml::Response.new(response_document_with_ds_namespace_at_the_root)}
let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
@@ -18,10 +19,13 @@
let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
let(:response_valid_signed_without_x509certificate) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) }
let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) }
let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
+ let(:response_no_conditions) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64")) }
+ let(:response_no_authnstatement) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64")) }
+ let(:response_empty_destination) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64")) }
let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) }
let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) }
let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) }
let(:response_statuscode_responder_and_msg) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responer_and_msg.xml.base64")) }
let(:response_encrypted_attrs) { OneLogin::RubySaml::Response.new(read_invalid_response("response_encrypted_attrs.xml.base64")) }
@@ -29,10 +33,16 @@
let(:response_multiple_signed) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) }
let(:response_invalid_audience) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64")) }
let(:response_invalid_signed_element) { OneLogin::RubySaml::Response.new(read_invalid_response("response_invalid_signed_element.xml.base64")) }
let(:response_invalid_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64")) }
let(:response_invalid_issuer_message) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64")) }
+ let(:response_no_issuer_response) { OneLogin::RubySaml::Response.new(read_invalid_response("no_issuer_response.xml.base64")) }
+ let(:response_no_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("no_issuer_assertion.xml.base64")) }
+ let(:response_no_nameid) { OneLogin::RubySaml::Response.new(read_invalid_response("no_nameid.xml.base64")) }
+ let(:response_empty_nameid) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_nameid.xml.base64")) }
+ let(:response_wrong_spnamequalifier) { OneLogin::RubySaml::Response.new(read_invalid_response("wrong_spnamequalifier.xml.base64")) }
+ let(:response_duplicated_attributes) { OneLogin::RubySaml::Response.new(read_invalid_response("duplicated_attributes.xml.base64")) }
let(:response_no_subjectconfirmation_data) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_data.xml.base64")) }
let(:response_no_subjectconfirmation_method) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_method.xml.base64")) }
let(:response_invalid_subjectconfirmation_inresponse) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_inresponse.xml.base64")) }
let(:response_invalid_subjectconfirmation_recipient) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_recipient.xml.base64")) }
let(:response_invalid_subjectconfirmation_nb) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_nb.xml.base64")) }
@@ -436,10 +446,16 @@
response.settings = settings
response.settings.assertion_consumer_service_url = 'invalid_acs'
assert !response.send(:validate_destination)
assert_includes response.errors, "The response was received at #{response.destination} instead of #{response.settings.assertion_consumer_service_url}"
end
+
+ it "return false when the destination of the SAML Response is empty" do
+ response_empty_destination.settings = settings
+ assert !response_empty_destination.send(:validate_destination)
+ assert_includes response_empty_destination.errors, "The response has an empty Destination value"
+ end
end
describe "#validate_issuer" do
it "return true when the issuer of the Message/Assertion matches the IdP entityId" do
response_valid_signed.settings = settings
@@ -602,10 +618,24 @@
response_invalid_issuer_assertion.settings = settings
response_invalid_issuer_assertion.settings.idp_entity_id = 'http://idp.example.com/'
assert !response_invalid_issuer_assertion.send(:validate_issuer)
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/>"
end
+
+ it "return false when the no issuer at the Response" do
+ response_no_issuer_response.settings = settings
+ response_no_issuer_response.settings.idp_entity_id = 'http://idp.example.com/'
+ assert !response_no_issuer_response.send(:validate_issuer)
+ assert_includes response_no_issuer_response.errors, "Issuer of the Response not found or multiple."
+ end
+
+ it "return false when the no issuer at the Assertion" do
+ response_no_issuer_assertion.settings = settings
+ response_no_issuer_assertion.settings.idp_entity_id = 'http://idp.example.com/'
+ assert !response_no_issuer_assertion.send(:validate_issuer)
+ assert_includes response_no_issuer_assertion.errors, "Issuer of the Assertion not found or multiple."
+ end
end
describe "#validate_subject_confirmation" do
it "return true when valid subject confirmation" do
response_valid_signed.settings = settings
@@ -765,10 +795,50 @@
assert !response_wrapped.send(:validate_signature)
assert_includes response_wrapped.errors, "Invalid Signature on SAML Response"
end
end
+ describe "#validate nameid" do
+ it "return false when no nameid element and required by settings" do
+ settings.security[:want_name_id] = true
+ response_no_nameid.settings = settings
+ assert !response_no_nameid.send(:validate_name_id)
+ assert_includes response_no_nameid.errors, "No NameID element found in the assertion of the Response"
+ end
+
+ it "return false when no nameid element and required by settings" do
+ response_empty_nameid.settings = settings
+ assert !response_empty_nameid.send(:validate_name_id)
+ assert_includes response_empty_nameid.errors, "An empty NameID value found"
+ end
+
+ it "return false when no nameid value" do
+ response_empty_nameid.settings = settings
+ assert !response_empty_nameid.send(:validate_name_id)
+ assert_includes response_empty_nameid.errors, "An empty NameID value found"
+ end
+
+ it "return false when wrong_spnamequalifier" do
+ settings.issuer = 'sp_entity_id'
+ response_wrong_spnamequalifier.settings = settings
+ assert !response_wrong_spnamequalifier.send(:validate_name_id)
+ assert_includes response_wrong_spnamequalifier.errors, "The SPNameQualifier value mistmatch the SP entityID value."
+ end
+
+ it "return true when no nameid element but not required by settings" do
+ settings.security[:want_name_id] = false
+ response_no_nameid.settings = settings
+ assert response_no_nameid.send(:validate_name_id)
+ end
+
+ it "return true when nameid is valid and response_wrong_spnamequalifier matches the SP issuer" do
+ settings.issuer = 'wrong-sp-entityid'
+ response_wrong_spnamequalifier.settings = settings
+ assert response_wrong_spnamequalifier.send(:validate_name_id)
+ end
+ end
+
describe "#nameid" do
it "extract the value of the name id element" do
assert_equal "support@onelogin.com", response.nameid
assert_equal "someone@example.com", response_with_signed_assertion.nameid
end
@@ -796,10 +866,36 @@
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
assert_equal "_51be37965feb5579d803141076936dc2e9d1d98ebf", response.sessionindex
end
end
+ describe "#check_one_conditions" do
+ it "return false when none or more than one conditions element" do
+ response_no_conditions.soft = true
+ assert !response_no_conditions.send(:validate_one_conditions)
+ assert_includes response_no_conditions.errors, "The Assertion must include one Conditions element"
+ end
+
+ it "return true when one conditions element" do
+ response.soft = true
+ assert response.send(:validate_one_conditions)
+ end
+ end
+
+ describe "#check_one_authnstatement" do
+ it "return false when none or more than one authnstatement element" do
+ response_no_authnstatement.soft = true
+ assert !response_no_authnstatement.send(:validate_one_authnstatement)
+ assert_includes response_no_authnstatement.errors, "The Assertion must include one AuthnStatement element"
+ end
+
+ it "return true when one authnstatement element" do
+ response.soft = true
+ assert response.send(:validate_one_authnstatement)
+ end
+ end
+
describe "#check_conditions" do
it "check time conditions" do
response.soft = true
assert !response.send(:validate_conditions)
response_time_updated = OneLogin::RubySaml::Response.new(response_document_without_recipient_with_time_updated)
@@ -850,19 +946,36 @@
it "work for implicit namespaces" do
assert_equal "someone@example.com", response_with_signed_assertion.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
end
+ it "extract attributes from all AttributeStatement tags" do
+ assert_equal "smith", response_with_multiple_attribute_statements.attributes[:surname]
+ assert_equal "bob", response_with_multiple_attribute_statements.attributes[:firstname]
+ end
+
it "not raise errors about nil/empty attributes for EncryptedAttributes" do
response_no_cert_and_encrypted_attrs = OneLogin::RubySaml::Response.new(response_document_no_cert_and_encrypted_attrs)
assert_equal 'Demo', response_no_cert_and_encrypted_attrs.attributes["first_name"]
end
it "not raise on responses without attributes" do
assert_equal OneLogin::RubySaml::Attributes.new, response_unsigned.attributes
end
+ it "return false when validating a response with duplicate attributes" do
+ response_duplicated_attributes.settings = settings
+ response_duplicated_attributes.options[:check_duplicated_attributes] = true
+ assert !response_duplicated_attributes.send(:validate_no_duplicated_attributes)
+ assert_includes response_duplicated_attributes.errors, "Found an Attribute element with duplicated Name"
+ end
+
+ it "return true when validating a response with duplicate attributes but skip check" do
+ response_duplicated_attributes.settings = settings
+ assert response_duplicated_attributes.send(:validate_no_duplicated_attributes)
+ end
+
describe "#multiple values" do
it "extract single value as string" do
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
end
@@ -911,10 +1024,16 @@
OneLogin::RubySaml::Attributes.single_value_compatibility = false
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
+ it "return all of multiple values when multiple Attribute tags in multiple AttributeStatement tags" do
+ OneLogin::RubySaml::Attributes.single_value_compatibility = false
+ assert_equal ['role1', 'role2', 'role3'], response_with_multiple_attribute_statements.attributes.multi(:role)
+ OneLogin::RubySaml::Attributes.single_value_compatibility = true
+ end
+
it "return nil value correctly" do
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
end
it "return nil value correctly when not in compatibility mode off" do
@@ -957,20 +1076,10 @@
it "return nil when the value of the SessionNotOnOrAfter is not set" do
assert_nil response_without_attributes.session_expires_at
end
end
- describe "#issuers" do
- it "return the issuer inside the response assertion" do
- assert_includes response.issuers, "https://app.onelogin.com/saml/metadata/13590"
- end
-
- it "return the issuer inside the response" do
- assert_includes response_without_attributes.issuers, "wibble"
- end
- end
-
describe "#success" do
it "find a status code that says success" do
response.success?
end
end
@@ -988,15 +1097,15 @@
it 'Sign an unsigned SAML Response XML and initiate the SAML object with it' do
xml = Base64.decode64(fixture("test_sign.xml"))
document = XMLSecurity::Document.new(xml)
- formated_cert = OneLogin::RubySaml::Utils.format_cert(ruby_saml_cert_text)
- cert = OpenSSL::X509::Certificate.new(formated_cert)
+ formatted_cert = OneLogin::RubySaml::Utils.format_cert(ruby_saml_cert_text)
+ cert = OpenSSL::X509::Certificate.new(formatted_cert)
- formated_private_key = OneLogin::RubySaml::Utils.format_private_key(ruby_saml_key_text)
- private_key = OpenSSL::PKey::RSA.new(formated_private_key)
+ formatted_private_key = OneLogin::RubySaml::Utils.format_private_key(ruby_saml_key_text)
+ private_key = OpenSSL::PKey::RSA.new(formatted_private_key)
document.sign_document(private_key, cert)
signed_response = OneLogin::RubySaml::Response.new(document.to_s)
settings.idp_cert = ruby_saml_cert_text
signed_response.settings = settings
@@ -1286,6 +1395,44 @@
assert_empty response.errors
assert_equal "test", response.attributes[:uid]
assert_equal "ZdrjpwEdw22vKoxWAbZB78/gQ7s=", response.attributes.single('urn:oid:1.3.6.1.4.1.5923.1.1.1.10')
end
end
+
+ describe "signature wrapping attack with encrypted assertion" do
+ it "should not be valid" do
+ settings.private_key = ruby_saml_key_text
+ signature_wrapping_attack = read_invalid_response("encrypted_new_attack.xml.base64")
+ response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
+ response_wrapped.stubs(:conditions).returns(nil)
+ response_wrapped.stubs(:validate_subject_confirmation).returns(true)
+ settings.idp_cert_fingerprint = "385b1eec71143f00db6af936e2ea12a28771d72c"
+ assert !response_wrapped.is_valid?
+ assert_includes response_wrapped.errors, "Found an invalid Signed Element. SAML Response rejected"
+ end
+ end
+
+ describe "signature wrapping attack - concealed SAML response body" do
+ it "should not be valid" do
+ signature_wrapping_attack = read_invalid_response("response_with_concealed_signed_assertion.xml")
+ response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
+ settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
+ response_wrapped.stubs(:conditions).returns(nil)
+ response_wrapped.stubs(:validate_subject_confirmation).returns(true)
+ assert !response_wrapped.is_valid?
+ assert_includes response_wrapped.errors, "SAML Response must contain 1 assertion"
+ end
+ end
+
+ describe "signature wrapping attack - doubled signed assertion SAML response" do
+ it "should not be valid" do
+ signature_wrapping_attack = read_invalid_response("response_with_doubled_signed_assertion.xml")
+ response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
+ settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
+ response_wrapped.stubs(:conditions).returns(nil)
+ response_wrapped.stubs(:validate_subject_confirmation).returns(true)
+ assert !response_wrapped.is_valid?
+ assert_includes response_wrapped.errors, "SAML Response must contain 1 assertion"
+ end
+ end
+
end