Skip to content

Commit 20489a0

Browse files
authored
Merge pull request SAML-Toolkits#352 from LoyaltyNZ/feature/support-multiple-attribute-statements
Support multiple AttributeStatement tags
2 parents 807b005 + 2b55a61 commit 20489a0

File tree

3 files changed

+106
-23
lines changed

3 files changed

+106
-23
lines changed

lib/onelogin/ruby-saml/response.rb

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -120,30 +120,29 @@ def attributes
120120
@attr_statements ||= begin
121121
attributes = Attributes.new
122122

123-
stmt_element = xpath_first_from_signed_assertion('/a:AttributeStatement')
124-
return attributes if stmt_element.nil?
125-
126-
stmt_element.elements.each do |attr_element|
127-
name = attr_element.attributes["Name"]
128-
values = attr_element.elements.collect{|e|
129-
if (e.elements.nil? || e.elements.size == 0)
130-
# SAMLCore requires that nil AttributeValues MUST contain xsi:nil XML attribute set to "true" or "1"
131-
# otherwise the value is to be regarded as empty.
132-
["true", "1"].include?(e.attributes['xsi:nil']) ? nil : e.text.to_s
133-
# explicitly support saml2:NameID with saml2:NameQualifier if supplied in attributes
134-
# this is useful for allowing eduPersonTargetedId to be passed as an opaque identifier to use to
135-
# identify the subject in an SP rather than email or other less opaque attributes
136-
# NameQualifier, if present is prefixed with a "/" to the value
137-
else
138-
REXML::XPath.match(e,'a:NameID', { "a" => ASSERTION }).collect{|n|
139-
(n.attributes['NameQualifier'] ? n.attributes['NameQualifier'] +"/" : '') + n.text.to_s
140-
}
141-
end
142-
}
143-
144-
attributes.add(name, values.flatten)
123+
stmt_elements = xpath_from_signed_assertion('/a:AttributeStatement')
124+
stmt_elements.each do |stmt_element|
125+
stmt_element.elements.each do |attr_element|
126+
name = attr_element.attributes["Name"]
127+
values = attr_element.elements.collect{|e|
128+
if (e.elements.nil? || e.elements.size == 0)
129+
# SAMLCore requires that nil AttributeValues MUST contain xsi:nil XML attribute set to "true" or "1"
130+
# otherwise the value is to be regarded as empty.
131+
["true", "1"].include?(e.attributes['xsi:nil']) ? nil : e.text.to_s
132+
# explicitly support saml2:NameID with saml2:NameQualifier if supplied in attributes
133+
# this is useful for allowing eduPersonTargetedId to be passed as an opaque identifier to use to
134+
# identify the subject in an SP rather than email or other less opaque attributes
135+
# NameQualifier, if present is prefixed with a "/" to the value
136+
else
137+
REXML::XPath.match(e,'a:NameID', { "a" => ASSERTION }).collect{|n|
138+
(n.attributes['NameQualifier'] ? n.attributes['NameQualifier'] +"/" : '') + n.text.to_s
139+
}
140+
end
141+
}
142+
143+
attributes.add(name, values.flatten)
144+
end
145145
end
146-
147146
attributes
148147
end
149148
end

test/response_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class RubySamlTest < Minitest::Test
99
let(:settings) { OneLogin::RubySaml::Settings.new }
1010
let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
1111
let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
12+
let(:response_with_multiple_attribute_statements) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_statements)) }
1213
let(:response_without_reference_uri) { OneLogin::RubySaml::Response.new(response_document_without_reference_uri) }
1314
let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
1415
let(:response_with_ds_namespace_at_the_root) { OneLogin::RubySaml::Response.new(response_document_with_ds_namespace_at_the_root)}
@@ -852,6 +853,11 @@ class RubySamlTest < Minitest::Test
852853
assert_equal "[email protected]", response_with_signed_assertion.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
853854
end
854855

856+
it "extract attributes from all AttributeStatement tags" do
857+
assert_equal "smith", response_with_multiple_attribute_statements.attributes[:surname]
858+
assert_equal "bob", response_with_multiple_attribute_statements.attributes[:firstname]
859+
end
860+
855861
it "not raise errors about nil/empty attributes for EncryptedAttributes" do
856862
response_no_cert_and_encrypted_attrs = OneLogin::RubySaml::Response.new(response_document_no_cert_and_encrypted_attrs)
857863
assert_equal 'Demo', response_no_cert_and_encrypted_attrs.attributes["first_name"]
@@ -913,6 +919,12 @@ class RubySamlTest < Minitest::Test
913919
OneLogin::RubySaml::Attributes.single_value_compatibility = true
914920
end
915921

922+
it "return all of multiple values when multiple Attribute tags in multiple AttributeStatement tags" do
923+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
924+
assert_equal ['role1', 'role2', 'role3'], response_with_multiple_attribute_statements.attributes.multi(:role)
925+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
926+
end
927+
916928
it "return nil value correctly" do
917929
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
918930
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<samlp:Response xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="GOSAMLR12901174571794" Version="2.0" IssueInstant="2010-11-18T21:57:37Z" Destination="{recipient}">
2+
<samlp:Status>
3+
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
4+
<saml:Assertion xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="2.0" ID="pfxa46574df-b3b0-a06a-23c8-636413198772" IssueInstant="2010-11-18T21:57:37Z">
5+
<saml:Issuer>https://app.onelogin.com/saml/metadata/13590</saml:Issuer>
6+
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
7+
<ds:SignedInfo>
8+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
9+
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
10+
<ds:Reference URI="#pfxa46574df-b3b0-a06a-23c8-636413198772">
11+
<ds:Transforms>
12+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
13+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
14+
</ds:Transforms>
15+
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
16+
<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>
17+
</ds:Reference>
18+
</ds:SignedInfo>
19+
<ds:SignatureValue>yiveKcPdDpuDNj6shrQ3ABwr/cA3CryD2phG/xLZszKWxU5/mlaKt8ewbZOdKKvtOs2pHBy5Dua3k94AF+zxGyel5gOowmoyXJr+AOr+kPO0vli1V8o3hPPUZwRgSX6Q9pS1CqQghKiEasRyylqqJUaPYzmOzOE8/XlMkwiWmO0=</ds:SignatureValue>
20+
<ds:KeyInfo>
21+
<ds:X509Data>
22+
<ds:X509Certificate>MIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMDMwOTA5NTg0NVoXDTE1MDMwOTA5NTg0NVowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOjSu1fjPy8d5w4QyL1+zd4hIw1Mkkff4WY/TLG8OZkU5YTSWmmHPD5kvYH5uoXS/6qQ81qXpR2wV8CTowZJULg09ddRdRn8Qsqj1FyOC5slE3y2bZ2oFua72of/49fpujnFT6KnQ61CBMqlDoTQqOT62vGJ8nP6MZWvA6sxqud5AgMBAAEwAwYBAAMBAA==</ds:X509Certificate>
23+
</ds:X509Data>
24+
</ds:KeyInfo>
25+
</ds:Signature>
26+
<saml:Subject>
27+
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</saml:NameID>
28+
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
29+
<saml:SubjectConfirmationData NotOnOrAfter="2010-11-18T22:02:37Z" Recipient="{recipient}"/></saml:SubjectConfirmation>
30+
</saml:Subject>
31+
<saml:Conditions NotBefore="2010-11-18T21:52:37Z" NotOnOrAfter="2010-11-18T22:02:37Z">
32+
<saml:AudienceRestriction>
33+
<saml:Audience>{audience}</saml:Audience>
34+
</saml:AudienceRestriction>
35+
</saml:Conditions>
36+
<saml:AuthnStatement AuthnInstant="2010-11-18T21:57:37Z" SessionNotOnOrAfter="2010-11-19T21:57:37Z" SessionIndex="_531c32d283bdff7e04e487bcdbc4dd8d">
37+
<saml:AuthnContext>
38+
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
39+
</saml:AuthnContext>
40+
</saml:AuthnStatement>
41+
<saml:AttributeStatement>
42+
<saml:Attribute Name="surname">
43+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">smith</saml:AttributeValue>
44+
</saml:Attribute>
45+
<saml:Attribute Name="another_value">
46+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">value1</saml:AttributeValue>
47+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">value2</saml:AttributeValue>
48+
</saml:Attribute>
49+
<saml:Attribute Name="role">
50+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">role1</saml:AttributeValue>
51+
</saml:Attribute>
52+
</saml:AttributeStatement>
53+
<saml:AttributeStatement>
54+
<saml:Attribute Name="firstname">
55+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">bob</saml:AttributeValue>
56+
</saml:Attribute>
57+
<saml:Attribute Name="role">
58+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">role2</saml:AttributeValue>
59+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">role3</saml:AttributeValue>
60+
</saml:Attribute>
61+
<saml:Attribute Name="attribute_with_nil_value">
62+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
63+
</saml:Attribute>
64+
<saml:Attribute Name="attribute_with_nils_and_empty_strings">
65+
<saml:AttributeValue/>
66+
<saml:AttributeValue>valuePresent</saml:AttributeValue>
67+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
68+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
69+
</saml:Attribute>
70+
</saml:AttributeStatement>
71+
</saml:Assertion>
72+
</samlp:Response>

0 commit comments

Comments
 (0)