Skip to content

Commit 25160fe

Browse files
committed
Fixes #433. Refactor the OneLogin::RubySaml::Metadata class so it is easier to extend by breaking it into a series of methods. Also adds the #add_extras convenience method which is empty but can be extended. No change to behavior.
1 parent b55733f commit 25160fe

1 file changed

Lines changed: 56 additions & 22 deletions

File tree

lib/onelogin/ruby-saml/metadata.rb

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,50 @@ class Metadata
2121
#
2222
def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
2323
meta_doc = XMLSecurity::Document.new
24+
add_xml_declaration(meta_doc)
25+
root = add_root_element(meta_doc, settings, valid_until, cache_duration)
26+
sp_sso = add_sp_sso_element(root, settings)
27+
add_sp_certificates(sp_sso, settings)
28+
add_sp_service_elements(sp_sso, settings)
29+
add_extras(root, settings)
30+
embed_signature(meta_doc, settings)
31+
output_xml(meta_doc, pretty_print)
32+
end
33+
34+
protected
35+
36+
def add_xml_declaration(meta_doc)
37+
meta_doc << REXML::XMLDecl.new('1.0', 'UTF-8')
38+
end
39+
40+
def add_root_element(meta_doc, settings, valid_until, cache_duration)
2441
namespaces = {
2542
"xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
2643
}
44+
2745
if settings.attribute_consuming_service.configured?
2846
namespaces["xmlns:saml"] = "urn:oasis:names:tc:SAML:2.0:assertion"
2947
end
30-
root = meta_doc.add_element "md:EntityDescriptor", namespaces
31-
sp_sso = root.add_element "md:SPSSODescriptor", {
48+
49+
root = meta_doc.add_element("md:EntityDescriptor", namespaces)
50+
root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
51+
root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id
52+
root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z') if valid_until
53+
root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" if cache_duration
54+
root
55+
end
56+
57+
def add_sp_sso_element(root, settings)
58+
root.add_element "md:SPSSODescriptor", {
3259
"protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
3360
"AuthnRequestsSigned" => settings.security[:authn_requests_signed],
3461
"WantAssertionsSigned" => settings.security[:want_assertions_signed],
3562
}
63+
end
3664

37-
# Add KeyDescriptor if messages will be signed / encrypted
38-
# with SP certificate, and new SP certificate if any
65+
# Add KeyDescriptor if messages will be signed / encrypted
66+
# with SP certificate, and new SP certificate if any
67+
def add_sp_certificates(sp_sso, settings)
3968
cert = settings.get_sp_cert
4069
cert_new = settings.get_sp_cert_new
4170

@@ -58,27 +87,23 @@ def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
5887
end
5988
end
6089

61-
root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
62-
if settings.sp_entity_id
63-
root.attributes["entityID"] = settings.sp_entity_id
64-
end
65-
if valid_until
66-
root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z')
67-
end
68-
if cache_duration
69-
root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S"
70-
end
90+
sp_sso
91+
end
92+
93+
def add_sp_service_elements(sp_sso, settings)
7194
if settings.single_logout_service_url
7295
sp_sso.add_element "md:SingleLogoutService", {
7396
"Binding" => settings.single_logout_service_binding,
7497
"Location" => settings.single_logout_service_url,
7598
"ResponseLocation" => settings.single_logout_service_url
7699
}
77100
end
101+
78102
if settings.name_identifier_format
79103
nameid = sp_sso.add_element "md:NameIDFormat"
80104
nameid.text = settings.name_identifier_format
81105
end
106+
82107
if settings.assertion_consumer_service_url
83108
sp_sso.add_element "md:AssertionConsumerService", {
84109
"Binding" => settings.assertion_consumer_service_binding,
@@ -117,23 +142,32 @@ def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
117142
# <md:RoleDescriptor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:query="urn:oasis:names:tc:SAML:metadata:ext:query" xsi:type="query:AttributeQueryDescriptorType" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
118143
# <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
119144

120-
meta_doc << REXML::XMLDecl.new("1.0", "UTF-8")
145+
sp_sso
146+
end
121147

122-
# embed signature
123-
if settings.security[:metadata_signed] && settings.private_key && settings.certificate
124-
private_key = settings.get_sp_key
125-
meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
126-
end
148+
# can be overridden in subclass
149+
def add_extras(root, _settings)
150+
root
151+
end
152+
153+
def embed_signature(meta_doc, settings)
154+
return unless settings.security[:metadata_signed] && settings.private_key && settings.certificate
155+
156+
private_key = settings.get_sp_key
157+
meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
158+
end
159+
160+
def output_xml(meta_doc, pretty_print)
161+
ret = ''
127162

128-
ret = ""
129163
# pretty print the XML so IdP administrators can easily see what the SP supports
130164
if pretty_print
131165
meta_doc.write(ret, 1)
132166
else
133167
ret = meta_doc.to_s
134168
end
135169

136-
return ret
170+
ret
137171
end
138172
end
139173
end

0 commit comments

Comments
 (0)