Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDP metadata parsing improved #298

Merged
merged 2 commits into from
Apr 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 55 additions & 13 deletions lib/onelogin/ruby-saml/idp_metadata_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,30 @@ class IdpMetadataParser
# IdP values
#
# @param (see IdpMetadataParser#get_idp_metadata)
# @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object
# @return (see IdpMetadataParser#get_idp_metadata)
# @raise (see IdpMetadataParser#get_idp_metadata)
def parse_remote(url, validate_cert = true)
def parse_remote(url, validate_cert = true, options = {})
idp_metadata = get_idp_metadata(url, validate_cert)
parse(idp_metadata)
parse(idp_metadata, options)
end

# 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
#
def parse(idp_metadata)
def parse(idp_metadata, options = {})
@document = REXML::Document.new(idp_metadata)

OneLogin::RubySaml::Settings.new.tap do |settings|
(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
settings.idp_slo_target_url = single_logout_service_url
settings.idp_sso_target_binding ||= single_signon_service_binding(settings.idp_sso_target_parse_binding_priority)
settings.idp_sso_target_url = single_signon_service_url(settings.idp_sso_target_binding)
settings.idp_slo_target_binding ||= single_logout_service_binding(settings.idp_slo_target_parse_binding_priority)
settings.idp_slo_target_url = single_logout_service_url(settings.idp_slo_target_binding)
settings.idp_cert = certificate_base64
settings.idp_cert_fingerprint = fingerprint
settings.idp_cert_fingerprint = fingerprint(settings.idp_cert_fingerprint_algorithm)
end
end

Expand Down Expand Up @@ -112,23 +116,59 @@ def idp_name_id_format
node.text if node
end

# @param binding_priority [Array]
# @return [String|nil] SingleSignOnService binding if exists
#
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

# @param binding [String]
# @return [String|nil] SingleSignOnService endpoint if exists
#
def single_signon_service_url
def single_signon_service_url(binding = nil)
node = REXML::XPath.first(
document,
"/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService/@Location",
"/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService#{"[@Binding='#{binding}']" if binding}/@Location",
{ "md" => METADATA }
)
node.value if node
end

# @param binding_priority [Array]
# @return [String|nil] SingleLogoutService binding if exists
#
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

# @param binding [String]
# @return [String|nil] SingleLogoutService endpoint if exists
#
def single_logout_service_url
def single_logout_service_url(binding = nil)
node = REXML::XPath.first(
document,
"/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleLogoutService/@Location",
"/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleLogoutService#{"[@Binding='#{binding}']" if binding}/@Location",
{ "md" => METADATA }
)
node.value if node
Expand Down Expand Up @@ -158,11 +198,13 @@ def certificate

# @return [String|nil] the SHA-1 fingerpint of the X509Certificate if it exists
#
def fingerprint
def fingerprint(fingerprint_algorithm)
@fingerprint ||= begin
if certificate
cert = OpenSSL::X509::Certificate.new(certificate)
Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")

fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(fingerprint_algorithm).new
fingerprint_alg.hexdigest(cert.to_der).upcase.scan(/../).join(":")
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions lib/onelogin/ruby-saml/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ def initialize(overrides = {})

# IdP Data
attr_accessor :idp_entity_id
attr_accessor :idp_sso_target_parse_binding_priority
attr_accessor :idp_sso_target_binding
attr_accessor :idp_sso_target_url
attr_accessor :idp_slo_target_parse_binding_priority
attr_accessor :idp_slo_target_binding
attr_accessor :idp_slo_target_url
attr_accessor :idp_cert
attr_accessor :idp_cert_fingerprint
Expand Down