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