ruby-saml 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/README.md +24 -2
- data/changelog.md +5 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +12 -0
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +192 -173
- data/lib/onelogin/ruby-saml/logoutrequest.rb +11 -2
- data/lib/onelogin/ruby-saml/logoutresponse.rb +4 -7
- data/lib/onelogin/ruby-saml/saml_message.rb +6 -2
- data/lib/onelogin/ruby-saml/settings.rb +1 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +11 -2
- data/lib/onelogin/ruby-saml/utils.rb +1 -1
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/ruby-saml.gemspec +7 -3
- data/test/certificates/certificate.der +0 -0
- data/test/idp_metadata_parser_test.rb +9 -1
- data/test/logout_responses/logoutresponse_fixtures.rb +19 -0
- data/test/logoutrequest_test.rb +40 -6
- data/test/logoutresponse_test.rb +12 -5
- data/test/metadata/idp_multiple_descriptors.xml +6 -0
- data/test/metadata/idp_multiple_descriptors_2.xml +59 -0
- data/test/request_test.rb +17 -0
- data/test/settings_test.rb +1 -1
- data/test/slo_logoutresponse_test.rb +34 -0
- data/test/test_helper.rb +4 -0
- data/test/utils_test.rb +5 -0
- metadata +8 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8c5ca7da83f66d1548ef03b3ffe920eea1a07eff
         | 
| 4 | 
            +
              data.tar.gz: f27e46ad0136bb5325d55189f30bc8dac861b5d6
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 49ac1e001e3b676f080d5ca0d5955c18034cd6a7d7371c99840fece91cb5ac55ce2bd68abfb2ae901f9a192589c997db1e77b6050d9569b66b0aa7311c2875f3
         | 
| 7 | 
            +
              data.tar.gz: b6a30ef8efe897fcf8e69eb638b1214077cfd5027f83ab438164b3436d7396a7a4ee1a89f04f055e96d57c0b589e437c48ef5fd658ac2a136560b6b8620fa040
         | 
    
        data/.travis.yml
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,5 +1,11 @@ | |
| 1 1 | 
             
            # Ruby SAML [](http://travis-ci.org/onelogin/ruby-saml) [](https://coveralls.io/r/onelogin/ruby-saml?branch=master%0A) [](http://badge.fury.io/rb/ruby-saml)
         | 
| 2 2 |  | 
| 3 | 
            +
            # Updating from 1.9.0 to 1.10.0
         | 
| 4 | 
            +
            Version `1.10.0` improves IdpMetadataParser to allow parse multiple IDPSSODescriptor, Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user to be authenticated and updates the format_cert method to accept certs with /\x0d/
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ## Updating from 1.8.0 to 1.9.0
         | 
| 7 | 
            +
            Version `1.9.0` better supports Ruby 2.4+ and JRuby 9.2.0.0. `Settings` initialization now has a second parameter, `keep_security_settings` (default: false), which saves security settings attributes that are not explicitly overridden, if set to true.
         | 
| 8 | 
            +
             | 
| 3 9 | 
             
            ## Updating from 1.7.X to 1.8.0
         | 
| 4 10 | 
             
            On Version `1.8.0`, creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState param will not generate a URL with an empty RelayState parameter anymore. It also changes the invalid audience error message.
         | 
| 5 11 |  | 
| @@ -99,8 +105,10 @@ We created a demo project for Rails4 that uses the latest version of this librar | |
| 99 105 | 
             
            * 2.2.x
         | 
| 100 106 | 
             
            * 2.3.x
         | 
| 101 107 | 
             
            * 2.4.x
         | 
| 108 | 
            +
            * 2.5.x
         | 
| 102 109 | 
             
            * JRuby 1.7.19
         | 
| 103 110 | 
             
            * JRuby 9.0.0.0
         | 
| 111 | 
            +
            * JRuby 9.2.0.0
         | 
| 104 112 |  | 
| 105 113 | 
             
            ## Adding Features, Pull Requests
         | 
| 106 114 | 
             
            * Fork the repository
         | 
| @@ -121,7 +129,7 @@ Using `Gemfile` | |
| 121 129 |  | 
| 122 130 | 
             
            ```ruby
         | 
| 123 131 | 
             
            # latest stable
         | 
| 124 | 
            -
            gem 'ruby-saml', '~> 1. | 
| 132 | 
            +
            gem 'ruby-saml', '~> 1.9.0'
         | 
| 125 133 |  | 
| 126 134 | 
             
            # or track master for bleeding-edge
         | 
| 127 135 | 
             
            gem 'ruby-saml', :github => 'onelogin/ruby-saml'
         | 
| @@ -170,7 +178,7 @@ To override the default behavior and control the destination of log messages, pr | |
| 170 178 | 
             
            a ruby Logger object to the gem's logging singleton:
         | 
| 171 179 |  | 
| 172 180 | 
             
            ```ruby
         | 
| 173 | 
            -
            OneLogin::RubySaml::Logging.logger = Logger.new( | 
| 181 | 
            +
            OneLogin::RubySaml::Logging.logger = Logger.new('/var/log/ruby-saml.log')
         | 
| 174 182 | 
             
            ```
         | 
| 175 183 |  | 
| 176 184 | 
             
            ## The Initialization Phase
         | 
| @@ -184,6 +192,17 @@ def init | |
| 184 192 | 
             
            end
         | 
| 185 193 | 
             
            ```
         | 
| 186 194 |  | 
| 195 | 
            +
            If the SP knows who should be authenticated in the IdP, then can provide that info as follows:
         | 
| 196 | 
            +
             | 
| 197 | 
            +
            ```ruby
         | 
| 198 | 
            +
            def init
         | 
| 199 | 
            +
              request = OneLogin::RubySaml::Authrequest.new
         | 
| 200 | 
            +
              saml_settings.name_identifier_value_requested = "testuser@example.com"
         | 
| 201 | 
            +
              saml_settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
         | 
| 202 | 
            +
              redirect_to(request.create(saml_settings))
         | 
| 203 | 
            +
            end
         | 
| 204 | 
            +
            ```
         | 
| 205 | 
            +
             | 
| 187 206 | 
             
            Once you've redirected back to the identity provider, it will ensure that the user has been authorized and redirect back to your application for final consumption. This can look something like this (the `authorize_success` and `authorize_failure` methods are specific to your application):
         | 
| 188 207 |  | 
| 189 208 | 
             
            ```ruby
         | 
| @@ -498,6 +517,9 @@ pp(response.attributes.multi(:not_exists)) | |
| 498 517 | 
             
            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').
         | 
| 499 518 | 
             
            To add a `saml:AuthnContextDeclRef`, define `settings.authn_context_decl_ref`.
         | 
| 500 519 |  | 
| 520 | 
            +
            In a SP-initiaited flow, the SP can indicate to the IdP the subject that should be authenticated. This is done by defining the `settings.name_identifier_value_requested` before
         | 
| 521 | 
            +
            building the authrequest object.
         | 
| 522 | 
            +
             | 
| 501 523 |  | 
| 502 524 | 
             
            ## Signing
         | 
| 503 525 |  | 
    
        data/changelog.md
    CHANGED
    
    | @@ -1,4 +1,9 @@ | |
| 1 1 | 
             
            # RubySaml Changelog
         | 
| 2 | 
            +
            ### 1.10.0 (Mar 21, 2019)
         | 
| 3 | 
            +
            * Add Subject support on AuthNRequest to allow SPs provide info to the IdP about the user to be authenticated
         | 
| 4 | 
            +
            * Improves IdpMetadataParser to allow parse multiple IDPSSODescriptors
         | 
| 5 | 
            +
            * Improves format_cert method to accept certs with /\x0d/
         | 
| 6 | 
            +
            * Forces nokogiri >= 1.8.2 when possible
         | 
| 2 7 |  | 
| 3 8 | 
             
            ### 1.9.0 (Sept 03, 2018)
         | 
| 4 9 | 
             
            * [#458](https://github.com/onelogin/ruby-saml/pull/458) Remove ruby 2.4+ warnings
         | 
| @@ -121,6 +121,18 @@ module OneLogin | |
| 121 121 | 
             
                      issuer = root.add_element "saml:Issuer"
         | 
| 122 122 | 
             
                      issuer.text = settings.issuer
         | 
| 123 123 | 
             
                    end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    if settings.name_identifier_value_requested != nil
         | 
| 126 | 
            +
                      subject = root.add_element "saml:Subject"
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                      nameid = subject.add_element "saml:NameID"
         | 
| 129 | 
            +
                      nameid.attributes['Format'] = settings.name_identifier_format if settings.name_identifier_format
         | 
| 130 | 
            +
                      nameid.text = settings.name_identifier_value_requested
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                      subject_confirmation = subject.add_element "saml:SubjectConfirmation"
         | 
| 133 | 
            +
                      subject_confirmation.attributes['Method'] = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
         | 
| 134 | 
            +
                    end
         | 
| 135 | 
            +
             | 
| 124 136 | 
             
                    if settings.name_identifier_format != nil
         | 
| 125 137 | 
             
                      root.add_element "samlp:NameIDPolicy", {
         | 
| 126 138 | 
             
                          # Might want to make AllowCreate a setting?
         | 
| @@ -1,6 +1,4 @@ | |
| 1 1 | 
             
            require "base64"
         | 
| 2 | 
            -
            require "zlib"
         | 
| 3 | 
            -
            require "cgi"
         | 
| 4 2 | 
             
            require "net/http"
         | 
| 5 3 | 
             
            require "net/https"
         | 
| 6 4 | 
             
            require "rexml/document"
         | 
| @@ -14,12 +12,23 @@ module OneLogin | |
| 14 12 | 
             
                # Auxiliary class to retrieve and parse the Identity Provider Metadata
         | 
| 15 13 | 
             
                #
         | 
| 16 14 | 
             
                class IdpMetadataParser
         | 
| 15 | 
            +
                  module SamlMetadata
         | 
| 16 | 
            +
                    module Vocabulary
         | 
| 17 | 
            +
                      METADATA       = "urn:oasis:names:tc:SAML:2.0:metadata"
         | 
| 18 | 
            +
                      DSIG           = "http://www.w3.org/2000/09/xmldsig#"
         | 
| 19 | 
            +
                      NAME_FORMAT    = "urn:oasis:names:tc:SAML:2.0:attrname-format:*"
         | 
| 20 | 
            +
                      SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
         | 
| 21 | 
            +
                    end
         | 
| 17 22 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 23 | 
            +
                    NAMESPACE = {
         | 
| 24 | 
            +
                      "md" => Vocabulary::METADATA,
         | 
| 25 | 
            +
                      "NameFormat" => Vocabulary::NAME_FORMAT,
         | 
| 26 | 
            +
                      "saml" => Vocabulary::SAML_ASSERTION,
         | 
| 27 | 
            +
                      "ds" => Vocabulary::DSIG
         | 
| 28 | 
            +
                    }
         | 
| 29 | 
            +
                  end
         | 
| 22 30 |  | 
| 31 | 
            +
                  include SamlMetadata::Vocabulary
         | 
| 23 32 | 
             
                  attr_reader :document
         | 
| 24 33 | 
             
                  attr_reader :response
         | 
| 25 34 | 
             
                  attr_reader :options
         | 
| @@ -58,8 +67,25 @@ module OneLogin | |
| 58 67 | 
             
                  #
         | 
| 59 68 | 
             
                  # @raise [HttpError] Failure to fetch remote IdP metadata
         | 
| 60 69 | 
             
                  def parse_remote_to_hash(url, validate_cert = true, options = {})
         | 
| 70 | 
            +
                    parse_remote_to_array(url, validate_cert, options)[0]
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  # Parse all Identity Provider metadata and return the results as Array
         | 
| 74 | 
            +
                  #
         | 
| 75 | 
            +
                  # @param url [String] Url where the XML of the Identity Provider Metadata is published.
         | 
| 76 | 
            +
                  # @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked.
         | 
| 77 | 
            +
                  #
         | 
| 78 | 
            +
                  # @param options [Hash] options used for parsing the metadata
         | 
| 79 | 
            +
                  # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
         | 
| 80 | 
            +
                  # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
         | 
| 81 | 
            +
                  # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, all found IdPs are returned.
         | 
| 82 | 
            +
                  #
         | 
| 83 | 
            +
                  # @return [Array<Hash>]
         | 
| 84 | 
            +
                  #
         | 
| 85 | 
            +
                  # @raise [HttpError] Failure to fetch remote IdP metadata
         | 
| 86 | 
            +
                  def parse_remote_to_array(url, validate_cert = true, options = {})
         | 
| 61 87 | 
             
                    idp_metadata = get_idp_metadata(url, validate_cert)
         | 
| 62 | 
            -
                     | 
| 88 | 
            +
                    parse_to_array(idp_metadata, options)
         | 
| 63 89 | 
             
                  end
         | 
| 64 90 |  | 
| 65 91 | 
             
                  # Parse the Identity Provider metadata and update the settings with the IdP values
         | 
| @@ -98,28 +124,29 @@ module OneLogin | |
| 98 124 | 
             
                  #
         | 
| 99 125 | 
             
                  # @return [Hash]
         | 
| 100 126 | 
             
                  def parse_to_hash(idp_metadata, options = {})
         | 
| 127 | 
            +
                    parse_to_array(idp_metadata, options)[0]
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  # Parse all Identity Provider metadata and return the results as Array
         | 
| 131 | 
            +
                  #
         | 
| 132 | 
            +
                  # @param idp_metadata [String]
         | 
| 133 | 
            +
                  #
         | 
| 134 | 
            +
                  # @param options [Hash] options used for parsing the metadata and the returned Settings instance
         | 
| 135 | 
            +
                  # @option options [Array<String>, nil] :sso_binding an ordered list of bindings to detect the single signon URL. The first binding in the list that is included in the metadata will be used.
         | 
| 136 | 
            +
                  # @option options [Array<String>, nil] :slo_binding an ordered list of bindings to detect the single logout URL. The first binding in the list that is included in the metadata will be used.
         | 
| 137 | 
            +
                  # @option options [String, nil] :entity_id when this is given, the entity descriptor for this ID is used. When ommitted, all found IdPs are returned.
         | 
| 138 | 
            +
                  #
         | 
| 139 | 
            +
                  # @return [Array<Hash>]
         | 
| 140 | 
            +
                  def parse_to_array(idp_metadata, options = {})
         | 
| 101 141 | 
             
                    @document = REXML::Document.new(idp_metadata)
         | 
| 102 142 | 
             
                    @options = options
         | 
| 103 | 
            -
                    @entity_descriptor = nil
         | 
| 104 | 
            -
                    @certificates = nil
         | 
| 105 | 
            -
                    @fingerprint = nil
         | 
| 106 143 |  | 
| 107 | 
            -
                     | 
| 144 | 
            +
                    idpsso_descriptors = IdpMetadata::get_idps(@document, options[:entity_id])
         | 
| 145 | 
            +
                    if !idpsso_descriptors.any?
         | 
| 108 146 | 
             
                      raise ArgumentError.new("idp_metadata must contain an IDPSSODescriptor element")
         | 
| 109 147 | 
             
                    end
         | 
| 110 148 |  | 
| 111 | 
            -
                    {
         | 
| 112 | 
            -
                      :idp_entity_id => idp_entity_id,
         | 
| 113 | 
            -
                      :name_identifier_format => idp_name_id_format,
         | 
| 114 | 
            -
                      :idp_sso_target_url => single_signon_service_url(options),
         | 
| 115 | 
            -
                      :idp_slo_target_url => single_logout_service_url(options),
         | 
| 116 | 
            -
                      :idp_attribute_names => attribute_names,
         | 
| 117 | 
            -
                      :idp_cert => nil,
         | 
| 118 | 
            -
                      :idp_cert_fingerprint => nil,
         | 
| 119 | 
            -
                      :idp_cert_multi => nil
         | 
| 120 | 
            -
                    }.tap do |response_hash|
         | 
| 121 | 
            -
                      merge_certificates_into(response_hash) unless certificates.nil?
         | 
| 122 | 
            -
                    end
         | 
| 149 | 
            +
                    return idpsso_descriptors.map{|id| IdpMetadata.new(id, id.parent.attributes["entityID"]).to_hash(options)}
         | 
| 123 150 | 
             
                  end
         | 
| 124 151 |  | 
| 125 152 | 
             
                  private
         | 
| @@ -147,7 +174,7 @@ module OneLogin | |
| 147 174 | 
             
                    end
         | 
| 148 175 |  | 
| 149 176 | 
             
                    get = Net::HTTP::Get.new(uri.request_uri)
         | 
| 150 | 
            -
                    response = http.request(get)
         | 
| 177 | 
            +
                    @response = http.request(get)
         | 
| 151 178 | 
             
                    return response.body if response.is_a? Net::HTTPSuccess
         | 
| 152 179 |  | 
| 153 180 | 
             
                    raise OneLogin::RubySaml::HttpError.new(
         | 
| @@ -155,130 +182,129 @@ module OneLogin | |
| 155 182 | 
             
                    )
         | 
| 156 183 | 
             
                  end
         | 
| 157 184 |  | 
| 158 | 
            -
                   | 
| 159 | 
            -
                     | 
| 160 | 
            -
                       | 
| 161 | 
            -
                       | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
                  def entity_descriptor_path
         | 
| 167 | 
            -
                    path = "//md:EntityDescriptor"
         | 
| 168 | 
            -
                    entity_id = options[:entity_id]
         | 
| 169 | 
            -
                    return path unless entity_id
         | 
| 170 | 
            -
                    path << "[@entityID=\"#{entity_id}\"]"
         | 
| 171 | 
            -
                  end
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                  def idpsso_descriptor
         | 
| 174 | 
            -
                    unless entity_descriptor.nil?
         | 
| 175 | 
            -
                      return REXML::XPath.first(
         | 
| 176 | 
            -
                        entity_descriptor,
         | 
| 177 | 
            -
                        "md:IDPSSODescriptor",
         | 
| 178 | 
            -
                        namespace
         | 
| 185 | 
            +
                  class IdpMetadata
         | 
| 186 | 
            +
                    def self.get_idps(metadata_document, only_entity_id=nil)
         | 
| 187 | 
            +
                      path = "//md:EntityDescriptor#{only_entity_id && '[@entityID="' + only_entity_id + '"]'}/md:IDPSSODescriptor"
         | 
| 188 | 
            +
                      REXML::XPath.match(
         | 
| 189 | 
            +
                        metadata_document,
         | 
| 190 | 
            +
                        path,
         | 
| 191 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 179 192 | 
             
                      )
         | 
| 180 193 | 
             
                    end
         | 
| 181 | 
            -
                  end 
         | 
| 182 | 
            -
             | 
| 183 | 
            -
                  # @return [String|nil] IdP Entity ID value if exists
         | 
| 184 | 
            -
                  #
         | 
| 185 | 
            -
                  def idp_entity_id
         | 
| 186 | 
            -
                    entity_descriptor.attributes["entityID"]
         | 
| 187 | 
            -
                  end
         | 
| 188 194 |  | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
                     | 
| 193 | 
            -
                      entity_descriptor,
         | 
| 194 | 
            -
                      "md:IDPSSODescriptor/md:NameIDFormat",
         | 
| 195 | 
            -
                      namespace
         | 
| 196 | 
            -
                    )
         | 
| 197 | 
            -
                    Utils.element_text(node)
         | 
| 198 | 
            -
                  end
         | 
| 195 | 
            +
                    def initialize(idpsso_descriptor, entity_id)
         | 
| 196 | 
            +
                      @idpsso_descriptor = idpsso_descriptor
         | 
| 197 | 
            +
                      @entity_id = entity_id
         | 
| 198 | 
            +
                    end
         | 
| 199 199 |  | 
| 200 | 
            -
             | 
| 201 | 
            -
             | 
| 202 | 
            -
             | 
| 203 | 
            -
             | 
| 204 | 
            -
             | 
| 205 | 
            -
             | 
| 206 | 
            -
             | 
| 207 | 
            -
             | 
| 208 | 
            -
             | 
| 209 | 
            -
             | 
| 210 | 
            -
                       | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 213 | 
            -
                      nodes.first.value if nodes.any?
         | 
| 200 | 
            +
                    def to_hash(options = {})
         | 
| 201 | 
            +
                      {
         | 
| 202 | 
            +
                        :idp_entity_id => @entity_id,
         | 
| 203 | 
            +
                        :name_identifier_format => idp_name_id_format,
         | 
| 204 | 
            +
                        :idp_sso_target_url => single_signon_service_url(options),
         | 
| 205 | 
            +
                        :idp_slo_target_url => single_logout_service_url(options),
         | 
| 206 | 
            +
                        :idp_attribute_names => attribute_names,
         | 
| 207 | 
            +
                        :idp_cert => nil,
         | 
| 208 | 
            +
                        :idp_cert_fingerprint => nil,
         | 
| 209 | 
            +
                        :idp_cert_multi => nil
         | 
| 210 | 
            +
                      }.tap do |response_hash|
         | 
| 211 | 
            +
                        merge_certificates_into(response_hash) unless certificates.nil?
         | 
| 212 | 
            +
                      end
         | 
| 214 213 | 
             
                    end
         | 
| 215 | 
            -
                  end
         | 
| 216 214 |  | 
| 217 | 
            -
             | 
| 218 | 
            -
             | 
| 219 | 
            -
             | 
| 220 | 
            -
                  def single_signon_service_url(options = {})
         | 
| 221 | 
            -
                    binding = single_signon_service_binding(options[:sso_binding])
         | 
| 222 | 
            -
                    unless binding.nil?
         | 
| 215 | 
            +
                    # @return [String|nil] IdP Name ID Format value if exists
         | 
| 216 | 
            +
                    #
         | 
| 217 | 
            +
                    def idp_name_id_format
         | 
| 223 218 | 
             
                      node = REXML::XPath.first(
         | 
| 224 | 
            -
                         | 
| 225 | 
            -
                        "md: | 
| 226 | 
            -
                         | 
| 219 | 
            +
                        @idpsso_descriptor,
         | 
| 220 | 
            +
                        "md:NameIDFormat",
         | 
| 221 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 227 222 | 
             
                      )
         | 
| 228 | 
            -
                       | 
| 223 | 
            +
                      Utils.element_text(node)
         | 
| 229 224 | 
             
                    end
         | 
| 230 | 
            -
                  end
         | 
| 231 225 |  | 
| 232 | 
            -
             | 
| 233 | 
            -
             | 
| 234 | 
            -
             | 
| 235 | 
            -
             | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 241 | 
            -
             | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 226 | 
            +
                    # @param binding_priority [Array]
         | 
| 227 | 
            +
                    # @return [String|nil] SingleSignOnService binding if exists
         | 
| 228 | 
            +
                    #
         | 
| 229 | 
            +
                    def single_signon_service_binding(binding_priority = nil)
         | 
| 230 | 
            +
                      nodes = REXML::XPath.match(
         | 
| 231 | 
            +
                        @idpsso_descriptor,
         | 
| 232 | 
            +
                        "md:SingleSignOnService/@Binding",
         | 
| 233 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 234 | 
            +
                      )
         | 
| 235 | 
            +
                      if binding_priority
         | 
| 236 | 
            +
                        values = nodes.map(&:value)
         | 
| 237 | 
            +
                        binding_priority.detect{ |binding| values.include? binding }
         | 
| 238 | 
            +
                      else
         | 
| 239 | 
            +
                        nodes.first.value if nodes.any?
         | 
| 240 | 
            +
                      end
         | 
| 246 241 | 
             
                    end
         | 
| 247 | 
            -
                  end
         | 
| 248 242 |  | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
             | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 254 | 
            -
             | 
| 243 | 
            +
                    # @param options [Hash]
         | 
| 244 | 
            +
                    # @return [String|nil] SingleSignOnService endpoint if exists
         | 
| 245 | 
            +
                    #
         | 
| 246 | 
            +
                    def single_signon_service_url(options = {})
         | 
| 247 | 
            +
                      binding = single_signon_service_binding(options[:sso_binding])
         | 
| 248 | 
            +
                      return if binding.nil?
         | 
| 249 | 
            +
             | 
| 255 250 | 
             
                      node = REXML::XPath.first(
         | 
| 256 | 
            -
                         | 
| 257 | 
            -
                        "md: | 
| 258 | 
            -
                         | 
| 251 | 
            +
                        @idpsso_descriptor,
         | 
| 252 | 
            +
                        "md:SingleSignOnService[@Binding=\"#{binding}\"]/@Location",
         | 
| 253 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 259 254 | 
             
                      )
         | 
| 260 255 | 
             
                      return node.value if node
         | 
| 261 256 | 
             
                    end
         | 
| 262 | 
            -
                  end
         | 
| 263 257 |  | 
| 264 | 
            -
             | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
                     | 
| 268 | 
            -
                       | 
| 269 | 
            -
                         | 
| 270 | 
            -
                        "md: | 
| 271 | 
            -
                         | 
| 258 | 
            +
                    # @param binding_priority [Array]
         | 
| 259 | 
            +
                    # @return [String|nil] SingleLogoutService binding if exists
         | 
| 260 | 
            +
                    #
         | 
| 261 | 
            +
                    def single_logout_service_binding(binding_priority = nil)
         | 
| 262 | 
            +
                      nodes = REXML::XPath.match(
         | 
| 263 | 
            +
                        @idpsso_descriptor,
         | 
| 264 | 
            +
                        "md:SingleLogoutService/@Binding",
         | 
| 265 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 272 266 | 
             
                      )
         | 
| 267 | 
            +
                      if binding_priority
         | 
| 268 | 
            +
                        values = nodes.map(&:value)
         | 
| 269 | 
            +
                        binding_priority.detect{ |binding| values.include? binding }
         | 
| 270 | 
            +
                      else
         | 
| 271 | 
            +
                        nodes.first.value if nodes.any?
         | 
| 272 | 
            +
                      end
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                    # @param options [Hash]
         | 
| 276 | 
            +
                    # @return [String|nil] SingleLogoutService endpoint if exists
         | 
| 277 | 
            +
                    #
         | 
| 278 | 
            +
                    def single_logout_service_url(options = {})
         | 
| 279 | 
            +
                      binding = single_logout_service_binding(options[:slo_binding])
         | 
| 280 | 
            +
                      return if binding.nil?
         | 
| 273 281 |  | 
| 274 | 
            -
                       | 
| 275 | 
            -
                         | 
| 276 | 
            -
                        "md: | 
| 277 | 
            -
                         | 
| 282 | 
            +
                      node = REXML::XPath.first(
         | 
| 283 | 
            +
                        @idpsso_descriptor,
         | 
| 284 | 
            +
                        "md:SingleLogoutService[@Binding=\"#{binding}\"]/@Location",
         | 
| 285 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 278 286 | 
             
                      )
         | 
| 287 | 
            +
                      return node.value if node
         | 
| 288 | 
            +
                    end
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                    # @return [String|nil] Unformatted Certificate if exists
         | 
| 291 | 
            +
                    #
         | 
| 292 | 
            +
                    def certificates
         | 
| 293 | 
            +
                      @certificates ||= begin
         | 
| 294 | 
            +
                        signing_nodes = REXML::XPath.match(
         | 
| 295 | 
            +
                          @idpsso_descriptor,
         | 
| 296 | 
            +
                          "md:KeyDescriptor[not(contains(@use, 'encryption'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
         | 
| 297 | 
            +
                          SamlMetadata::NAMESPACE
         | 
| 298 | 
            +
                        )
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                        encryption_nodes = REXML::XPath.match(
         | 
| 301 | 
            +
                          @idpsso_descriptor,
         | 
| 302 | 
            +
                          "md:KeyDescriptor[not(contains(@use, 'signing'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
         | 
| 303 | 
            +
                          SamlMetadata::NAMESPACE
         | 
| 304 | 
            +
                        )
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                        return nil if signing_nodes.empty? && encryption_nodes.empty?
         | 
| 279 307 |  | 
| 280 | 
            -
                      certs = nil
         | 
| 281 | 
            -
                      unless signing_nodes.empty? && encryption_nodes.empty?
         | 
| 282 308 | 
             
                        certs = {}
         | 
| 283 309 | 
             
                        unless signing_nodes.empty?
         | 
| 284 310 | 
             
                          certs['signing'] = []
         | 
| @@ -293,71 +319,62 @@ module OneLogin | |
| 293 319 | 
             
                            certs['encryption'] << Utils.element_text(cert_node)
         | 
| 294 320 | 
             
                          end
         | 
| 295 321 | 
             
                        end
         | 
| 322 | 
            +
                        certs
         | 
| 296 323 | 
             
                      end
         | 
| 297 | 
            -
                      certs
         | 
| 298 324 | 
             
                    end
         | 
| 299 | 
            -
                  end
         | 
| 300 325 |  | 
| 301 | 
            -
             | 
| 302 | 
            -
             | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 326 | 
            +
                    # @return [String|nil] the fingerpint of the X509Certificate if it exists
         | 
| 327 | 
            +
                    #
         | 
| 328 | 
            +
                    def fingerprint(certificate, fingerprint_algorithm = XMLSecurity::Document::SHA1)
         | 
| 329 | 
            +
                      @fingerprint ||= begin
         | 
| 330 | 
            +
                        return unless certificate
         | 
| 331 | 
            +
             | 
| 306 332 | 
             
                        cert = OpenSSL::X509::Certificate.new(Base64.decode64(certificate))
         | 
| 307 333 |  | 
| 308 334 | 
             
                        fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(fingerprint_algorithm).new
         | 
| 309 335 | 
             
                        fingerprint_alg.hexdigest(cert.to_der).upcase.scan(/../).join(":")
         | 
| 310 336 | 
             
                      end
         | 
| 311 337 | 
             
                    end
         | 
| 312 | 
            -
                  end
         | 
| 313 338 |  | 
| 314 | 
            -
             | 
| 315 | 
            -
             | 
| 316 | 
            -
             | 
| 317 | 
            -
             | 
| 318 | 
            -
             | 
| 319 | 
            -
             | 
| 320 | 
            -
             | 
| 321 | 
            -
             | 
| 322 | 
            -
             | 
| 323 | 
            -
             | 
| 324 | 
            -
             | 
| 325 | 
            -
                  def namespace
         | 
| 326 | 
            -
                    {
         | 
| 327 | 
            -
                      "md" => METADATA,
         | 
| 328 | 
            -
                      "NameFormat" => NAME_FORMAT,
         | 
| 329 | 
            -
                      "saml" => SAML_ASSERTION,
         | 
| 330 | 
            -
                      "ds" => DSIG
         | 
| 331 | 
            -
                    }
         | 
| 332 | 
            -
                  end
         | 
| 339 | 
            +
                    # @return [Array] the names of all SAML attributes if any exist
         | 
| 340 | 
            +
                    #
         | 
| 341 | 
            +
                    def attribute_names
         | 
| 342 | 
            +
                      nodes = REXML::XPath.match(
         | 
| 343 | 
            +
                        @idpsso_descriptor  ,
         | 
| 344 | 
            +
                        "saml:Attribute/@Name",
         | 
| 345 | 
            +
                        SamlMetadata::NAMESPACE
         | 
| 346 | 
            +
                      )
         | 
| 347 | 
            +
                      nodes.map(&:value)
         | 
| 348 | 
            +
                    end
         | 
| 333 349 |  | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 350 | 
            +
                    def merge_certificates_into(parsed_metadata)
         | 
| 351 | 
            +
                      if (certificates.size == 1 &&
         | 
| 336 352 | 
             
                          (certificates_has_one('signing') || certificates_has_one('encryption'))) ||
         | 
| 337 353 | 
             
                          (certificates_has_one('signing') && certificates_has_one('encryption') &&
         | 
| 338 354 | 
             
                          certificates["signing"][0] == certificates["encryption"][0])
         | 
| 339 355 |  | 
| 340 | 
            -
             | 
| 341 | 
            -
             | 
| 342 | 
            -
             | 
| 343 | 
            -
             | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 356 | 
            +
                        if certificates.key?("signing")
         | 
| 357 | 
            +
                          parsed_metadata[:idp_cert] = certificates["signing"][0]
         | 
| 358 | 
            +
                          parsed_metadata[:idp_cert_fingerprint] = fingerprint(
         | 
| 359 | 
            +
                            parsed_metadata[:idp_cert],
         | 
| 360 | 
            +
                            parsed_metadata[:idp_cert_fingerprint_algorithm]
         | 
| 361 | 
            +
                          )
         | 
| 362 | 
            +
                        else
         | 
| 363 | 
            +
                          parsed_metadata[:idp_cert] = certificates["encryption"][0]
         | 
| 364 | 
            +
                          parsed_metadata[:idp_cert_fingerprint] = fingerprint(
         | 
| 365 | 
            +
                            parsed_metadata[:idp_cert],
         | 
| 366 | 
            +
                            parsed_metadata[:idp_cert_fingerprint_algorithm]
         | 
| 367 | 
            +
                          )
         | 
| 368 | 
            +
                        end
         | 
| 346 369 | 
             
                      else
         | 
| 347 | 
            -
                         | 
| 348 | 
            -
                        parsed_metadata[: | 
| 349 | 
            -
                          parsed_metadata[:idp_cert],
         | 
| 350 | 
            -
                          parsed_metadata[:idp_cert_fingerprint_algorithm]
         | 
| 351 | 
            -
                        )
         | 
| 370 | 
            +
                        # symbolize keys of certificates and pass it on
         | 
| 371 | 
            +
                        parsed_metadata[:idp_cert_multi] = Hash[certificates.map { |k, v| [k.to_sym, v] }]
         | 
| 352 372 | 
             
                      end
         | 
| 353 | 
            -
                    else
         | 
| 354 | 
            -
                      # symbolize keys of certificates and pass it on
         | 
| 355 | 
            -
                      parsed_metadata[:idp_cert_multi] = Hash[certificates.map { |k, v| [k.to_sym, v] }]
         | 
| 356 373 | 
             
                    end
         | 
| 357 | 
            -
                  end
         | 
| 358 374 |  | 
| 359 | 
            -
             | 
| 360 | 
            -
             | 
| 375 | 
            +
                    def certificates_has_one(key)
         | 
| 376 | 
            +
                      certificates.key?(key) && certificates[key].size == 1
         | 
| 377 | 
            +
                    end
         | 
| 361 378 | 
             
                  end
         | 
| 362 379 |  | 
| 363 380 | 
             
                  def merge_parsed_metadata_into(settings, parsed_metadata)
         | 
| @@ -367,6 +384,8 @@ module OneLogin | |
| 367 384 |  | 
| 368 385 | 
             
                    settings
         | 
| 369 386 | 
             
                  end
         | 
| 387 | 
            +
             | 
| 388 | 
            +
                  private_constant :SamlMetadata, :IdpMetadata
         | 
| 370 389 | 
             
                end
         | 
| 371 390 | 
             
              end
         | 
| 372 391 | 
             
            end
         | 
| @@ -89,6 +89,11 @@ module OneLogin | |
| 89 89 | 
             
                  # @return [String] The SAMLRequest String.
         | 
| 90 90 | 
             
                  #
         | 
| 91 91 | 
             
                  def create_logout_request_xml_doc(settings)
         | 
| 92 | 
            +
                    document = create_xml_document(settings)
         | 
| 93 | 
            +
                    sign_document(document, settings)
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  def create_xml_document(settings)
         | 
| 92 97 | 
             
                    time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
         | 
| 93 98 |  | 
| 94 99 | 
             
                    request_doc = XMLSecurity::Document.new
         | 
| @@ -122,14 +127,18 @@ module OneLogin | |
| 122 127 | 
             
                      sessionindex.text = settings.sessionindex
         | 
| 123 128 | 
             
                    end
         | 
| 124 129 |  | 
| 130 | 
            +
                    request_doc
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  def sign_document(document, settings)
         | 
| 125 134 | 
             
                    # embed signature
         | 
| 126 135 | 
             
                    if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
         | 
| 127 136 | 
             
                      private_key = settings.get_sp_key
         | 
| 128 137 | 
             
                      cert = settings.get_sp_cert
         | 
| 129 | 
            -
                       | 
| 138 | 
            +
                      document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
         | 
| 130 139 | 
             
                    end
         | 
| 131 140 |  | 
| 132 | 
            -
                     | 
| 141 | 
            +
                    document
         | 
| 133 142 | 
             
                  end
         | 
| 134 143 | 
             
                end
         | 
| 135 144 | 
             
              end
         | 
| @@ -52,10 +52,7 @@ module OneLogin | |
| 52 52 | 
             
                  # @raise [ValidationError] if soft == false and validation fails
         | 
| 53 53 | 
             
                  #
         | 
| 54 54 | 
             
                  def success?
         | 
| 55 | 
            -
                     | 
| 56 | 
            -
                      return append_error("Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <#@status_code>")
         | 
| 57 | 
            -
                    end
         | 
| 58 | 
            -
                    true
         | 
| 55 | 
            +
                    return status_code == "urn:oasis:names:tc:SAML:2.0:status:Success"
         | 
| 59 56 | 
             
                  end
         | 
| 60 57 |  | 
| 61 58 | 
             
                  # @return [String|nil] Gets the InResponseTo attribute from the Logout Response if exists.
         | 
| @@ -65,7 +62,7 @@ module OneLogin | |
| 65 62 | 
             
                      node = REXML::XPath.first(
         | 
| 66 63 | 
             
                        document,
         | 
| 67 64 | 
             
                        "/p:LogoutResponse",
         | 
| 68 | 
            -
                        { "p" => PROTOCOL | 
| 65 | 
            +
                        { "p" => PROTOCOL }
         | 
| 69 66 | 
             
                      )
         | 
| 70 67 | 
             
                      node.nil? ? nil : node.attributes['InResponseTo']
         | 
| 71 68 | 
             
                    end
         | 
| @@ -88,7 +85,7 @@ module OneLogin | |
| 88 85 | 
             
                  #
         | 
| 89 86 | 
             
                  def status_code
         | 
| 90 87 | 
             
                    @status_code ||= begin
         | 
| 91 | 
            -
                      node = REXML::XPath.first(document, "/p:LogoutResponse/p:Status/p:StatusCode", { "p" => PROTOCOL | 
| 88 | 
            +
                      node = REXML::XPath.first(document, "/p:LogoutResponse/p:Status/p:StatusCode", { "p" => PROTOCOL })
         | 
| 92 89 | 
             
                      node.nil? ? nil : node.attributes["Value"]
         | 
| 93 90 | 
             
                    end
         | 
| 94 91 | 
             
                  end
         | 
| @@ -98,7 +95,7 @@ module OneLogin | |
| 98 95 | 
             
                      node = REXML::XPath.first(
         | 
| 99 96 | 
             
                        document,
         | 
| 100 97 | 
             
                        "/p:LogoutResponse/p:Status/p:StatusMessage",
         | 
| 101 | 
            -
                        { "p" => PROTOCOL | 
| 98 | 
            +
                        { "p" => PROTOCOL }
         | 
| 102 99 | 
             
                      )
         | 
| 103 100 | 
             
                      Utils.element_text(node)
         | 
| 104 101 | 
             
                    end
         | 
| @@ -105,7 +105,7 @@ module OneLogin | |
| 105 105 | 
             
                  def encode_raw_saml(saml, settings)
         | 
| 106 106 | 
             
                    saml = deflate(saml) if settings.compress_request
         | 
| 107 107 |  | 
| 108 | 
            -
                    CGI.escape( | 
| 108 | 
            +
                    CGI.escape(encode(saml))
         | 
| 109 109 | 
             
                  end
         | 
| 110 110 |  | 
| 111 111 | 
             
                  # Base 64 decode method
         | 
| @@ -121,7 +121,11 @@ module OneLogin | |
| 121 121 | 
             
                  # @return [String] The encoded string
         | 
| 122 122 | 
             
                  #
         | 
| 123 123 | 
             
                  def encode(string)
         | 
| 124 | 
            -
                    Base64. | 
| 124 | 
            +
                    if Base64.respond_to?('strict_encode64')
         | 
| 125 | 
            +
                      Base64.strict_encode64(string)
         | 
| 126 | 
            +
                    else
         | 
| 127 | 
            +
                      Base64.encode64(string).gsub(/\n/, "")
         | 
| 128 | 
            +
                    end
         | 
| 125 129 | 
             
                  end
         | 
| 126 130 |  | 
| 127 131 | 
             
                  # Check if a string is base64 encoded
         | 
| @@ -45,6 +45,7 @@ module OneLogin | |
| 45 45 | 
             
                  attr_accessor :sp_name_qualifier
         | 
| 46 46 | 
             
                  attr_accessor :name_identifier_format
         | 
| 47 47 | 
             
                  attr_accessor :name_identifier_value
         | 
| 48 | 
            +
                  attr_accessor :name_identifier_value_requested
         | 
| 48 49 | 
             
                  attr_accessor :sessionindex
         | 
| 49 50 | 
             
                  attr_accessor :compress_request
         | 
| 50 51 | 
             
                  attr_accessor :compress_response
         | 
| @@ -97,6 +97,11 @@ module OneLogin | |
| 97 97 | 
             
                  # @return [String] The SAMLResponse String.
         | 
| 98 98 | 
             
                  #
         | 
| 99 99 | 
             
                  def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil)
         | 
| 100 | 
            +
                    document = create_xml_document(settings, request_id, logout_message)
         | 
| 101 | 
            +
                    sign_document(document, settings)
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def create_xml_document(settings, request_id = nil, logout_message = nil)
         | 
| 100 105 | 
             
                    time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
         | 
| 101 106 |  | 
| 102 107 | 
             
                    response_doc = XMLSecurity::Document.new
         | 
| @@ -126,14 +131,18 @@ module OneLogin | |
| 126 131 | 
             
                    status_message = status.add_element 'samlp:StatusMessage'
         | 
| 127 132 | 
             
                    status_message.text = logout_message
         | 
| 128 133 |  | 
| 134 | 
            +
                    response_doc
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  def sign_document(document, settings)
         | 
| 129 138 | 
             
                    # embed signature
         | 
| 130 139 | 
             
                    if settings.security[:logout_responses_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
         | 
| 131 140 | 
             
                      private_key = settings.get_sp_key
         | 
| 132 141 | 
             
                      cert = settings.get_sp_cert
         | 
| 133 | 
            -
                       | 
| 142 | 
            +
                      document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
         | 
| 134 143 | 
             
                    end
         | 
| 135 144 |  | 
| 136 | 
            -
                     | 
| 145 | 
            +
                    document
         | 
| 137 146 | 
             
                  end
         | 
| 138 147 |  | 
| 139 148 | 
             
                end
         | 
| @@ -22,7 +22,7 @@ module OneLogin | |
| 22 22 | 
             
                  #
         | 
| 23 23 | 
             
                  def self.format_cert(cert)
         | 
| 24 24 | 
             
                    # don't try to format an encoded certificate or if is empty or nil
         | 
| 25 | 
            -
                    return cert if cert.nil? || cert.empty? || cert. | 
| 25 | 
            +
                    return cert if cert.nil? || cert.empty? || !cert.ascii_only?
         | 
| 26 26 |  | 
| 27 27 | 
             
                    if cert.scan(/BEGIN CERTIFICATE/).length > 1
         | 
| 28 28 | 
             
                      formatted_cert = []
         | 
    
        data/ruby-saml.gemspec
    CHANGED
    
    | @@ -29,15 +29,19 @@ Gem::Specification.new do |s| | |
| 29 29 | 
             
              # Nokogiri's version dependent on the Ruby version, even though we would
         | 
| 30 30 | 
             
              # have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions.
         | 
| 31 31 | 
             
              if defined?(JRUBY_VERSION)
         | 
| 32 | 
            -
                 | 
| 33 | 
            -
             | 
| 32 | 
            +
                if JRUBY_VERSION < '9.2.0.0'
         | 
| 33 | 
            +
                  s.add_runtime_dependency('nokogiri', '>= 1.8.2', '<= 1.8.5')
         | 
| 34 | 
            +
                  s.add_runtime_dependency('jruby-openssl', '>= 0.9.8')
         | 
| 35 | 
            +
                else
         | 
| 36 | 
            +
                  s.add_runtime_dependency('nokogiri', '>= 1.8.2')
         | 
| 37 | 
            +
                end
         | 
| 34 38 | 
             
              elsif RUBY_VERSION < '1.9'
         | 
| 35 39 | 
             
                s.add_runtime_dependency('uuid')
         | 
| 36 40 | 
             
                s.add_runtime_dependency('nokogiri', '<= 1.5.11')
         | 
| 37 41 | 
             
              elsif RUBY_VERSION < '2.1'
         | 
| 38 42 | 
             
                s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1')
         | 
| 39 43 | 
             
              else
         | 
| 40 | 
            -
                s.add_runtime_dependency('nokogiri', '>= 1. | 
| 44 | 
            +
                s.add_runtime_dependency('nokogiri', '>= 1.8.2')
         | 
| 41 45 | 
             
              end
         | 
| 42 46 |  | 
| 43 47 | 
             
              s.add_development_dependency('minitest', '~> 5.5')
         | 
| Binary file | 
| @@ -319,7 +319,7 @@ class IdpMetadataParserTest < Minitest::Test | |
| 319 319 | 
             
              describe "parsing metadata with many entity descriptors" do
         | 
| 320 320 | 
             
                before do
         | 
| 321 321 | 
             
                  @idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
         | 
| 322 | 
            -
                  @idp_metadata =  | 
| 322 | 
            +
                  @idp_metadata = idp_metadata_multiple_descriptors2
         | 
| 323 323 | 
             
                  @settings = @idp_metadata_parser.parse(@idp_metadata)
         | 
| 324 324 | 
             
                end
         | 
| 325 325 |  | 
| @@ -342,6 +342,14 @@ class IdpMetadataParserTest < Minitest::Test | |
| 342 342 | 
             
                  assert_equal "https://hello.example.com/access/saml/logout", @settings.idp_slo_target_url
         | 
| 343 343 | 
             
                  assert_equal ["AuthToken", "SSOStartPage"], @settings.idp_attribute_names
         | 
| 344 344 | 
             
                end
         | 
| 345 | 
            +
             | 
| 346 | 
            +
                it "should handle multiple descriptors at once" do
         | 
| 347 | 
            +
                  settings = @idp_metadata_parser.parse_to_array(@idp_metadata)
         | 
| 348 | 
            +
                  assert_equal "https://foo.example.com/access/saml/idp.xml", settings.first[:idp_entity_id]
         | 
| 349 | 
            +
                  assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.first[:idp_cert_fingerprint]
         | 
| 350 | 
            +
                  assert_equal "https://bar.example.com/access/saml/idp.xml", settings.last[:idp_entity_id]
         | 
| 351 | 
            +
                  assert_equal "08:EB:6E:60:A2:14:4E:89:EC:FA:05:74:9D:72:BF:5D:BE:54:F0:1A", settings.last[:idp_cert_fingerprint]
         | 
| 352 | 
            +
                end
         | 
| 345 353 | 
             
              end
         | 
| 346 354 |  | 
| 347 355 | 
             
              describe "parsing metadata with no IDPSSODescriptor element" do
         | 
| @@ -44,6 +44,25 @@ def unsuccessful_logout_response_document(opts = {}) | |
| 44 44 | 
             
                  </samlp:LogoutResponse>"
         | 
| 45 45 | 
             
            end
         | 
| 46 46 |  | 
| 47 | 
            +
            def unsuccessful_logout_response_with_message_document(opts = {})
         | 
| 48 | 
            +
              opts = default_logout_response_opts.merge(opts)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              "<samlp:LogoutResponse
         | 
| 51 | 
            +
                    xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
         | 
| 52 | 
            +
                    ID=\"#{random_id}\" Version=\"2.0\"
         | 
| 53 | 
            +
                    IssueInstant=\"#{opts[:issue_instant]}\"
         | 
| 54 | 
            +
                    Destination=\"#{opts[:settings].single_logout_service_url}\"
         | 
| 55 | 
            +
                    InResponseTo=\"#{opts[:uuid]}\">
         | 
| 56 | 
            +
                  <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{opts[:settings].issuer}</saml:Issuer>
         | 
| 57 | 
            +
                  <samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">
         | 
| 58 | 
            +
                  <samlp:StatusCode xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
         | 
| 59 | 
            +
                      Value=\"urn:oasis:names:tc:SAML:2.0:status:Requester\">
         | 
| 60 | 
            +
                  </samlp:StatusCode>
         | 
| 61 | 
            +
                  <samlp:StatusMessage>Logoutrequest expired</samlp:StatusMessage>
         | 
| 62 | 
            +
                  </samlp:Status>
         | 
| 63 | 
            +
                  </samlp:LogoutResponse>"
         | 
| 64 | 
            +
            end
         | 
| 65 | 
            +
             | 
| 47 66 | 
             
            def invalid_xml_logout_response_document
         | 
| 48 67 | 
             
              "<samlp:SomethingAwful
         | 
| 49 68 | 
             
                    xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
         | 
    
        data/test/logoutrequest_test.rb
    CHANGED
    
    | @@ -104,6 +104,40 @@ class RequestTest < Minitest::Test | |
| 104 104 | 
             
                    settings.private_key = ruby_saml_key_text
         | 
| 105 105 | 
             
                  end
         | 
| 106 106 |  | 
| 107 | 
            +
                  it "doesn't sign through create_xml_document" do
         | 
| 108 | 
            +
                    unauth_req = OneLogin::RubySaml::Logoutrequest.new
         | 
| 109 | 
            +
                    inflated = unauth_req.create_xml_document(settings).to_s
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 112 | 
            +
                    refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 113 | 
            +
                    refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  it "sign unsigned request" do
         | 
| 117 | 
            +
                    unauth_req = OneLogin::RubySaml::Logoutrequest.new
         | 
| 118 | 
            +
                    unauth_req_doc = unauth_req.create_xml_document(settings)
         | 
| 119 | 
            +
                    inflated = unauth_req_doc.to_s
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 122 | 
            +
                    refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 123 | 
            +
                    refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    inflated = unauth_req.sign_document(unauth_req_doc, settings).to_s
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 128 | 
            +
                    assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 129 | 
            +
                    assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  it "signs through create_logout_request_xml_doc" do
         | 
| 133 | 
            +
                    unauth_req = OneLogin::RubySaml::Logoutrequest.new
         | 
| 134 | 
            +
                    inflated = unauth_req.create_logout_request_xml_doc(settings).to_s
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 137 | 
            +
                    assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 138 | 
            +
                    assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 139 | 
            +
                  end
         | 
| 140 | 
            +
             | 
| 107 141 | 
             
                  it "created a signed logout request" do
         | 
| 108 142 | 
             
                    settings.compress_request = true
         | 
| 109 143 |  | 
| @@ -185,8 +219,8 @@ class RequestTest < Minitest::Test | |
| 185 219 | 
             
                    query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
         | 
| 186 220 |  | 
| 187 221 | 
             
                    signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
         | 
| 188 | 
            -
                    assert_equal signature_algorithm, OpenSSL::Digest::SHA256 | 
| 189 | 
            -
                    assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string) | 
| 222 | 
            +
                    assert_equal signature_algorithm, OpenSSL::Digest::SHA256
         | 
| 223 | 
            +
                    assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
         | 
| 190 224 | 
             
                  end
         | 
| 191 225 |  | 
| 192 226 | 
             
                  it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
         | 
| @@ -201,8 +235,8 @@ class RequestTest < Minitest::Test | |
| 201 235 | 
             
                    query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
         | 
| 202 236 |  | 
| 203 237 | 
             
                    signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
         | 
| 204 | 
            -
                    assert_equal signature_algorithm, OpenSSL::Digest::SHA384 | 
| 205 | 
            -
                    assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string) | 
| 238 | 
            +
                    assert_equal signature_algorithm, OpenSSL::Digest::SHA384
         | 
| 239 | 
            +
                    assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
         | 
| 206 240 | 
             
                  end
         | 
| 207 241 |  | 
| 208 242 | 
             
                  it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
         | 
| @@ -217,8 +251,8 @@ class RequestTest < Minitest::Test | |
| 217 251 | 
             
                    query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
         | 
| 218 252 |  | 
| 219 253 | 
             
                    signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
         | 
| 220 | 
            -
                    assert_equal signature_algorithm, OpenSSL::Digest::SHA512 | 
| 221 | 
            -
                    assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string) | 
| 254 | 
            +
                    assert_equal signature_algorithm, OpenSSL::Digest::SHA512
         | 
| 255 | 
            +
                    assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
         | 
| 222 256 | 
             
                  end
         | 
| 223 257 |  | 
| 224 258 | 
             
                end
         | 
    
        data/test/logoutresponse_test.rb
    CHANGED
    
    | @@ -107,15 +107,22 @@ class RubySamlTest < Minitest::Test | |
| 107 107 | 
             
                      assert_includes logoutresponse.errors, "The InResponseTo of the Logout Response: #{logoutresponse.in_response_to}, does not match the ID of the Logout Request sent by the SP: #{expected_request_id}"
         | 
| 108 108 | 
             
                    end
         | 
| 109 109 |  | 
| 110 | 
            -
                    it "invalidate logout response with  | 
| 110 | 
            +
                    it "invalidate logout response with unexpected request status" do
         | 
| 111 111 | 
             
                      logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
         | 
| 112 112 |  | 
| 113 113 | 
             
                      assert !logoutresponse.success?
         | 
| 114 114 | 
             
                      assert !logoutresponse.validate
         | 
| 115 | 
            -
                      assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
         | 
| 116 115 | 
             
                      assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
         | 
| 117 116 | 
             
                    end
         | 
| 118 117 |  | 
| 118 | 
            +
                    it "invalidate logout response with unexpected request status and status message" do
         | 
| 119 | 
            +
                      logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_with_message_document, settings)
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                      assert !logoutresponse.success?
         | 
| 122 | 
            +
                      assert !logoutresponse.validate
         | 
| 123 | 
            +
                      assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester -> Logoutrequest expired"
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
             | 
| 119 126 | 
             
                    it "invalidate logout response when in lack of issuer setting" do
         | 
| 120 127 | 
             
                      bad_settings = settings
         | 
| 121 128 | 
             
                      bad_settings.issuer = nil
         | 
| @@ -137,7 +144,7 @@ class RubySamlTest < Minitest::Test | |
| 137 144 | 
             
                      logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
         | 
| 138 145 | 
             
                      collect_errors = true
         | 
| 139 146 | 
             
                      assert !logoutresponse.validate(collect_errors)
         | 
| 140 | 
            -
                      assert_includes logoutresponse.errors, " | 
| 147 | 
            +
                      assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
         | 
| 141 148 | 
             
                      assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
         | 
| 142 149 | 
             
                    end
         | 
| 143 150 |  | 
| @@ -185,14 +192,14 @@ class RubySamlTest < Minitest::Test | |
| 185 192 | 
             
                      logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
         | 
| 186 193 |  | 
| 187 194 | 
             
                      assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
         | 
| 188 | 
            -
                      assert_includes logoutresponse.errors, " | 
| 195 | 
            +
                      assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
         | 
| 189 196 | 
             
                    end
         | 
| 190 197 |  | 
| 191 198 | 
             
                    it "raise validation error when in bad state" do
         | 
| 192 199 | 
             
                      # no settings
         | 
| 193 200 | 
             
                      logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
         | 
| 194 201 | 
             
                      assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
         | 
| 195 | 
            -
                      assert_includes logoutresponse.errors, " | 
| 202 | 
            +
                      assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
         | 
| 196 203 | 
             
                    end
         | 
| 197 204 |  | 
| 198 205 | 
             
                    it "raise validation error when in lack of issuer setting" do
         | 
| @@ -1,5 +1,11 @@ | |
| 1 1 | 
             
            <?xml version="1.0" encoding="UTF-8"?>
         | 
| 2 2 | 
             
            <md:EntitiesDescriptor validUntil="2014-04-17T18:02:33.910Z" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
         | 
| 3 | 
            +
              <md:EntityDescriptor entityID="https://biz.example.com/app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="md:EntityDescriptorType">
         | 
| 4 | 
            +
                <md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
         | 
| 5 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
         | 
| 6 | 
            +
                  <md:AssertionConsumerService isDefault="true" index="1" binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" location="https://biz.example.com/app/sso"></md:AssertionConsumerService>
         | 
| 7 | 
            +
                </md:SPSSODescriptor>
         | 
| 8 | 
            +
              </md:EntityDescriptor>
         | 
| 3 9 | 
             
              <md:EntityDescriptor entityID="https://foo.example.com/access/saml/idp.xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="md:EntityDescriptorType">
         | 
| 4 10 | 
             
                <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
         | 
| 5 11 | 
             
                  <md:KeyDescriptor use="signing">
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            <?xml version="1.0" encoding="UTF-8"?>
         | 
| 2 | 
            +
            <md:EntitiesDescriptor validUntil="2014-04-17T18:02:33.910Z" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
         | 
| 3 | 
            +
              <md:EntityDescriptor entityID="https://biz.example.com/app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="md:EntityDescriptorType">
         | 
| 4 | 
            +
                <md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
         | 
| 5 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
         | 
| 6 | 
            +
                  <md:AssertionConsumerService isDefault="true" index="1" binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" location="https://biz.example.com/app/sso"></md:AssertionConsumerService>
         | 
| 7 | 
            +
                </md:SPSSODescriptor>
         | 
| 8 | 
            +
              </md:EntityDescriptor>
         | 
| 9 | 
            +
              <md:EntityDescriptor entityID="https://foo.example.com/access/saml/idp.xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="md:EntityDescriptorType">
         | 
| 10 | 
            +
                <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
         | 
| 11 | 
            +
                  <md:KeyDescriptor use="signing">
         | 
| 12 | 
            +
                    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
         | 
| 13 | 
            +
                      <ds:X509Data>
         | 
| 14 | 
            +
                        <ds:X509Certificate>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=</ds:X509Certificate>
         | 
| 15 | 
            +
                      </ds:X509Data>
         | 
| 16 | 
            +
                    </ds:KeyInfo>
         | 
| 17 | 
            +
                  </md:KeyDescriptor>
         | 
| 18 | 
            +
                  <md:KeyDescriptor use="encryption">
         | 
| 19 | 
            +
                    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
         | 
| 20 | 
            +
                      <ds:X509Data>
         | 
| 21 | 
            +
                        <ds:X509Certificate>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=</ds:X509Certificate>
         | 
| 22 | 
            +
                      </ds:X509Data>
         | 
| 23 | 
            +
                    </ds:KeyInfo>
         | 
| 24 | 
            +
                  </md:KeyDescriptor>
         | 
| 25 | 
            +
                  <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://hello.example.com/access/saml/logout" ResponseLocation="https://hello.example.com/access/saml/logout"/>
         | 
| 26 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
         | 
| 27 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
         | 
| 28 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
         | 
| 29 | 
            +
                  <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://hello.example.com/access/saml/login"/>
         | 
| 30 | 
            +
                  <saml:Attribute Name="AuthToken" NameFormat="urn:oasis:names:tc:SAML:2.0:att rname-format:basic" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>
         | 
| 31 | 
            +
                  <saml:Attribute Name="SSOStartPage" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>
         | 
| 32 | 
            +
                </md:IDPSSODescriptor>
         | 
| 33 | 
            +
              </md:EntityDescriptor>
         | 
| 34 | 
            +
              <md:EntityDescriptor entityID="https://bar.example.com/access/saml/idp.xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="md:EntityDescriptorType">
         | 
| 35 | 
            +
                <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
         | 
| 36 | 
            +
                  <md:KeyDescriptor use="signing">
         | 
| 37 | 
            +
                    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
         | 
| 38 | 
            +
                      <ds:X509Data>
         | 
| 39 | 
            +
                        <ds:X509Certificate>MIICfDCCAeWgAwIBAgIBADANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJ1czEYMBYGA1UECAwPYmFyLmV4YW1wbGUuY29tMRgwFgYDVQQKDA9iYXIuZXhhbXBsZS5jb20xGDAWBgNVBAMMD2Jhci5leGFtcGxlLmNvbTAeFw0xOTAzMjExMzAzMTJaFw0yOTAzMTgxMzAzMTJaMFsxCzAJBgNVBAYTAnVzMRgwFgYDVQQIDA9iYXIuZXhhbXBsZS5jb20xGDAWBgNVBAoMD2Jhci5leGFtcGxlLmNvbTEYMBYGA1UEAwwPYmFyLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv7Xe9pxNYjxLhXhjQy7L6BbU0IVwQS9pbWqXYDtH3z1fIy+tWMEnE+XhuSFLilRcfn8Ksk0VeFLByQw2HfxAHY3O2dsCv5yLHye3/5bLKa+/pYu7r9ltE5JNjELAH1Yo0/SvAWX9Nuw6Ovw7D6frXUxBPhJaEQ+1VnatuZBUOfQIDAQABo1AwTjAdBgNVHQ4EFgQU6xbFedBYUDFTdXoQvRrELtH4jP8wHwYDVR0jBBgwFoAU6xbFedBYUDFTdXoQvRrELtH4jP8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBvDqYSONEhlDB4+k2+hMg03XCwINLh0UHWd+QfFO1yT6iZK+Duaq9pDBl97RO9sN6qHfOFSQXYBOj6S1Xf+6Orq1yeep/Uh+UrL7wzWYMzPryZxTYn8B1PbdXePRIZCzU7d7ueMnEfUoBj9HqhQphRYD9WgXP9/xzvu9JQjTNk9g==</ds:X509Certificate>
         | 
| 40 | 
            +
                      </ds:X509Data>
         | 
| 41 | 
            +
                    </ds:KeyInfo>
         | 
| 42 | 
            +
                  </md:KeyDescriptor>
         | 
| 43 | 
            +
                  <md:KeyDescriptor use="encryption">
         | 
| 44 | 
            +
                    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
         | 
| 45 | 
            +
                      <ds:X509Data>
         | 
| 46 | 
            +
                        <ds:X509Certificate>MIICfDCCAeWgAwIBAgIBADANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJ1czEYMBYGA1UECAwPYmFyLmV4YW1wbGUuY29tMRgwFgYDVQQKDA9iYXIuZXhhbXBsZS5jb20xGDAWBgNVBAMMD2Jhci5leGFtcGxlLmNvbTAeFw0xOTAzMjExMzAzMTJaFw0yOTAzMTgxMzAzMTJaMFsxCzAJBgNVBAYTAnVzMRgwFgYDVQQIDA9iYXIuZXhhbXBsZS5jb20xGDAWBgNVBAoMD2Jhci5leGFtcGxlLmNvbTEYMBYGA1UEAwwPYmFyLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv7Xe9pxNYjxLhXhjQy7L6BbU0IVwQS9pbWqXYDtH3z1fIy+tWMEnE+XhuSFLilRcfn8Ksk0VeFLByQw2HfxAHY3O2dsCv5yLHye3/5bLKa+/pYu7r9ltE5JNjELAH1Yo0/SvAWX9Nuw6Ovw7D6frXUxBPhJaEQ+1VnatuZBUOfQIDAQABo1AwTjAdBgNVHQ4EFgQU6xbFedBYUDFTdXoQvRrELtH4jP8wHwYDVR0jBBgwFoAU6xbFedBYUDFTdXoQvRrELtH4jP8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBvDqYSONEhlDB4+k2+hMg03XCwINLh0UHWd+QfFO1yT6iZK+Duaq9pDBl97RO9sN6qHfOFSQXYBOj6S1Xf+6Orq1yeep/Uh+UrL7wzWYMzPryZxTYn8B1PbdXePRIZCzU7d7ueMnEfUoBj9HqhQphRYD9WgXP9/xzvu9JQjTNk9g==</ds:X509Certificate>
         | 
| 47 | 
            +
                      </ds:X509Data>
         | 
| 48 | 
            +
                    </ds:KeyInfo>
         | 
| 49 | 
            +
                  </md:KeyDescriptor>
         | 
| 50 | 
            +
                  <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://hello.example.com/access/saml/logout" ResponseLocation="https://hello.example.com/access/saml/logout"/>
         | 
| 51 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
         | 
| 52 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
         | 
| 53 | 
            +
                  <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
         | 
| 54 | 
            +
                  <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://hello.example.com/access/saml/login"/>
         | 
| 55 | 
            +
                  <saml:Attribute Name="AuthToken" NameFormat="urn:oasis:names:tc:SAML:2.0:att rname-format:basic" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>
         | 
| 56 | 
            +
                  <saml:Attribute Name="SSOStartPage" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>
         | 
| 57 | 
            +
                </md:IDPSSODescriptor>
         | 
| 58 | 
            +
              </md:EntityDescriptor>
         | 
| 59 | 
            +
            </md:EntitiesDescriptor>
         | 
    
        data/test/request_test.rb
    CHANGED
    
    | @@ -121,6 +121,23 @@ class RequestTest < Minitest::Test | |
| 121 121 | 
             
                  assert_match /<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated
         | 
| 122 122 | 
             
                end
         | 
| 123 123 |  | 
| 124 | 
            +
                it "create the SAMLRequest URL parameter with Subject" do
         | 
| 125 | 
            +
                  settings.name_identifier_value_requested = "testuser@example.com"
         | 
| 126 | 
            +
                  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
         | 
| 127 | 
            +
                  auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
         | 
| 128 | 
            +
                  assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url
         | 
| 129 | 
            +
                  payload = CGI.unescape(auth_url.split("=").last)
         | 
| 130 | 
            +
                  decoded = Base64.decode64(payload)
         | 
| 131 | 
            +
                  zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
         | 
| 132 | 
            +
                  inflated = zstream.inflate(decoded)
         | 
| 133 | 
            +
                  zstream.finish
         | 
| 134 | 
            +
                  zstream.close
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  assert inflated.include?('<saml:Subject>')
         | 
| 137 | 
            +
                  assert inflated.include?("<saml:NameID Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'>testuser@example.com</saml:NameID>")
         | 
| 138 | 
            +
                  assert inflated.include?("<saml:SubjectConfirmation Method='urn:oasis:names:tc:SAML:2.0:cm:bearer'/>")
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
             | 
| 124 141 | 
             
                it "accept extra parameters" do
         | 
| 125 142 | 
             
                  auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" })
         | 
| 126 143 | 
             
                  assert_match /&hello=there$/, auth_url
         | 
    
        data/test/settings_test.rb
    CHANGED
    
    | @@ -15,7 +15,7 @@ class SettingsTest < Minitest::Test | |
| 15 15 | 
             
                    :idp_cert, :idp_cert_fingerprint, :idp_cert_fingerprint_algorithm, :idp_cert_multi,
         | 
| 16 16 | 
             
                    :idp_attribute_names, :issuer, :assertion_consumer_service_url, :assertion_consumer_service_binding,
         | 
| 17 17 | 
             
                    :single_logout_service_url, :single_logout_service_binding,
         | 
| 18 | 
            -
                    :sp_name_qualifier, :name_identifier_format, :name_identifier_value,
         | 
| 18 | 
            +
                    :sp_name_qualifier, :name_identifier_format, :name_identifier_value, :name_identifier_value_requested,
         | 
| 19 19 | 
             
                    :sessionindex, :attributes_index, :passive, :force_authn,
         | 
| 20 20 | 
             
                    :compress_request, :double_quote_xml_attribute_values, :protocol_binding,
         | 
| 21 21 | 
             
                    :security, :certificate, :private_key,
         | 
| @@ -73,6 +73,40 @@ class SloLogoutresponseTest < Minitest::Test | |
| 73 73 | 
             
                    settings.security[:embed_sign] = true
         | 
| 74 74 | 
             
                  end
         | 
| 75 75 |  | 
| 76 | 
            +
                  it "doesn't sign through create_xml_document" do
         | 
| 77 | 
            +
                    unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
         | 
| 78 | 
            +
                    inflated = unauth_res.create_xml_document(settings).to_s
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 81 | 
            +
                    refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 82 | 
            +
                    refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  it "sign unsigned request" do
         | 
| 86 | 
            +
                    unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
         | 
| 87 | 
            +
                    unauth_res_doc = unauth_res.create_xml_document(settings)
         | 
| 88 | 
            +
                    inflated = unauth_res_doc.to_s
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 91 | 
            +
                    refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 92 | 
            +
                    refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    inflated = unauth_res.sign_document(unauth_res_doc, settings).to_s
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 97 | 
            +
                    assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 98 | 
            +
                    assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  it "signs through create_logout_response_xml_doc" do
         | 
| 102 | 
            +
                    unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
         | 
| 103 | 
            +
                    inflated = unauth_res.create_logout_response_xml_doc(settings).to_s
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
         | 
| 106 | 
            +
                    assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
         | 
| 107 | 
            +
                    assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 76 110 | 
             
                  it "create a signed logout response" do
         | 
| 77 111 | 
             
                    logout_request.settings = settings
         | 
| 78 112 | 
             
                    params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message")
         | 
    
        data/test/test_helper.rb
    CHANGED
    
    | @@ -178,6 +178,10 @@ class Minitest::Test | |
| 178 178 | 
             
                @idp_metadata_multiple_descriptors ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_multiple_descriptors.xml'))
         | 
| 179 179 | 
             
              end
         | 
| 180 180 |  | 
| 181 | 
            +
              def idp_metadata_multiple_descriptors2
         | 
| 182 | 
            +
                @idp_metadata_multiple_descriptors2 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_multiple_descriptors_2.xml'))
         | 
| 183 | 
            +
              end
         | 
| 184 | 
            +
             | 
| 181 185 | 
             
              def idp_metadata_multiple_certs
         | 
| 182 186 | 
             
                @idp_metadata_multiple_descriptors ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_metadata_multi_certs.xml'))
         | 
| 183 187 | 
             
              end
         | 
    
        data/test/utils_test.rb
    CHANGED
    
    | @@ -29,6 +29,11 @@ class UtilsTest < Minitest::Test | |
| 29 29 | 
             
                  assert_equal formatted_certificate, OneLogin::RubySaml::Utils.format_cert(invalid_certificate2)
         | 
| 30 30 | 
             
                end
         | 
| 31 31 |  | 
| 32 | 
            +
                it "returns the cert when it's encoded" do
         | 
| 33 | 
            +
                  encoded_certificate = read_certificate("certificate.der")
         | 
| 34 | 
            +
                  assert_equal encoded_certificate, OneLogin::RubySaml::Utils.format_cert(encoded_certificate)
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 32 37 | 
             
                it "reformats the certificate when there line breaks and no headers" do
         | 
| 33 38 | 
             
                  invalid_certificate3 = read_certificate("invalid_certificate3")
         | 
| 34 39 | 
             
                  assert_equal formatted_certificate, OneLogin::RubySaml::Utils.format_cert(invalid_certificate3)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ruby-saml
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.10.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - OneLogin LLC
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2019-03-21 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: nokogiri
         | 
| @@ -16,14 +16,14 @@ dependencies: | |
| 16 16 | 
             
                requirements:
         | 
| 17 17 | 
             
                - - ">="
         | 
| 18 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: 1. | 
| 19 | 
            +
                    version: 1.8.2
         | 
| 20 20 | 
             
              type: :runtime
         | 
| 21 21 | 
             
              prerelease: false
         | 
| 22 22 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 23 | 
             
                requirements:
         | 
| 24 24 | 
             
                - - ">="
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: 1. | 
| 26 | 
            +
                    version: 1.8.2
         | 
| 27 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 28 | 
             
              name: minitest
         | 
| 29 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -187,6 +187,7 @@ files: | |
| 187 187 | 
             
            - lib/schemas/xmldsig-core-schema.xsd
         | 
| 188 188 | 
             
            - lib/xml_security.rb
         | 
| 189 189 | 
             
            - ruby-saml.gemspec
         | 
| 190 | 
            +
            - test/certificates/certificate.der
         | 
| 190 191 | 
             
            - test/certificates/certificate1
         | 
| 191 192 | 
             
            - test/certificates/certificate_without_head_foot
         | 
| 192 193 | 
             
            - test/certificates/formatted_certificate
         | 
| @@ -226,6 +227,7 @@ files: | |
| 226 227 | 
             
            - test/metadata/idp_metadata_multi_signing_certs.xml
         | 
| 227 228 | 
             
            - test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml
         | 
| 228 229 | 
             
            - test/metadata/idp_multiple_descriptors.xml
         | 
| 230 | 
            +
            - test/metadata/idp_multiple_descriptors_2.xml
         | 
| 229 231 | 
             
            - test/metadata/no_idp_descriptor.xml
         | 
| 230 232 | 
             
            - test/metadata_test.rb
         | 
| 231 233 | 
             
            - test/request_test.rb
         | 
| @@ -348,6 +350,7 @@ signing_key: | |
| 348 350 | 
             
            specification_version: 4
         | 
| 349 351 | 
             
            summary: SAML Ruby Tookit
         | 
| 350 352 | 
             
            test_files:
         | 
| 353 | 
            +
            - test/certificates/certificate.der
         | 
| 351 354 | 
             
            - test/certificates/certificate1
         | 
| 352 355 | 
             
            - test/certificates/certificate_without_head_foot
         | 
| 353 356 | 
             
            - test/certificates/formatted_certificate
         | 
| @@ -387,6 +390,7 @@ test_files: | |
| 387 390 | 
             
            - test/metadata/idp_metadata_multi_signing_certs.xml
         | 
| 388 391 | 
             
            - test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml
         | 
| 389 392 | 
             
            - test/metadata/idp_multiple_descriptors.xml
         | 
| 393 | 
            +
            - test/metadata/idp_multiple_descriptors_2.xml
         | 
| 390 394 | 
             
            - test/metadata/no_idp_descriptor.xml
         | 
| 391 395 | 
             
            - test/metadata_test.rb
         | 
| 392 396 | 
             
            - test/request_test.rb
         |