require "rexml/document"
require "rexml/xpath"
require "uri"
require "samlsso/logging"
# Class to return SP metadata based on the settings requested.
# Return this XML in a controller, then give that URL to the the
# IdP administrator. The IdP will poll the URL and your settings
# will be updated automatically
module Samlsso
include REXML
class Metadata
def generate(settings)
meta_doc = REXML::Document.new
root = meta_doc.add_element "md:EntityDescriptor", {
"xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
}
sp_sso = root.add_element "md:SPSSODescriptor", {
"protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
"AuthnRequestsSigned" => settings.security[:authn_requests_signed],
# However we would like assertions signed if idp_cert_fingerprint or idp_cert is set
"WantAssertionsSigned" => !!(settings.idp_cert_fingerprint || settings.idp_cert)
}
if settings.issuer
root.attributes["entityID"] = settings.issuer
end
if settings.single_logout_service_url
sp_sso.add_element "md:SingleLogoutService", {
"Binding" => settings.single_logout_service_binding,
"Location" => settings.single_logout_service_url,
"ResponseLocation" => settings.single_logout_service_url,
"isDefault" => true,
"index" => 0
}
end
if settings.name_identifier_format
name_id = sp_sso.add_element "md:NameIDFormat"
name_id.text = settings.name_identifier_format
end
if settings.assertion_consumer_service_url
sp_sso.add_element "md:AssertionConsumerService", {
"Binding" => settings.assertion_consumer_service_binding,
"Location" => settings.assertion_consumer_service_url,
"isDefault" => true,
"index" => 0
}
end
# Add KeyDescriptor if messages will be signed
cert = settings.get_sp_cert()
if cert
kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
xd = ki.add_element "ds:X509Data"
xc = xd.add_element "ds:X509Certificate"
xc.text = Base64.encode64(cert.to_der).gsub("\n", '')
end
if settings.attribute_consuming_service.configured?
sp_acs = sp_sso.add_element "md:AttributeConsumingService", {
"isDefault" => "true",
"index" => settings.attribute_consuming_service.index
}
srv_name = sp_acs.add_element "md:ServiceName", {
"xml:lang" => "en"
}
srv_name.text = settings.attribute_consuming_service.name
settings.attribute_consuming_service.attributes.each do |attribute|
sp_req_attr = sp_acs.add_element "md:RequestedAttribute", {
"NameFormat" => attribute[:name_format],
"Name" => attribute[:name],
"FriendlyName" => attribute[:friendly_name]
}
unless attribute[:attribute_value].nil?
sp_attr_val = sp_req_attr.add_element "md:AttributeValue"
sp_attr_val.text = attribute[:attribute_value]
end
end
end
# With OpenSSO, it might be required to also include
#
#
meta_doc << REXML::XMLDecl.new("1.0", "UTF-8")
ret = ""
# pretty print the XML so IdP administrators can easily see what the SP supports
meta_doc.write(ret, 1)
return ret
end
end
end