test/response_test.rb in ruby-saml-0.8.18 vs test/response_test.rb in ruby-saml-0.9

- old
+ new

@@ -1,694 +1,380 @@ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper")) -class ResponseTest < Minitest::Test +class RubySamlTest < Test::Unit::TestCase - describe "Response" do - it "raise an exception when response is initialized with nil" do - err = assert_raises(ArgumentError) do - OneLogin::RubySaml::Response.new(nil) - end - assert_equal "Response cannot be nil", err.message + context "Response" do + should "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 + should "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! end - it "adapt namespace" do + should "adapt namespace" do response = OneLogin::RubySaml::Response.new(response_document) - assert !response.name_id.nil? + assert_not_nil response.name_id response = OneLogin::RubySaml::Response.new(response_document_2) - assert !response.name_id.nil? + assert_not_nil response.name_id response = OneLogin::RubySaml::Response.new(response_document_3) - assert !response.name_id.nil? + assert_not_nil response.name_id end - it "default to raw input when a response is not Base64 encoded" do + should "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 end - describe "Assertion" do - it "only retreive an assertion with an ID that matches the signature's reference URI" do + context "Assertion" do + should "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 settings.idp_cert_fingerprint = signature_fingerprint_1 response.settings = settings - assert response.name_id.nil? + assert_nil response.name_id end end - describe "#validate!" do - it "raise when settings not initialized" do + context "#validate!" do + should "raise when encountering a condition that prevents the document from being valid" do response = OneLogin::RubySaml::Response.new(response_document) - err = assert_raises(OneLogin::RubySaml::ValidationError) do + assert_raise(OneLogin::RubySaml::ValidationError) do response.validate! end - assert_equal "No settings on response", err.message end + end - it "raise when encountering a condition that prevents the document from being valid" do - response = OneLogin::RubySaml::Response.new(response_document) - response.settings = settings - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.validate! - end - assert_equal "Current time is on or after NotOnOrAfter condition", err.message + context "#validate_structure" do + should "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" end - - it "raises an exception when no cert or fingerprint provided" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = nil - settings.idp_cert_fingerprint = nil - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.validate! - end - assert_equal "No fingerprint or certificate on settings", err.message - end - - it "raise when no signature" do - response_no_signed_elements = OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) - settings.idp_cert_fingerprint = signature_fingerprint_1 - response_no_signed_elements.settings = settings - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response_no_signed_elements.validate! - end - assert_equal "Found an unexpected number of Signature Element. SAML Response rejected", err.message - end - - it "raise when multiple signatures" do - response_multiple_signed = OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) - settings.idp_cert_fingerprint = signature_fingerprint_1 - response_multiple_signed.settings = settings - response_multiple_signed.stubs(:validate_structure).returns(true) - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response_multiple_signed.validate! - end - assert_equal "Duplicated ID. SAML Response rejected", err.message - end - - it "raise when fingerprint missmatch" do - resp_xml = Base64.decode64(response_document_valid_signed) - response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml)) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - settings.idp_cert_fingerprint = signature_fingerprint_1 - response.settings = settings - - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.validate! - end - assert_equal 'Fingerprint mismatch', err.message - end - end - describe "#is_valid?" do - it "return false when response is initialized with blank data" do + context "#is_valid?" do + should "return false when response is initialized with blank data" do response = OneLogin::RubySaml::Response.new('') assert !response.is_valid? end - it "return false if settings have not been set" do + should "return false if settings have not been set" do response = OneLogin::RubySaml::Response.new(response_document) assert !response.is_valid? end - it "return false when no cert or fingerprint provided" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) + should "return true 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 = nil - settings.idp_cert_fingerprint = nil assert !response.is_valid? - end - - it "return true when the response is initialized with valid data" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - 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? - response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res - response.validate! + settings.idp_cert_fingerprint = signature_fingerprint_1 + assert response.is_valid? end - it "should be idempotent when the response is initialized with invalid data" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) + should "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? end - it "should be idempotent when the response is initialized with valid data" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) + should "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 - response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res + settings.idp_cert_fingerprint = signature_fingerprint_1 assert response.is_valid? assert response.is_valid? end - it "return true when valid response and using fingerprint" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) + should "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 = nil - settings.idp_cert_fingerprint = "4B:68:C4:53:C7:D9:94:AA:D9:02:5C:99:D5:EF:CF:56:62:87:FE:8D" + settings.idp_cert = signature_1 assert response.is_valid? end - it "return true when valid response using certificate" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = valid_cert - assert response.is_valid? - end - - it "not allow signature wrapping attack" do + should "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 response.is_valid? assert response.name_id == "test@onelogin.com" end - it "not allow element wrapping attack" do - response_wrapped = OneLogin::RubySaml::Response.new(response_document_wrapped) - response_wrapped.stubs(:conditions).returns(nil) - response_wrapped.stubs(:validate_subject_confirmation).returns(true) - settings = OneLogin::RubySaml::Settings.new - response_wrapped.settings = settings - response_wrapped.settings.idp_cert_fingerprint = signature_fingerprint_1 - - assert !response_wrapped.is_valid? - assert_nil response_wrapped.name_id - end - - it "support dynamic namespace resolution on signature elements" do + should "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! end - it "support signature elements with no KeyInfo if cert provided" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = ruby_saml_cert - settings.idp_cert_fingerprint = nil - XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true) - assert response.validate! - end - - it "support signature elements with no KeyInfo if cert provided as text" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = ruby_saml_cert_text - settings.idp_cert_fingerprint = nil - XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true) - assert response.validate! - end - - it "returns an error if the signature contains no KeyInfo, cert is not provided and soft" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = nil - settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA" - assert !response.is_valid? - end - - it "raises an exception if the signature contains no KeyInfo, cert is not provided and no soft" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = nil - settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA" - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.validate! - end - assert_equal "Certificate element missing in response (ds:X509Certificate) and not cert provided at settings", err.message - end - - it "validate ADFS assertions" do + should "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" response.settings = settings assert response.validate! end - it "validate the digest" do + should "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) response.settings = settings assert response.validate! 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) - - assert_equal "support@onelogin.com", response.name_id - assert_equal "smith", response.attributes["surname"] - end - - describe '#validate_audience' do - it "return true when sp_entity_id not set or empty" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert_fingerprint = signature_fingerprint_valid_res - assert response.is_valid? - settings.sp_entity_id = '' - assert response.is_valid? - end - - it "return false when sp_entity_id set to incorrectly" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert_fingerprint = signature_fingerprint_valid_res - settings.sp_entity_id = 'wrong_audience' - assert !response.is_valid? - end - - it "return true when sp_entity_id set to correctly" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - response.stubs(:conditions).returns(nil) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert_fingerprint = signature_fingerprint_valid_res - settings.sp_entity_id = 'https://someone.example.com/audience' - assert response.is_valid? - end - end - end - - describe "#validate_issuer" do - it "return true when the issuer of the Message/Assertion matches the IdP entityId" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) + should "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 + settings.idp_cert_fingerprint = signature_fingerprint_1 response.settings = settings - assert response.send(:validate_issuer) - - response.settings.idp_entity_id = 'https://app.onelogin.com/saml2' - assert response.send(:validate_issuer) + assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response.validate! } end - - it "return false when the issuer of the Message does not match the IdP entityId" do - response = OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64")) - response.settings = settings - response.settings.idp_entity_id = 'http://idp.example.com/' - assert !response.send(:validate_issuer) - end - - it "return false when the issuer of the Assertion does not match the IdP entityId" do - response = OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64")) - response.settings = settings - response.settings.idp_entity_id = 'http://idp.example.com/' - assert !response.send(:validate_issuer) - end end - describe "#name_id" do - it "extract the value of the name id element" do + context "#name_id" do + should "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 end - it "be extractable from an OpenSAML response" do + should "be extractable from an OpenSAML response" do response = OneLogin::RubySaml::Response.new(fixture(:open_saml)) assert_equal "someone@example.org", response.name_id end - it "be extractable from a Simple SAML PHP response" do + should "be extractable from a Simple SAML PHP response" do response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php)) assert_equal "someone@example.com", response.name_id end end - describe "#name_id_format" do - it "extract the value of the name id element" do + context "#check_conditions" do + should "check time conditions" do response = OneLogin::RubySaml::Response.new(response_document) - response_signed = OneLogin::RubySaml::Response.new(response_document_valid_signed) - assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", response.name_id_format - assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", response_signed.name_id_format - 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 "_51be37965feb5579d803141076936dc2e9d1d98ebf", response.sessionindex - end - end - - describe "#response_id and assertion_id" do - it "extract the value of the Response and Assertion IDs" do - response = OneLogin::RubySaml::Response.new(response_document) - assert_equal "GOSAMLR12901174571794", response.response_id - assert_equal "pfxa46574df-b3b0-a06a-23c8-636413198772", response.assertion_id - 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) end - it "optionally allow for clock drift" do + should "optionally allow for clock drift" do # The NotBefore condition in the document is 2011-06-14T18:21:01.516Z - expected_time = Time.parse("2011-06-14T18:21:01Z") - Time.stubs(:now).returns(expected_time) + Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z")) response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.515) assert !response.send(:validate_conditions, true) - expected_time = Time.parse("2011-06-14T18:21:01Z") - Time.stubs(:now).returns(expected_time) + Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z")) response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.516) assert response.send(:validate_conditions, true) end end - describe "validate_signature" do - it "raises an exception when no cert or fingerprint provided" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = nil - settings.idp_cert_fingerprint = nil - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.send(:validate_signature, false) - end - assert_equal "No fingerprint or certificate on settings", err.message + context "#attributes" do + should "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 "raises an exception when wrong cert provided" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = ruby_saml_cert2 - settings.idp_cert_fingerprint = nil - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.send(:validate_signature, false) - end - assert_equal "Fingerprint mismatch", err.message + should "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 "raises an exception when wrong fingerprint provided" do - response = OneLogin::RubySaml::Response.new(response_document_valid_signed) - settings = OneLogin::RubySaml::Settings.new - response.settings = settings - settings.idp_cert = nil - settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA" - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response.send(:validate_signature, false) - end - assert_equal "Fingerprint mismatch", err.message + should "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 "raises an exception when no signature" do - response_no_signed_elements = OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) - settings.idp_cert_fingerprint = signature_fingerprint_1 - response_no_signed_elements.settings = settings - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response_no_signed_elements.validate! - end - assert_equal "Found an unexpected number of Signature Element. SAML Response rejected", err.message + should "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"] end - end - describe "#attributes" do - before do - @response = OneLogin::RubySaml::Response.new(response_document) + should "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"] end - it "extract the first attribute in a hash accessed via its symbol" do - assert_equal "demo", @response.attributes[:uid] + should "not raise on responses without attributes" do + response = OneLogin::RubySaml::Response.new(response_document_4) + assert_equal OneLogin::RubySaml::Attributes.new, response.attributes end - it "extract the first attribute in a hash accessed via its name" do - assert_equal "demo", @response.attributes["uid"] - end + context "#multiple values" do + should "extract single value as string" do + response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) + assert_equal "demo", response.attributes[:uid] + end - it "extract all attributes" do - assert_equal "demo", @response.attributes[:uid] - assert_equal "value", @response.attributes[:another_value] - end + should "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] + # classes are not reloaded between tests so restore default + OneLogin::RubySaml::Attributes.single_value_compatibility = true + end - it "work for implicit namespaces" do - response_3 = OneLogin::RubySaml::Response.new(response_document_3) - assert_equal "someone@example.com", response_3.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"] - end + should "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] + end - it "not raise on responses without attributes" do - response_4 = OneLogin::RubySaml::Response.new(response_document_4) - assert_equal OneLogin::RubySaml::Attributes.new, response_4.attributes - end + should "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] + OneLogin::RubySaml::Attributes.single_value_compatibility = true + 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 + should "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) + end - it "be manipulable by hash methods such as #merge and not raise an exception" do - @response.attributes.merge({ :testing_attribute => "test" }) - end + should "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) + OneLogin::RubySaml::Attributes.single_value_compatibility = true + end - it "be manipulable by hash methods such as #shift and not raise an exception" do - @response.attributes.shift - end + should "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] + end - it "be manipulable by hash methods such as #merge! and actually contain the value" do - @response.attributes.merge!({ :testing_attribute => "test" }) - assert @response.attributes[:testing_attribute] - end + should "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] + OneLogin::RubySaml::Attributes.single_value_compatibility = true + end - it "be manipulable by hash methods such as #shift and actually remove the value" do - removed_value = @response.attributes.shift - assert_nil @response.attributes[removed_value[0]] + should "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) + end + + should "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) + OneLogin::RubySaml::Attributes.single_value_compatibility = true + end + + should "return nil value correctly" do + response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) + assert_nil response.attributes[:attribute_with_nil_value] + end + + should "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] + OneLogin::RubySaml::Attributes.single_value_compatibility = true + end + + should "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 + + should "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] + OneLogin::RubySaml::Attributes.single_value_compatibility = true + end + + should "check what happens when trying retrieve attribute that does not exists" do + response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) + assert_equal nil, response.attributes[:attribute_not_exists] + assert_equal nil, response.attributes.single(:attribute_not_exists) + assert_equal nil, response.attributes.multi(:attribute_not_exists) + + OneLogin::RubySaml::Attributes.single_value_compatibility = false + assert_equal nil, response.attributes[:attribute_not_exists] + assert_equal nil, response.attributes.single(:attribute_not_exists) + assert_equal nil, response.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 + context "#session_expires_at" do + should "extract the value of the SessionNotOnOrAfter attribute" do response = OneLogin::RubySaml::Response.new(response_document) assert response.session_expires_at.is_a?(Time) response = OneLogin::RubySaml::Response.new(response_document_2) - assert response.session_expires_at.nil? + assert_nil response.session_expires_at end end - describe "#issuer" do - it "return the issuer inside the response assertion" do + context "#issuer" do + should "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 end - it "return the issuer inside the response" do + should "return the issuer inside the response" do response = OneLogin::RubySaml::Response.new(response_document_2) assert_equal "wibble", response.issuer end end - describe "#success" do - it "find a status code that says success" do + context "#success" do + should "find a status code that says success" do response = OneLogin::RubySaml::Response.new(response_document) - assert response.send(:success?) + response.success? end end - describe '#xpath_first_from_signed_assertion' do - it 'not allow arbitrary code execution' do + context '#xpath_first_from_signed_assertion' do + should '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) - assert_nil $evalled - end - end - - describe "#multiple values" do - it "extract single value as string" do - assert_equal "demo", response_multiple_attr_values.attributes[:uid] - end - - it "extract single value as string in compatibility mode off" do - OneLogin::RubySaml::Attributes.single_value_compatibility = false - 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 - 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 - OneLogin::RubySaml::Attributes.single_value_compatibility = false - 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 - 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 - OneLogin::RubySaml::Attributes.single_value_compatibility = false - 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 - 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 - OneLogin::RubySaml::Attributes.single_value_compatibility = false - 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 - 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 - 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 - OneLogin::RubySaml::Attributes.single_value_compatibility = false - assert [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 - OneLogin::RubySaml::Attributes.single_value_compatibility = false - 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 - assert_nil response_multiple_attr_values.attributes[:attribute_not_exists] - assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists) - assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists) - - OneLogin::RubySaml::Attributes.single_value_compatibility = false - assert_nil response_multiple_attr_values.attributes[:attribute_not_exists] - assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists) - assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists) - OneLogin::RubySaml::Attributes.single_value_compatibility = true - end - end - - describe "signature wrapping attack with encrypted assertion" do - it "should not be valid" do - settings = OneLogin::RubySaml::Settings.new - settings.private_key = valid_key - signature_wrapping_attack = read_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? - err = assert_raises(OneLogin::RubySaml::ValidationError) do - response_wrapped.validate! - end - assert_equal "Found an invalid Signed Element. SAML Response rejected", err.message - end - end - - describe "signature wrapping attack - concealed SAML response body" do - it "should not be valid" do - settings = OneLogin::RubySaml::Settings.new - signature_wrapping_attack = read_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) - response_wrapped.stubs(:validate_structure).returns(true) - assert !response_wrapped.is_valid? - assert !response_wrapped.validate! - end - end - - describe "signature wrapping attack - doubled signed assertion SAML response" do - it "should not be valid" do - settings = OneLogin::RubySaml::Settings.new - signature_wrapping_attack = read_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_equal($evalled, nil) end end end end