diff --git a/.travis.yml b/.travis.yml index 12a22ec81..2d92c4ebb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: ruby rvm: - - 1.8.7 - 1.9.3 - 2.0.0 - 2.1.10 @@ -10,7 +9,7 @@ rvm: - 2.5.8 - 2.6.6 - 2.7.2 - - ree + - 3.0.0 - jruby-1.7.27 - jruby-9.1.17.0 - jruby-9.2.13.0 @@ -21,10 +20,6 @@ before_install: - gem update bundler matrix: exclude: - - rvm: 1.8.7 - gemfile: Gemfile - - rvm: ree - gemfile: Gemfile - rvm: jruby-1.7.27 gemfile: gemfiles/nokogiri-1.5.gemfile - rvm: jruby-9.1.17.0 @@ -33,6 +28,8 @@ matrix: gemfile: gemfiles/nokogiri-1.5.gemfile - rvm: 2.1.5 gemfile: gemfiles/nokogiri-1.5.gemfile + - rvm: 2.1.10 + gemfile: gemfiles/nokogiri-1.5.gemfile - rvm: 2.2.10 gemfile: gemfiles/nokogiri-1.5.gemfile - rvm: 2.3.8 @@ -45,5 +42,7 @@ matrix: gemfile: gemfiles/nokogiri-1.5.gemfile - rvm: 2.7.2 gemfile: gemfiles/nokogiri-1.5.gemfile + - rvm: 3.0.0 + gemfile: gemfiles/nokogiri-1.5.gemfile env: - JRUBY_OPTS="--debug" diff --git a/README.md b/README.md index 87e6314c9..0b30924a0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.svg)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master) [![Gem Version](https://badge.fury.io/rb/ruby-saml.svg)](http://badge.fury.io/rb/ruby-saml) +## Updating from 1.11.x to 1.12.0 +Version `1.12.0` adds support for gcm algorithm and +change/adds specific error messages for signature validations + ## Updating from 1.10.x to 1.11.0 Version `1.11.0` deprecates the use of `settings.issuer` in favour of `settings.sp_entity_id`. There are two new security settings: `settings.security[:check_idp_cert_expiration]` and `settings.security[:check_sp_cert_expiration]` (both false by default) that check if the IdP or SP X.509 certificate has expired, respectively. @@ -261,8 +265,8 @@ def saml_settings settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume" settings.sp_entity_id = "http://#{request.host}/saml/metadata" settings.idp_entity_id = "https://app.onelogin.com/saml/metadata/#{OneLoginAppId}" - settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}" - settings.idp_slo_target_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}" + settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}" + settings.idp_slo_service_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}" settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1" settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" @@ -327,7 +331,7 @@ class SamlController < ApplicationController settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume" settings.sp_entity_id = "http://#{request.host}/saml/metadata" - settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}" + settings.idp_sso_service_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}" settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" @@ -400,8 +404,8 @@ end The following attributes are set: * idp_entity_id * name_identifier_format - * idp_sso_target_url - * idp_slo_target_url + * idp_sso_service_url + * idp_slo_service_url * idp_attribute_names * idp_cert * idp_cert_fingerprint @@ -467,6 +471,9 @@ Imagine this `saml:AttributeStatement` + + usersName + ``` @@ -477,7 +484,8 @@ pp(response.attributes) # is an OneLogin::RubySaml::Attributes object "another_value"=>["value1", "value2"], "role"=>["role1", "role2", "role3"], "attribute_with_nil_value"=>[nil], - "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]}> + "attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil] + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"=>["usersName"]}> # Active single_value_compatibility OneLogin::RubySaml::Attributes.single_value_compatibility = true @@ -494,6 +502,9 @@ pp(response.attributes.single(:role)) pp(response.attributes.multi(:role)) # => ["role1", "role2", "role3"] +pp(response.attributes.fetch(:role)) +# => "role1" + pp(response.attributes[:attribute_with_nil_value]) # => nil @@ -509,6 +520,9 @@ pp(response.attributes.single(:not_exists)) pp(response.attributes.multi(:not_exists)) # => nil +pp(response.attributes.fetch(/givenname/)) +# => "usersName" + # Deactive single_value_compatibility OneLogin::RubySaml::Attributes.single_value_compatibility = false @@ -524,6 +538,9 @@ pp(response.attributes.single(:role)) pp(response.attributes.multi(:role)) # => ["role1", "role2", "role3"] +pp(response.attributes.fetch(:role)) +# => ["role1", "role2", "role3"] + pp(response.attributes[:attribute_with_nil_value]) # => [nil] @@ -538,6 +555,9 @@ pp(response.attributes.single(:not_exists)) pp(response.attributes.multi(:not_exists)) # => nil + +pp(response.attributes.fetch(/givenname/)) +# => ["usersName"] ``` The `saml:AuthnContextClassRef` of the AuthNRequest can be provided by `settings.authn_context`; possible values are described at [SAMLAuthnCxt]. The comparison method can be set using `settings.authn_context_comparison` parameter. Possible values include: 'exact', 'better', 'maximum' and 'minimum' (default value is 'exact'). @@ -623,21 +643,27 @@ def sp_logout_request # LogoutRequest accepts plain browser requests w/o paramters settings = saml_settings - if settings.idp_slo_target_url.nil? + if settings.idp_slo_service_url.nil? logger.info "SLO IdP Endpoint not found in settings, executing then a normal logout'" delete_session else - # Since we created a new SAML request, save the transaction_id - # to compare it with the response we get back logout_request = OneLogin::RubySaml::Logoutrequest.new() - session[:transaction_id] = logout_request.uuid - logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{session[:transaction_id]}'" + logger.info "New SP SLO for userid '#{session[:userid]}' transactionid '#{logout_request.uuid}'" if settings.name_identifier_value.nil? settings.name_identifier_value = session[:userid] end + # Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34]) + logged_user = session[:userid] + logger.info "Delete session for '#{session[:userid]}'" + delete_session + + # Save the transaction_id to compare it with the response we get back + session[:transaction_id] = logout_request.uuid + session[:logged_out_user] = logged_user + relayState = url_for controller: 'saml', action: 'index' redirect_to(logout_request.create(settings, :RelayState => relayState)) end @@ -665,7 +691,7 @@ def process_logout_response logger.error "The SAML Logout Response is invalid" else # Actually log out this session - logger.info "Delete session for '#{session[:userid]}'" + logger.info "SLO completed for '#{session[:logged_out_user]}'" delete_session end end @@ -674,6 +700,8 @@ end def delete_session session[:userid] = nil session[:attributes] = nil + session[:transaction_id] = nil + session[:logged_out_user] = nil end ``` @@ -741,6 +769,14 @@ class SamlController < ApplicationController end ``` +You can add ValidUntil and CacheDuration to the XML Metadata using instead +```ruby + # Valid until => 2 days from now + # Cache duration = 604800s = 1 week + valid_until = Time.now + 172800 + cache_duration = 604800 + meta.generate(settings, false, valid_until, cache_duration) +``` ## Clock Drift diff --git a/changelog.md b/changelog.md index f04c9ed13..acf83293c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,23 @@ # RubySaml Changelog +### 1.12.0 (Feb 18, 2021) +* Support AES-128-GCM, AES-192-GCM, and AES-256-GCM encryptions +* Parse & return SLO ResponseLocation in IDPMetadataParser & Settings +* Adding idp_sso_service_url and idp_slo_service_url settings +* [#536](https://github.com/onelogin/ruby-saml/pull/536) Adding feth method to be able retrieve attributes based on regex +* Reduce size of built gem by excluding the test folder +* Improve protection on Zlib deflate decompression bomb attack. +* Add ValidUntil and cacheDuration support on Metadata generator +* Add support for cacheDuration at the IdpMetadataParser +* Support customizable statusCode on generated LogoutResponse +* [#545](https://github.com/onelogin/ruby-saml/pull/545) More specific error messages for signature validation +* Support Process Transform +* Raise SettingError if invoking an action with no endpoint defined on the settings +* Made IdpMetadataParser more extensible for subclasses +*[#548](https://github.com/onelogin/ruby-saml/pull/548) Add :skip_audience option +* [#555](https://github.com/onelogin/ruby-saml/pull/555) Define 'soft' variable to prevent exception when doc cert is invalid +* Improve documentation + ### 1.11.0 (Jul 24, 2019) * Deprecate settings.issuer in favor of settings.sp_entity_id diff --git a/lib/onelogin/ruby-saml/attributes.rb b/lib/onelogin/ruby-saml/attributes.rb index c002429a3..054084fe3 100644 --- a/lib/onelogin/ruby-saml/attributes.rb +++ b/lib/onelogin/ruby-saml/attributes.rb @@ -113,6 +113,29 @@ def ==(other) end end + # Fetch attribute value using name or regex + # @param name [String|Regexp] The attribute name + # @return [String|Array] Depending on the single value compatibility status this returns: + # - First value if single_value_compatibility = true + # response.attributes['mail'] # => 'user@example.com' + # - All values if single_value_compatibility = false + # response.attributes['mail'] # => ['user@example.com','user@example.net'] + # + def fetch(name) + attributes.each_key do |attribute_key| + if name.is_a?(Regexp) + if name.method_exists? :match? + return self[attribute_key] if name.match?(attribute_key) + else + return self[attribute_key] if name.match(attribute_key) + end + elsif canonize_name(name) == canonize_name(attribute_key) + return self[attribute_key] + end + end + nil + end + protected # stringifies all names so both 'email' and :email return the same result diff --git a/lib/onelogin/ruby-saml/authrequest.rb b/lib/onelogin/ruby-saml/authrequest.rb index a4fbf0ca4..d061f994f 100644 --- a/lib/onelogin/ruby-saml/authrequest.rb +++ b/lib/onelogin/ruby-saml/authrequest.rb @@ -24,6 +24,10 @@ def initialize @uuid = OneLogin::RubySaml::Utils.uuid end + def request_id + @uuid + end + # Creates the AuthNRequest string. # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState @@ -31,14 +35,14 @@ def initialize # def create(settings, params = {}) params = create_params(settings, params) - params_prefix = (settings.idp_sso_target_url =~ /\?/) ? '&' : '?' + params_prefix = (settings.idp_sso_service_url =~ /\?/) ? '&' : '?' saml_request = CGI.escape(params.delete("SAMLRequest")) request_params = "#{params_prefix}SAMLRequest=#{saml_request}" params.each_pair do |key, value| request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}" end - raise SettingError.new "Invalid settings, idp_sso_target_url is not set!" if settings.idp_sso_target_url.nil? or settings.idp_sso_target_url.empty? - @login_url = settings.idp_sso_target_url + request_params + raise SettingError.new "Invalid settings, idp_sso_service_url is not set!" if settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty? + @login_url = settings.idp_sso_service_url + request_params end # Creates the Get parameters for the request. @@ -108,7 +112,7 @@ def create_xml_document(settings) root.attributes['ID'] = uuid root.attributes['IssueInstant'] = time root.attributes['Version'] = "2.0" - root.attributes['Destination'] = settings.idp_sso_target_url unless settings.idp_sso_target_url.nil? or settings.idp_sso_target_url.empty? + root.attributes['Destination'] = settings.idp_sso_service_url unless settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty? root.attributes['IsPassive'] = settings.passive unless settings.passive.nil? root.attributes['ProtocolBinding'] = settings.protocol_binding unless settings.protocol_binding.nil? root.attributes["AttributeConsumingServiceIndex"] = settings.attributes_index unless settings.attributes_index.nil? diff --git a/lib/onelogin/ruby-saml/idp_metadata_parser.rb b/lib/onelogin/ruby-saml/idp_metadata_parser.rb index 3cb45e21a..a45cd2fc4 100644 --- a/lib/onelogin/ruby-saml/idp_metadata_parser.rb +++ b/lib/onelogin/ruby-saml/idp_metadata_parser.rb @@ -113,6 +113,16 @@ def parse_remote_to_array(url, validate_cert = true, options = {}) def parse(idp_metadata, options = {}) parsed_metadata = parse_to_hash(idp_metadata, options) + unless parsed_metadata[:cache_duration].nil? + cache_valid_until_timestamp = OneLogin::RubySaml::Utils.parse_duration(parsed_metadata[:cache_duration]) + if parsed_metadata[:valid_until].nil? || cache_valid_until_timestamp < Time.parse(parsed_metadata[:valid_until], Time.now.utc).to_i + parsed_metadata[:valid_until] = Time.at(cache_valid_until_timestamp).utc.strftime("%Y-%m-%dT%H:%M:%SZ") + end + end + # Remove the cache_duration because on the settings + # we only gonna suppot valid_until + parsed_metadata.delete(:cache_duration) + settings = options[:settings] if settings.nil? @@ -210,13 +220,15 @@ def to_hash(options = {}) { :idp_entity_id => @entity_id, :name_identifier_format => idp_name_id_format, - :idp_sso_target_url => single_signon_service_url(options), - :idp_slo_target_url => single_logout_service_url(options), + :idp_sso_service_url => single_signon_service_url(options), + :idp_slo_service_url => single_logout_service_url(options), + :idp_slo_response_service_url => single_logout_response_service_url(options), :idp_attribute_names => attribute_names, :idp_cert => nil, :idp_cert_fingerprint => nil, :idp_cert_multi => nil, - :valid_until => valid_until + :valid_until => valid_until, + :cache_duration => cache_duration, }.tap do |response_hash| merge_certificates_into(response_hash) unless certificates.nil? end @@ -240,6 +252,13 @@ def valid_until root.attributes['validUntil'] if root && root.attributes end + # @return [String|nil] 'cacheDuration' attribute of metadata + # + def cache_duration + root = @idpsso_descriptor.root + root.attributes['cacheDuration'] if root && root.attributes + end + # @param binding_priority [Array] # @return [String|nil] SingleSignOnService binding if exists # @@ -304,6 +323,21 @@ def single_logout_service_url(options = {}) return node.value if node end + # @param options [Hash] + # @return [String|nil] SingleLogoutService response url if exists + # + def single_logout_response_service_url(options = {}) + binding = single_logout_service_binding(options[:slo_binding]) + return if binding.nil? + + node = REXML::XPath.first( + @idpsso_descriptor, + "md:SingleLogoutService[@Binding=\"#{binding}\"]/@ResponseLocation", + SamlMetadata::NAMESPACE + ) + return node.value if node + end + # @return [String|nil] Unformatted Certificate if exists # def certificates diff --git a/lib/onelogin/ruby-saml/logoutrequest.rb b/lib/onelogin/ruby-saml/logoutrequest.rb index 8e8fa739e..0187b2f34 100644 --- a/lib/onelogin/ruby-saml/logoutrequest.rb +++ b/lib/onelogin/ruby-saml/logoutrequest.rb @@ -21,6 +21,10 @@ def initialize @uuid = OneLogin::RubySaml::Utils.uuid end + def request_id + @uuid + end + # Creates the Logout Request string. # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState diff --git a/lib/onelogin/ruby-saml/logoutresponse.rb b/lib/onelogin/ruby-saml/logoutresponse.rb index 798891435..47b496e86 100644 --- a/lib/onelogin/ruby-saml/logoutresponse.rb +++ b/lib/onelogin/ruby-saml/logoutresponse.rb @@ -47,6 +47,10 @@ def initialize(response, settings = nil, options = {}) @document = XMLSecurity::SignedDocument.new(@response) end + def response_id + id(document) + end + # Checks if the Status has the "Success" code # @return [Boolean] True if the StatusCode is Sucess # @raise [ValidationError] if soft == false and validation fails diff --git a/lib/onelogin/ruby-saml/metadata.rb b/lib/onelogin/ruby-saml/metadata.rb index 4d27840c7..d85338035 100644 --- a/lib/onelogin/ruby-saml/metadata.rb +++ b/lib/onelogin/ruby-saml/metadata.rb @@ -15,9 +15,11 @@ class Metadata # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings # @param pretty_print [Boolean] Pretty print or not the response # (No pretty print if you gonna validate the signature) + # @param valid_until [DateTime] Metadata's valid time + # @param cache_duration [Integer] Duration of the cache in seconds # @return [String] XML Metadata of the Service Provider # - def generate(settings, pretty_print=false) + def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil) meta_doc = XMLSecurity::Document.new namespaces = { "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata" @@ -60,6 +62,12 @@ def generate(settings, pretty_print=false) if settings.sp_entity_id root.attributes["entityID"] = settings.sp_entity_id end + if valid_until + root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z') + end + if cache_duration + root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" + end if settings.single_logout_service_url sp_sso.add_element "md:SingleLogoutService", { "Binding" => settings.single_logout_service_binding, diff --git a/lib/onelogin/ruby-saml/response.rb b/lib/onelogin/ruby-saml/response.rb index 520beaaeb..5be271425 100644 --- a/lib/onelogin/ruby-saml/response.rb +++ b/lib/onelogin/ruby-saml/response.rb @@ -354,6 +354,17 @@ def assertion_encrypted? ).nil? end + def response_id + id(document) + end + + def assertion_id + @assertion_id ||= begin + node = xpath_first_from_signed_assertion("") + node.nil? ? nil : node.attributes['ID'] + end + end + private # Validates the SAML Response (calls several validation methods) @@ -448,7 +459,7 @@ def validate_response_state # @return [Boolean] True if the SAML Response contains an ID, otherwise returns False # def validate_id - unless id(document) + unless response_id return append_error("Missing ID attribute on SAML Response") end @@ -836,7 +847,7 @@ def validate_signature end if sig_elements.size != 1 - if sig_elements.size == 0 + if sig_elements.size == 0 append_error("Signed element id ##{doc.signed_element_id} is not found") else append_error("Signed element id ##{doc.signed_element_id} is found more than once") @@ -844,6 +855,7 @@ def validate_signature return append_error(error_msg) end + old_errors = @errors.clone idp_certs = settings.get_idp_cert_multi if idp_certs.nil? || idp_certs[:signing].empty? @@ -867,21 +879,27 @@ def validate_signature valid = false expired = false idp_certs[:signing].each do |idp_cert| - valid = doc.validate_document_with_cert(idp_cert) + valid = doc.validate_document_with_cert(idp_cert, true) if valid if settings.security[:check_idp_cert_expiration] if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert) expired = true end end + + # At least one certificate is valid, restore the old accumulated errors + @errors = old_errors break end + end if expired error_msg = "IdP x509 certificate expired" return append_error(error_msg) end unless valid + # Remove duplicated errors + @errors = @errors.uniq return append_error(error_msg) end end diff --git a/lib/onelogin/ruby-saml/saml_message.rb b/lib/onelogin/ruby-saml/saml_message.rb index 6f7083cec..d184200a1 100644 --- a/lib/onelogin/ruby-saml/saml_message.rb +++ b/lib/onelogin/ruby-saml/saml_message.rb @@ -22,6 +22,8 @@ class SamlMessage BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z) @@mutex = Mutex.new + MAX_BYTE_SIZE = 250000 + # @return [Nokogiri::XML::Schema] Gets the schema object of the SAML 2.0 Protocol schema # def self.schema @@ -89,6 +91,10 @@ def valid_saml?(document, soft = true) def decode_raw_saml(saml) return saml unless base64_encoded?(saml) + if saml.bytesize > MAX_BYTE_SIZE + raise ValidationError.new("Encoded SAML Message exceeds " + MAX_BYTE_SIZE.to_s + " bytes, so was rejected") + end + decoded = decode(saml) begin inflate(decoded) diff --git a/lib/onelogin/ruby-saml/settings.rb b/lib/onelogin/ruby-saml/settings.rb index c5a40caf0..616a2e165 100644 --- a/lib/onelogin/ruby-saml/settings.rb +++ b/lib/onelogin/ruby-saml/settings.rb @@ -31,8 +31,10 @@ def initialize(overrides = {}, keep_security_attributes = false) # IdP Data attr_accessor :idp_entity_id - attr_accessor :idp_sso_target_url - attr_accessor :idp_slo_target_url + + attr_accessor :idp_sso_service_url + attr_accessor :idp_slo_service_url + attr_accessor :idp_slo_response_service_url attr_accessor :idp_cert attr_accessor :idp_cert_fingerprint attr_accessor :idp_cert_fingerprint_algorithm @@ -69,6 +71,36 @@ def initialize(overrides = {}, keep_security_attributes = false) attr_accessor :assertion_consumer_logout_service_url attr_accessor :assertion_consumer_logout_service_binding attr_accessor :issuer + attr_accessor :idp_sso_target_url + attr_accessor :idp_slo_target_url + + # @return [String] IdP Single Sign On Service URL + # + def idp_sso_service_url + val = nil + if @idp_sso_service_url.nil? + if @idp_sso_target_url + val = @idp_sso_target_url + end + else + val = @idp_sso_service_url + end + val + end + + # @return [String] IdP Single Logout Service URL + # + def idp_slo_service_url + val = nil + if @idp_slo_service_url.nil? + if @idp_slo_target_url + val = @idp_slo_target_url + end + else + val = @idp_slo_service_url + end + val + end # @return [String] SP Entity ID # diff --git a/lib/onelogin/ruby-saml/slo_logoutrequest.rb b/lib/onelogin/ruby-saml/slo_logoutrequest.rb index fac38f337..22efce984 100644 --- a/lib/onelogin/ruby-saml/slo_logoutrequest.rb +++ b/lib/onelogin/ruby-saml/slo_logoutrequest.rb @@ -47,6 +47,10 @@ def initialize(request, options = {}) @document = REXML::Document.new(@request) end + def request_id + id(document) + end + # Validates the Logout Request with the default values (soft = true) # @param collect_errors [Boolean] Stop validation when first error appears or keep validating. # @return [Boolean] TRUE if the Logout Request is valid diff --git a/lib/onelogin/ruby-saml/slo_logoutresponse.rb b/lib/onelogin/ruby-saml/slo_logoutresponse.rb index beecb1016..5cab121cc 100644 --- a/lib/onelogin/ruby-saml/slo_logoutresponse.rb +++ b/lib/onelogin/ruby-saml/slo_logoutresponse.rb @@ -22,24 +22,30 @@ def initialize @uuid = OneLogin::RubySaml::Utils.uuid end + def response_id + @uuid + end + # Creates the Logout Response string. # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings # @param request_id [String] The ID of the LogoutRequest sent by this SP to the IdP. That ID will be placed as the InResponseTo in the logout response # @param logout_message [String] The Message to be placed as StatusMessage in the logout response # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState + # @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response # @return [String] Logout Request string that includes the SAMLRequest # - def create(settings, request_id = nil, logout_message = nil, params = {}) - params = create_params(settings, request_id, logout_message, params) + def create(settings, request_id = nil, logout_message = nil, params = {}, logout_status_code = nil) + params = create_params(settings, request_id, logout_message, params, logout_status_code) params_prefix = (settings.idp_slo_target_url =~ /\?/) ? '&' : '?' + url = settings.idp_slo_response_service_url || settings.idp_slo_target_url saml_response = CGI.escape(params.delete("SAMLResponse")) response_params = "#{params_prefix}SAMLResponse=#{saml_response}" params.each_pair do |key, value| response_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}" end - raise SettingError.new "Invalid settings, idp_slo_target_url is not set!" if settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty? - @logout_url = settings.idp_slo_target_url + response_params + raise SettingError.new "Invalid settings, idp_slo_target_url is not set!" if url.nil? or url.empty? + @logout_url = url + response_params end # Creates the Get parameters for the logout response. @@ -47,9 +53,10 @@ def create(settings, request_id = nil, logout_message = nil, params = {}) # @param request_id [String] The ID of the LogoutRequest sent by this SP to the IdP. That ID will be placed as the InResponseTo in the logout response # @param logout_message [String] The Message to be placed as StatusMessage in the logout response # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState + # @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response # @return [Hash] Parameters # - def create_params(settings, request_id = nil, logout_message = nil, params = {}) + def create_params(settings, request_id = nil, logout_message = nil, params = {}, logout_status_code = nil) # The method expects :RelayState but sometimes we get 'RelayState' instead. # Based on the HashWithIndifferentAccess value in Rails we could experience # conflicts so this line will solve them. @@ -60,7 +67,7 @@ def create_params(settings, request_id = nil, logout_message = nil, params = {}) params.delete('RelayState') end - response_doc = create_logout_response_xml_doc(settings, request_id, logout_message) + response_doc = create_logout_response_xml_doc(settings, request_id, logout_message, logout_status_code) response_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values response = "" @@ -96,39 +103,43 @@ def create_params(settings, request_id = nil, logout_message = nil, params = {}) # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings # @param request_id [String] The ID of the LogoutRequest sent by this SP to the IdP. That ID will be placed as the InResponseTo in the logout response # @param logout_message [String] The Message to be placed as StatusMessage in the logout response + # @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response # @return [String] The SAMLResponse String. # - def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil) - document = create_xml_document(settings, request_id, logout_message) + def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil, logout_status_code = nil) + document = create_xml_document(settings, request_id, logout_message, logout_status_code) sign_document(document, settings) end - def create_xml_document(settings, request_id = nil, logout_message = nil) + def create_xml_document(settings, request_id = nil, logout_message = nil, status_code = nil) time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ') response_doc = XMLSecurity::Document.new response_doc.uuid = uuid + destination = settings.idp_slo_response_service_url || settings.idp_slo_target_url + root = response_doc.add_element 'samlp:LogoutResponse', { 'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol', "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" } root.attributes['ID'] = uuid root.attributes['IssueInstant'] = time root.attributes['Version'] = '2.0' root.attributes['InResponseTo'] = request_id unless request_id.nil? - root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty? + root.attributes['Destination'] = destination unless destination.nil? or destination.empty? if settings.sp_entity_id != nil issuer = root.add_element "saml:Issuer" issuer.text = settings.sp_entity_id end - # add success message + # add status status = root.add_element 'samlp:Status' - # success status code - status_code = status.add_element 'samlp:StatusCode' - status_code.attributes['Value'] = 'urn:oasis:names:tc:SAML:2.0:status:Success' + # status code + status_code ||= 'urn:oasis:names:tc:SAML:2.0:status:Success' + status_code_elem = status.add_element 'samlp:StatusCode' + status_code_elem.attributes['Value'] = status_code - # success status message + # status message logout_message ||= 'Successfully Signed Out' status_message = status.add_element 'samlp:StatusMessage' status_message.text = logout_message diff --git a/lib/onelogin/ruby-saml/utils.rb b/lib/onelogin/ruby-saml/utils.rb index 0e9619d61..b3b81feca 100644 --- a/lib/onelogin/ruby-saml/utils.rb +++ b/lib/onelogin/ruby-saml/utils.rb @@ -15,6 +15,7 @@ class Utils DSIG = "http://www.w3.org/2000/09/xmldsig#" XENC = "http://www.w3.org/2001/04/xmlenc#" + DURATION_FORMAT = %r(^(-?)P(?:(?:(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?)|(?:(\d+)W))$) # Checks if the x509 cert provided is expired # @@ -28,6 +29,48 @@ def self.is_cert_expired(cert) return cert.not_after < Time.now end + # Interprets a ISO8601 duration value relative to a given timestamp. + # + # @param duration [String] The duration, as a string. + # @param timestamp [Integer] The unix timestamp we should apply the + # duration to. Optional, default to the + # current time. + # + # @return [Integer] The new timestamp, after the duration is applied. + # + def self.parse_duration(duration, timestamp=Time.now.utc) + matches = duration.match(DURATION_FORMAT) + + if matches.nil? + raise Exception.new("Invalid ISO 8601 duration") + end + + durYears = matches[2].to_i + durMonths = matches[3].to_i + durDays = matches[4].to_i + durHours = matches[5].to_i + durMinutes = matches[6].to_i + durSeconds = matches[7].to_f + durWeeks = matches[8].to_i + + if matches[1] == "-" + durYears = -durYears + durMonths = -durMonths + durDays = -durDays + durHours = -durHours + durMinutes = -durMinutes + durSeconds = -durSeconds + durWeeks = -durWeeks + end + + initial_datetime = Time.at(timestamp).utc.to_datetime + final_datetime = initial_datetime.next_year(durYears) + final_datetime = final_datetime.next_month(durMonths) + final_datetime = final_datetime.next_day((7*durWeeks) + durDays) + final_timestamp = final_datetime.to_time.utc.to_i + (durHours * 3600) + (durMinutes * 60) + durSeconds + return final_timestamp + end + # Return a properly formatted x509 certificate # # @param cert [String] The original certificate @@ -253,6 +296,9 @@ def self.retrieve_plaintext(cipher_text, symmetric_key, algorithm) when 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' then cipher = OpenSSL::Cipher.new('AES-128-CBC').decrypt when 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' then cipher = OpenSSL::Cipher.new('AES-192-CBC').decrypt when 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' then cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt + when 'http://www.w3.org/2009/xmlenc11#aes128-gcm' then auth_cipher = OpenSSL::Cipher.new('AES-128-GCM').decrypt + when 'http://www.w3.org/2009/xmlenc11#aes192-gcm' then auth_cipher = OpenSSL::Cipher.new('AES-192-GCM').decrypt + when 'http://www.w3.org/2009/xmlenc11#aes256-gcm' then auth_cipher = OpenSSL::Cipher.new('AES-256-GCM').decrypt when 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' then rsa = symmetric_key when 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' then oaep = symmetric_key end @@ -263,6 +309,16 @@ def self.retrieve_plaintext(cipher_text, symmetric_key, algorithm) cipher.padding, cipher.key, cipher.iv = 0, symmetric_key, cipher_text[0..iv_len-1] assertion_plaintext = cipher.update(data) assertion_plaintext << cipher.final + elsif auth_cipher + iv_len, text_len, tag_len = auth_cipher.iv_len, cipher_text.length, 16 + data = cipher_text[iv_len..text_len-1-tag_len] + auth_cipher.padding = 0 + auth_cipher.key = symmetric_key + auth_cipher.iv = cipher_text[0..iv_len-1] + auth_cipher.auth_data = '' + auth_cipher.auth_tag = cipher_text[text_len-tag_len..-1] + assertion_plaintext = auth_cipher.update(data) + assertion_plaintext << auth_cipher.final elsif rsa rsa.private_decrypt(cipher_text) elsif oaep diff --git a/lib/onelogin/ruby-saml/version.rb b/lib/onelogin/ruby-saml/version.rb index 60bf948ea..b3a33a341 100644 --- a/lib/onelogin/ruby-saml/version.rb +++ b/lib/onelogin/ruby-saml/version.rb @@ -1,5 +1,5 @@ module OneLogin module RubySaml - VERSION = '1.11.0' + VERSION = '1.12.0' end end diff --git a/lib/xml_security.rb b/lib/xml_security.rb index c316fe759..86b89ac3b 100644 --- a/lib/xml_security.rb +++ b/lib/xml_security.rb @@ -212,7 +212,7 @@ def validate_document(idp_cert_fingerprint, soft = true, options = {}) begin cert = OpenSSL::X509::Certificate.new(cert_text) rescue OpenSSL::X509::CertificateError => _e - return append_error("Certificate Error", soft) + return append_error("Document Certificate Error", soft) end if options[:fingerprint_alg] @@ -224,7 +224,6 @@ def validate_document(idp_cert_fingerprint, soft = true, options = {}) # check cert matches registered idp cert if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase - @errors << "Fingerprint mismatch" return append_error("Fingerprint mismatch", soft) end else @@ -255,12 +254,12 @@ def validate_document_with_cert(idp_cert, soft = true) begin cert = OpenSSL::X509::Certificate.new(cert_text) rescue OpenSSL::X509::CertificateError => _e - return append_error("Certificate Error", soft) + return append_error("Document Certificate Error", soft) end # check saml response cert matches provided idp cert if idp_cert.to_pem != cert.to_pem - return false + return append_error("Certificate of the Signature element does not match provided certificate", soft) end else base64_cert = Base64.encode64(idp_cert.to_pem) @@ -345,7 +344,6 @@ def validate_signature(base64_cert, soft = true) digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value)) unless digests_match?(hash, digest_value) - @errors << "Digest mismatch" return append_error("Digest mismatch", soft) end diff --git a/ruby-saml.gemspec b/ruby-saml.gemspec index 997a42b03..90d097aa5 100644 --- a/ruby-saml.gemspec +++ b/ruby-saml.gemspec @@ -15,14 +15,14 @@ Gem::Specification.new do |s| "LICENSE", "README.md" ] - s.files = `git ls-files`.split("\n") + s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } s.homepage = %q{https://github.com/onelogin/ruby-saml} s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] s.rubygems_version = %q{1.3.7} s.required_ruby_version = '>= 1.8.7' s.summary = %q{SAML Ruby Tookit} - s.test_files = `git ls-files test/*`.split("\n") + s.test_files = `git ls-files test/*`.split("\x0") # Because runtime dependencies are determined at build time, we cannot make # Nokogiri's version dependent on the Ruby version, even though we would @@ -31,6 +31,7 @@ Gem::Specification.new do |s| if JRUBY_VERSION < '9.2.0.0' s.add_runtime_dependency('nokogiri', '>= 1.8.2', '<= 1.8.5') s.add_runtime_dependency('jruby-openssl', '>= 0.9.8') + s.add_runtime_dependency('json', '< 2.3.0') else s.add_runtime_dependency('nokogiri', '>= 1.8.2') end @@ -39,10 +40,12 @@ Gem::Specification.new do |s| s.add_runtime_dependency('nokogiri', '<= 1.5.11') elsif RUBY_VERSION < '2.1' s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1') + s.add_runtime_dependency('json', '< 2.3.0') elsif RUBY_VERSION < '2.3' s.add_runtime_dependency('nokogiri', '>= 1.9.1', '<= 1.10.0') else s.add_runtime_dependency('nokogiri', '>= 1.10.5') + s.add_runtime_dependency('rexml') end s.add_development_dependency('coveralls') diff --git a/test/attributes_test.rb b/test/attributes_test.rb new file mode 100644 index 000000000..b98b65b98 --- /dev/null +++ b/test/attributes_test.rb @@ -0,0 +1,30 @@ +require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper')) + +require 'onelogin/ruby-saml/attributes' + +class AttributesTest < Minitest::Test + describe 'Attributes' do + let(:attributes) do + OneLogin::RubySaml::Attributes.new({ + 'email' => ['tom@hanks.com'], + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname' => ['Tom'], + 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname' => ['Hanks'] + }) + end + + it 'fetches string attribute' do + assert_equal('tom@hanks.com', attributes.fetch('email')) + end + + it 'fetches symbol attribute' do + assert_equal('tom@hanks.com', attributes.fetch(:email)) + end + + it 'fetches regexp attribute' do + assert_equal('Tom', attributes.fetch(/givenname/)) + assert_equal('Tom', attributes.fetch(/gi(.*)/)) + assert_nil(attributes.fetch(/^z.*/)) + assert_equal('Hanks', attributes.fetch(/surname/)) + end + end +end diff --git a/test/idp_metadata_parser_test.rb b/test/idp_metadata_parser_test.rb index 436a32ae2..8998ebe21 100644 --- a/test/idp_metadata_parser_test.rb +++ b/test/idp_metadata_parser_test.rb @@ -24,9 +24,9 @@ def initialize; end settings = idp_metadata_parser.parse(idp_metadata_descriptor) assert_equal "https://hello.example.com/access/saml/idp.xml", settings.idp_entity_id - assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_target_url + assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_service_url assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint - assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_target_url + assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", settings.name_identifier_format assert_equal ["AuthToken", "SSOStartPage"], settings.idp_attribute_names assert_equal '2014-04-17T18:02:33.910Z', settings.valid_until @@ -60,7 +60,7 @@ def initialize; end idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new idp_metadata = idp_metadata_descriptor3 settings = idp_metadata_parser.parse(idp_metadata) - assert_equal "https://idp.example.com/idp/profile/Shibboleth/SSO", settings.idp_sso_target_url + assert_equal "https://idp.example.com/idp/profile/Shibboleth/SSO", settings.idp_sso_service_url end it "extract SSO endpoint with specific binding" do @@ -69,15 +69,15 @@ def initialize; end options = {} options[:sso_binding] = ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'] settings = idp_metadata_parser.parse(idp_metadata, options) - assert_equal "https://idp.example.com/idp/profile/SAML2/POST/SSO", settings.idp_sso_target_url + assert_equal "https://idp.example.com/idp/profile/SAML2/POST/SSO", settings.idp_sso_service_url options[:sso_binding] = ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] settings = idp_metadata_parser.parse(idp_metadata, options) - assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", settings.idp_sso_target_url + assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", settings.idp_sso_service_url options[:sso_binding] = ['invalid_binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] settings = idp_metadata_parser.parse(idp_metadata, options) - assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", settings.idp_sso_target_url + assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", settings.idp_sso_service_url end it "uses settings options as hash for overrides" do @@ -117,9 +117,9 @@ def initialize; end metadata = idp_metadata_parser.parse_to_hash(idp_metadata_descriptor) assert_equal "https://hello.example.com/access/saml/idp.xml", metadata[:idp_entity_id] - assert_equal "https://hello.example.com/access/saml/login", metadata[:idp_sso_target_url] + assert_equal "https://hello.example.com/access/saml/login", metadata[:idp_sso_service_url] assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", metadata[:idp_cert_fingerprint] - assert_equal "https://hello.example.com/access/saml/logout", metadata[:idp_slo_target_url] + assert_equal "https://hello.example.com/access/saml/logout", metadata[:idp_slo_service_url] assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", metadata[:name_identifier_format] assert_equal ["AuthToken", "SSOStartPage"], metadata[:idp_attribute_names] assert_equal '2014-04-17T18:02:33.910Z', metadata[:valid_until] @@ -153,7 +153,7 @@ def initialize; end idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new idp_metadata = idp_metadata_descriptor3 metadata = idp_metadata_parser.parse_to_hash(idp_metadata) - assert_equal "https://idp.example.com/idp/profile/Shibboleth/SSO", metadata[:idp_sso_target_url] + assert_equal "https://idp.example.com/idp/profile/Shibboleth/SSO", metadata[:idp_sso_service_url] end it "extract SSO endpoint with specific binding" do @@ -162,15 +162,15 @@ def initialize; end options = {} options[:sso_binding] = ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'] parsed_metadata = idp_metadata_parser.parse_to_hash(idp_metadata, options) - assert_equal "https://idp.example.com/idp/profile/SAML2/POST/SSO", parsed_metadata[:idp_sso_target_url] + assert_equal "https://idp.example.com/idp/profile/SAML2/POST/SSO", parsed_metadata[:idp_sso_service_url] options[:sso_binding] = ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] parsed_metadata = idp_metadata_parser.parse_to_hash(idp_metadata, options) - assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", parsed_metadata[:idp_sso_target_url] + assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", parsed_metadata[:idp_sso_service_url] options[:sso_binding] = ['invalid_binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] parsed_metadata = idp_metadata_parser.parse_to_hash(idp_metadata, options) - assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", parsed_metadata[:idp_sso_target_url] + assert_equal "https://idp.example.com/idp/profile/SAML2/Redirect/SSO", parsed_metadata[:idp_sso_service_url] end it "ignores a given :settings hash" do @@ -207,8 +207,8 @@ def initialize; end settings = idp_metadata_parser.parse(idp_metadata_descriptor2) assert_equal "https://hello.example.com/access/saml/idp.xml", settings.idp_entity_id - assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_target_url - assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_target_url + assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_service_url + assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", settings.name_identifier_format assert_equal ["AuthToken", "SSOStartPage"], settings.idp_attribute_names @@ -239,9 +239,9 @@ def initialize; end settings = idp_metadata_parser.parse_remote(@url) assert_equal "https://hello.example.com/access/saml/idp.xml", settings.idp_entity_id - assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_target_url + assert_equal "https://hello.example.com/access/saml/login", settings.idp_sso_service_url assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint - assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_target_url + assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", settings.name_identifier_format assert_equal ["AuthToken", "SSOStartPage"], settings.idp_attribute_names assert_equal '2014-04-17T18:02:33.910Z', settings.valid_until @@ -273,9 +273,9 @@ def initialize; end parsed_metadata = idp_metadata_parser.parse_remote_to_hash(@url) assert_equal "https://hello.example.com/access/saml/idp.xml", parsed_metadata[:idp_entity_id] - assert_equal "https://hello.example.com/access/saml/login", parsed_metadata[:idp_sso_target_url] + assert_equal "https://hello.example.com/access/saml/login", parsed_metadata[:idp_sso_service_url] assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", parsed_metadata[:idp_cert_fingerprint] - assert_equal "https://hello.example.com/access/saml/logout", parsed_metadata[:idp_slo_target_url] + assert_equal "https://hello.example.com/access/saml/logout", parsed_metadata[:idp_slo_service_url] assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", parsed_metadata[:name_identifier_format] assert_equal ["AuthToken", "SSOStartPage"], parsed_metadata[:idp_attribute_names] assert_equal '2014-04-17T18:02:33.910Z', parsed_metadata[:valid_until] @@ -320,6 +320,47 @@ def initialize; end end end + describe "parsing metadata with and without ValidUntil and CacheDuration" do + before do + @idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new + end + + it "if no ValidUntil or CacheDuration return nothing" do + settings = @idp_metadata_parser.parse(idp_metadata_descriptor3) + assert_nil settings.valid_until + end + + it "if ValidUntil and not CacheDuration return ValidUntil value" do + settings = @idp_metadata_parser.parse(idp_metadata_descriptor) + assert_equal '2014-04-17T18:02:33.910Z', settings.valid_until + end + + it "if no ValidUntil but CacheDuration return CacheDuration converted in ValidUntil" do + Timecop.freeze(Time.parse("2020-01-02T10:02:33Z", Time.now.utc)) do + settings = @idp_metadata_parser.parse(idp_metadata_descriptor5) + assert_equal '2020-01-03T10:02:33Z', settings.valid_until + end + end + + it "if ValidUntil and CacheDuration return the sooner timestamp" do + Timecop.freeze(Time.parse("2020-01-01T10:12:55Z", Time.now.utc)) do + settings = @idp_metadata_parser.parse(idp_metadata_descriptor6) + assert_equal '2020-01-03T10:12:55Z', settings.valid_until + end + + Timecop.freeze(Time.parse("2020-01-01T10:12:55Z", Time.now.utc)) do + settings = @idp_metadata_parser.parse(idp_metadata_descriptor6) + assert_equal '2020-01-03T10:12:55Z', settings.valid_until + end + + Timecop.freeze(Time.parse("2020-01-03T10:12:55Z", Time.now.utc)) do + settings = @idp_metadata_parser.parse(idp_metadata_descriptor6) + assert_equal '2020-01-04T18:02:33.910Z', settings.valid_until + end + end + + end + describe "parsing metadata with many entity descriptors" do before do @idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new @@ -341,9 +382,9 @@ def initialize; end it "should retreive data" do assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", @settings.name_identifier_format - assert_equal "https://hello.example.com/access/saml/login", @settings.idp_sso_target_url + assert_equal "https://hello.example.com/access/saml/login", @settings.idp_sso_service_url assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", @settings.idp_cert_fingerprint - assert_equal "https://hello.example.com/access/saml/logout", @settings.idp_slo_target_url + assert_equal "https://hello.example.com/access/saml/logout", @settings.idp_slo_service_url assert_equal ["AuthToken", "SSOStartPage"], @settings.idp_attribute_names assert_equal '2014-04-17T18:02:33.910Z', @settings.valid_until end @@ -434,8 +475,8 @@ def initialize; end assert_equal expected_multi_cert, @settings.idp_cert_multi assert_equal "https://idp.examle.com/saml/metadata", @settings.idp_entity_id assert_equal "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", @settings.name_identifier_format - assert_equal "https://idp.examle.com/saml/sso", @settings.idp_sso_target_url - assert_equal "https://idp.examle.com/saml/slo", @settings.idp_slo_target_url + assert_equal "https://idp.examle.com/saml/sso", @settings.idp_sso_service_url + assert_equal "https://idp.examle.com/saml/slo", @settings.idp_slo_service_url end end @@ -479,8 +520,8 @@ def initialize; end assert_equal expected_multi_cert, @settings.idp_cert_multi assert_equal "https://idp.examle.com/saml/metadata", @settings.idp_entity_id assert_equal "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", @settings.name_identifier_format - assert_equal "https://idp.examle.com/saml/sso", @settings.idp_sso_target_url - assert_equal "https://idp.examle.com/saml/slo", @settings.idp_slo_target_url + assert_equal "https://idp.examle.com/saml/sso", @settings.idp_sso_service_url + assert_equal "https://idp.examle.com/saml/slo", @settings.idp_slo_service_url end end @@ -519,8 +560,8 @@ def initialize; end assert_nil @settings.idp_cert_multi assert_equal "https://app.onelogin.com/saml/metadata/383123", @settings.idp_entity_id assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", @settings.name_identifier_format - assert_equal "https://app.onelogin.com/trust/saml2/http-post/sso/383123", @settings.idp_sso_target_url - assert_nil @settings.idp_slo_target_url + assert_equal "https://app.onelogin.com/trust/saml2/http-post/sso/383123", @settings.idp_sso_service_url + assert_nil @settings.idp_slo_service_url end end @@ -587,8 +628,29 @@ def initialize; end assert_equal expected_multi_cert, @settings.idp_cert_multi assert_equal "https://app.onelogin.com/saml/metadata/383123", @settings.idp_entity_id assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", @settings.name_identifier_format - assert_equal "https://app.onelogin.com/trust/saml2/http-post/sso/383123", @settings.idp_sso_target_url - assert_nil @settings.idp_slo_target_url + assert_equal "https://app.onelogin.com/trust/saml2/http-post/sso/383123", @settings.idp_sso_service_url + assert_nil @settings.idp_slo_service_url + end + end + describe "metadata with different singlelogout response location" do + it "should return the responselocation if it exists" do + idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new + + settings = idp_metadata_parser.parse(idp_different_slo_response_location) + + + assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url + assert_equal "https://hello.example.com/access/saml/logout/return", settings.idp_slo_response_service_url + end + + it "should set the responselocation to nil if it doesnt exist" do + idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new + + settings = idp_metadata_parser.parse(idp_without_slo_response_location) + + + assert_equal "https://hello.example.com/access/saml/logout", settings.idp_slo_service_url + assert_nil settings.idp_slo_response_service_url end end end diff --git a/test/logout_responses/logoutresponse_fixtures.rb b/test/logout_responses/logoutresponse_fixtures.rb index fe8510fdf..c1110228f 100644 --- a/test/logout_responses/logoutresponse_fixtures.rb +++ b/test/logout_responses/logoutresponse_fixtures.rb @@ -77,8 +77,8 @@ def settings :single_logout_service_url => "http://app.muda.no/sso/consume_logout", :issuer => "http://app.muda.no", :sp_name_qualifier => "http://sso.muda.no", - :idp_sso_target_url => "http://sso.muda.no/sso", - :idp_slo_target_url => "http://sso.muda.no/slo", + :idp_sso_service_url => "http://sso.muda.no/sso", + :idp_slo_service_url => "http://sso.muda.no/slo", :idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00", :name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", } diff --git a/test/metadata/idp_descriptor_5.xml b/test/metadata/idp_descriptor_5.xml new file mode 100644 index 000000000..3bff97306 --- /dev/null +++ b/test/metadata/idp_descriptor_5.xml @@ -0,0 +1,72 @@ + + + + + + + MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF +BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj +aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW +T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy +MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz +Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV +BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo +3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw +tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx +VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5 +L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t +1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB +pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD +VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL +DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC +FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B +AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM +GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c +hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB +vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37 +MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ +WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw== + + + + + + + MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF +BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj +aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW +T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy +MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz +Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV +BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo +3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw +tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx +VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5 +L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t +1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB +pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD +VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL +DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC +FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B +AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM +GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c +hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB +vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37 +MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ +WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw== + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + + + diff --git a/test/metadata/idp_descriptor_6.xml b/test/metadata/idp_descriptor_6.xml new file mode 100644 index 000000000..fa6e21db0 --- /dev/null +++ b/test/metadata/idp_descriptor_6.xml @@ -0,0 +1,72 @@ + + + + + + + MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF +BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj +aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW +T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy +MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz +Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV +BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo +3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw +tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx +VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5 +L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t +1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB +pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD +VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL +DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC +FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B +AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM +GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c +hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB +vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37 +MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ +WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw== + + + + + + + MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF +BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj +aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW +T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy +MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz +Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV +BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo +3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw +tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx +VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5 +L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t +1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB +pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD +VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL +DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC +FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B +AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM +GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c +hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB +vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37 +MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ +WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw== + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + + + diff --git a/test/metadata/idp_different_slo_response_location.xml b/test/metadata/idp_different_slo_response_location.xml new file mode 100644 index 000000000..aa53cfc5b --- /dev/null +++ b/test/metadata/idp_different_slo_response_location.xml @@ -0,0 +1,26 @@ + + + + + + + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + + + + + + + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + + + diff --git a/test/metadata/idp_without_slo_response_location.xml b/test/metadata/idp_without_slo_response_location.xml new file mode 100644 index 000000000..d6af0a1c1 --- /dev/null +++ b/test/metadata/idp_without_slo_response_location.xml @@ -0,0 +1,26 @@ + + + + + + + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + + + + + + + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + + + diff --git a/test/metadata_test.rb b/test/metadata_test.rb index 643e7a2d0..67035db8e 100644 --- a/test/metadata_test.rb +++ b/test/metadata_test.rb @@ -75,6 +75,18 @@ class MetadataTest < Minitest::Test assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd") end + it "generates Service Provider Metadata with ValidUntil and CacheDuration" do + valid_until = Time.now + 172800 + cache_duration = 604800 + xml_metadata = OneLogin::RubySaml::Metadata.new.generate(settings, false, valid_until, cache_duration) + start = ".*<\/ds:X509Certificate>/, + "an-invalid-certificate") + response_invalid_x509certificate = OneLogin::RubySaml::Response.new(content) + response_invalid_x509certificate.settings = settings + assert !response_invalid_x509certificate.send(:validate_signature) + assert_includes response_invalid_x509certificate.errors, "Document Certificate Error" + assert_includes response_invalid_x509certificate.errors, "Invalid Signature on SAML Response" + end + + it "return true when X509Certificate and the cert provided at settings matches" do settings.idp_cert_fingerprint = nil settings.idp_cert = ruby_saml_cert_text response_valid_signed_without_x509certificate.settings = settings @@ -933,7 +968,7 @@ def generate_audience_error(expected, actual) :encryption => [] } response_valid_signed.settings = settings - assert response_valid_signed.send(:validate_signature) + res = response_valid_signed.send(:validate_signature) assert_empty response_valid_signed.errors end @@ -945,6 +980,7 @@ def generate_audience_error(expected, actual) } response_valid_signed.settings = settings assert !response_valid_signed.send(:validate_signature) + assert_includes response_valid_signed.errors, "Certificate of the Signature element does not match provided certificate" assert_includes response_valid_signed.errors, "Invalid Signature on SAML Response" end end @@ -1572,6 +1608,30 @@ def generate_audience_error(expected, actual) assert_equal "test", response.attributes[:uid] assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid end + + it "EncryptionMethod AES-128-GCM && Key Encryption Algorithm RSA-OAEP-MGF1P" do + return unless OpenSSL::Cipher.ciphers.include? 'AES-128-GCM' + unsigned_message_aes128gcm_encrypted_signed_assertion = read_response('unsigned_message_aes128gcm_encrypted_signed_assertion.xml.base64') + response = OneLogin::RubySaml::Response.new(unsigned_message_aes128gcm_encrypted_signed_assertion, :settings => settings) + assert_equal "test", response.attributes[:uid] + assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid + end + + it "EncryptionMethod AES-192-GCM && Key Encryption Algorithm RSA-OAEP-MGF1P" do + return unless OpenSSL::Cipher.ciphers.include? 'AES-192-GCM' + unsigned_message_aes192gcm_encrypted_signed_assertion = read_response('unsigned_message_aes192gcm_encrypted_signed_assertion.xml.base64') + response = OneLogin::RubySaml::Response.new(unsigned_message_aes192gcm_encrypted_signed_assertion, :settings => settings) + assert_equal "test", response.attributes[:uid] + assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid + end + + it "EncryptionMethod AES-256-GCM && Key Encryption Algorithm RSA-OAEP-MGF1P" do + return unless OpenSSL::Cipher.ciphers.include? 'AES-256-GCM' + unsigned_message_aes256gcm_encrypted_signed_assertion = read_response('unsigned_message_aes256gcm_encrypted_signed_assertion.xml.base64') + response = OneLogin::RubySaml::Response.new(unsigned_message_aes256gcm_encrypted_signed_assertion, :settings => settings) + assert_equal "test", response.attributes[:uid] + assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid + end end end diff --git a/test/responses/unsigned_message_aes128gcm_encrypted_signed_assertion.xml.base64 b/test/responses/unsigned_message_aes128gcm_encrypted_signed_assertion.xml.base64 new file mode 100644 index 000000000..d42f624c8 --- /dev/null +++ b/test/responses/unsigned_message_aes128gcm_encrypted_signed_assertion.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIgogIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4KICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CiAgPHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPgogICAgPHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiCiAgICAgIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij4KICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDkveG1sZW5jMTEjYWVzMTI4LWdjbSIvPgogICAgICA8ZHM6S2V5SW5mbyB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICAgICAgPHhlbmM6RW5jcnlwdGVkS2V5PgogICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+CiAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5FYllOb2p4ZUkvWHNlWGFPS0xpRGpFZHZGVlF5YWo4NnIzZHlkYU9uU2RIbCtDZlZkaTFKSkszSDdkRG9MR3M5IGFZbHdlL21xa0E2ZUl2dTFtNWM3eFQ2c2NUek5sbFlqcWplaUlDVEtaaUdzb2hjTE0xbUt5Zm1mUUpJeXQ4ZzAgSTdSMjE5V0pHSE53bnYwaXU5NzlveW9KRDB4bjczSGk2Vmcxd0NkTWFLUT08L3hlbmM6Q2lwaGVyVmFsdWU+CiAgICAgICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4KICAgICAgICA8L3hlbmM6RW5jcnlwdGVkS2V5PgogICAgICA8L2RzOktleUluZm8+CiAgICAgIDx4ZW5jOkNpcGhlckRhdGE+CiAgICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+NFM4SVFhWXZOczlEL3FIb3o0RytMTHhkUVpnWGVURDdESzVLR2Q2dWd4OFhMcThsZTdaRXlGdEdCSHZHSmZjcktSNy9aMm03UC8zZHpCOHFka0E5bUVsT0Q2RlpHQnVST3RteE51WXRDU1B0ZXczUlh3Yk05cGRjby9ZMVJ0NjU2T1ZMM3pYMVovclZ1b0F3QXNvNDlBV0x1SUUwQytiVFRiTnhONGI5TzFtbXFaZWxTTnQ3M2pWd1BGaThmT3A1ZW00SlNVeGFVNTUrc0VTems5blorL21wK0E4aFVzUzdCZUduQzVoMnFrL3grZkkxam42ckRUUTA2djR6K2RGSDgvc2Vtb3YyTVJnNmJwV3dtVmc3b2plSi9WM1RlTTJCRUQ5Z0FLcG1xK3Uwb20zaGd0UEp3K3F1cVVzL0pucUNQN3ZJV1V3RzkrM1BuUEREQkJqM3BRaGdZbkdqbitNVTRrVzZ3Uk1mam5SSWV6WW80K1BuY01BdjhJRmQrNUdMQ0hFMTZxQXI0bG1pT0FIeFoyVFEyU3lFV0V4OVdHUkh6bVBIODM3bDlIeGk1c3U3UTJFdkZ6K2U5Qy9Hd0VDanpqaDdNNWtSTlE5TmlMekZWK08xUml4bmhJeHlGQlZlczgvbjI4WHZmajVVMzZKQzlJZ3FlNlozOWYrYVg0MjV3alFGSTgwcWdDK3pwUFZSTU9sMnhaNThVY1FHcVJreEJra0lmN0N0RDZNNUtKRnQzNXlib2M1blhETUZjQnFMNC9YOWdlcXZPU3FqbXNhd3h3SDc3ZEJGQ0lNVlN5OFFUYkFEQ0ZFMXJHcEUwRTFSTzhsSzNjaFNhelgxd3h4ZTd5ajhTTFIvVm5hTWpzdGlmMDhPR2tQayt5bDFsZDloSTBtYVk1OEQ3VDgva3IvNVcxQVN1bkE3dGFOVEwwa3R5NFNoblp0ZWpScWR0VURkNTRFYUdjazdOZENqMHQ3Tm5sY3VaQU02MkdqVVlqalIvRG5ENklpeFF2alp0eDBYck9GK0xxR2NRUWwySUZPWVhOVWFlbkxoV3FZMXJBNWVWWUlISXNCU1NTYVhCOGQ0Z2svWXhnY3lYRm41eUVqcHFSYzF1QkZJY3hVVjZCWG10TjZiVnpTditqWGZsckJ4SmxsY3hKNVpHbUtsN1FxN2oxZUs4MGtiUkFZc0NjMGNWNnF2ak5aSXk4aklTdEFPemV1OWcrOUYwRWxBSXBsajZtME00RzZDQUFiMHdzcWIvRU92c3V5UTFuWWR2SW52QVQ4L2YwVThvTWRjNmN1dVdGTnRvdi9OZjAvZk1rMk5yTGJDQ2hSalpMOTgwN2NlVGZubHE0VjByNGd0Z3JCQWNFLzlNNE0zUHNNdlQrKzFEVkwvMGMyU0syUVFCeEdzRXhCeVlqdGw5RWREOU5rcFVRVVRrK2FjaGdJWGVTbDM4UjZwaU5RWHlmSVJ2UDJpR2pnOHRVSDhSdTY1OE1FY3RtblBlWXF2LzUxZXFwNVRMVUU4ZzF2alZPd090RHJVc0FMeDNZT3ZCTXI1TkNoTVVsZWVUNHFzSmVNOG5YWkNvVlM1RHppZHpmM3pPWVRxWDNmQkVzVDFFLzNkQzBUNEFldzFjK3ZyT3NZTkQ5bndneUpkcVVNZ3NmK01PVTdYcjIvWXFteFp5ZzcwOXNDUGZURjJRQi9teVVWWTdEem1YQ2gweFVDelVJMS9yczBnNGs3b0RWeWNaQ0pEQ2lzYlU2QWVrNW1pSmFHQ29BRDgyRzZValRIbTV2RjArcDJVT2dCQkY5ME8rRW9aNXREV0FzZHk3KzlZTnZpMEFhU1g4d1h4UElaUWRmcmNucFlybERxS1luVnVWVnhicUlMVTA0UEZqNXF6RVhEYllEWk45YmswYUZpRjNMSlhEZFprTFh5RjA0YVp4NCtJZDA5Qm1CeWRiRlhxWlFsSU1oTU1xYjJ0d3RwM1pmWTZEU2kzeUZBS0R3QVVvR1ovMEJ3bVlyS2pTZFVEcWh0Z3NVVW1VK2xybFZ1bTdhY2RtYktVa2lmc1NHMGlQTDcvQmJrcmV3M2p2TTlwR1FMNzlQTExNT3UrdUo4cVdsYWlPSFFNSHNHVHpQVEhpUGxZVVR2YkxickFCT3FnbzZDblJ6R3BDaXFaT3B1ZjI0WW9hQjFlalR4YU5RcVE5RzZsQlVYMlJQcTEzd3ZuZFFnTGM4QmhjRmhCV3oxcGJ5OTFsRUpMSE9yQjRnamlIZ1d0eFFnVWNITWcxcEY5YXZZNnB0d3d4VEhkR3V3RjlSU0piRVUwUG9RUXJTSVJKem91dHgrdUNMRExaTU81cE8rcmZRQzVyVVZ2Q1ZKZUE0cHpNeHNYRkVQaVprd3V1U1B5SmIvd1lXMzNxSERlb1NNOTY0aXVyb2gxRkhUUW03cGx2VUJYQ3VqYWxWZXNLUUh1bnFvTy9kUVRUcGdUMkpUQXlhazN0ZG1zQVpKcUNqNXlwUTgrVjNHOEU3bEZIY0xVR2p3eVg4VU54eGc3cXhobmVQYXJ4NEhYOVo5MTZVUys5RnpnZzR2djRxeWlqSXdaSHBweWVmbjlyY3lrcTVVUGxyTHhvM0hQazZPaDVaT3FvWEFNRHNOUUVsTjJjdHRSS1ROSjFOcEFwK1o2MlkvVmxBWm1lVzRZTE1ER3Y4THZhN0ZQSllZMldMMSsxekxVbUFJYnRpUnFiVThIaXc3UUcwbW5MQlJlZ3A4Q3dobG9UNEJQSWlCdGFWdWNwOG5WKzhDbENBR21ZczUzak9rQlNsQVdSc2QvNmFTZEpYbFRNT3hFOHdxcXZvL3haWmlKTXNaSyt1MlZnbHpMU3NsSDEwa0FyWTRoTU5LNjlYbndUNi9UQlJUVGtrSVVoQXFpaDVEYnN5bGpLcjNRKzBBbTRsQnVQL1V5VTUvRktFcVBkd0RuOFZGQjdJMWpFUGhWczJrR3grTGM3WHZyK1J4T1FHUWVqblBZVHI5azZ4NFI2OHEzeUM5WHE2NlZIUlBSR0tJMFVhSFI0ZGZmQys4OFFkWnZBMFVkMStuaE5vSzhPazJHYXVyZUo3OXB2QytSbG5QaWFRUHpiS0treWtxREg1ZExpdWR4U0lZY3ZXY2NvcmpJa0h1ZHJORmpWM1AzQXlKY1VucGMwNHRwc09QWldZckFtTjZ3ZWdKMnYzay9tWVJiS2tVOVdua0Y5MWQzTzJMR2htUzBZMUR6Ni9nTlR1aUUvbk1pMDhFYk4vSU5DMkMxeGNXQkZrUVRUWGRVM1lvUnFxOGRqdXFSUG1SRUhMVWRoU1NKaTFiVTE0ZDVYcWNUMFhLR2NZekdtR29QMkFGT3VWTTZiWlI5VXNvcXFlQnFWZVVHN282eWI5eGxxNXV6VkhHWnFSU0NUUUNKRjgrNHJNSkw5TEYwUFZEL3p4U1Jpc2xnWk9EbHdEcjFxL2ZOMituMzI3S2w3ZDF1SncrOS9FcExIaFNzWm90ZzhsemNGenpwMUZ3SzJjNEdlZ2MvYjBZb3l1aGd1ekRLYy9sRU1QdmNuVFR5NUhGZ2VWL2JkMmxkOXJBRWRGeTdwR3lEamlERU1nV3BLdmRneHF3cWhzaTVJWjN1QXRBK0VxVTk5bmZBeTVMQXArZytaWHZuY0N0OStmb01ldEFrWmxsZGNyUm9iZytPejZuaWtzcTBLd3V5ajR4L253ZUdlTnl1dnpWSnBEZklya25xanB1VFVVVzZ0L2p5dXU1U1l2VTA0NFFoMHVzdjVpQWJyR202SklOZStYNWQrMjNHNkgrbGswUjZEYVU3ZXREUXc4ckxaMHg4dGZVV2xHengrRnhmempISUpxRStadm5YTm1HY1F0Umordm4vN054bVJHNlQ1dlIxQ0oyUEdoOHhBRk8rRWlaekRQcXBTbWp6NWIxRGhLNWIrdlVwcWVZMUxMU3lFU2NxaUR2eWxaWjB6VFFoZmdwNW1xZHI1OXo5NnJ2QjNjeUVuM0FrUzVqbGcyN0tOZE1rV2pNZnh0T1ZUVXRzZGdrdzA0cm12dzE4SWxrQUluZGwyMkltMTVNUFU0UWJEU0Zra2VoR1FPclQ5a1pUTjJuVjlIaXRBSHVRMUxGdjhEa1Q3RVhrZ00xYUVoUkcxRmdPb3FsYUdTbytlUnNYZkRQc2hVYkxRMHRZTk5BeUJjckFhUGxCVkhucGIvODQzNmJuVlpyRURGdENERjhuNjJTNkdQMnNjckh1bW5wQWd5TUsvMFFJSi9NYzdrK2tZN3dUb0hzSkJqUWFEeU9nZU51c0NZck9ZVFdENU1MTVlvcXRGb2xCM1lERkhIS1JjRk5WOUp0M3NsZjRMeVBPZ2FYV011bENpRlk4WmVOQy8xNzlpRUtFbkY4SUJMZ2lOYnRxY2ZPbzdBQU9yaVpiOXhVQWhROTZHRUlXeEprMHp3U1pzQ1BYS0Y5Mk9HODlkNUpKbWk3OHVkUnZic2hUbmExaFlQd2owVEZVRUMyNWF3YVZYaWQxK2xFdVBRZ0toYU1VTENITW5VcUhVeStiRWM5R1ZSMHNMN2ZGZGpiQk4xRlRDNFA1TmV4V1NQT2VLd3g2OWNQbjd2KzVKeWI1TzFmMURyNGtZeFpzODBITlFsT0hOb0Jhb2c4REFuT09naEVEQ2RjenRqelAyd0VoRHhjVXN2dy9QaDhUOUJrcmFDa1h1dEhPU2dDTFBIeDJrdExxWW1HTDFvUmdWNHg4b29EUTNySmZ6UXlZRHpWL2ZWQXpQMEVQeU54TmpxK05VREN2bXlCbjNQOWw0cUs2eVNHTURYRm5COWZYdDVlcENKTW1nZWhZaXpETTJMTS9OVnRvM0NCYm85akUvUTJJcm9ma2w4V2VVQjZMVmtEYTRzQWIzcUI2czdzQ3hUSURHbWUwWEw4Q3FXNG42QUdRTzRyMmV0ajJwTDVpLzlWbGV4eFZuOFVHZkcvamZ3WjltNUlIYmdacTF0SVpBL2F4ZUgxQnQ0T1hRR2QvQmM3Yjd3bGJZY3V5ZUlqVVhuZ0hSdzNjZVhwZGhJY2tvbnpVRTBzMEJhaFNPbWovN1ZzMWx6M3d5Ulp2YS9BNDZ4azQzNTdBVVJRVGcxK3hmVkd3dHJzVHhRSGZyWlhydFdYLzBMY1N3SDF1aFM0cXNHaHZuZ3hhWGtFc05NZUlUa1RjVmxpRWxxb3NLKy9SaWFmNE4vRXB5RjdDVFBUeHJTQm1lSzFRTmYwejhycFBXTzYycG5nWjFkcjJjekh3T0dFQkM3U29CTHIwbDBVYmU4TnRxTC9YZnlwUHQ3MVFoVG42bkRjMEtnUlBpZC9zei9vU21Cd3NrVTlxTjhmZ0RMSGRuMVJLWU9tMkdyc3MyTDN6QmZGeUM1alV4STBtMG5pZG53anZudy9tR1FFa2xOSzhpaTRZT2ZYdDlYL3ZNSlg0MHZvTFV3UmZhOVovcWZydGN1dlZVWTdScmc2aFRnc2hpVzNDTk54dExBWUd1YkMxTUZCSnd0REVzL2FhbjN3elRQOEtnYVdNb2YrZ2JkdTVpZUdkcDlheHhrWmx3SXQ2dlRvUm9BTGlYRkY4NVlkRHgwVWlQS2lmcXRBNFV6SGxoblZIdG1Dek43QTBKMENSS1FSUG9yVWpGNGMvVmFOYWh4UjJmUGxoQ3NsVUJqZFhQQ1FCQjA0VkhKdFpHeFoxSXIwaG90cnF3YVh1MGtKS0dKQUVWZnRhV1F3NkRhaUg0eXlOMzVybVFCWENlZ215UEZRekdpYXFSR3J2TkIxekVWby9VTzlmQ1FPbDdVWVlMZldBYXBIalJNUnlWZG1VWGhFR09OdzhIMjYyZVFxTVBIVEY3ckxXVVNmeGtoQWR0ZmQ3N001VzU5bjRnTHRLWXV5Wm42cTBoOXA5Ny94QXA1c3hzZlJvejc2RWlmYVJQUGxtTW5yK1laSlhGRmc0Tkt1TzE2YzFBalFreHlObWJNVEJ3T2taVWtSUndZUWMyUFpvVFBGejJ2Y1hMVUVIOXA0ajIreFh0c3Q1cXZPZ1cxRlZISHMyZ1A2WHg1bTBFQjJyZys3R0tBUlM4VW5aVDg3ZXNVcjdreXVlV3JBODJmck5nWUpkNWFhYnVjUFVTOU1LUlp5SnA3bEJZRUthNU5UMDNqejBuNi9MWkZzM3JXcWlNUWhhZmNSNW5wQzBjREtvblVVY0tGWnVsOU9ONmc5TGN4QWJHRkJCS1J2L01VMnRrT3QvRHRlNTMranF1QklYWG02d0hTYzVNLzBodVl3U0F0S2lIUDE4OGd3SDllSnNEUEJ2di9KSzE2MjU2UWNFWGk0V0RDMFZqbW1rZjB1eDN2U1RlUkFtd0g4RzhXSnVuVWJpaGRKckpFWTBUSG9temVOWUFSb2tTaUYva3MwajlySS9Za0oxbWQyNXZadGR3QXh0L0tqcVptUjYxb2NDYVdYNlRPSHpFUzMyNVdHRzJvdE5aZitXTTNaUThsNEhJanh6SVRXOCtnWEg5T2FrRXprYXBqcGtyakp3MGZNdVArMkxVTW1XVmlxL0dRTDVRbVFPaWkvTDNvQW5kZEdBRUZBNGRySUZldktLdE1maFZrVVQvRXRIQnFGV2dkbVV2V0tyOXBTa1ZPaVJFaXdid2xBaGFlMFFNaDdKKzJPTUdCQ0RUUzNMcGRLUEYxR3ZwZ1pXSDN3OHpsSU9Ock1mbSs4MjQvTnNFYVZxUU80VjFWSDFhNE5FaVI1SEtvNENZTVBtM0xuNGpuMU0reFpXVGE5cHU3VW1qRi9mbVFLSFh4UDl0UmZrR1h6S0VZajloSWtKb2xDK0tsOWw2aWtWcHNyQytRbmh4QUFXZlY1YWRvNzBWb2xSUEtsQjlmOGVFcTNPMjB1REQ2ZTZyNloySVhBb0grbXhwWVArZkhpUzVIcHpyTUE1YXlkL2YzV3krNloxWTE4elVsSjI2QVBQcEdPTzZlWUsyeWVzaVdZS20yRERvcUd2S2haaFhBM0o1NjdCTFg3eXprb25KVWRqKzJkd2VkMGY0ZDVvaE1RVU8ra3BBT04yTXRtMHV3NnM0U044NzZzWGtyNzdwLzQyVWc3SGh2UzdjTUVIVnpOTGtWdDg4V2RyVmpVTkxuRG5wREVSTTdWVEZJTUNkUVNjaVJUSm5ZK1RUbHJROVdVb2NNVG1Pck5NLzdqMWlzUFB1RFlaSmxXRjdFTld3Y3pwMWVFVXZTTXNFVytOMDQ5STJmUWI2bk1wdFRyRHh2T2o1cmFNSXdaMXYzYlIxWXd6MHJXcjZwTkZMd0E9PC94ZW5jOkNpcGhlclZhbHVlPgogICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4KICAgIDwveGVuYzpFbmNyeXB0ZWREYXRhPgogIDwvc2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+Cjwvc2FtbHA6UmVzcG9uc2U+ diff --git a/test/responses/unsigned_message_aes192gcm_encrypted_signed_assertion.xml.base64 b/test/responses/unsigned_message_aes192gcm_encrypted_signed_assertion.xml.base64 new file mode 100644 index 000000000..5fb9a7033 --- /dev/null +++ b/test/responses/unsigned_message_aes192gcm_encrypted_signed_assertion.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIgogIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4KICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CiAgPHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPgogICAgPHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiCiAgICAgIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij4KICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDkveG1sZW5jMTEjYWVzMTkyLWdjbSIvPgogICAgICA8ZHM6S2V5SW5mbyB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICAgICAgPHhlbmM6RW5jcnlwdGVkS2V5PgogICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+CiAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+CiAgICAgICAgICAgIDx4ZW5jOkNpcGhlclZhbHVlPjNiSXZ2bFA4b0w2TS9zdWY3Zk04R2hVYW4xYVNoTERxOFQwaWRPYUF5S0ZZLysvYkpYYk9iSHhKendBVWxvY1MgVTRrL1crc0tsL3J5cCtEaW11L0ZPQi82dm5sNnErNlJaMjZjVXVUNWFQQ0xWS3A4Q3RpL2R6SlNyT3lYQkR0dyBCVDB5dklZUGxnWEpJL3lzUnJjR2tENXBVeU9EazJWOXZtNjl5M3ZrNzE0PTwveGVuYzpDaXBoZXJWYWx1ZT4KICAgICAgICAgIDwveGVuYzpDaXBoZXJEYXRhPgogICAgICAgIDwveGVuYzpFbmNyeXB0ZWRLZXk+CiAgICAgIDwvZHM6S2V5SW5mbz4KICAgICAgPHhlbmM6Q2lwaGVyRGF0YT4KICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5lcjVEazdjWkx4bVJKR3JLTWlpZm1SdjNZTXRlUGZ4MWQrcEM4Wmw1NFBMeUlkSmMxY3JZcU9Mc0hNa1lSVTVnMkZVM0VQcTBGZ0YwNjlYNUFHckQ2WUxhZW5rS1h5RlNrTkpoQStVcjBGMTlwUTBZNU1SSFJoblVLRUZvK0ErT0lwV21qc3I0cEJrOGxycno3TmlUL1UzNGoxM3BiSWJ0eHN6OW5ha054YXNLYXN4bEozb0VxZ2R0REF6RmhtRUVHek9WeGVBM3dCVUVQcWFQRGpZT1dLVEZ3YlJSYlBoZHB1eE1EU1lob2lUWGluRDR3QUd0QXdjeGo5QXhLU3MxYm8zRGFRaWJKeVQ3M3UwM1E2amQ1ZWxRQnRLeklwcUxqWDc0RXdDOXN2eU1uenJ0cEFYK0tBM1lLdnM4K1hmZG43R2s1emw4em43UXY2aFFBdUs3eW5TdlQxRSsxcFBEU2pxdWMwUEhCNm0vS1ptbERjZldhQllXYVE1UWtMMkVTS3JzKzFqb2RkYnpoYXBRU2dvaUJoeGZiT1BtM3lrd2dMSUkyZ0ExUmNBSSs2amtoSXNZM0xmNjZqM0pUeTRSTWM3VVdwRGI5NVZVaFZYN2pBa05nYWZVNzZGUW15bGpGY2RaT2VEL0pSc0tGWWljREdlMERsMUczRyt4OE5tTnhMVndXS1V1SUJSM0RXeElwZk1kdHRkaUlpbGRNa3I2K1U1Q0cybjVURW9VNGNuVWJ0dkI2VTRBdTNVQVNuU3YyRCt5V0JIemNycGp5SXNxSkdvOVhCaUhrUlN5bEphS3B3M1hIc0RJelJWMHZSVUs3K0c4djNaQVdNYkpqUDFhcStYckNYT29EYk5RbzF1bGJxQXQxRUFhT29OTVRLRVBsNmFKMllvSGpSUUcramJoeHhlRXFpK2Raa29RcWErQ3JTbFpSVXI1V1lkWE5aU1g0ak8yRHdTY0lNMnhWSnA0SDU5eWVIaDluVVg5S2hjWVlxUmtIQVNzY3lZT0M4Y1RWbXdSWGpmOHdHb1JsWVNtY2dpYWhHbkRZN2l5eFlVSUR5UUZrYUhWdlgwWktpKzd6YlcrMjNSYUZObUJvVGdCYU5RSmprN0RsVVpTM0lJQ2tsQ0dIWTVWSlFmN3lGRmdITXBJVTBFUVRtOXIwN2NGekRaR25idjU5dmR0Uyt5aElvcDFUSFRsOU9SOVRINDhxQ2RpV29NSWtjV0lENFVEZEZoNXF3OEFneXNHdnVuTElMK0RHbHgwb1RsUDUvOU9zUlBlRXovTzhmTDN6OTJTUmQ3UHRVRFBMbnZ0TjVMc3hOSGJlOEphazY0dG1TZXNhYnFvS05qZEpIcno1bVlqODVrQlR3ZlRzdmxMOVpmS3FYM3oyQUlkakdoUjB1bHFyQ2IzYU5EZkRYRzE2eFNiSjFRRUwranMwZFlRMEtGc0VWYkNKRjVyNDNkcldRME04RjRkTnYrRW9VQXVSQlVGbXFjYjR0eXlhajRZTDVGNEdsV2dzaERrNkpWa3lnYmZPZHliVWRKSUlFTDUzWGRIdEc2M2JDZHV6SHR2YVFLTENaN0Z1Zjdud1pPRnRzZ1dXQUcrTVpsQmdoNzYrWHZYbFI2clZ5RmN3LzI1R0hsaFZFa1JvTVUxUzQ2bmpQRlBXS1cwamdjYWlGTkZ3QXBnc2VSWEFwanRGTjg3R0pNSS8zMjNGQ1dkaVkwR1FXS292Uk5jSU9LNjJYQkQxTmlhVDJKM0lQdFN6UkZRL09zYUlOU1J2OXVURitZRGFKUlA5L0hOUkdKMWVtbXIvaTg3WlFYU0k0amtsY0NTZmx3MVc3cG14RGhuWTlXa3NpdzErUEFJajZSTVgyejZVS2w2RVFTcThmalN6REV2dUoxYWZCOHhEZ2VkaDlGSVcvZTh4VXBXN2dCR2hReE5taW1qNU9mQmROSzYyTzhDT3RJMThDcU9jU0k4UENhaGZOSEdMS0hlYm8yMlYrcXMrQUk4SHV5S202RnN1S2V5aVZPOVo1UlVMR2YzUW5sY0ZvU2ZuWFJ2TXc4ZzVPL3ZRTGdvcUtzaXlJTkE0V3JKc1V1OUFaMjJOcUFhTWxac0h3Sk5IZk1Fa2RYQ2JIWVFKWFdhRVNJMm9wbDZSNi91RE5yUklWREk3VHVCNjE2bHh2VlM0LzBkZ2IwSThPaU9PUHhuQ2Q2blhmT3lTRVFBeVBka3pMaTVseWVFOExYWkhjV3BPdkRrY3FDWXRscmp0UmtkQVoxTEhiRDNsOEQxU3gwTlB2YStLeERURk00N2N0WXhOZVVRQm9EZGlOcVdEYStHOUZaZ1FpNmtVbUhqZDl5d0V6OWFELzllRFVUVkhBQmNoZWJ1azdaaUEzcG1xb1laa09hdzVGSW5JUlJYYVlMZkVENWFGN05sMWJJY3djMW43amJQOEplU2dFNEI0ZzZ2dklZdzE4K0NiR3hVcUV1bDVBTWhKdEMrR1RBWjk2aEJlQjNiQjI1Q1lwMXBvZ01HMldXMjRiNkFjVHdUQWhwOXhtYzYvOTdvbHZiTFJtMGdtTVkrNG84NnI3cWZLY0F4dUZYQUYyaHYzWExaN2cwZUdhWkJRbUdqNTE1d3pjZ1JMTGFmQ1JNRVo1STdVNUdTMFRjc2I3aG9zMzNzMnZxMnd5QnZWUmEyNHhwbzFsTW1DRlJqbS8ya3VqUlZtZ1MxZ0F1N2JQMEtValdRK1ZPZHU2OE8xRXo5TDlOSFB4NjQ1RnFpU0dLTnI5QnNwd08xMzFiNUp6OTNaQkRJNnVjdnpvNGU5anhQWFNZbmRsa1RWR0diUmRoOHE1bm1lQWxEZTNMYjVqM3RrZkVSQ1BPcklnT3M2SW11bXllRG5wNU16bStjVTVXNDRSM2tKSlB1cWRYdFZPZWVkTVFza0d5ZkxnckxvcTU3Ukh4OWNsVnlSQnpXaExkOEpYYW83WHhMbWNwVmFSSEJSYXNEaVpSc0NLSFcyV2NkcXJqWlFZOGFWaS9oL1NBVy9vUTUzRWtDSTJnZFhXSkhlN1JWVHZwWnZCcVJMRlNvbFhBQzZNbXFISG9xUmFBMEtRRGJUMm8zYzZjUVlGNk43VkV0NnZZNnhlMXNtR2JpU1pXcXFpSk1lbTM1UWtxUzNwN2hQTWVNRUJCb1VCcEEzeGZScFg1MVM0N0NmOTl5N2l5UWtjcWV4NjgrTUZabXlCVzRsOFZrWiswRXlRRXlydEtKSDAzcFZyNUQxa29QMExyMWZ2cUIyNVo4TzFBQ1duWDladkFYbUhocG1YTUlPNWU0ci9QYUNlakVyZGRqYTlKb1BsMEtrVDZhOFk0R1hMZXJKUURwTHdDTzQzSCtIa0hGSERyVFUyRHMreCtteUpkSkFOSkJKZGtraytMTERvVlVpVk1UUi9RS0tCUnVRR2Q3QU5IY1Y2OGJwUnp6eGZOa2czWHgvN0o0NEY0ZHlLQ0RnazFYdUR0U080Q2dsNDZNR2NCcUFzeURpRTlyZG5ZRmsxTFRac0dTWStQbUloT3huVzBUK1dxRVRBVE9IVk5FRTFBb1Q4UkJVSnRRcXdWTXR3cUx4MVB4bWZnWHVKU1RIUHhFZCttRkpTWFMvdFlZUmJUTGFDdmJxL3lwTkxSSlZVcEY2K2ZUck5GdUM5M2RZKzkvOEVLaklyL1NMR0twai96bXFIVjdXYmgwUlVXMG5tbzdZM2RIdXdlaDJrdlNiQjA4MXkxOEFmaXM3dUhnTFdsdW1UdTV1M3crNEZidmMzYUpDQVFMdjFCV3VuZy9iQVZ3U1g5dGtuWmpGSUgrNnRMY1QrSGVjVW9DanVBcXlRaWdpdFZSQzFrZEl3SFVPZThxVzd2UXBNbVdOV2k0aUQ4R2U4RmpMbXdNNWpvZWtra0xWNnphMjlKc3U2WHc1aTNjNWh0ZjR6eGp0VWFOekNXTDY3cFBjVEFETlhxL2tCMTRCRmFSYlVWRVNVa3BCcHJZTXFOZnBkeTZMcXl4Nm5TMHZJeGhyWlREdkpCRENQekE0NHJxeGFNTkZUSklOYXRoMUhkMm56ZDZwdjdYQ1FtRzAxbW9FMHIxNWFmRVkvcE9CcG9KSHlKZzNOZWpBMVlPSVRJNy8yWU11VG5MRUw4MXE3MHpicDM3L25Vd3ZwcytBUUZRdmdEclVINkxWQnk2alhWeFhnOFJIV2pDeDVTa2JHNjFDc2lqREROZXR6djBUNUNuallDc0pPZnAzR0xheFRkQzBEZ2NKYTlqdHpzUnJhSml6UklPbTZDdXJ0ZU45b3dKejBMVjZWMVJQTGdiQjlIL2liVVVLeXZjc1p1aTA1alBLMWlFVlZKSGJBNURLVFZkY1c5Q3Rzdjh4WTIrRXN1RmFnY3QzVFhDRmVtR0QwU1VsZ29uOFJZYmZPMWdRK3JjblBjMHZ3eUcyQk1GL0VwaEVlZG9iaHNVWmwyeis4QW9kc1dxRnJiOTh0ZEFTMnNXQ2liM2NLMmFZcGo1VWlRbnR3Nzd6THJaNVhzcHdjOTIzajJJdzVVejNyVm94UldhM0dUN2ZPdG5uKzlnOFpwUXRJdUhrUG1SS3BWNWVZczI3cUZLT09xZ29iN3VVNHlQRVh0c0UyK2dFRkdKeEpuZVUwbUpsOUtaZW05WUR5bTJSSkIxREMzc2J4M2szTWxXN1F2Wmx3bGJQek1GQ1lUYm84ejBWQTEwVUtMS3RsT0x4NlhPRCtRclFVU2hJMEVUMkhIb0MyME92aFYxaUlIclREL2w2ZkErT1YrcGRPVENCNVNQMWpldTlHbC91T3FNUXlOVTFJOGJ2ZjhJT3k5bTNSYW9DWnpYT0NpZ3kvKzM2aFlpMFFiQWtzWWtuWk94bThMQ2pZaU5HQVEraXpmMzBaR2p6ZWlEd2kvS3hwTzQybU9uZml5ZXMwS08zeDRDWWF0VllvYklYUkkrcDN1Z3FCNGxya1NKUnN1blNvRjc0eVhSYmF2Y1dPT2cyOTEySzNMaE1JUU4zMFhmditYTFRXVzkva2VSVlUwdE1lWU42MWM2eW53OGpFd04yQzVNYzk4NC9WcCsyZVpYY0lMd3FkNXJQQ3p3NStSdnFJU25JOFlBb05FL1Bvalh6RGpmbTlSZVVYanduQi9hcHIzOGZqMDFNRVM4NGhUY1p6U2swQWFWdXUxOThjQ1RlYzVxTEp3SjhiK3kxM3dQNFErWXlXWXFZMXpnZHdBQlB0QXo2ZTJ4L085Tis4M05pa1J2RmZDUVlScjZaVG9iMDgxVmxISCtKUmdOWHBFNTVCakhsVzZ0M2lQUXhUNkVYZDd6MlBBcG5hdGpOd0wzS0R5bGpMY2RKL0xuTmU5NWw1UjN1M3FmUmlHd1FXb1J2MittQUxGVFUrOHB6azJHUDNCRm9pU3RLK1I0SFoySjJSUEhDQWVWaUw5ZE1FZ0lyczArMllHL0EybVNUNWkvSkFWamxMSHVzU2N3dVhDS3ZoNC90ck9HeGJPUDVNb21DTTdIc1I3ZjRhLzVveXJyMkJ2VHhJbXFSeERLR1diSU9PR2xaMytUaTFQd1pjVUVYNC9oQTRaUEVSOWFza3BEYlpwOHEzWUN5NE1wbjdzN1JrZUxsTmFUN25JZHJkTnRuWnF0aUxCZHJaaXhCZWlUNXdCb056K3FkenBTdXJtTUxEMXdhQnlGb0I2Qmx3K1hEMWxkUWtlVVRZaFBkM0M1aVlER3ozbjR4M1Y2NmkzQ1p0UWlhQkc2ajA1dVZTbU0wYVhESHMxZXY0QlNlekZUdi80dkl2Rzl0WVB3aDM2TGl2Vjk0YVJGQmRqVHRpcjR0L3NUTVNMUHNBMFVuMVduNVZuUnl5MGN5U0d5QTNISHFicnYzSTMwSjhpSGV3bmNtek5qQUh6MlExeDZ0YVhyL1c4VlZLNWtuVUtwTnVuSzRPMktGaDZUVk54WVdUcUlUQXRZYXowN1ZydnRoK3p1bmYzZkx2RXJUWXY2WE1oUXo5eGcvd1FxMzZHZ1AxSE10Q2ZSY2VjU0xTZkswYStTRWI0WmtuRFpiQ3h6ci9lUDJxcmcxcHUxOExHYXpTZUJ3OUZKU2M0VTc5S3RpaFlTN2lyV010ZVh4MzFNMWtRNnpXUlFUQ0pNeWVUeEczdnZWVm96MFNXYXdnQWRnQVBkY2c4TWU1dlNleWN6WkxINW1QM0E3eW03RCtYV3JPT05qVktMTVZKU0hxdkhnbXZMeEdOSHB4aDRIVFJNdTgyanVSamhtRStHY0lHSE4yeEE5c0F2N0szQWo0OWhsZm5RL2owQzBkL3NqLzQ1SVRKUU9oSXlhUEY4dDVxOWd4QVhNeWJpWnlCOUpMUmtDdzV5RnpEaWxEWnE0bTl1Rk1aUTNyOG96MHp3WWVSYjcwZFlQNEU4WEZyZXdqSlNTd3NhR2FDckFqNzZrcE1haU16SXdLK2tEU3NEbCtjU250bTNhbGNtSVRSU01tTmFjdDFiOThTeTVPSU5sTDVhVEZ5WjhKYU1GS0FFQTdoWUF1V3I0cDYySUhUNTFQZlZyRDN4cTc0VHpCWWZsdzJoOStyOXZNUlFKbHdYaWNqK2dUbW1BeHpid1RUdkkrMTMvMTFITFowNzNjNTI3dW5iYXBpcW1NSlBjbHlsandSQzhaZS9ST2hZbDA0S01MT3dLNjY5SHdYNUdCQld0SXVPVTlYcXBPNUtxQlV5WXJqZ1NsTnJKMXlER1RDN3kzNEtiVHgybzRlbGhSYkR2S3c1L0VQK3d1d3QxSERsbkpLUzdpTEppU0hONmJDS0EwVU1GU2tIRXB5c3lpcDNwUWJuL3hqdjNZR2VwV0J3WTNNZUZ3MHROQUdkeUtlcHhKbDFNMkRZQ0R4QkRGazJwWG4xODVDWEN0SjRxY3NXREhmLzY4RngxZVhTZk9GUlk1MTYzVXJHanZvQ1Z3R2pGU1hNbENCMU5ySUlaaTRHRW5SVVoybDFVV3Jtb3FrM0p5KzdoSWFHRFJzZ1poa3BLcG5yL1d6Y1lsSnpKcHF5b0RQeWhzTUhoSFlCcXhKLzNjdGY2cUJ0SjgxTUkyZDBRZktVbHNNanVWMGJxNUpBcTBKYWdKTVdxbldwK1h1YjdPTUR0bkVhdjNEblZxNUNKSzNDRHVUTnNKRnBsSzFRSHVubjVlTXJ2STF0dEdkaVd5SlkrRGxCT2puRnQ3RnRtY0lGcDhSQkc1RG9VYktkNjU3Z3VPTFYvT3cwMGN5a0ZPSDdWQnNWV3ZuZzUzOGtaVmVHTkExL3k4WlhRUHIyZXhYRDZUTT08L3hlbmM6Q2lwaGVyVmFsdWU+CiAgICAgIDwveGVuYzpDaXBoZXJEYXRhPgogICAgPC94ZW5jOkVuY3J5cHRlZERhdGE+CiAgPC9zYW1sOkVuY3J5cHRlZEFzc2VydGlvbj4KPC9zYW1scDpSZXNwb25zZT4= diff --git a/test/responses/unsigned_message_aes256gcm_encrypted_signed_assertion.xml.base64 b/test/responses/unsigned_message_aes256gcm_encrypted_signed_assertion.xml.base64 new file mode 100644 index 000000000..cfcb85dc6 --- /dev/null +++ b/test/responses/unsigned_message_aes256gcm_encrypted_signed_assertion.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIgogIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOGU4ZGM1ZjY5YTk4Y2M0YzFmZjM0MjdlNWNlMzQ2MDZmZDY3MmY5MWU2IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wNy0xN1QwMTowMTo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vc3AuZXhhbXBsZS5jb20vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl80ZmVlM2IwNDYzOTVjNGU3NTEwMTFlOTdmODkwMGI1MjczZDU2Njg1Ij4KICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CiAgPHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPgogICAgPHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiCiAgICAgIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij4KICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDkveG1sZW5jMTEjYWVzMjU2LWdjbSIvPgogICAgICA8ZHM6S2V5SW5mbyB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICAgICAgPHhlbmM6RW5jcnlwdGVkS2V5PgogICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+CiAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5nMmhNbVVoTVU0MFdFemtZTW1RK1Q4WE1PSlhiMXNWMGFXS015cTVxLzhUdkFWNVFDZ3BHRmxIUVBMQTZEVEJPIGpRRGFINkZrK0c0cWNmcVZ0VFI5Zy90WC9iZWNyMWduaVRzQkI1Zmkwd1JPRmVzejVpUUZ0bHdyMVpkdkdrd3kgaTVYNmRzR1B5VVYyKzBDL1Y2dU1HZXhXNUJSVDBWNDREeEpHdVpkY3JxUT08L3hlbmM6Q2lwaGVyVmFsdWU+CiAgICAgICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4KICAgICAgICA8L3hlbmM6RW5jcnlwdGVkS2V5PgogICAgICA8L2RzOktleUluZm8+CiAgICAgIDx4ZW5jOkNpcGhlckRhdGE+CiAgICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+cDJWc0RDSWFjYzB5Ry83ME02aHhpSGp2dzUveFFqb0JDT1ovZm1xcEQ5WnlwejlrVldMU0UzM3lvUkJOc2g0bGZqSHFpYVdSRU9oSDhuQVpMV21QY3gvWjNVTUxQWU1mdWZrWDFCcjFSTGp0VitITDEvRjBwa3pNTnc1VEZjY1dHcVpMbUVaRCszS0FpQ2NMY0lVTnJDeTBBaDVVMWlzTjdxd2llT3ZsRlU2eFZsUGZqN3JuY1o0S29qWDFDQkplaEFpS1ZIVmtWb25LeUxEOW9ocDl0NHZHbzBJbWlFZTRLTEF5ZTI3R3FEL2grQnJZMGdneXJ2UjdFYVJ0Ym5MYzVvdFRBQ1BpMmVua1ZJYS90SjlrUitCcEhNTnI0Qk91eEhKYXRCMS8zSUhCWmZvcTAwc3NNdVRGdXFETk9RT0tUYkxsYmpGb1Vsa3ZscmRrRVlZRzFtcERUVmppRFRhTW40UFdYc1hFb1pueks1R3ZFWHE4Z3ROTXZIRk9SbVBmVmx0dm9ZL1JpMlpYTU1yYThEdVFNOEUxQTIzY3ZvQzZTbmE3dCtYdmRJdmtETE5TNlE5dEY3cFJWL0VDVVp1QWVQdXRNVCtCSEFBZmY5YkY2ekYzOTZBUmE4UHdlZVdZMHdEM1B3dVRuaVlNeFZYV1hIWHl0SytqLytqT3FvcXF0aTZOSHdxaXRuWWNNcHJQT2hNbytDWXRVNS9VUE1SbkN5UStoYXhaaEFUS2pNcTY2WmhMZ1NibEtmVjljK1NYbUhCaHRiNy9pemdRcnN0SGNjZFdOSUIrRDdVQ3JtQjJhMXpna2t1U1FlaEJEZ1lyeUJ0cHkrdk1rN042SDFDYXpkNXFqb2lSL0JFOHlrSGVRVFhNWU1mWklUZUpWYUY5QjlqYzBMeEpLN3ZpSXNsSDZzeUxsdGpOeGhyeTlDcHR6UFM4ZGJOYU5ReTRMVDVqeEdFeWtSTEg5eFhZTUFQOGFIWldtV2FxUlMwczRleGdKVjExRUlmU01TZmNPTjZzb2MzNVM3MGlTWjJVZE9TV2dNM0xiRlQvdmltUHU3S3o2TER5VTlobGVZT0xTbE0zUjl3dm83eDRtdFhjeWthR2VkeGU2TjFENEV3Wk1TaGR2bEMydVpUK2VJWEkzM0RHRVRVcVhyb0dBWkwweUl3QVpEemRDZE00S0VQTndIZVQwTFFVVUZTTHRibE96VFh6Zkw0VHZ6cHRqWGF0d1JvVWI0cmdxSkE2VUwxeWRvY1dKcUFyMkFsVS9hV2hTc2lWVm5ONS8yMmRpVHRnQTVxWTMrTGl3ZWJZMWl2MlB5YTUvTTNhcFQ2VkZtRWpUdEo4aGIyTDUyWksyZlhqVThvUHM5YmQzQmtQR25zWWt1cWxBYUJaaW90MWFVcWVnRVlLdTFVSmtSVXdVK21Qcys0TDNWemFqTW1LazNFMk42U2xOL0NvdlFpS0FnOCtIUWlYVzVmczZQd0M1KzRQYnZuZjFDcDN6ZjVKQmRzT21JYjZxeVlkek5YOUFxaFJORnlZOWlXZWlZTHB0eXdEUnluVitXcm5VSlNUcis1Y3Y1UUZDcTRMbm5FaUhFek0vMW9CWjZPTXh4WE5qelFMQUJDdDZuMzV5TEFCVllFSlhKcHpVWHU5QTduaW1GSmExaWxmS20rL1lMRitXaFJJeWpEbW5oVHM3NnBkY0UwWFR1eTFjSGJ6TGlrcWpaRGtGbytFUkdBM1JnYmFPZmc5VmZiZGNIRUdSTkxSRENIbU1ZS211UjVCbWUwT0RFazJjV3dpQlBHWm5YNXcwSjNqRDJWV2hOU1k3UzVJREwxNzRvVkR3T2J2RXo3VWE2Q2wvc0RKaHlWRWdjaDQ2VCtRbkNXd0hMaEo0TkJsWkEwUWFrSzBPNWxTeit6cE50aE9zWTIxZG80cHZQd1NPNXJuMGt1M0VZNkhscjJGVWgxaXdxdEU0cDF5c0I0RVVTcXNGRjlQenQxN0xHNHFyZ2FqU2lBVkVRUDZiUmNLNFp4WTJab1RDQTBHU3JYVEZLVGUwY0tzRDFoQVlyN08rN0VzVENqRFB5YTg0bVlOQUplS0hFNnFwVitCQk1aUW9rYlJFU3VpU3pyVEp6Si9ubDRMZnNxdFBteWhVdmcxL2ZudXBESlVETEE1VDIyMjE3c1FYZmtmeHdoVXJzSTk3dEpvZ1JDRkU4dSs3VlRrbUVCY0RLRUh6WWJNaEdwanA1U1dyS24rUlZHaWxHaUdOSFpkcnJOZzlZT0xWajk5RkdhQ2I5V1F3TVZsMXoxS2l5ZGJFS3pnKzI4QmdZRlJsc0ZZb1NRQUJhejEzbHhGQU9JVm1hVnNLYXRWdDYwQ25TOEdGeUw2MkdDN1kzY1FzazlybndHL2xqQlYxNS9LMU9WY1YyWnFybDV3clkwQ21pNW01eWR0UC9IVTZPaEdBSlZ0RjJoYzVRYTI0dWJVZ2JsemFyeDhmbG5XVEUyb1NLOXo0Z2xSTjQ2K2hzaDBiUXV3ekdLcXZON3dhWFlXRFhGRG5aSGRZOHlDTlhTdWVDUy82eFoxakFWNG10aDlyRWpJZUNDNFNXKzVrN0ZWUmFYanpEOUJOSjRwZHRDNzRSZGdFWmhlVFlOSThPZnU0SS9ER2tob2dJVHVnOU13RmkxdmFBTm9vSmJEcHNnSGJYL2lIYjNkZklMQXVxM2pidm5xRWswdHg0a1BicVI2SWtKSVdVU2pSQmh3SlJRLzRoRDFTbGNXNXBObDVsRW1BRFRZZFhVUk0xeGdlM2Y1czB2d0FBRzUrRDc0a09FWVJOQmNNaVRWaGttbFkzTVZQUWdVOVJZMFBUV1ppa0IwUDA0UGVkRDhkMnRTQXVmd01zTWZoRVpheTVwUXpTaUg0Zjd4T1lMSmt2aFJkZFRiM1BYdUJXdkQxcDZSeWxMMVFmN0h0RVh0Z2pvTzdLUVo3SUVJNFgxOW85cDdDU01JYU5qR040VUNlNUczYkJkQWhIbXFndnFBSXplOTh2cjRuTWZUZ01FUFY4RWpTRnpVWExNM0RyKzZScEpMckFnRUZ0bGhEREpoNVRMZUtncGFybFN4Q0RMazhJWEdPQ2ptZ0o5TDIzMWphV0MzK1NzY2cvWURkTWFLVUtaSG9nUDBWUlB3MitCeERiRXovaW1kMTdEaXprMU5FRFg5M2FDSGNWMWE1b3M3cnR5ampsVGRUTmhueVFhb2RYK2tyQ3NUdnFnaUR5ZGN4bjR6NW1VdlUwQUpvWW1DU0N1QnhnSTJlMmV0UmREMTBUU2lUNklGWFlEWmVYM0N4SHMxN1NmYXhjSUV6TGdtMGdsRVQ0VUxUZEdDblVlY2xoVHhxeTd0OGF0ZytGcUdDQlNEZWpaNTloODN5M0lnRE0rNjFwVTRFZzQ1R2JqK0RveFY5OGlSSzVPelpBNCtSTTl3eFlZaDBmQ2h6bitKczBQakFrd0ZGZ0VqOWpLaDhNWWJwd2hGeDM0cnUycGNUSGt2dmlVdjYrWDJIZmQzQisyeTVCUzZ5Z2UxWWVDYkZpamlJTkhQQS9kNHlzSlVLZnZTM0duSEYyaG1wWTF2UEZLRmVlRUh3VFJtVkdPUExKN0hWMGZKQVNVemtQWmVxWDIwcnZDdmdnOEdRbm4yTDM3dXdCd0xvYlM1T2xSamUxOEdLQStTYlBSZ2U3Q25rUjZoVzhDSEl6SStwVlJOTkNzT2VTV3lUUTY0Q1p6b3lTeHJxdFNVcnMzUWVhMWZCOG9JRURyWWNIU05ja3dHMmt4WUJLdmRqbzhxa0ZaVlNZWlhjV0IvRnhSSkxObU96OXN0TFg0ZnJiMnJGTzMwd0w5R2FaOHFFeHl2clJzb0lTeWExei91ZE8zR0wxU3k2a2V6WGxpK1ZNRGRRV2pyYkRXVnczT0dmSXFKUWRjTlhnN2pYa1JmeVFLaGYxODlxTWdtL3dxcmpUUUR6aGNhREsvN0tvSnZVOFJ6blFHb2FIMmJTOURmREdObDBqc2ZBQmVDVUpPZTBtVlA3aUZNa0xmeG16L3RyQ0ZMMElRbHFBdXlDM3h1Wjl2VWtKRDE1S1REcW5kQlQ3TEdMMWxpbGpCU3BQYXZLQzArTUVuaHMzUTJRM0lKcDdoaE1wdGcxd3NQY3o2VUNOMTBTb1pqV202cXJGRGk1SVkxcXlGMFlKT29tU2J0VGo4SXZVZEJtQ1ArRmxUQzR5eldOY3FQQm13MHJlbDdqd2xHZFNhN1Z0dVpwazhFK0t3TFBTRE1hUUR4S0w4Tm5XZXNuMFF3dVEreHYvaWU0ck5qVlovSXoyWHNrbG1Tc1VTclg4Q1h1V05YNmxTWFlpVzdnMFIwTXg4ZnFZS1RsUWZ2M2Y1U3VpenZsUGx0UzhCMlVnUlRPUE1PTEc2Z3h6dElpMXdBSFAxT2hkVmtlZGRmNTdiVlN4SDZvQlh0V3gzZEpFT2FVQ0lXOXNCQ2FhN3Z0RGFWS2pUUUQyWWlZc1BIMlFuTWFjZlZqR3hIZi9XTWZHRWlJODRwaTQrMU5jQVE5eTFFc2hZRll4YVB1S3h2L0dNRUpqbGlmejFYL3I1czNxbC9PenpGTXNkdDFlcTZGVWx1Wmt1NklFNGVkUldrZXRDc1F5RHRxVS9qNzNiZWthSGhpaGwrcjVISzB1QkVDZDdIVGE1M3VOaXVRNEpKVzd2U3ZFK1JmbStiREdrcjFmZHBOQkVjSHZaL1VxWnhwV3VmUDBPZ3B6SGdmVS9mSS9GQ3haTXFpemFjZnhzUTJoZi81TCtnMXB5eVJnMXNWamxFTFByOHpObHFTWldhOE9FK2JmbFVjWnAyamFsVkZKYzlpR3gvQ0dJVnlacEJ2Y3dqc29DakpFekVnS1I5Y1REbE92aVlFNzBEQmVJVFJub1ZDeUlaS2p3YiszUHk3WTRkQ2gwSmR4c2Fmc1Rrc1UrRFh2cHhoNHpjVXpKTUM1L1hZa1hXWjU3SGZnWllFSkI5WnNjU3pVYTZWY2lxVmI4SElSR2RINlNhSjluK0pJZWVQV3ExMFZCVGdvT2Jma2RtYUcwT1VITDVTNjN1Q2NyK2dBOWxXRHQva1p0V2gvWmNyK0NhMkl1cGtGZHFYTWpWNXNka3NJdndDTHZKenlYVjFZeExaU3Z4OFFtN0JVVmxFZ0doSUxpNEphbkRVLzlTeXRURytCMElRTUdKWTM2T2d1QllqN21lL2UxZXkyeHlSbW8xU1U5bnpCU1FHV1lLTWhrZlYzajhlclZxaFA2bFJaT0FxM21uaHNBRUpCZ1FnQVRmQ0VsMk1GRFRlSTdTbFZ6aTIyRXZSc2Zvc2ttVFlhWHNoZG5uY0N3alp1WUlieDR3TWtwL3RtRVBzMCtTWndJNyt1NDE5aVpWelVmUkV0Y3JMVXEwUHFKWVd0RCtHOEJnZy95NTZMWUtHM1Q0QXNxUHM5SjZhWDltd04zaHRnSDdEWEpkb0R2dzhxMzluWjdBNm4wV1ZxYkZDMU5oWDdsczZsT0Z1NmsyRm5YSkxXUEwwbmxGeGZPb1JNTi9zN3h4Qk1KMzlJck9DL2htdktmY3RZRHRSeGpHZS9nZ3hibWoxMmJDbGozSjk5c0VzZktWb0VkMUNHbDRDSVl3dWcxQkN1SG9DMngwbXd4VCthUGljV2oxVUV2YUxwM1kzLzFTdVlxSjRxN0xWMlY3N213Q0szNEhpbDZPUXFhdUVjdVdBS0lybEs4MGJ3aG5ibEMrSTZPajh2ZGlMem9lTS9UOFhNWDVVU3lmbDNNeFcxV3EwbVdaN0luRGU1TElnRmZueHNzRXdZcCtYcldPVzdIemZpWnVWSmpTNDBaWnZTd1EzTGlEVTNVQmVVWjdrb0l0Ulp3ZEN4M00wN1c4TXFVT3dqa3JCZUZSVEhRSWZ5dUQ4UkFlVEpZQ2NaUFJtQzRYcDVyU2tOa1dlUmpoVEFZajAvc3NQSUdPMkZsN3FkVVRkYUxtMFJCZ0pMMGRjWnVjZk94djhJZHVtSndETUNaSlllMjlMY2NZbENYUTBFNklhNmpqWDFueFVjaGVNMkloTVNhZEY5WnNNL0pTSzlPNTFHVk1PYU82WUlrb2lrMTBiUjNtbkVhUlRiMldUMDlaRzdCSVZFTG1oYkdGTkppcWZVNEdxdklkakJzSEJVNjhqdkd1dDBSVTI1ZHdHSVk4azFQM0RPdEZjbTZqcTkvcGZZK21BWFVISTlBeGxJUE9YVmpySTZQUkJTY01HVVZSNUJ1L0VJOHNJSlMxNm5mRDkrQWdnbCtPUXAwUXVuQk5jcWNoTUorN2NlWDAzb0l6cVVnRHZvZCtqOWJYN2hnZ1UxL1VySW9Dc3JZYUphcWZjaEJXUFd0QkgzUS9VMVhFOWhrbUdreWxKamJqRU11MzBxQ3ZMbm1Fc2pEMlpJTllSWmVnaENPRCtvR0IzTXdaWXZuQmJtckNZRVZEbGV5S29oQ3cxSmtrZHgxU21JdVVIdC9tMXlXME8zb0RJdzdPdWpDWGM3RTU3U0MrckErcjB0eGFJNDV4aTh3YWkyTTN0VVl2NStObGJDWllhNkNoeVhSRVZkWXJnSnY0cVY0YzZ4UXlUSGpmY2xCZENsazVxWGtOUDlHcmMzQnI5V2N0WFVpZVBiM3A2OTVPS1NpMDUyZ1p2WFMrclFXakljekk0eG56NXphcTBuTmNMRktOWWR2MHF5cG05YlA2YVp1dEczRWxEQU9aYWdGS3lnMEFIdHdaeXpma09UVTUxQkp5ZTdneU9WR2hQaXcvSHhya21iRDMwQnFjendtKzJXOFgvU1V6Zk01NWhVcjVuNHJDNVQxOXAwY3RpQjAzMzJPU0UrbUdWbGh2c3hvQjY2bG8xVG5zZGFqZ2tKakJnRzk4Vnd2VE5UN0JOUDhCSWlNaS93enBiQUh5MndoQWtBZWFRS25HUGdNL096OWppVmI4Tm9ia2J3bm8vTEd4Y1o3L2w3Nno0OElHMWxlZXJBL28rYVR2TmN0ZldNbXowaW4raGhYOE9FVnZkdzMvbFk5UUJXNWxqWWgwVWZpdmEvWG1oQVo4TjJlWGtCcHNXZGFSc1dLdys2TE12bnJnVWdmaWZyN1V4TDJKTS9UclBqRktPR3IzK0NiR2RXMkF0WVFPQ1FFcHRObFJKOStmVklQVFZVVGZZZzZwb2V1NjlMMndYbEZmeGpxeFlQRVBPZjlTU2tNZ050NWN2NzEvSi9rbU9LMGZGWWlDVElzWUhKYWIwNXFqNXFSaTJKUndmTWZqTWNRTVQrOHN0R0U9PC94ZW5jOkNpcGhlclZhbHVlPgogICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4KICAgIDwveGVuYzpFbmNyeXB0ZWREYXRhPgogIDwvc2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+Cjwvc2FtbHA6UmVzcG9uc2U+ diff --git a/test/saml_message_test.rb b/test/saml_message_test.rb index 6c060c076..7aa494d38 100644 --- a/test/saml_message_test.rb +++ b/test/saml_message_test.rb @@ -52,5 +52,23 @@ class RubySamlTest < Minitest::Test decoded_inflated = saml_message.send(:inflate, decoded) assert response_document_xml, decoded_inflated end + + describe "Prevent Zlib bomb attack" do + it "raises error when SAML Message exceed the allowed bytes" do + prefix= """ + + """ + suffix= """ + ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7 + """ + + data = prefix + "A" * (200000 * 1024) + suffix + bomb = Base64.encode64(Zlib::Deflate.deflate(data, 9)[2..-5]) + assert_raises(OneLogin::RubySaml::ValidationError, "Encoded SAML Message exceeds " + OneLogin::RubySaml::SamlMessage::MAX_BYTE_SIZE.to_s + " bytes, so was rejected") do + saml_message = OneLogin::RubySaml::SamlMessage.new + saml_message.send(:decode_raw_saml, bomb) + end + end + end end end \ No newline at end of file diff --git a/test/settings_test.rb b/test/settings_test.rb index e2a3b29bc..e9b0a8dbe 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -12,7 +12,7 @@ class SettingsTest < Minitest::Test it "should provide getters and settings" do accessors = [ - :idp_entity_id, :idp_sso_target_url, :idp_slo_target_url, :valid_until, + :idp_entity_id, :idp_sso_target_url, :idp_sso_service_url, :idp_slo_target_url, :idp_slo_service_url, :valid_until, :idp_cert, :idp_cert_fingerprint, :idp_cert_fingerprint_algorithm, :idp_cert_multi, :idp_attribute_names, :issuer, :assertion_consumer_service_url, :assertion_consumer_service_binding, :single_logout_service_url, :single_logout_service_binding, @@ -38,8 +38,8 @@ class SettingsTest < Minitest::Test :assertion_consumer_service_url => "http://app.muda.no/sso", :issuer => "http://muda.no", :sp_name_qualifier => "http://sso.muda.no", - :idp_sso_target_url => "http://sso.muda.no/sso", - :idp_slo_target_url => "http://sso.muda.no/slo", + :idp_sso_service_url => "http://sso.muda.no/sso", + :idp_slo_service_url => "http://sso.muda.no/slo", :idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00", :valid_until => '2029-04-16T03:35:08.277Z', :name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", @@ -125,6 +125,24 @@ class SettingsTest < Minitest::Test end end + describe "#idp_sso_service_url" do + it "when idp_sso_service_url is nil but idp_sso_target_url returns its value" do + @settings.idp_sso_service_url = nil + @settings.idp_sso_target_url = "https://idp.example.com/sso" + + assert_equal "https://idp.example.com/sso", @settings.idp_sso_service_url + end + end + + describe "#idp_slo_service_url" do + it "when idp_slo_service_url is nil but idp_slo_target_url returns its value" do + @settings.idp_slo_service_url = nil + @settings.idp_slo_target_url = "https://idp.example.com/slo" + + assert_equal "https://idp.example.com/slo", @settings.idp_slo_service_url + end + end + describe "#get_idp_cert" do it "returns nil when the cert is an empty string" do @settings.idp_cert = "" diff --git a/test/slo_logoutresponse_test.rb b/test/slo_logoutresponse_test.rb index 189037cf6..865db63b3 100644 --- a/test/slo_logoutresponse_test.rb +++ b/test/slo_logoutresponse_test.rb @@ -65,6 +65,24 @@ class SloLogoutresponseTest < Minitest::Test assert_match /Custom Logout Message<\/samlp:StatusMessage>/, inflated end + it "set a custom logout message and an status on the response" do + unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, "Custom Logout Message", {}, "urn:oasis:names:tc:SAML:2.0:status:PartialLogout") + + inflated = decode_saml_response_payload(unauth_url) + assert_match /Custom Logout Message<\/samlp:StatusMessage>/, inflated + assert_match /.*<\/ds:X509Certificate>/, + "an-invalid-certificate") + end + + it 'is not valid' do + assert !document.validate_document_with_cert(idp_cert), 'Document should be valid' + assert_equal(["Document Certificate Error"], document.errors) + end + end + + describe 'when response cert is different from idp cert' do + let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text2) } + + it 'is not valid' do + exception = assert_raises(OneLogin::RubySaml::ValidationError) do + document.validate_document_with_cert(idp_cert, false) + end + assert_equal("Certificate of the Signature element does not match provided certificate", exception.message) + end + + it 'is not valid (soft = true)' do + document.validate_document_with_cert(idp_cert) + assert_equal(["Certificate of the Signature element does not match provided certificate"], document.errors) + end + end end end end