diff --git a/README.md b/README.md index 5b0965940..59d764199 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ In the above there are a few assumptions in place, one being that the response.n def saml_settings settings = OneLogin::RubySaml::Settings.new - settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize" + settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume" settings.issuer = "http://#{request.host}/saml/metadata" settings.idp_sso_target_url = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}" settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}" diff --git a/lib/onelogin/ruby-saml/logoutresponse.rb b/lib/onelogin/ruby-saml/logoutresponse.rb index 6bcb23f73..4e605f218 100644 --- a/lib/onelogin/ruby-saml/logoutresponse.rb +++ b/lib/onelogin/ruby-saml/logoutresponse.rb @@ -21,6 +21,8 @@ class Logoutresponse < SamlMessage attr_reader :response attr_reader :options + attr_accessor :soft + # Constructs the Logout Response. A Logout Response Object that is an extension of the SamlMessage class. # @param response [String] A UUEncoded logout response from the IdP. # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings @@ -31,7 +33,13 @@ class Logoutresponse < SamlMessage def initialize(response, settings = nil, options = {}) @errors = [] raise ArgumentError.new("Logoutresponse cannot be nil") if response.nil? - self.settings = settings + @settings = settings + + if settings.nil? || settings.soft.nil? + @soft = true + else + @soft = settings.soft + end @options = options @response = decode_raw_saml(response) @@ -40,7 +48,7 @@ def initialize(response, settings = nil, options = {}) # Append the cause to the errors array, and based on the value of soft, return false or raise # an exception - def append_error(soft, error_msg) + def append_error(error_msg) @errors << error_msg return soft ? false : validation_error(error_msg) end @@ -50,36 +58,27 @@ def reset_errors! @errors = [] end - # Hard aux function to validate the Logout Response (soft = false) - # @return [Boolean] TRUE if the SAML Response is valid - # @raise [ValidationError] If validation fails - # - def validate! - validate(false) - end - # Aux function to validate the Logout Response # @return [Boolean] TRUE if the SAML Response is valid # @raise [ValidationError] if soft == false and validation fails # - def validate(soft = true) + def validate reset_errors! - validate_structure(soft) && - valid_state?(soft) && - valid_in_response_to?(soft) && - valid_issuer?(soft) && - success?(soft) + validate_structure && + valid_state? && + valid_in_response_to? && + valid_issuer? && + success? end # Checks if the Status has the "Success" code - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the logout response is invalid or not) # @return [Boolean] True if the StatusCode is Sucess # @raise [ValidationError] if soft == false and validation fails # - def success?(soft = true) + def success? unless status_code == "urn:oasis:names:tc:SAML:2.0:status:Success" - return append_error(soft, "Bad status code. Expected , but was: <#@status_code> ") + return append_error("Bad status code. Expected , but was: <#@status_code> ") end true end @@ -98,7 +97,6 @@ def in_response_to def issuer @issuer ||= begin node = REXML::XPath.first(document, "/p:LogoutResponse/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION }) - node ||= REXML::XPath.first(document, "/p:LogoutResponse/a:Assertion/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION }) node.nil? ? nil : node.text end end @@ -115,13 +113,12 @@ def status_code private # Validates the Logout Response against the specified schema. - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the logout response is invalid or not) # @return [Boolean] True if the XML is valid, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate_structure(soft = true) + def validate_structure unless valid_saml?(document, soft) - return append_error(soft, "Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd") + return append_error("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd") end true @@ -129,19 +126,18 @@ def validate_structure(soft = true) # Validates that the Logout Response provided in the initialization is not empty, # also check that the setting and the IdP cert were also provided - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the logout response is invalid or not) # @return [Boolean] True if the required info is found, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def valid_state?(soft = true) - return append_error(soft, "Blank response") if response.empty? + def valid_state? + return append_error("Blank logout response") if response.empty? - return append_error(soft, "No settings on response") if settings.nil? + return append_error("No settings on logout response") if settings.nil? - return append_error(soft, "No issuer in settings") if settings.issuer.nil? + return append_error("No issuer in settings of the logout response") if settings.issuer.nil? if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil? - return append_error(soft, "No fingerprint or certificate on settings") + return append_error("No fingerprint or certificate on settings of the logout response") end true @@ -152,26 +148,25 @@ def valid_state?(soft = true) # @return [Boolean] True if there is no request_id or it match, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def valid_in_response_to?(soft = true) - return true unless self.options.has_key? :matches_request_id + def valid_in_response_to? + return true unless options.has_key? :matches_request_id - unless self.options[:matches_request_id] == in_response_to - return append_error(soft, "Response does not match the request ID, expected: <#{self.options[:matches_request_id]}>, but was: <#{in_response_to}>") + unless options[:matches_request_id] == in_response_to + return append_error("Response does not match the request ID, expected: <#{options[:matches_request_id]}>, but was: <#{in_response_to}>") end true end # Validates the Issuer of the Logout Response - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the logout response is invalid or not) # @return [Boolean] True if the Issuer matchs the IdP entityId, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def valid_issuer?(soft = true) - return true if self.settings.idp_entity_id.nil? or self.issuer.nil? + def valid_issuer? + return true if settings.idp_entity_id.nil? || issuer.nil? - unless URI.parse(self.issuer) == URI.parse(self.settings.idp_entity_id) - append_error(soft, "Doesn't match the issuer, expected: <#{self.settings.issuer}>, but was: <#{issuer}>") + unless URI.parse(issuer) == URI.parse(settings.idp_entity_id) + append_error("Doesn't match the issuer, expected: <#{settings.issuer}>, but was: <#{issuer}>") end true end diff --git a/lib/onelogin/ruby-saml/response.rb b/lib/onelogin/ruby-saml/response.rb index 46a01edf2..4020a2e05 100644 --- a/lib/onelogin/ruby-saml/response.rb +++ b/lib/onelogin/ruby-saml/response.rb @@ -23,13 +23,16 @@ class Response < SamlMessage # Array with the causes [Array of strings] attr_accessor :errors - attr_reader :options - attr_reader :response attr_reader :document + attr_reader :response + attr_reader :options + + attr_accessor :soft # Constructs the SAML Response. A Response Object that is an extension of the SamlMessage class. # @param response [String] A UUEncoded SAML response from the IdP. - # @param options [Hash] Some options for the response validation process like skip the conditions validation + # @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object + # Or some options for the response validation process like skip the conditions validation # with the :skip_conditions, or allow a clock_drift when checking dates with :allowed_clock_drift # def initialize(response, options = {}) @@ -37,13 +40,22 @@ def initialize(response, options = {}) raise ArgumentError.new("Response cannot be nil") if response.nil? @options = options + + @soft = true + if !options.empty? && !options[:settings].nil? + @settings = options[:settings] + if !options[:settings].soft.nil? + @soft = options[:settings].soft + end + end + @response = decode_raw_saml(response) @document = XMLSecurity::SignedDocument.new(@response, @errors) end # Append the cause to the errors array, and based on the value of soft, return false or raise # an exception - def append_error(soft, error_msg) + def append_error(error_msg) @errors << error_msg return soft ? false : validation_error(error_msg) end @@ -60,16 +72,6 @@ def is_valid? validate end - # Hard aux function to validate the SAML Response (soft = false) - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not) - # @param request_id [String|nil] request_id The ID of the AuthNRequest sent by this SP to the IdP (if was sent any) - # @return [Boolean] TRUE if the SAML Response is valid - # @raise [ValidationError] if soft == false and validation fails - # - def validate! - validate(false) - end - # @return [String] the NameID provided by the SAML response from the IdP. # def name_id @@ -194,40 +196,37 @@ def issuer private # Validates the SAML Response (calls several validation methods) - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not) # @return [Boolean] True if the SAML Response is valid, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate(soft = true) + def validate reset_errors! - validate_response_state(soft) && - validate_structure(soft) && - validate_conditions(soft) && - validate_issuer(soft) && + validate_response_state && + validate_structure && + validate_conditions && + validate_issuer && document.validate_document(settings.get_fingerprint, soft, :fingerprint_alg => settings.idp_cert_fingerprint_algorithm) && - validate_success_status(soft) + validate_success_status end # Validates the Status of the SAML Response - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not) # @return [Boolean] True if the SAML Response contains a Success code, otherwise False if soft == false # @raise [ValidationError] if soft == false and validation fails # - def validate_success_status(soft = true) + def validate_success_status return true if success? - return append_error(soft, status_message) + return append_error(status_message) end # Validates the SAML Response against the specified schema. - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not) # @return [Boolean] True if the XML is valid, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate_structure(soft = true) + def validate_structure unless valid_saml?(document, soft) - return append_error(soft, "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd") + return append_error("Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd") end true @@ -240,12 +239,12 @@ def validate_structure(soft = true) # @raise [ValidationError] if soft == false and validation fails # def validate_response_state(soft = true) - return append_error(soft, "Blank response") if response.empty? + return append_error("Blank response") if response.empty? - return append_error(soft, "No settings on response") if settings.nil? + return append_error("No settings on response") if settings.nil? if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil? - return append_error(soft, "No fingerprint or certificate on settings") + return append_error("No fingerprint or certificate on settings") end true @@ -274,11 +273,10 @@ def xpath_first_from_signed_assertion(subelt=nil) # Validates the Conditions. (If the response was initialized with the :skip_conditions option, this validation is skipped, # If the response was initialized with the :allowed_clock_drift option, the timing validations are relaxed by the allowed_clock_drift value) - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not) # @return [Boolean] True if satisfies the conditions, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate_conditions(soft = true) + def validate_conditions return true if conditions.nil? return true if options[:skip_conditions] @@ -286,27 +284,26 @@ def validate_conditions(soft = true) if not_before && (now + (options[:allowed_clock_drift] || 0)) < not_before error_msg = "Current time is earlier than NotBefore condition #{(now + (options[:allowed_clock_drift] || 0))} < #{not_before})" - return append_error(soft, error_msg) + return append_error(error_msg) end if not_on_or_after && now >= not_on_or_after error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{not_on_or_after})" - return append_error(soft, error_msg) + return append_error(error_msg) end true end # Validates the Issuer (Of the SAML Response or of the SAML Assertion) - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not) # @return [Boolean] True if the Issuer matchs the IdP entityId, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate_issuer(soft = true) + def validate_issuer return true if settings.idp_entity_id.nil? unless URI.parse(issuer) == URI.parse(settings.idp_entity_id) - return append_error(soft, "Doesn't match the issuer, expected: <#{settings.idp_entity_id}>, but was: <#{issuer}>") + return append_error("Doesn't match the issuer, expected: <#{settings.idp_entity_id}>, but was: <#{issuer}>") end true end diff --git a/lib/onelogin/ruby-saml/settings.rb b/lib/onelogin/ruby-saml/settings.rb index 294401afd..56a2ca63f 100644 --- a/lib/onelogin/ruby-saml/settings.rb +++ b/lib/onelogin/ruby-saml/settings.rb @@ -13,9 +13,9 @@ def initialize(overrides = {}) config = DEFAULTS.merge(overrides) config.each do |k,v| acc = "#{k.to_s}=".to_sym - if self.respond_to? acc + if respond_to? acc value = v.is_a?(Hash) ? v.dup : v - self.send(acc, value) + send(acc, value) end end @attribute_consuming_service = AttributeService.new @@ -43,13 +43,15 @@ def initialize(overrides = {}) attr_accessor :protocol_binding attr_accessor :attributes_index attr_accessor :force_authn - attr_accessor :security attr_accessor :certificate attr_accessor :private_key attr_accessor :authn_context attr_accessor :authn_context_comparison attr_accessor :authn_context_decl_ref attr_reader :attribute_consuming_service + # Work-flow + attr_accessor :security + attr_accessor :soft # Compability attr_accessor :assertion_consumer_logout_service_url attr_accessor :assertion_consumer_logout_service_binding @@ -102,10 +104,10 @@ def single_logout_service_binding=(url) # @return [String] The fingerprint # def get_fingerprint - self.idp_cert_fingerprint || begin + idp_cert_fingerprint || begin idp_cert = get_idp_cert if idp_cert - fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(self.idp_cert_fingerprint_algorithm).new + fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(idp_cert_fingerprint_algorithm).new fingerprint_alg.hexdigest(idp_cert.to_der).upcase.scan(/../).join(":") end end @@ -146,6 +148,7 @@ def get_sp_key :idp_cert_fingerprint_algorithm => XMLSecurity::Document::SHA1, :compress_request => true, :compress_response => true, + :soft => true, :security => { :authn_requests_signed => false, :logout_requests_signed => false, diff --git a/lib/onelogin/ruby-saml/slo_logoutrequest.rb b/lib/onelogin/ruby-saml/slo_logoutrequest.rb index e2a7cfb54..047ace949 100644 --- a/lib/onelogin/ruby-saml/slo_logoutrequest.rb +++ b/lib/onelogin/ruby-saml/slo_logoutrequest.rb @@ -12,30 +12,45 @@ module RubySaml # class SloLogoutrequest < SamlMessage + # OneLogin::RubySaml::Settings Toolkit settings + attr_accessor :settings + # Array with the causes [Array of strings] attr_accessor :errors - attr_reader :options - attr_reader :request attr_reader :document + attr_reader :request + attr_reader :options + + attr_accessor :soft # Constructs the Logout Request. A Logout Request Object that is an extension of the SamlMessage class. # @param request [String] A UUEncoded Logout Request from the IdP. - # @param options [Hash] Some options for the logout request validation process like allow a clock drift when checking dates with :allowed_clock_drift + # @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object + # Or :allowed_clock_drift for the logout request validation process to allow a clock drift when checking dates with + # # @raise [ArgumentError] If Request is nil # def initialize(request, options = {}) @errors = [] - raise ArgumentError.new("Request cannot be nil") if request.nil? @options = options + + @soft = true + if !options.empty? && !options[:settings].nil? + @settings = options[:settings] + if !options[:settings].soft.nil? + @soft = options[:settings].soft + end + end + @request = decode_raw_saml(request) @document = REXML::Document.new(@request) end # Append the cause to the errors array, and based on the value of soft, return false or raise # an exception - def append_error(soft, error_msg) + def append_error(error_msg) @errors << error_msg return soft ? false : validation_error(error_msg) end @@ -52,14 +67,6 @@ def is_valid? validate end - # Validates the Logout Request (soft = false) - # @return [Boolean] TRUE if the Logout Request is valid - # @raise [ValidationError] if validation fails - # - def validate! - validate(false) - end - # @return [String] Gets the NameID of the Logout Request. # def name_id @@ -91,37 +98,34 @@ def issuer private # Hard aux function to validate the Logout Request - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the logout request is invalid or not) # @return [Boolean] TRUE if the Logout Request is valid # @raise [ValidationError] if soft == false and validation fails # - def validate(soft = true) + def validate reset_errors! - validate_request_state(soft) && - validate_structure(soft) + validate_request_state && + validate_structure end # Validates the Logout Request against the specified schema. - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the request is invalid or not) # @return [Boolean] True if the XML is valid, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate_structure(soft = true) + def validate_structure unless valid_saml?(document, soft) - return append_error(soft, "Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd") + return append_error("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd") end true end # Validates that the Logout Request provided in the initialization is not empty, - # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the logout request is invalid or not) # @return [Boolean] True if the required info is found, otherwise False if soft=True # @raise [ValidationError] if soft == false and validation fails # - def validate_request_state(soft = true) - return append_error(soft, "Blank request") if request.empty? + def validate_request_state + return append_error("Blank logout request") if request.empty? true end diff --git a/test/logout_responses/logoutresponse_fixtures.rb b/test/logout_responses/logoutresponse_fixtures.rb index da40603e9..d1de1a17a 100644 --- a/test/logout_responses/logoutresponse_fixtures.rb +++ b/test/logout_responses/logoutresponse_fixtures.rb @@ -1,6 +1,6 @@ #encoding: utf-8 -def default_response_opts +def default_logout_response_opts { :uuid => "_28024690-000e-0130-b6d2-38f6b112be8b", :issue_instant => Time.now.strftime('%Y-%m-%dT%H:%M:%SZ'), @@ -8,8 +8,8 @@ def default_response_opts } end -def valid_response(opts = {}) - opts = default_response_opts.merge!(opts) +def valid_logout_response_document(opts = {}) + opts = default_logout_response_opts.merge(opts) "" end -def unsuccessful_response(opts = {}) - opts = default_response_opts.merge!(opts) +def unsuccessful_logout_response_document(opts = {}) + opts = default_logout_response_opts.merge(opts) "" end -def invalid_xml_response +def invalid_xml_logout_response_document " diff --git a/test/logoutresponse_test.rb b/test/logoutresponse_test.rb index 3ca1ba963..8ce861405 100644 --- a/test/logoutresponse_test.rb +++ b/test/logoutresponse_test.rb @@ -6,105 +6,116 @@ class RubySamlTest < Minitest::Test describe "Logoutresponse" do + + let(:valid_logout_response_without_settings) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document) } + let(:valid_logout_response) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings) } + describe "#new" do it "raise an exception when response is initialized with nil" do assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) } end it "default to empty settings" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new( valid_response) - assert_nil logoutresponse.settings + assert_nil valid_logout_response_without_settings.settings end it "accept constructor-injected settings" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings) - refute_nil logoutresponse.settings + refute_nil valid_logout_response.settings end it "accept constructor-injected options" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} ) + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, nil, { :foo => :bar} ) assert !logoutresponse.options.empty? end it "support base64 encoded responses" do - expected_response = valid_response - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings) - - assert_equal expected_response, logoutresponse.response + generated_logout_response = valid_logout_response_document + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(generated_logout_response), settings) + assert_equal generated_logout_response, logoutresponse.response end end describe "#validate" do - it "validate the response" do - in_relation_to_request_id = random_id + describe "when soft=true" do + before do + settings.soft = true + end - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings) + it "validate the response" do + in_relation_to_request_id = random_id - assert logoutresponse.validate + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings) - assert_equal settings.issuer, logoutresponse.issuer - assert_equal in_relation_to_request_id, logoutresponse.in_response_to + assert logoutresponse.validate - assert logoutresponse.success? - end + assert_equal settings.issuer, logoutresponse.issuer + assert_equal in_relation_to_request_id, logoutresponse.in_response_to - it "invalidate responses with wrong id when given option :matches_uuid" do + assert logoutresponse.success? + end - expected_request_id = "_some_other_expected_uuid" - opts = { :matches_request_id => expected_request_id} + it "invalidate responses with wrong id when given option :matches_uuid" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts) + expected_request_id = "_some_other_expected_uuid" + opts = { :matches_request_id => expected_request_id} - assert !logoutresponse.validate - refute_equal expected_request_id, logoutresponse.in_response_to - end + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts) + + assert !logoutresponse.validate + refute_equal expected_request_id, logoutresponse.in_response_to + end - it "invalidate responses with wrong request status" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings) + it "invalidate responses with wrong request status" do + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings) - assert !logoutresponse.validate - assert !logoutresponse.success? + assert !logoutresponse.validate + assert !logoutresponse.success? + end end - end - describe "#validate!" do - it "validates good responses" do - in_relation_to_request_id = random_id + describe "when soft=false" do + before do + settings.soft = false + end - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings) + it "validates good responses" do + in_relation_to_request_id = random_id - logoutresponse.validate! - end + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings) - it "raises validation error when matching for wrong request id" do + assert logoutresponse.validate + end - expected_request_id = "_some_other_expected_id" - opts = { :matches_request_id => expected_request_id} + it "raises validation error when matching for wrong request id" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts) + expected_request_id = "_some_other_expected_id" + opts = { :matches_request_id => expected_request_id} - assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! } - end + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts) - it "raise validation error for wrong request status" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings) + assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate } + end - assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! } - end + it "raise validation error for wrong request status" do + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings) - it "raise validation error when in bad state" do - # no settings - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response) - assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! } - end + assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate } + end - it "raise validation error when in lack of issuer setting" do - bad_settings = settings - bad_settings.issuer = nil - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, bad_settings) - assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! } - end + it "raise validation error when in bad state" do + # no settings + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings) + assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate } + end + + it "raise validation error when in lack of issuer setting" do + bad_settings = settings + bad_settings.issuer = nil + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, bad_settings) + assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate } + end - it "raise error for invalid xml" do - logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings) + it "raise error for invalid xml" do + logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings) - assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! } + assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate } + end end end end diff --git a/test/response_test.rb b/test/response_test.rb index 66444bfaa..a74cb0a9f 100644 --- a/test/response_test.rb +++ b/test/response_test.rb @@ -24,7 +24,7 @@ class RubySamlTest < Minitest::Test ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document) ampersands_response.settings = settings ampersands_response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783' - ampersands_response.validate! + assert ampersands_response.is_valid? end it "adapt namespace" do @@ -48,10 +48,11 @@ class RubySamlTest < Minitest::Test end end - describe "#validate!" do + describe "#is_valid?" do it "raise when encountering a condition that prevents the document from being valid" do + response.soft = false assert_raises(OneLogin::RubySaml::ValidationError) do - response.validate! + response.is_valid? end end @@ -59,8 +60,9 @@ class RubySamlTest < Minitest::Test settings.idp_cert_fingerprint = nil settings.idp_cert = nil response.settings = settings + response.soft = false assert_raises(OneLogin::RubySaml::ValidationError, "No fingerprint or certificate on settings") do - response.validate! + response.is_valid? end end @@ -68,13 +70,15 @@ class RubySamlTest < Minitest::Test describe "#validate_structure" do it "false when encountering a mailformed element that prevents the document from being valid" do - response_without_attributes.send(:validate_structure, true) + 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, false) + response_without_attributes.send(:validate_structure) } end end @@ -82,10 +86,12 @@ class RubySamlTest < Minitest::Test describe "#is_valid?" do 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? end it "return false if settings have not been set" do + response.soft = true assert !response.is_valid? end @@ -134,7 +140,8 @@ class RubySamlTest < Minitest::Test 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.validate! + no_signature_response.soft = true + assert no_signature_response.is_valid? end it "validate ADFS assertions" do @@ -142,7 +149,8 @@ class RubySamlTest < Minitest::Test adfs_response.stubs(:conditions).returns(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" adfs_response.settings = settings - assert adfs_response.validate! + adfs_response.soft = true + assert adfs_response.is_valid? end it "validate the digest" do @@ -150,7 +158,7 @@ class RubySamlTest < Minitest::Test response_with_signed_assertion_2.stubs(:conditions).returns(nil) settings.idp_cert = Base64.decode64(certificate_without_head_foot) response_with_signed_assertion_2.settings = settings - assert response_with_signed_assertion_2.validate! + assert response_with_signed_assertion_2.is_valid? end it "validate SAML 2.0 XML structure" do @@ -159,7 +167,8 @@ class RubySamlTest < Minitest::Test response_unsigned_mod.stubs(:conditions).returns(nil) settings.idp_cert_fingerprint = signature_fingerprint_1 response_unsigned_mod.settings = settings - assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response_unsigned_mod.validate! } + response_unsigned_mod.soft = false + assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response_unsigned_mod.is_valid? } end end @@ -182,23 +191,28 @@ class RubySamlTest < Minitest::Test describe "#check_conditions" do it "check time conditions" do - 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) - assert response_time_updated.send(:validate_conditions, true) + response_time_updated.soft = true + assert response_time_updated.send(:validate_conditions) time = Time.parse("2011-06-14T18:25:01.516Z") Time.stubs(:now).returns(time) response_with_saml2_namespace = OneLogin::RubySaml::Response.new(response_document_with_saml2_namespace) - assert response_with_saml2_namespace.send(:validate_conditions, true) + response_with_saml2_namespace.soft = true + assert response_with_saml2_namespace.send(:validate_conditions) 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 + settings.soft = true special_response_with_saml2_namespace = OneLogin::RubySaml::Response.new( response_document_with_saml2_namespace, - :allowed_clock_drift => 0.515 + :allowed_clock_drift => 0.515, + :settings => settings ) - assert !special_response_with_saml2_namespace.send(:validate_conditions, true) + assert !special_response_with_saml2_namespace.send(:validate_conditions) end Timecop.freeze(Time.parse("2011-06-14T18:21:01Z")) do @@ -206,7 +220,7 @@ class RubySamlTest < Minitest::Test response_document_with_saml2_namespace, :allowed_clock_drift => 0.516 ) - assert special_response_with_saml2_namespace.send(:validate_conditions, true) + assert special_response_with_saml2_namespace.send(:validate_conditions) end end end @@ -357,7 +371,7 @@ class RubySamlTest < Minitest::Test malicious_response_document = fixture('response_eval', false) malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document) malicious_response.send(:xpath_first_from_signed_assertion) - assert_equal($evalled, nil) + assert_nil $evalled end end @@ -379,7 +393,7 @@ class RubySamlTest < Minitest::Test signed_response.settings = settings time = Time.parse("2015-03-18T04:50:24Z") Time.stubs(:now).returns(time) - signed_response.validate! + assert signed_response.is_valid? end end end diff --git a/test/slo_logoutrequest_test.rb b/test/slo_logoutrequest_test.rb index 6bad6dc4f..9700c90e8 100644 --- a/test/slo_logoutrequest_test.rb +++ b/test/slo_logoutrequest_test.rb @@ -6,58 +6,58 @@ class RubySamlTest < Minitest::Test describe "SloLogoutrequest" do - it "raise an exception when response is initialized with nil" do + it "raise an exception when the logout request is initialized with nil" do assert_raises(ArgumentError) { OneLogin::RubySaml::SloLogoutrequest.new(nil) } end describe "#is_valid?" do - it "return false when response is initialized with blank data" do - request = OneLogin::RubySaml::SloLogoutrequest.new('') - assert !request.is_valid? + it "return false when the logout request is initialized with blank data" do + logout_request = OneLogin::RubySaml::SloLogoutrequest.new('') + assert !logout_request.is_valid? end - it "return true when the request is initialized with valid data" do - request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) - assert request.is_valid? - assert_equal 'someone@example.org', request.name_id + it "return true when the logout_request is initialized with valid data" do + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) + assert logout_request.is_valid? + assert_equal 'someone@example.org', logout_request.name_id end - it "should be idempotent when the response is initialized with invalid data" do - request = OneLogin::RubySaml::SloLogoutrequest.new(invalid_xml_response) - assert !request.is_valid? - assert !request.is_valid? + it "should be idempotent when the logout_request is initialized with invalid data" do + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(invalid_xml_logout_response_document) + assert !logout_request.is_valid? + assert !logout_request.is_valid? end it "should be idempotent when the response is initialized with valid data" do - request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) - assert request.is_valid? - assert request.is_valid? + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) + assert logout_request.is_valid? + assert logout_request.is_valid? end - it "raise error for invalid xml" do - logout_request = OneLogin::RubySaml::SloLogoutrequest.new(invalid_xml_response) - assert_raises(OneLogin::RubySaml::ValidationError) { logout_request.validate! } + it "return false for invalid xml" do + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(invalid_xml_logout_response_document) + assert !logout_request.is_valid? end end describe "#name_id" do it "extract the value of the name id element" do - request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) - assert_equal "someone@example.org", request.name_id + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) + assert_equal "someone@example.org", logout_request.name_id end end describe "#issuer" do - it "return the issuer inside the request" do - request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) - assert_equal "https://app.onelogin.com/saml/metadata/SOMEACCOUNT", request.issuer + it "return the issuer inside the logout request" do + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) + assert_equal "https://app.onelogin.com/saml/metadata/SOMEACCOUNT", logout_request.issuer end end describe "#id" do it "extract the value of the ID attribute" do - request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) - assert_equal "_c0348950-935b-0131-1060-782bcb56fcaa", request.id + logout_request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) + assert_equal "_c0348950-935b-0131-1060-782bcb56fcaa", logout_request.id end end end diff --git a/test/xml_security_test.rb b/test/xml_security_test.rb index 528093218..9fdf7afa9 100644 --- a/test/xml_security_test.rb +++ b/test/xml_security_test.rb @@ -166,7 +166,7 @@ class XmlSecurityTest < Minitest::Test response.settings = settings assert !response.is_valid? settings.idp_cert_fingerprint = "e6 38 9a 20 b7 4f 13 db 6a bc b1 42 6a e7 52 1d d6 56 d4 1b".upcase.gsub(" ", ":") - assert response.validate! + assert response.is_valid? end it "return an empty list when inclusive namespace element is missing" do @@ -240,7 +240,7 @@ class XmlSecurityTest < Minitest::Test it "be able to validate a good response" do Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do - assert @response.validate! + assert @response.is_valid? end end