class OneLogin::RubySaml::IdpMetadataParser

Auxiliary class to retrieve and parse the Identity Provider Metadata

Constants

DSIG
METADATA
NAME_FORMAT
SAML_ASSERTION

Attributes

document[R]
response[R]

Public Instance Methods

parse(idp_metadata, options = {}) click to toggle source

Parse the Identity Provider metadata and update the settings with the IdP values @param idp_metadata [String] @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 42
def parse(idp_metadata, options = {})
  @document = REXML::Document.new(idp_metadata)

  (options[:settings] || OneLogin::RubySaml::Settings.new).tap do |settings|
    settings.idp_entity_id = idp_entity_id
    settings.name_identifier_format = idp_name_id_format
    settings.idp_sso_target_url = single_signon_service_url(options)
    settings.idp_slo_target_url = single_logout_service_url(options)
    settings.idp_cert = certificate_base64
    settings.idp_cert_fingerprint = fingerprint(settings.idp_cert_fingerprint_algorithm)
    settings.idp_attribute_names = attribute_names
    settings.idp_cert_fingerprint = fingerprint(settings.idp_cert_fingerprint_algorithm)
  end
end
parse_remote(url, validate_cert = true, options = {}) click to toggle source

Parse the Identity Provider metadata and update the settings with the IdP values

@param (see #get_idp_metadata) @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object @return (see #get_idp_metadata) @raise (see #get_idp_metadata)

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 33
def parse_remote(url, validate_cert = true, options = {})
  idp_metadata = get_idp_metadata(url, validate_cert)
  parse(idp_metadata, options)
end

Private Instance Methods

attribute_names() click to toggle source

@return [Array] the names of all SAML attributes if any exist

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 225
def attribute_names
  nodes = REXML::XPath.match(
    document,
    "/md:EntityDescriptor/md:IDPSSODescriptor/saml:Attribute/@Name",
    { "md" => METADATA, "NameFormat" => NAME_FORMAT, "saml" => SAML_ASSERTION }
  )
  nodes.map(&:value)
end
certificate() click to toggle source

@return [String|nil] X509Certificate if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 203
def certificate
  @certificate ||= begin
    Base64.decode64(certificate_base64) if certificate_base64
  end
end
certificate_base64() click to toggle source

@return [String|nil] Unformatted Certificate if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 182
def certificate_base64
  @certificate_base64 ||= begin
    node = REXML::XPath.first(
        document,
        "/md:EntityDescriptor/md:IDPSSODescriptor/md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
        { "md" => METADATA, "ds" => DSIG }
    )

    unless node
      node = REXML::XPath.first(
          document,
          "/md:EntityDescriptor/md:IDPSSODescriptor/md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
          { "md" => METADATA, "ds" => DSIG }
      )
    end
    node.text if node
  end
end
fingerprint(fingerprint_algorithm) click to toggle source

@return [String|nil] the SHA-1 fingerpint of the X509Certificate if it exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 212
def fingerprint(fingerprint_algorithm)
  @fingerprint ||= begin
    if certificate
      cert = OpenSSL::X509::Certificate.new(certificate)

      fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(fingerprint_algorithm).new
      fingerprint_alg.hexdigest(cert.to_der).upcase.scan(/../).join(":")
    end
  end
end
get_idp_metadata(url, validate_cert) click to toggle source

Retrieve the remote IdP metadata from the URL or a cached copy. @param url [String] Url where the XML of the Identity Provider Metadata is published. @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked. @return [REXML::document] Parsed XML IdP metadata @raise [HttpError] Failure to fetch remote IdP metadata

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 64
def get_idp_metadata(url, validate_cert)
  uri = URI.parse(url)
  if uri.scheme == "http"
    response = Net::HTTP.get_response(uri)
    meta_text = response.body
  elsif uri.scheme == "https"
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    # Most IdPs will probably use self signed certs
    if validate_cert
      http.verify_mode = OpenSSL::SSL::VERIFY_PEER

      # Net::HTTP in Ruby 1.8 did not set the default certificate store
      # automatically when VERIFY_PEER was specified.
      if RUBY_VERSION < '1.9' && !http.ca_file && !http.ca_path && !http.cert_store
        http.cert_store = OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
      end
    else
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end
    get = Net::HTTP::Get.new(uri.request_uri)
    response = http.request(get)
    meta_text = response.body
  else
    raise ArgumentError.new("url must begin with http or https")
  end

  unless response.is_a? Net::HTTPSuccess
    raise OneLogin::RubySaml::HttpError.new("Failed to fetch idp metadata")
  end

  meta_text
end
idp_entity_id() click to toggle source

@return [String|nil] IdP Entity ID value if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 100
def idp_entity_id
  node = REXML::XPath.first(
    document,
    "/md:EntityDescriptor/@entityID",
    { "md" => METADATA }
  )
  node.value if node
end
idp_name_id_format() click to toggle source

@return [String|nil] IdP Name ID Format value if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 111
def idp_name_id_format
  node = REXML::XPath.first(
    document,
    "/md:EntityDescriptor/md:IDPSSODescriptor/md:NameIDFormat",
    { "md" => METADATA }
  )
  node.text if node
end
single_logout_service_binding(binding_priority = nil) click to toggle source

@param binding_priority [Array] @return [String|nil] SingleLogoutService binding if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 153
def single_logout_service_binding(binding_priority = nil)
  nodes = REXML::XPath.match(
    document,
    "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleLogoutService/@Binding",
    { "md" => METADATA }
  )
  if binding_priority
    values = nodes.map(&:value)
    binding_priority.detect{ |binding| values.include? binding }
  else
    nodes.first.value if nodes.any?
  end
end
single_logout_service_url(options = {}) click to toggle source

@param options [Hash] @return [String|nil] SingleLogoutService endpoint if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 170
def single_logout_service_url(options = {})
  binding = options[:slo_binding] || "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
  node = REXML::XPath.first(
    document,
    "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleLogoutService[@Binding=\"#{binding}\"]/@Location",
    { "md" => METADATA }
  )
  node.value if node
end
single_signon_service_binding(binding_priority = nil) click to toggle source

@param binding_priority [Array] @return [String|nil] SingleSignOnService binding if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 123
def single_signon_service_binding(binding_priority = nil)
  nodes = REXML::XPath.match(
    document,
    "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService/@Binding",
    { "md" => METADATA }
  )
  if binding_priority
    values = nodes.map(&:value)
    binding_priority.detect{ |binding| values.include? binding }
  else
    nodes.first.value if nodes.any?
  end
end
single_signon_service_url(options = {}) click to toggle source

@param options [Hash] @return [String|nil] SingleSignOnService endpoint if exists

# File lib/onelogin/ruby-saml/idp_metadata_parser.rb, line 140
def single_signon_service_url(options = {})
  binding = options[:sso_binding] || "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
  node = REXML::XPath.first(
    document,
    "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService[@Binding=\"#{binding}\"]/@Location",
    { "md" => METADATA }
  )
  node.value if node
end