test/response_test.rb in ruby-saml-0.9.4 vs test/response_test.rb in ruby-saml-1.0.0
- old
+ new
@@ -3,396 +3,857 @@
require 'onelogin/ruby-saml/response'
class RubySamlTest < Minitest::Test
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_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
+ let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
+ let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
+ let(:response_multiple_attr_values) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) }
+ let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
+ 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_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")) }
+ let(:response_no_signed_elements) { OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) }
+ 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_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")) }
+ let(:response_invalid_subjectconfirmation_noa) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64")) }
+ let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) }
+ let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) }
+
it "raise an exception when response is initialized with nil" do
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
end
it "be able to parse a document which contains ampersands" do
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
- response = OneLogin::RubySaml::Response.new(ampersands_response)
- settings = OneLogin::RubySaml::Settings.new
- settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
- response.settings = settings
- response.validate!
+ ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document)
+ ampersands_response.settings = settings
+ ampersands_response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
+
+ assert !ampersands_response.is_valid?
+ assert_includes ampersands_response.errors, "SAML Response must contain 1 assertion"
end
+ describe "Prevent XEE attack" do
+ before do
+ @response = OneLogin::RubySaml::Response.new(fixture(:attackxee))
+ end
+
+ it "false when evil attack vector is present, soft = true" do
+ @response.soft = true
+ assert !@response.send(:validate_structure)
+ assert_includes @response.errors, "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
+ end
+
+ it "raise when evil attack vector is present, soft = false " do
+ @response.soft = false
+
+ assert_raises(OneLogin::RubySaml::ValidationError) do
+ @response.send(:validate_structure)
+ end
+ end
+ end
+
it "adapt namespace" do
- response = OneLogin::RubySaml::Response.new(response_document)
- refute_nil response.name_id
- response = OneLogin::RubySaml::Response.new(response_document_2)
- refute_nil response.name_id
- response = OneLogin::RubySaml::Response.new(response_document_3)
- refute_nil response.name_id
+ refute_nil response.nameid
+ refute_nil response_without_attributes.nameid
+ refute_nil response_with_signed_assertion.nameid
end
it "default to raw input when a response is not Base64 encoded" do
- decoded = Base64.decode64(response_document_2)
- response = OneLogin::RubySaml::Response.new(decoded)
- assert response.document
+ decoded = Base64.decode64(response_document_without_attributes)
+ response_from_raw = OneLogin::RubySaml::Response.new(decoded)
+ assert response_from_raw.document
end
describe "Assertion" do
it "only retreive an assertion with an ID that matches the signature's reference URI" do
- response = OneLogin::RubySaml::Response.new(wrapped_response_2)
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
+ response_wrapped.stubs(:conditions).returns(nil)
settings.idp_cert_fingerprint = signature_fingerprint_1
- response.settings = settings
- assert_nil response.name_id
+ response_wrapped.settings = settings
+ assert_nil response_wrapped.nameid
end
end
- describe "#validate!" do
- it "raise when encountering a condition that prevents the document from being valid" do
- response = OneLogin::RubySaml::Response.new(response_document)
- assert_raises(OneLogin::RubySaml::ValidationError) do
- response.validate!
+ describe "#is_valid?" do
+ describe "soft = false" do
+
+ before do
+ response.soft = false
+ response_valid_signed.soft = false
end
+
+ it "raise when response is initialized with blank data" do
+ blank_response = OneLogin::RubySaml::Response.new('')
+ blank_response.soft = false
+ error_msg = "Blank response"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ blank_response.is_valid?
+ end
+ assert_includes blank_response.errors, error_msg
+ end
+
+ it "raise when settings have not been set" do
+ error_msg = "No settings on response"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response.is_valid?
+ end
+ assert_includes response.errors, error_msg
+ end
+
+ it "raise when No fingerprint or certificate on settings" do
+ settings.idp_cert_fingerprint = nil
+ settings.idp_cert = nil
+ response.settings = settings
+ error_msg = "No fingerprint or certificate on settings"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response.is_valid?
+ end
+ assert_includes response.errors, error_msg
+ end
+
+ it "raise when signature wrapping attack" do
+ response_wrapped.stubs(:conditions).returns(nil)
+ response_wrapped.stubs(:validate_subject_confirmation).returns(true)
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_wrapped.settings = settings
+ assert !response_wrapped.is_valid?
+ end
+
+ it "validate SAML 2.0 XML structure" do
+ resp_xml = Base64.decode64(response_document_unsigned).gsub(/emailAddress/,'test')
+ response_unsigned_mod = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
+ response_unsigned_mod.stubs(:conditions).returns(nil)
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_unsigned_mod.settings = settings
+ response_unsigned_mod.soft = false
+ assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch') do
+ response_unsigned_mod.is_valid?
+ end
+ end
+
+ it "raise when encountering a condition that prevents the document from being valid" do
+ settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
+ response.settings = settings
+ response.soft = false
+ error_msg = "Current time is on or after NotOnOrAfter condition"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response.is_valid?
+ end
+ assert_includes response.errors[0], error_msg
+ end
+
+ it "raise when encountering a SAML Response with bad formatted" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_without_attributes.settings = settings
+ response_without_attributes.soft = false
+ assert_raises(OneLogin::RubySaml::ValidationError) do
+ response_without_attributes.is_valid?
+ end
+ end
+
+ it "raise when the inResponseTo value does not match the Request ID" do
+ settings.soft = false
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ opts = {}
+ opts[:settings] = settings
+ opts[:matches_request_id] = "invalid_request_id"
+ response_valid_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
+ 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"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response_valid_signed.is_valid?
+ end
+ assert_includes response_valid_signed.errors, error_msg
+ end
+
+ it "raise when the assertion contains encrypted attributes" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_encrypted_attrs.settings = settings
+ response_encrypted_attrs.soft = false
+ error_msg = "There is an EncryptedAttribute in the Response and this SP not support them"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response_encrypted_attrs.is_valid?
+ end
+ assert_includes response_encrypted_attrs.errors, error_msg
+ end
+
+ it "raise when there is no valid audience" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ settings.issuer = 'invalid'
+ response_valid_signed.settings = settings
+ response_valid_signed.soft = false
+ error_msg = "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response_valid_signed.is_valid?
+ end
+ assert_includes response_valid_signed.errors, error_msg
+ end
+
+ it "raise when no ID present in the SAML Response" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_no_id.settings = settings
+ response_no_id.soft = false
+ error_msg = "Missing ID attribute on SAML Response"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response_no_id.is_valid?
+ end
+ assert_includes response_no_id.errors, error_msg
+ end
+
+ it "raise when no 2.0 Version present in the SAML Response" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_no_version.settings = settings
+ response_no_version.soft = false
+ error_msg = "Unsupported SAML version"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response_no_version.is_valid?
+ end
+ assert_includes response_no_version.errors, error_msg
+ end
end
+
+ describe "soft = true" do
+ before do
+ response.soft = true
+ response_valid_signed.soft = true
+ end
+
+ it "return true when the response is initialized with valid data" do
+ response_valid_signed.stubs(:conditions).returns(nil)
+ response_valid_signed.settings = settings
+ response_valid_signed.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
+ assert response_valid_signed.is_valid?
+ assert_empty response_valid_signed.errors
+ end
+
+ it "return true when the response is initialized with valid data and using certificate instead of fingerprint" do
+ response_valid_signed.stubs(:conditions).returns(nil)
+ response_valid_signed.settings = settings
+ response_valid_signed.settings.idp_cert = ruby_saml_cert_text
+ assert response_valid_signed.is_valid?
+ assert_empty response_valid_signed.errors
+ end
+
+ it "return false when response is initialized with blank data" do
+ blank_response = OneLogin::RubySaml::Response.new('')
+ blank_response.soft = true
+ assert !blank_response.is_valid?
+ assert_includes blank_response.errors, "Blank response"
+ end
+
+ it "return false if settings have not been set" do
+ assert !response.is_valid?
+ assert_includes response.errors, "No settings on response"
+ end
+
+ it "return false if fingerprint or certificate not been set on settings" do
+ response.settings = settings
+ assert !response.is_valid?
+ assert_includes response.errors, "No fingerprint or certificate on settings"
+ end
+
+ it "should be idempotent when the response is initialized with invalid data" do
+ response_unsigned.stubs(:conditions).returns(nil)
+ response_unsigned.settings = settings
+ assert !response_unsigned.is_valid?
+ assert !response_unsigned.is_valid?
+ end
+
+ it "should be idempotent when the response is initialized with valid data" do
+ response_valid_signed.stubs(:conditions).returns(nil)
+ response_valid_signed.settings = settings
+ response_valid_signed.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
+ assert response_valid_signed.is_valid?
+ assert response_valid_signed.is_valid?
+ end
+
+ it "not allow signature wrapping attack" do
+ response_wrapped.stubs(:conditions).returns(nil)
+ response_wrapped.stubs(:validate_subject_confirmation).returns(true)
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_wrapped.settings = settings
+ assert !response_wrapped.is_valid?
+ end
+
+ it "support dynamic namespace resolution on signature elements" do
+ no_signature_response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
+ no_signature_response.stubs(:conditions).returns(nil)
+ no_signature_response.stubs(:validate_subject_confirmation).returns(true)
+ no_signature_response.settings = settings
+ 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"
+ XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
+ assert no_signature_response.is_valid?
+ end
+
+ it "validate ADFS assertions" do
+ adfs_response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
+ adfs_response.stubs(:conditions).returns(nil)
+ adfs_response.stubs(:validate_subject_confirmation).returns(true)
+ settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
+ adfs_response.settings = settings
+ adfs_response.soft = true
+ assert adfs_response.is_valid?
+ end
+
+ it "validate SAML 2.0 XML structure" do
+ resp_xml = Base64.decode64(response_document_unsigned).gsub(/emailAddress/,'test')
+ response_unsigned_mod = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
+ response_unsigned_mod.stubs(:conditions).returns(nil)
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_unsigned_mod.settings = settings
+ response_unsigned_mod.soft = true
+ assert !response_unsigned_mod.is_valid?
+ end
+
+ it "return false when encountering a condition that prevents the document from being valid" do
+ settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
+ response.settings = settings
+ error_msg = "Current time is on or after NotOnOrAfter condition"
+ assert !response.is_valid?
+ assert_includes response.errors[0], "Current time is on or after NotOnOrAfter condition"
+ end
+
+ it "return false when encountering a SAML Response with bad formatted" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_without_attributes.settings = settings
+ response_without_attributes.soft = true
+ error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
+ response_without_attributes.is_valid?
+ assert_includes response_without_attributes.errors, error_msg
+ end
+
+ it "return false when the inResponseTo value does not match the Request ID" do
+ settings.soft = true
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ opts = {}
+ opts[:settings] = settings
+ opts[:matches_request_id] = "invalid_request_id"
+ response_valid_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
+ response_valid_signed.is_valid?
+ 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"
+ end
+
+ it "return false when the assertion contains encrypted attributes" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_encrypted_attrs.settings = settings
+ response_encrypted_attrs.soft = true
+ response_encrypted_attrs.is_valid?
+ assert_includes response_encrypted_attrs.errors, "There is an EncryptedAttribute in the Response and this SP not support them"
+ end
+
+ it "return false when there is no valid audience" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ settings.issuer = 'invalid'
+ response_valid_signed.settings = settings
+ response_valid_signed.is_valid?
+ assert_includes response_valid_signed.errors, "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
+ end
+
+ it "return false when no ID present in the SAML Response" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_no_id.settings = settings
+ response_no_id.soft = true
+ response_no_id.is_valid?
+ assert_includes response_no_id.errors, "Missing ID attribute on SAML Response"
+ end
+
+ it "return false when no 2.0 Version present in the SAML Response" do
+ settings.idp_cert_fingerprint = signature_fingerprint_1
+ response_no_version.settings = settings
+ response_no_version.soft = true
+ error_msg = "Unsupported SAML version"
+ response_no_version.is_valid?
+ assert_includes response_no_version.errors, "Unsupported SAML version"
+ end
+ end
end
+ describe "#validate_audience" do
+ it "return true when the audience is valid" do
+ response.settings = settings
+ response.settings.issuer = '{audience}'
+ assert response.send(:validate_audience)
+ assert_empty response.errors
+ end
+
+ it "return false when the audience is valid" do
+ response.settings = settings
+ response.settings.issuer = 'invalid_audience'
+ assert !response.send(:validate_audience)
+ assert_includes response.errors, "#{response.settings.issuer} is not a valid audience for this Response"
+ 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
+ assert response_valid_signed.send(:validate_issuer)
+
+ response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
+ assert response_valid_signed.send(:validate_issuer)
+ end
+
+ it "return false when the issuer of the Message does not match the IdP entityId" do
+ response_invalid_issuer_message.settings = settings
+ response_invalid_issuer_message.settings.idp_entity_id = 'http://idp.example.com/'
+ assert !response_invalid_issuer_message.send(:validate_issuer)
+ 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/>"
+ end
+
+ it "return false when the issuer of the Assertion does not match the IdP entityId" do
+ 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
+ end
+
+ describe "#validate_num_assertion" do
+ it "return true when SAML Response contains 1 assertion" do
+ assert response.send(:validate_num_assertion)
+ assert_empty response.errors
+ end
+
+ it "return false when no 2.0 Version present in the SAML Response" do
+ assert !response_multi_assertion.send(:validate_num_assertion)
+ assert_includes response_multi_assertion.errors, "SAML Response must contain 1 assertion"
+ end
+ end
+
+ describe "validate_success_status" do
+ it "return true when the status is 'Success'" do
+ assert response.send(:validate_success_status)
+ assert_empty response.errors
+ end
+
+ it "return false when the status if no Status provided" do
+ assert !response_no_status.send(:validate_success_status)
+ assert_includes response_no_status.errors, "The status code of the Response was not Success"
+ end
+
+ it "return false when the status if no StatusCode provided" do
+ assert !response_no_statuscode.send(:validate_success_status)
+ assert_includes response_no_statuscode.errors, "The status code of the Response was not Success"
+ end
+
+ it "return false when the status is not 'Success'" do
+ assert !response_statuscode_responder.send(:validate_success_status)
+ assert_includes response_statuscode_responder.errors, "The status code of the Response was not Success, was Responder"
+ end
+
+ it "return false when the status is not 'Success', and shows the StatusMessage" do
+ assert !response_statuscode_responder_and_msg.send(:validate_success_status)
+ assert_includes response_statuscode_responder_and_msg.errors, "The status code of the Response was not Success, was Responder -> something_is_wrong"
+ end
+ end
+
describe "#validate_structure" do
- it "raise when encountering a condition that prevents the document from being valid" do
- response = OneLogin::RubySaml::Response.new(response_document_2)
- response.send(:validate_structure)
- assert response.errors.include? "Schema validation failed"
+ it "return true when encountering a wellformed SAML Response" do
+ assert response.send(:validate_structure)
+ assert_empty response.errors
end
+
+ it "return false when encountering a mailformed element that prevents the document from being valid" do
+ response_without_attributes.soft = true
+ response_without_attributes.send(:validate_structure)
+ assert response_without_attributes.errors.include? "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
+ end
+
+ it "raise when encountering a mailformed element that prevents the document from being valid" do
+ response_without_attributes.soft = false
+ assert_raises(OneLogin::RubySaml::ValidationError) {
+ response_without_attributes.send(:validate_structure)
+ }
+ end
end
- describe "#is_valid?" do
- it "return false when response is initialized with blank data" do
- response = OneLogin::RubySaml::Response.new('')
- assert !response.is_valid?
+ describe "#validate_in_response_to" do
+ it "return true when the inResponseTo value matches the Request ID" do
+ response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "_fc4a34b0-7efb-012e-caae-782bcb13bb38")
+ assert response.send(:validate_in_response_to)
+ assert_empty response.errors
+ end
+
+ it "return true when no Request ID is provided for checking" do
+ response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings)
+ assert response.send(:validate_in_response_to)
+ assert_empty response.errors
end
- it "return false if settings have not been set" do
- response = OneLogin::RubySaml::Response.new(response_document)
- assert !response.is_valid?
+ it "return false when the inResponseTo value does not match the Request ID" do
+ response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "invalid_request_id")
+ assert !response.send(:validate_in_response_to)
+ 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"
end
+ end
- it "return true when the response is initialized with valid data" do
- response = OneLogin::RubySaml::Response.new(response_document_4)
- response.stubs(:conditions).returns(nil)
- assert !response.is_valid?
- settings = OneLogin::RubySaml::Settings.new
- assert !response.is_valid?
- response.settings = settings
- assert !response.is_valid?
- settings.idp_cert_fingerprint = signature_fingerprint_1
- assert response.is_valid?
+ describe "#validate_no_encrypted_attributes" do
+ it "return true when the assertion does not contain encrypted attributes" do
+ response_valid_signed.settings = settings
+ assert response_valid_signed.send(:validate_no_encrypted_attributes)
+ assert_empty response_valid_signed.errors
end
- it "should be idempotent when the response is initialized with invalid data" do
- response = OneLogin::RubySaml::Response.new(response_document_4)
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- response.settings = settings
- assert !response.is_valid?
- assert !response.is_valid?
+ it "return false when the assertion contains encrypted attributes" do
+ response_encrypted_attrs.settings = settings
+ assert !response_encrypted_attrs.send(:validate_no_encrypted_attributes)
+ assert_includes response_encrypted_attrs.errors, "There is an EncryptedAttribute in the Response and this SP not support them"
end
+ end
- it "should be idempotent when the response is initialized with valid data" do
- response = OneLogin::RubySaml::Response.new(response_document_4)
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- response.settings = settings
- settings.idp_cert_fingerprint = signature_fingerprint_1
- assert response.is_valid?
- assert response.is_valid?
+ describe "#validate_audience" do
+ it "return true when the audience is valid" do
+ response_valid_signed.settings = settings
+ response_valid_signed.settings.issuer = "https://someone.example.com/audience"
+ assert response_valid_signed.send(:validate_audience)
+ assert_empty response_valid_signed.errors
end
- it "return true when using certificate instead of fingerprint" do
- response = OneLogin::RubySaml::Response.new(response_document_4)
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- response.settings = settings
- settings.idp_cert = signature_1
- assert response.is_valid?
+ it "return true when there is not issuer defined" do
+ response_valid_signed.settings = settings
+ response_valid_signed.settings.issuer = nil
+ assert response_valid_signed.send(:validate_audience)
+ assert_empty response_valid_signed.errors
end
- it "not allow signature wrapping attack" do
- response = OneLogin::RubySaml::Response.new(response_document_4)
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- settings.idp_cert_fingerprint = signature_fingerprint_1
- response.settings = settings
- assert response.is_valid?
- assert_equal response.name_id, "test@onelogin.com"
+ it "return false when there is no valid audience" do
+ response_invalid_audience.settings = settings
+ response_invalid_audience.settings.issuer = "https://invalid.example.com/audience"
+ assert !response_invalid_audience.send(:validate_audience)
+ assert_includes response_invalid_audience.errors, "#{response_invalid_audience.settings.issuer} is not a valid audience for this Response"
end
+ end
- it "Prevent node text with comment (VU#475445) attack" do
- response_doc = File.read(File.join(File.dirname(__FILE__), "responses", 'response_node_text_attack.xml.base64'))
- response = OneLogin::RubySaml::Response.new(response_doc)
+ describe "#validate_issuer" do
+ it "return true when the issuer of the Message/Assertion matches the IdP entityId or it was empty" do
+ response_valid_signed.settings = settings
+ assert response_valid_signed.send(:validate_issuer)
+ assert_empty response_valid_signed.errors
- assert_equal "support@onelogin.com", response.name_id
- assert_equal "smith", response.attributes["surname"]
+ response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
+ assert response_valid_signed.send(:validate_issuer)
+ assert_empty response_valid_signed.errors
end
- it "support dynamic namespace resolution on signature elements" do
- response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- response.settings = settings
- settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
- XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
- assert response.validate!
+ it "return false when the issuer of the Message does not match the IdP entityId" do
+ response_invalid_issuer_message.settings = settings
+ response_invalid_issuer_message.settings.idp_entity_id = 'http://idp.example.com/'
+ assert !response_invalid_issuer_message.send(:validate_issuer)
+ 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/>"
end
- it "validate ADFS assertions" do
- response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
+ it "return false when the issuer of the Assertion does not match the IdP entityId" do
+ 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
+ end
+
+ describe "#validate_subject_confirmation" do
+ it "return true when valid subject confirmation" do
+ response_valid_signed.settings = settings
+ response_valid_signed.settings.assertion_consumer_service_url = 'recipient'
+ assert response_valid_signed.send(:validate_subject_confirmation)
+ assert_empty response_valid_signed.errors
+ end
+
+ it "return false when no subject confirmation data" do
+ response_no_subjectconfirmation_data.settings = settings
+ assert !response_no_subjectconfirmation_data.send(:validate_subject_confirmation)
+ assert_includes response_no_subjectconfirmation_data.errors, "A valid SubjectConfirmation was not found on this Response"
+ end
+
+ it "return false when no valid subject confirmation method" do
+ response_no_subjectconfirmation_method.settings = settings
+ assert !response_no_subjectconfirmation_method.send(:validate_subject_confirmation)
+ assert_includes response_no_subjectconfirmation_method.errors, "A valid SubjectConfirmation was not found on this Response"
+ end
+
+ it "return false when invalid inresponse" do
+ response_invalid_subjectconfirmation_inresponse.settings = settings
+ assert !response_invalid_subjectconfirmation_inresponse.send(:validate_subject_confirmation)
+ assert_includes response_invalid_subjectconfirmation_inresponse.errors, "A valid SubjectConfirmation was not found on this Response"
+ end
+
+ it "return false when invalid NotBefore" do
+ response_invalid_subjectconfirmation_nb.settings = settings
+ assert !response_invalid_subjectconfirmation_nb.send(:validate_subject_confirmation)
+ assert_includes response_invalid_subjectconfirmation_nb.errors, "A valid SubjectConfirmation was not found on this Response"
+ end
+
+ it "return false when invalid NotOnOrAfter" do
+ response_invalid_subjectconfirmation_noa.settings = settings
+ assert !response_invalid_subjectconfirmation_noa.send(:validate_subject_confirmation)
+ assert_includes response_invalid_subjectconfirmation_noa.errors, "A valid SubjectConfirmation was not found on this Response"
+ end
+ end
+
+ describe "#validate_session_expiration" do
+ it "return true when the session has not expired" do
+ response_valid_signed.settings = settings
+ assert response_valid_signed.send(:validate_session_expiration)
+ assert_empty response_valid_signed.errors
+ end
+
+ it "return false when the session has expired" do
response.settings = settings
- assert response.validate!
+ assert !response.send(:validate_session_expiration)
+ assert_includes response.errors, "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response"
end
+ end
- it "validate the digest" do
- response = OneLogin::RubySaml::Response.new(r1_response_document_6)
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
- settings.idp_cert = Base64.decode64(r1_signature_2)
+ describe "#validate_signature" do
+ it "return true when the signature is valid" do
+ settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
+ response_valid_signed.settings = settings
+ assert response_valid_signed.send(:validate_signature)
+ assert_empty response_valid_signed.errors
+ end
+
+ it "return false when no fingerprint" do
+ settings.idp_cert_fingerprint = nil
+ settings.idp_cert = nil
response.settings = settings
- assert response.validate!
+ assert !response.send(:validate_signature)
+ assert_includes response.errors, "Invalid Signature on SAML Response"
end
- it "validate SAML 2.0 XML structure" do
- resp_xml = Base64.decode64(response_document_4).gsub(/emailAddress/,'test')
- response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
- response.stubs(:conditions).returns(nil)
- settings = OneLogin::RubySaml::Settings.new
+ it "return false when the signature is invalid" do
settings.idp_cert_fingerprint = signature_fingerprint_1
response.settings = settings
- assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response.validate! }
+ assert !response.send(:validate_signature)
+ assert_includes response.errors, "Invalid Signature on SAML Response"
end
end
- describe "#name_id" do
+ describe "#nameid" do
it "extract the value of the name id element" do
- response = OneLogin::RubySaml::Response.new(response_document)
- assert_equal "support@onelogin.com", response.name_id
-
- response = OneLogin::RubySaml::Response.new(response_document_3)
- assert_equal "someone@example.com", response.name_id
+ assert_equal "support@onelogin.com", response.nameid
+ assert_equal "someone@example.com", response_with_signed_assertion.nameid
end
it "be extractable from an OpenSAML response" do
- response = OneLogin::RubySaml::Response.new(fixture(:open_saml))
- assert_equal "someone@example.org", response.name_id
+ response_open_saml = OneLogin::RubySaml::Response.new(fixture(:open_saml))
+ assert_equal "someone@example.org", response_open_saml.nameid
end
it "be extractable from a Simple SAML PHP response" do
+ response_ssp = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
+ assert_equal "someone@example.com", response_ssp.nameid
+ end
+ end
+
+ describe "#sessionindex" do
+ it "extract the value of the sessionindex element" do
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
- assert_equal "someone@example.com", response.name_id
+ assert_equal "_51be37965feb5579d803141076936dc2e9d1d98ebf", response.sessionindex
end
end
describe "#check_conditions" do
it "check time conditions" do
- response = OneLogin::RubySaml::Response.new(response_document)
- assert !response.send(:validate_conditions, true)
- response = OneLogin::RubySaml::Response.new(response_document_6)
- assert response.send(:validate_conditions, true)
- time = Time.parse("2011-06-14T18:25:01.516Z")
- Time.stubs(:now).returns(time)
- response = OneLogin::RubySaml::Response.new(response_document_5)
- assert response.send(:validate_conditions, true)
+ response.soft = true
+ assert !response.send(:validate_conditions)
+ response_time_updated = OneLogin::RubySaml::Response.new(response_document_without_recipient_with_time_updated)
+ response_time_updated.soft = true
+ assert response_time_updated.send(:validate_conditions)
+ Timecop.freeze(Time.parse("2011-06-14T18:25:01.516Z")) do
+ response_with_saml2_namespace = OneLogin::RubySaml::Response.new(response_document_with_saml2_namespace)
+ response_with_saml2_namespace.soft = true
+ assert response_with_saml2_namespace.send(:validate_conditions)
+ end
end
it "optionally allows for clock drift" do
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do
- response = OneLogin::RubySaml::Response.new(
- response_document_5,
- :allowed_clock_drift => 0.515
+ settings.soft = true
+ special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new(
+ response_document_with_saml2_namespace,
+ :allowed_clock_drift => 0.515,
+ :settings => settings
)
- assert !response.send(:validate_conditions, true)
+ assert !special_response_with_saml2_namespace.send(:validate_conditions)
end
Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do
- response = OneLogin::RubySaml::Response.new(
- response_document_5,
+ special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new(
+ response_document_with_saml2_namespace,
:allowed_clock_drift => 0.516
)
- assert response.send(:validate_conditions, true)
+ assert special_response_with_saml2_namespace.send(:validate_conditions)
end
end
end
describe "#attributes" do
it "extract the first attribute in a hash accessed via its symbol" do
- response = OneLogin::RubySaml::Response.new(response_document)
assert_equal "demo", response.attributes[:uid]
end
it "extract the first attribute in a hash accessed via its name" do
- response = OneLogin::RubySaml::Response.new(response_document)
assert_equal "demo", response.attributes["uid"]
end
it "extract all attributes" do
- response = OneLogin::RubySaml::Response.new(response_document)
assert_equal "demo", response.attributes[:uid]
assert_equal "value", response.attributes[:another_value]
end
it "work for implicit namespaces" do
- response = OneLogin::RubySaml::Response.new(response_document_3)
- assert_equal "someone@example.com", response.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
+ assert_equal "someone@example.com", response_with_signed_assertion.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
end
it "not raise errors about nil/empty attributes for EncryptedAttributes" do
- response = OneLogin::RubySaml::Response.new(response_document_7)
- assert_equal 'Demo', response.attributes["first_name"]
+ 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
- response = OneLogin::RubySaml::Response.new(response_document_4)
- assert_equal OneLogin::RubySaml::Attributes.new, response.attributes
+ assert_equal OneLogin::RubySaml::Attributes.new, response_unsigned.attributes
end
describe "#multiple values" do
it "extract single value as string" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_equal "demo", response.attributes[:uid]
+ assert_equal "demo", response_multiple_attr_values.attributes[:uid]
end
it "extract single value as string in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal ["demo"], response.attributes[:uid]
+ assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
# classes are not reloaded between tests so restore default
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "extract first of multiple values as string for b/w compatibility" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_equal 'value1', response.attributes[:another_value]
+ assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
end
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal ['value1', 'value2'], response.attributes[:another_value]
+ assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "return array with all attributes when asked in XML order" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
+ assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
end
it "return array with all attributes when asked in XML order in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
+ assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "return first of multiple values when multiple Attribute tags in XML" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_equal 'role1', response.attributes[:role]
+ assert_equal 'role1', response_multiple_attr_values.attributes[:role]
end
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal ['role1', 'role2', 'role3'], response.attributes[:role]
+ assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
+ assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
end
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
+ assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "return nil value correctly" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_nil response.attributes[:attribute_with_nil_value]
+ assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
end
it "return nil value correctly when not in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal [nil], response.attributes[:attribute_with_nil_value]
+ assert_equal [nil], response_multiple_attr_values.attributes[:attribute_with_nil_value]
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "return multiple values including nil and empty string" do
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
end
it "return multiple values from [] when not in compatibility mode off" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_equal ["", "valuePresent", nil, nil], response.attributes[:attribute_with_nils_and_empty_strings]
+ assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
it "check what happens when trying retrieve attribute that does not exists" do
- response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
- assert_nil response.attributes[:attribute_not_exists]
- assert_nil response.attributes.single(:attribute_not_exists)
- assert_nil response.attributes.multi(:attribute_not_exists)
+ assert_equal nil, response_multiple_attr_values.attributes[:attribute_not_exists]
+ assert_equal nil, response_multiple_attr_values.attributes.single(:attribute_not_exists)
+ assert_equal nil, response_multiple_attr_values.attributes.multi(:attribute_not_exists)
OneLogin::RubySaml::Attributes.single_value_compatibility = false
- assert_nil response.attributes[:attribute_not_exists]
- assert_nil response.attributes.single(:attribute_not_exists)
- assert_nil response.attributes.multi(:attribute_not_exists)
+ assert_equal nil, response_multiple_attr_values.attributes[:attribute_not_exists]
+ assert_equal nil, response_multiple_attr_values.attributes.single(:attribute_not_exists)
+ assert_equal nil, response_multiple_attr_values.attributes.multi(:attribute_not_exists)
OneLogin::RubySaml::Attributes.single_value_compatibility = true
end
end
end
describe "#session_expires_at" do
it "extract the value of the SessionNotOnOrAfter attribute" do
- response = OneLogin::RubySaml::Response.new(response_document)
assert response.session_expires_at.is_a?(Time)
+ end
- response = OneLogin::RubySaml::Response.new(response_document_2)
- assert_nil response.session_expires_at
+ it "return nil when the value of the SessionNotOnOrAfter is not set" do
+ assert_nil response_without_attributes.session_expires_at
end
end
- describe "#issuer" do
+ describe "#issuers" do
it "return the issuer inside the response assertion" do
- response = OneLogin::RubySaml::Response.new(response_document)
- assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
+ assert_includes response.issuers, "https://app.onelogin.com/saml/metadata/13590"
end
it "return the issuer inside the response" do
- response = OneLogin::RubySaml::Response.new(response_document_2)
- assert_equal "wibble", response.issuer
+ assert_includes response_without_attributes.issuers, "wibble"
end
end
describe "#success" do
it "find a status code that says success" do
- response = OneLogin::RubySaml::Response.new(response_document)
response.success?
end
end
describe '#xpath_first_from_signed_assertion' do
it 'not allow arbitrary code execution' do
malicious_response_document = fixture('response_eval', false)
- response = OneLogin::RubySaml::Response.new(malicious_response_document)
- response.send(:xpath_first_from_signed_assertion)
+ malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document)
+ malicious_response.send(:xpath_first_from_signed_assertion)
assert_nil $evalled
end
end
describe '#sign_document' do
@@ -406,16 +867,228 @@
formated_private_key = OneLogin::RubySaml::Utils.format_private_key(ruby_saml_key_text)
private_key = OpenSSL::PKey::RSA.new(formated_private_key)
document.sign_document(private_key, cert)
- saml_response = OneLogin::RubySaml::Response.new(document.to_s)
- settings = OneLogin::RubySaml::Settings.new
+ signed_response = OneLogin::RubySaml::Response.new(document.to_s)
settings.idp_cert = ruby_saml_cert_text
- saml_response.settings = settings
- time = Time.parse("2015-03-18T04:50:24Z")
- Time.stubs(:now).returns(time)
- saml_response.validate!
+ signed_response.settings = settings
+ Timecop.freeze(Time.parse("2015-03-18T04:50:24Z")) do
+ assert signed_response.is_valid?
+ end
+ assert_empty signed_response.errors
+ end
+ end
+
+ describe "retrieve nameID" do
+ it 'is possible when nameID inside the assertion' do
+ response_valid_signed.settings = settings
+ assert_equal "test@onelogin.com", response_valid_signed.nameid
+ end
+
+ it 'is not possible when encryptID inside the assertion but no private key' do
+ response_encrypted_nameid.settings = settings
+ assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
+ assert_equal "test@onelogin.com", response_encrypted_nameid.nameid
+ end
+ end
+
+ it 'is possible when encryptID inside the assertion and settings has the private key' do
+ settings.private_key = ruby_saml_key_text
+ response_encrypted_nameid.settings = settings
+ assert_equal "test@onelogin.com", response_encrypted_nameid.nameid
+ end
+
+ end
+
+ end
+
+ describe 'try to initialize an encrypted response' do
+ it 'raise if an encrypted assertion is found and no sp private key to decrypt it' do
+ 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"
+
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
+ end
+
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
+ end
+
+ settings.certificate = ruby_saml_cert_text
+ settings.private_key = ruby_saml_key_text
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ response3 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
+ response3.settings
+ end
+ end
+
+ it 'raise if an encrypted assertion is found and the sp private key is wrong' do
+ settings.certificate = ruby_saml_cert_text
+ wrong_private_key = ruby_saml_key_text.sub!('A', 'B')
+ settings.private_key = wrong_private_key
+
+ error_msg = "Neither PUB key nor PRIV key: nested asn1 error"
+ assert_raises(OpenSSL::PKey::RSAError, error_msg) do
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
+ end
+ end
+
+ it 'return true if an encrypted assertion is found and settings initialized with private_key' do
+ settings.certificate = ruby_saml_cert_text
+ settings.private_key = ruby_saml_key_text
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
+ assert response.decrypted_document
+
+ response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_signed_assertion, :settings => settings)
+ assert response2.decrypted_document
+
+ response3 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_signed_assertion, :settings => settings)
+ assert response3.decrypted_document
+
+ response4 = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
+ assert response4.decrypted_document
+ end
+ end
+
+ describe "retrieve nameID and attributes from encrypted assertion" do
+
+ before do
+ settings.idp_cert_fingerprint = 'EE:17:4E:FB:A8:81:71:12:0D:2A:78:43:BC:E7:0C:07:58:79:F4:F4'
+ settings.issuer = 'http://rubysaml.com:3000/saml/metadata'
+ settings.assertion_consumer_service_url = 'http://rubysaml.com:3000/saml/acs'
+ settings.certificate = ruby_saml_cert_text
+ settings.private_key = ruby_saml_key_text
+ end
+
+ it 'is possible when signed_message_encrypted_unsigned_assertion' do
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
+ Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
+ assert response.is_valid?
+ assert_empty response.errors
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
+ end
+ end
+
+ it 'is possible when signed_message_encrypted_signed_assertion' do
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_signed_assertion, :settings => settings)
+ Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
+ assert response.is_valid?
+ assert_empty response.errors
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
+ end
+ end
+
+ it 'is possible when unsigned_message_encrypted_signed_assertion' do
+ response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_signed_assertion, :settings => settings)
+ Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
+ assert response.is_valid?
+ assert_empty response.errors
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "98e2bb61075e951b37d6b3be6954a54b340d86c7", response.nameid
+ end
+ end
+
+ it 'is not possible when unsigned_message_encrypted_unsigned_assertion' do
+ response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_unsigned_assertion, :settings => settings)
+ Timecop.freeze(Time.parse("2015-03-19T14:30:31Z")) do
+ assert !response.is_valid?
+ assert_includes response.errors, "Found an unexpected number of Signature Element. SAML Response rejected"
+ end
+ end
+ end
+
+ describe "#decrypt_assertion" do
+ before do
+ settings.private_key = ruby_saml_key_text
+ end
+
+ describe "check right settings" do
+
+ it "is not possible to decrypt the assertion if no private key" do
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
+
+ encrypted_assertion_node = REXML::XPath.first(
+ response.document,
+ "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
+ { "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
+ )
+ response.settings.private_key = nil
+
+ error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it"
+ assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
+ decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
+ end
+ end
+
+ it "is possible to decrypt the assertion if private key" do
+ response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
+
+ encrypted_assertion_node = REXML::XPath.first(
+ response.document,
+ "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
+ { "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
+ )
+ decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
+
+ encrypted_assertion_node2 = REXML::XPath.first(
+ decrypted,
+ "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
+ { "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
+ )
+ assert_nil encrypted_assertion_node2
+ assert decrypted.name, "Assertion"
+ end
+
+ it "is possible to decrypt the assertion if private key but no saml namespace on the Assertion Element that is inside the EncryptedAssertion" do
+ unsigned_message_encrypted_assertion_without_saml_namespace = read_response('unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64')
+ response = OneLogin::RubySaml::Response.new(unsigned_message_encrypted_assertion_without_saml_namespace, :settings => settings)
+ encrypted_assertion_node = REXML::XPath.first(
+ response.document,
+ "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
+ { "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
+ )
+ decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
+
+ encrypted_assertion_node2 = REXML::XPath.first(
+ decrypted,
+ "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
+ { "p" => "urn:oasis:names:tc:SAML:2.0:protocol", "a" => "urn:oasis:names:tc:SAML:2.0:assertion" }
+ )
+ assert_nil encrypted_assertion_node2
+ assert decrypted.name, "Assertion"
+ end
+ end
+
+ describe "check different encrypt methods supported" do
+ it "EncryptionMethod DES-192 && Key Encryption Algorithm RSA-1_5" do
+ unsigned_message_des192_encrypted_signed_assertion = read_response('unsigned_message_des192_encrypted_signed_assertion.xml.base64')
+ response = OneLogin::RubySaml::Response.new(unsigned_message_des192_encrypted_signed_assertion, :settings => settings)
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
+ end
+
+ it "EncryptionMethod AES-128 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
+ unsigned_message_aes128_encrypted_signed_assertion = read_response('unsigned_message_aes128_encrypted_signed_assertion.xml.base64')
+ response = OneLogin::RubySaml::Response.new(unsigned_message_aes128_encrypted_signed_assertion, :settings => settings)
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
+ end
+
+ it "EncryptionMethod AES-192 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
+ unsigned_message_aes192_encrypted_signed_assertion = read_response('unsigned_message_aes192_encrypted_signed_assertion.xml.base64')
+ response = OneLogin::RubySaml::Response.new(unsigned_message_aes192_encrypted_signed_assertion, :settings => settings)
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
+ end
+
+ it "EncryptionMethod AES-256 && Key Encryption Algorithm RSA-OAEP-MGF1P" do
+ unsigned_message_aes256_encrypted_signed_assertion = read_response('unsigned_message_aes256_encrypted_signed_assertion.xml.base64')
+ response = OneLogin::RubySaml::Response.new(unsigned_message_aes256_encrypted_signed_assertion, :settings => settings)
+ assert_equal "test", response.attributes[:uid]
+ assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
end
end
end
end