-
-
Notifications
You must be signed in to change notification settings - Fork 590
Expand file tree
/
Copy pathmetadata.rb
More file actions
173 lines (142 loc) · 6.09 KB
/
metadata.rb
File metadata and controls
173 lines (142 loc) · 6.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# frozen_string_literal: true
require "uri"
require "ruby_saml/logging"
require "ruby_saml/utils"
# Only supports SAML 2.0
module RubySaml
# SAML2 Metadata. XML Metadata Builder
#
class Metadata
# Return SP metadata based on the settings.
# @param settings [RubySaml::Settings|nil] Toolkit settings
# @param pretty_print [Boolean] Pretty print or not the response
# (No pretty print if you gonna validate the signature)
# @param valid_until [DateTime] Metadata's valid time
# @param cache_duration [Integer] Duration of the cache in seconds
# @return [String] XML Metadata of the Service Provider
#
def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
meta_doc = RubySaml::XML::Document.new
add_xml_declaration(meta_doc)
root = add_root_element(meta_doc, settings, valid_until, cache_duration)
sp_sso = add_sp_sso_element(root, settings)
add_sp_certificates(sp_sso, settings)
add_sp_service_elements(sp_sso, settings)
add_extras(root, settings)
embed_signature(meta_doc, settings)
output_xml(meta_doc, pretty_print)
end
protected
def add_xml_declaration(meta_doc)
meta_doc << REXML::XMLDecl.new('1.0', 'UTF-8')
end
def add_root_element(meta_doc, settings, valid_until, cache_duration)
namespaces = {
"xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
}
if settings.attribute_consuming_service.configured?
namespaces["xmlns:saml"] = "urn:oasis:names:tc:SAML:2.0:assertion"
end
root = meta_doc.add_element("md:EntityDescriptor", namespaces)
root.attributes["ID"] = RubySaml::Utils.uuid
root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id
root.attributes["validUntil"] = valid_until.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if valid_until
root.attributes["cacheDuration"] = "PT#{cache_duration}S" if cache_duration
root
end
def add_sp_sso_element(root, settings)
root.add_element "md:SPSSODescriptor", {
"protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
"AuthnRequestsSigned" => settings.security[:authn_requests_signed],
"WantAssertionsSigned" => settings.security[:want_assertions_signed]
}
end
# Add KeyDescriptor elements for SP certificates.
def add_sp_certificates(sp_sso, settings)
certs = settings.get_sp_certs
certs[:signing].each { |cert, _| add_sp_cert_element(sp_sso, cert, :signing) }
if settings.security[:want_assertions_encrypted]
certs[:encryption].each { |cert, _| add_sp_cert_element(sp_sso, cert, :encryption) }
end
sp_sso
end
def add_sp_service_elements(sp_sso, settings)
if settings.sp_slo_service_url
sp_sso.add_element "md:SingleLogoutService", {
"Binding" => settings.sp_slo_service_binding,
"Location" => settings.sp_slo_service_url,
"ResponseLocation" => settings.sp_slo_service_url
}
end
if settings.name_identifier_format
nameid = sp_sso.add_element "md:NameIDFormat"
nameid.text = settings.name_identifier_format
end
if settings.sp_assertion_consumer_service_url
sp_sso.add_element "md:AssertionConsumerService", {
"Binding" => settings.sp_assertion_consumer_service_binding,
"Location" => settings.sp_assertion_consumer_service_url,
"isDefault" => true,
"index" => 0
}
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],
"isRequired" => attribute[:is_required] || false
}
next if attribute[:attribute_value].nil?
Array(attribute[:attribute_value]).each do |value|
sp_attr_val = sp_req_attr.add_element "saml:AttributeValue"
sp_attr_val.text = value.to_s
end
end
end
# With OpenSSO, it might be required to also include
# <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"/>
# <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
sp_sso
end
# can be overridden in subclass
def add_extras(root, _settings)
root
end
def embed_signature(meta_doc, settings)
return unless settings.security[:metadata_signed]
cert, private_key = settings.get_sp_signing_pair
return unless private_key && cert
meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
end
def output_xml(meta_doc, pretty_print)
ret = +''
# pretty print the XML so IdP administrators can easily see what the SP supports
if pretty_print
meta_doc.write(ret, 1)
else
ret = meta_doc.to_s
end
ret
end
private
def add_sp_cert_element(sp_sso, cert, use)
return unless cert
cert_text = Base64.encode64(cert.to_der).gsub("\n", '')
kd = sp_sso.add_element "md:KeyDescriptor", { "use" => use.to_s }
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 = cert_text
end
end
end