Skip to content

Commit 0f026a7

Browse files
committed
Move SAML namespaces into constants
1 parent 31d8500 commit 0f026a7

17 files changed

+111
-128
lines changed

lib/ruby_saml/authrequest.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ def create_xml_document(settings)
8888
time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
8989
assign_uuid(settings)
9090
root_attributes = {
91-
'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol',
92-
'xmlns:saml' => 'urn:oasis:names:tc:SAML:2.0:assertion',
91+
'xmlns:samlp' => RubySaml::XML::NS_PROTOCOL,
92+
'xmlns:saml' => RubySaml::XML::NS_ASSERTION,
9393
'ID' => uuid,
9494
'IssueInstant' => time,
9595
'Version' => '2.0',

lib/ruby_saml/idp_metadata_parser.rb

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,20 @@ module RubySaml
1111
# make sure to validate it properly before use it in a parse_remote method.
1212
# Read the `Security warning` section of the README.md file to get more info
1313
class IdpMetadataParser
14-
module SamlMetadata
15-
module Vocabulary
16-
METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
17-
DSIG = "http://www.w3.org/2000/09/xmldsig#"
18-
NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:*"
19-
SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
20-
end
21-
22-
NAMESPACE = {
23-
"md" => Vocabulary::METADATA,
24-
"NameFormat" => Vocabulary::NAME_FORMAT,
25-
"saml" => Vocabulary::SAML_ASSERTION,
26-
"ds" => Vocabulary::DSIG
27-
}.freeze
28-
end
14+
NAMESPACES = {
15+
"ds" => RubySaml::XML::DSIG,
16+
"md" => RubySaml::XML::NS_METADATA,
17+
"saml" => RubySaml::XML::NS_ASSERTION
18+
}.freeze
2919

30-
include SamlMetadata::Vocabulary
3120
attr_reader :document
3221
attr_reader :response
3322
attr_reader :options
3423

3524
# fetch IdP descriptors from a metadata document
3625
def self.get_idps(noko_document, only_entity_id = nil)
3726
path = "//md:EntityDescriptor#{"[@entityID=\"#{only_entity_id}\"]" if only_entity_id}/md:IDPSSODescriptor"
38-
noko_document.xpath(path, SamlMetadata::NAMESPACE)
27+
noko_document.xpath(path, NAMESPACES)
3928
end
4029

4130
# Parse the Identity Provider metadata and update the settings with the
@@ -272,7 +261,7 @@ def cache_duration
272261
def idp_name_id_format(name_id_priority = nil)
273262
nodes = @idpsso_descriptor.xpath(
274263
"md:NameIDFormat",
275-
SamlMetadata::NAMESPACE
264+
NAMESPACES
276265
)
277266
first_ranked_text(nodes, name_id_priority)
278267
end
@@ -283,7 +272,7 @@ def idp_name_id_format(name_id_priority = nil)
283272
def single_signon_service_binding(binding_priority = nil)
284273
nodes = @idpsso_descriptor.xpath(
285274
"md:SingleSignOnService/@Binding",
286-
SamlMetadata::NAMESPACE
275+
NAMESPACES
287276
)
288277
first_ranked_value(nodes, binding_priority)
289278
end
@@ -294,7 +283,7 @@ def single_signon_service_binding(binding_priority = nil)
294283
def single_logout_service_binding(binding_priority = nil)
295284
nodes = @idpsso_descriptor.xpath(
296285
"md:SingleLogoutService/@Binding",
297-
SamlMetadata::NAMESPACE
286+
NAMESPACES
298287
)
299288
first_ranked_value(nodes, binding_priority)
300289
end
@@ -308,7 +297,7 @@ def single_signon_service_url(binding_priority = nil)
308297

309298
@idpsso_descriptor.at_xpath(
310299
"md:SingleSignOnService[@Binding=\"#{binding}\"]/@Location",
311-
SamlMetadata::NAMESPACE
300+
NAMESPACES
312301
)&.value
313302
end
314303

@@ -321,7 +310,7 @@ def single_logout_service_url(binding_priority = nil)
321310

322311
@idpsso_descriptor.at_xpath(
323312
"md:SingleLogoutService[@Binding=\"#{binding}\"]/@Location",
324-
SamlMetadata::NAMESPACE
313+
NAMESPACES
325314
)&.value
326315
end
327316

@@ -334,7 +323,7 @@ def single_logout_response_service_url(binding_priority = nil)
334323

335324
node = @idpsso_descriptor.at_xpath(
336325
"md:SingleLogoutService[@Binding=\"#{binding}\"]/@ResponseLocation",
337-
SamlMetadata::NAMESPACE
326+
NAMESPACES
338327
)
339328
node&.value
340329
end
@@ -345,12 +334,12 @@ def certificates
345334
@certificates ||= begin
346335
signing_nodes = @idpsso_descriptor.xpath(
347336
"md:KeyDescriptor[not(contains(@use, 'encryption'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
348-
SamlMetadata::NAMESPACE
337+
NAMESPACES
349338
)
350339

351340
encryption_nodes = @idpsso_descriptor.xpath(
352341
"md:KeyDescriptor[not(contains(@use, 'signing'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
353-
SamlMetadata::NAMESPACE
342+
NAMESPACES
354343
)
355344

356345
return nil if signing_nodes.empty? && encryption_nodes.empty?
@@ -389,7 +378,7 @@ def fingerprint(certificate, fingerprint_algorithm = RubySaml::XML::SHA256)
389378
def attribute_names
390379
nodes = @idpsso_descriptor.xpath(
391380
"saml:Attribute/@Name",
392-
SamlMetadata::NAMESPACE
381+
NAMESPACES
393382
)
394383
nodes.map(&:value)
395384
end

lib/ruby_saml/logoutrequest.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ def create_xml_document(settings)
8989
assign_uuid(settings)
9090

9191
root_attributes = {
92-
'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol',
93-
'xmlns:saml' => 'urn:oasis:names:tc:SAML:2.0:assertion',
92+
'xmlns:samlp' => RubySaml::XML::NS_PROTOCOL,
93+
'xmlns:saml' => RubySaml::XML::NS_ASSERTION,
9494
'ID' => uuid,
9595
'IssueInstant' => time,
9696
'Version' => '2.0',

lib/ruby_saml/logoutresponse.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def in_response_to
6464
node = REXML::XPath.first(
6565
document,
6666
"/p:LogoutResponse",
67-
{ "p" => PROTOCOL }
67+
{ "p" => RubySaml::XML::NS_PROTOCOL }
6868
)
6969
node.nil? ? nil : node.attributes['InResponseTo']
7070
end
@@ -77,7 +77,7 @@ def issuer
7777
node = REXML::XPath.first(
7878
document,
7979
"/p:LogoutResponse/a:Issuer",
80-
{ "p" => PROTOCOL, "a" => ASSERTION }
80+
{ "p" => RubySaml::XML::NS_PROTOCOL, "a" => RubySaml::XML::NS_ASSERTION }
8181
)
8282
Utils.element_text(node)
8383
end
@@ -87,7 +87,7 @@ def issuer
8787
#
8888
def status_code
8989
@status_code ||= begin
90-
node = REXML::XPath.first(document, "/p:LogoutResponse/p:Status/p:StatusCode", { "p" => PROTOCOL })
90+
node = REXML::XPath.first(document, "/p:LogoutResponse/p:Status/p:StatusCode", { "p" => RubySaml::XML::NS_PROTOCOL })
9191
node.nil? ? nil : node.attributes["Value"]
9292
end
9393
end
@@ -97,7 +97,7 @@ def status_message
9797
node = REXML::XPath.first(
9898
document,
9999
"/p:LogoutResponse/p:Status/p:StatusMessage",
100-
{ "p" => PROTOCOL }
100+
{ "p" => RubySaml::XML::NS_PROTOCOL }
101101
)
102102
Utils.element_text(node)
103103
end

lib/ruby_saml/metadata.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
3232

3333
# Add saml namespace if attribute consuming service is configured
3434
if settings.attribute_consuming_service.configured?
35-
root_attributes['xmlns:saml'] = 'urn:oasis:names:tc:SAML:2.0:assertion'
35+
root_attributes['xmlns:saml'] = RubySaml::XML::NS_ASSERTION
3636
end
3737

3838
xml['md'].EntityDescriptor(root_attributes) do
3939
sp_sso_attributes = {
40-
'protocolSupportEnumeration' => 'urn:oasis:names:tc:SAML:2.0:protocol',
40+
'protocolSupportEnumeration' => RubySaml::XML::NS_PROTOCOL,
4141
'AuthnRequestsSigned' => settings.security[:authn_requests_signed] ? 'true' : 'false',
4242
'WantAssertionsSigned' => settings.security[:want_assertions_signed] ? 'true' : 'false'
4343
}

lib/ruby_saml/response.rb

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@ module RubySaml
1010
class Response < SamlMessage
1111
include ErrorHandling
1212

13-
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
14-
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
15-
DSIG = "http://www.w3.org/2000/09/xmldsig#"
16-
XENC = "http://www.w3.org/2001/04/xmlenc#"
13+
# TODO: Migrate this to RubySaml::XML
1714
SAML_NAMESPACES = {
18-
"p" => PROTOCOL,
19-
"a" => ASSERTION
15+
'p' => RubySaml::XML::NS_PROTOCOL,
16+
'a' => RubySaml::XML::NS_ASSERTION
2017
}.freeze
2118

2219
# TODO: Settings should probably be initialized too... WDYT?
@@ -197,7 +194,7 @@ def handle_rexml_attribute(node, attributes)
197194
# this is useful for allowing eduPersonTargetedId to be passed as an opaque identifier to use to
198195
# identify the subject in an SP rather than email or other less opaque attributes
199196
# NameQualifier, if present is prefixed with a "/" to the value
200-
REXML::XPath.match(e,'a:NameID', { "a" => ASSERTION }).collect do |n|
197+
REXML::XPath.match(e,'a:NameID', { "a" => RubySaml::XML::NS_ASSERTION }).map do |n|
201198
base_path = n.attributes['NameQualifier'] ? "#{n.attributes['NameQualifier']}/" : ''
202199
"#{base_path}#{Utils.element_text(n)}"
203200
end
@@ -224,7 +221,7 @@ def handle_nokogiri_attribute(node, attributes)
224221
# this is useful for allowing eduPersonTargetedId to be passed as an opaque identifier to use to
225222
# identify the subject in an SP rather than email or other less opaque attributes
226223
# NameQualifier, if present is prefixed with a "/" to the value
227-
e.xpath('a:NameID', { "a" => ASSERTION }).map do |n|
224+
e.xpath('a:NameID', { "a" => RubySaml::XML::NS_ASSERTION }).map do |n|
228225
next unless (value = n&.content)
229226
base_path = n['NameQualifier'] ? "#{n['NameQualifier']}/" : ''
230227
"#{base_path}#{value}"
@@ -281,7 +278,7 @@ def status_code
281278
nodes = REXML::XPath.match(
282279
document,
283280
"/p:Response/p:Status/p:StatusCode",
284-
{ "p" => PROTOCOL }
281+
{ "p" => RubySaml::XML::NS_PROTOCOL }
285282
)
286283
if nodes.size == 1
287284
node = nodes[0]
@@ -291,9 +288,9 @@ def status_code
291288
nodes = REXML::XPath.match(
292289
document,
293290
"/p:Response/p:Status/p:StatusCode/p:StatusCode",
294-
{ "p" => PROTOCOL }
291+
{ "p" => RubySaml::XML::NS_PROTOCOL }
295292
)
296-
statuses = nodes.collect do |inner_node|
293+
statuses = nodes.map do |inner_node|
297294
inner_node.attributes["Value"]
298295
end
299296

@@ -312,7 +309,7 @@ def status_message
312309
nodes = REXML::XPath.match(
313310
document,
314311
"/p:Response/p:Status/p:StatusMessage",
315-
{ "p" => PROTOCOL }
312+
{ "p" => RubySaml::XML::NS_PROTOCOL }
316313
)
317314

318315
Utils.element_text(nodes.first) if nodes.size == 1
@@ -376,7 +373,7 @@ def in_response_to
376373
node = REXML::XPath.first(
377374
document,
378375
"/p:Response",
379-
{ "p" => PROTOCOL }
376+
{ "p" => RubySaml::XML::NS_PROTOCOL }
380377
)
381378
node.nil? ? nil : node.attributes['InResponseTo']
382379
end
@@ -389,7 +386,7 @@ def destination
389386
node = REXML::XPath.first(
390387
document,
391388
"/p:Response",
392-
{ "p" => PROTOCOL }
389+
{ "p" => RubySaml::XML::NS_PROTOCOL }
393390
)
394391
node.nil? ? nil : node.attributes['Destination']
395392
end
@@ -546,12 +543,12 @@ def validate_num_assertion
546543
assertions = REXML::XPath.match(
547544
document,
548545
"//a:Assertion",
549-
{ "a" => ASSERTION }
546+
{ "a" => RubySaml::XML::NS_ASSERTION }
550547
)
551548
encrypted_assertions = REXML::XPath.match(
552549
document,
553550
"//a:EncryptedAssertion",
554-
{ "a" => ASSERTION }
551+
{ "a" => RubySaml::XML::NS_ASSERTION }
555552
)
556553

557554
unless assertions.size + encrypted_assertions.size == 1
@@ -562,7 +559,7 @@ def validate_num_assertion
562559
assertions = REXML::XPath.match(
563560
decrypted_document,
564561
"//a:Assertion",
565-
{ "a" => ASSERTION }
562+
{ "a" => RubySaml::XML::NS_ASSERTION }
566563
)
567564
unless assertions.size == 1
568565
return append_error(error_msg)
@@ -598,7 +595,7 @@ def validate_signed_elements
598595
signature_nodes = REXML::XPath.match(
599596
decrypted_document.nil? ? document : decrypted_document,
600597
"//ds:Signature",
601-
{"ds"=>DSIG}
598+
{ "ds" => RubySaml::XML::DSIG }
602599
)
603600
signed_elements = []
604601
verified_seis = []
@@ -620,7 +617,7 @@ def validate_signed_elements
620617
verified_ids.push(id)
621618

622619
# Check that reference URI matches the parent ID and no duplicate References or IDs
623-
ref = REXML::XPath.first(signature_node, ".//ds:Reference", {"ds"=>DSIG})
620+
ref = REXML::XPath.first(signature_node, ".//ds:Reference", { "ds" => RubySaml::XML::DSIG })
624621
if ref
625622
uri = ref.attributes.get_attribute("URI")
626623
if uri && !uri.value.empty?
@@ -838,7 +835,7 @@ def validate_subject_confirmation
838835
confirmation_data_node = REXML::XPath.first(
839836
subject_confirmation,
840837
'a:SubjectConfirmationData',
841-
{ "a" => ASSERTION }
838+
{ "a" => RubySaml::XML::NS_ASSERTION }
842839
)
843840

844841
next unless confirmation_data_node
@@ -870,7 +867,7 @@ def validate_subject_confirmation_nokogiri(subject_confirmation_nodes)
870867
next
871868
end
872869

873-
confirmation_data_node = subject_confirmation.at_xpath('a:SubjectConfirmationData', { "a" => ASSERTION })
870+
confirmation_data_node = subject_confirmation.at_xpath('a:SubjectConfirmationData', { "a" => RubySaml::XML::NS_ASSERTION })
874871

875872
next unless confirmation_data_node
876873

@@ -916,7 +913,7 @@ def doc_to_validate
916913
sig_elements = REXML::XPath.match(
917914
document,
918915
"/p:Response[@ID=$id]/ds:Signature",
919-
{ "p" => PROTOCOL, "ds" => DSIG },
916+
{ "p" => RubySaml::XML::NS_PROTOCOL, "ds" => RubySaml::XML::DSIG },
920917
{ 'id' => document.signed_element_id }
921918
)
922919

@@ -941,7 +938,7 @@ def validate_signature
941938
sig_elements = REXML::XPath.match(
942939
document,
943940
"/p:Response[@ID=$id]/ds:Signature",
944-
{ "p" => PROTOCOL, "ds" => DSIG },
941+
{ "p" => RubySaml::XML::NS_PROTOCOL, "ds" => RubySaml::XML::DSIG },
945942
{ 'id' => document.signed_element_id }
946943
)
947944

@@ -950,7 +947,7 @@ def validate_signature
950947
sig_elements = REXML::XPath.match(
951948
doc,
952949
"/p:Response/a:Assertion[@ID=$id]/ds:Signature",
953-
SAML_NAMESPACES.merge({ "ds" => DSIG }),
950+
SAML_NAMESPACES.merge({ "ds" => RubySaml::XML::DSIG }),
954951
{ 'id' => doc.signed_element_id }
955952
)
956953
end
@@ -1037,10 +1034,10 @@ def cached_signed_assertion
10371034

10381035
assertion = empty_doc
10391036
if root.name == "Response"
1040-
if REXML::XPath.first(root, "a:Assertion", {"a" => ASSERTION})
1041-
assertion = REXML::XPath.first(root, "a:Assertion", {"a" => ASSERTION})
1042-
elsif REXML::XPath.first(root, "a:EncryptedAssertion", {"a" => ASSERTION})
1043-
assertion = RubySaml::XML::Decryptor.decrypt_assertion(REXML::XPath.first(root, "a:EncryptedAssertion", {"a" => ASSERTION}), settings&.get_sp_decryption_keys)
1037+
if REXML::XPath.first(root, "a:Assertion", {"a" => RubySaml::XML::NS_ASSERTION})
1038+
assertion = REXML::XPath.first(root, "a:Assertion", {"a" => RubySaml::XML::NS_ASSERTION})
1039+
elsif REXML::XPath.first(root, "a:EncryptedAssertion", {"a" => RubySaml::XML::NS_ASSERTION})
1040+
assertion = RubySaml::XML::Decryptor.decrypt_assertion(REXML::XPath.first(root, "a:EncryptedAssertion", {"a" => RubySaml::XML::NS_ASSERTION}), settings&.get_sp_decryption_keys)
10441041
end
10451042
elsif root.name == "Assertion"
10461043
assertion = root

0 commit comments

Comments
 (0)