test/response_test.rb in ruby-saml-0.8.13 vs test/response_test.rb in ruby-saml-0.8.14

- old
+ new

@@ -2,11 +2,14 @@ class ResponseTest < Minitest::Test describe "Response" do it "raise an exception when response is initialized with nil" do - assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) } + err = assert_raises(ArgumentError) do + OneLogin::RubySaml::Response.new(nil) + end + assert_equal "Response cannot be nil", err.message 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) @@ -43,16 +46,75 @@ assert response.name_id.nil? end end describe "#validate!" do + it "raise when settings not initialized" do + response = OneLogin::RubySaml::Response.new(response_document) + err = assert_raises(OneLogin::RubySaml::ValidationError) do + response.validate! + end + assert_equal "No settings on response", err.message + end + 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.settings = settings + err = assert_raises(OneLogin::RubySaml::ValidationError) do response.validate! end + assert_equal "Current time is on or after NotOnOrAfter condition", err.message 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 response = OneLogin::RubySaml::Response.new('') @@ -62,10 +124,20 @@ it "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) + 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 @@ -78,11 +150,11 @@ it "should be idempotent when the response is initialized with invalid data" do response = OneLogin::RubySaml::Response.new(response_document_valid_signed) response.stubs(:conditions).returns(nil) settings = OneLogin::RubySaml::Settings.new - response.settings = settings + 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 @@ -93,15 +165,25 @@ response.settings.idp_cert_fingerprint = signature_fingerprint_valid_res assert response.is_valid? assert response.is_valid? end - it "return true when using certificate instead of fingerprint" do + it "return true when valid response and using fingerprint" 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 = "4B:68:C4:53:C7:D9:94:AA:D9:02:5C:99:D5:EF:CF:56:62:87:FE:8D" + 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 @@ -124,40 +206,54 @@ assert !response_wrapped.is_valid? assert_nil response_wrapped.name_id 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 - error_msg = "Found an unexpected number of Signature Element. SAML Response rejected" - assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do - response_no_signed_elements.validate! - end - 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 - error_msg = "Duplicated ID. SAML Response rejected" - assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do - response_multiple_signed.validate! - end - 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! 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 "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 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" @@ -172,20 +268,10 @@ settings.idp_cert = Base64.decode64(r1_signature_2) response.settings = settings assert response.validate! end - it "validate SAML 2.0 XML structure" do - resp_xml = Base64.decode64(response_document_valid_signed).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_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ 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 @@ -270,10 +356,58 @@ 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 + 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 + 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 + 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 + end + end + describe "#attributes" do before do @response = OneLogin::RubySaml::Response.new(response_document) end @@ -462,10 +596,14 @@ 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 @@ -473,11 +611,12 @@ 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_raises(OneLogin::RubySaml::ValidationError, "SAML Response must contain 1 assertion"){ response_wrapped.validate! } + assert !response_wrapped.validate! end end describe "signature wrapping attack - doubled signed assertion SAML response" do it "should not be valid" do