Skip to content

Commit 4ac5805

Browse files
author
Roland Hedberg
committed
Rewrote conversion from AttributeStatement to dictionary. Can now handle undefined name format as well as different name format for different attributes in the Statement.
1 parent b28f7bc commit 4ac5805

File tree

4 files changed

+77
-26
lines changed

4 files changed

+77
-26
lines changed

src/saml2/attribute_converter.py

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from saml2.s_utils import factory, do_ava
2323
from saml2 import saml, extension_elements_to_elements, SAMLError
24-
from saml2.saml import NAME_FORMAT_URI
24+
from saml2.saml import NAME_FORMAT_URI, NAME_FORMAT_UNSPECIFIED
2525

2626

2727
class UnknownNameFormat(SAMLError):
@@ -121,14 +121,22 @@ def to_local(acs, statement):
121121
"""
122122
if not acs:
123123
acs = [AttributeConverter()]
124+
acsd = {"": acs}
125+
else:
126+
acsd = dict([(a.name_format, a) for a in acs])
124127

125-
ava = []
126-
for aconv in acs:
128+
ava = {}
129+
for attr in statement.attribute:
127130
try:
128-
ava = aconv.fro(statement)
129-
break
130-
except UnknownNameFormat:
131-
pass
131+
key, val = acsd[attr.name_format].ava_from(attr)
132+
except KeyError:
133+
key, val = acs[0].lcd_ava_from(attr)
134+
135+
try:
136+
ava[key].extend(val)
137+
except KeyError:
138+
ava[key] = val
139+
132140
return ava
133141

134142

@@ -233,10 +241,39 @@ def from_dict(self, mapdict):
233241
if self._fro is None or self._to is None:
234242
self.adjust()
235243

244+
def lcd_ava_from(self, attribute):
245+
"""
246+
In nothing else works, this should
247+
248+
:param attribute:
249+
:return:
250+
"""
251+
try:
252+
name = attribute.friendly_name.strip()
253+
except AttributeError:
254+
name = attribute.name.strip()
255+
256+
values = []
257+
for value in attribute.attribute_value:
258+
if not value.text:
259+
values.append('')
260+
else:
261+
values.append(value.text.strip())
262+
263+
return name, values
264+
236265
def fail_safe_fro(self, statement):
237-
""" In case there is not formats defined """
266+
""" In case there is not formats defined or if the name format is
267+
undefined
268+
269+
:param statement: AttributeStatement instance
270+
:return: A dictionary with names and values
271+
"""
238272
result = {}
239273
for attribute in statement.attribute:
274+
if attribute.name_format and \
275+
attribute.name_format != NAME_FORMAT_UNSPECIFIED:
276+
continue
240277
try:
241278
name = attribute.friendly_name.strip()
242279
except AttributeError:
@@ -281,8 +318,8 @@ def ava_from(self, attribute):
281318
return attr, val
282319

283320
def fro(self, statement):
284-
""" Get the attributes and the attribute values
285-
321+
""" Get the attributes and the attribute values.
322+
286323
:param statement: The AttributeStatement.
287324
:return: A dictionary containing attributes and values
288325
"""
@@ -294,15 +331,12 @@ def fro(self, statement):
294331
for attribute in statement.attribute:
295332
if attribute.name_format and self.name_format and \
296333
attribute.name_format != self.name_format:
297-
raise UnknownNameFormat
334+
continue
298335

299336
(key, val) = self.ava_from(attribute)
300337
result[key] = val
301338

302-
if not result:
303-
return self.fail_safe_fro(statement)
304-
else:
305-
return result
339+
return result
306340

307341
def to_format(self, attr):
308342
""" Creates an Attribute instance with name, name_format and

tests/attribute_statement_data.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,13 @@
173173
<ns0:AttributeValue>Roland</ns0:AttributeValue>
174174
</ns0:Attribute>
175175
</ns0:AttributeStatement>"""
176+
177+
STATEMENT4 = """<?xml version='1.0' encoding='UTF-8'?>
178+
<ns0:AttributeStatement xmlns:ns0="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
179+
<ns0:Attribute Name="user_id" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
180+
<ns0:AttributeValue xsi:type="xs:string">bob</ns0:AttributeValue>
181+
</ns0:Attribute>
182+
<ns0:Attribute Name="NameID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
183+
<ns0:AttributeValue xsi:type="xs:string">bobsnameagain</ns0:AttributeValue>
184+
</ns0:Attribute>
185+
</ns0:AttributeStatement>"""

tests/test_19_attribute_converter.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from attribute_statement_data import *
66

77
from pathutils import full_path
8-
from saml2.attribute_converter import AttributeConverterNOOP
8+
from saml2.attribute_converter import AttributeConverterNOOP, to_local
99

1010

1111
def _eq(l1,l2):
@@ -15,10 +15,12 @@ def _eq(l1,l2):
1515
URI_NF = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
1616
SAML1 = 'urn:mace:shibboleth:1.0:attributeNamespace:uri'
1717

18+
1819
def test_default():
1920
acs = attribute_converter.ac_factory()
2021
assert acs
2122

23+
2224
class TestAC():
2325
def setup_class(self):
2426
self.acs = attribute_converter.ac_factory(full_path("attributemaps"))
@@ -51,13 +53,10 @@ def test_ava_fro_1(self):
5153
def test_ava_fro_2(self):
5254
ats = saml.attribute_statement_from_string(STATEMENT2)
5355
#print ats
54-
ava = None
56+
ava = {}
5557
for ac in self.acs:
56-
try:
57-
ava = ac.fro(ats)
58-
break
59-
except attribute_converter.UnknownNameFormat:
60-
pass
58+
ava.update(ac.fro(ats))
59+
6160
print ava.keys()
6261
assert _eq(ava.keys(),['uid', 'swissedupersonuniqueid',
6362
'swissedupersonhomeorganizationtype',
@@ -167,7 +166,12 @@ def test_to_and_for(self):
167166
oava = basic_ac.fro(attr_state)
168167

169168
assert _eq(ava.keys(), oava.keys())
170-
169+
170+
def test_unspecified_name_format(self):
171+
ats = saml.attribute_statement_from_string(STATEMENT4)
172+
ava = to_local(self.acs, ats)
173+
assert ava == {'user_id': ['bob'], 'NameID': ['bobsnameagain']}
174+
171175

172176
def test_noop_attribute_conversion():
173177
ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg" }
@@ -187,4 +191,7 @@ def test_noop_attribute_conversion():
187191

188192

189193
if __name__ == "__main__":
190-
test_noop_attribute_conversion()
194+
t = TestAC()
195+
t.setup_class()
196+
t.test_ava_fro_2()
197+
#test_noop_attribute_conversion()

tests/test_51_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,6 @@ def test_post_sso(self):
430430
# tc.test_response()
431431

432432
if __name__ == "__main__":
433-
tc = TestClientWithDummy()
433+
tc = TestClient()
434434
tc.setup_class()
435-
tc.test_post_sso()
435+
tc.test_sign_auth_request_0()

0 commit comments

Comments
 (0)