Rewrote conversion from AttributeStatement to dictionary. Can now handle undefined name format as well as different name format for different attributes in the Statement.

This commit is contained in:
Roland Hedberg
2013-07-04 14:19:58 +02:00
parent b28f7bc918
commit 4ac5805ddd
4 changed files with 77 additions and 26 deletions

View File

@@ -21,7 +21,7 @@ from importlib import import_module
from saml2.s_utils import factory, do_ava from saml2.s_utils import factory, do_ava
from saml2 import saml, extension_elements_to_elements, SAMLError from saml2 import saml, extension_elements_to_elements, SAMLError
from saml2.saml import NAME_FORMAT_URI from saml2.saml import NAME_FORMAT_URI, NAME_FORMAT_UNSPECIFIED
class UnknownNameFormat(SAMLError): class UnknownNameFormat(SAMLError):
@@ -121,14 +121,22 @@ def to_local(acs, statement):
""" """
if not acs: if not acs:
acs = [AttributeConverter()] acs = [AttributeConverter()]
acsd = {"": acs}
else:
acsd = dict([(a.name_format, a) for a in acs])
ava = [] ava = {}
for aconv in acs: for attr in statement.attribute:
try: try:
ava = aconv.fro(statement) key, val = acsd[attr.name_format].ava_from(attr)
break except KeyError:
except UnknownNameFormat: key, val = acs[0].lcd_ava_from(attr)
pass
try:
ava[key].extend(val)
except KeyError:
ava[key] = val
return ava return ava
@@ -233,10 +241,39 @@ class AttributeConverter(object):
if self._fro is None or self._to is None: if self._fro is None or self._to is None:
self.adjust() self.adjust()
def lcd_ava_from(self, attribute):
"""
In nothing else works, this should
:param attribute:
:return:
"""
try:
name = attribute.friendly_name.strip()
except AttributeError:
name = attribute.name.strip()
values = []
for value in attribute.attribute_value:
if not value.text:
values.append('')
else:
values.append(value.text.strip())
return name, values
def fail_safe_fro(self, statement): def fail_safe_fro(self, statement):
""" In case there is not formats defined """ """ In case there is not formats defined or if the name format is
undefined
:param statement: AttributeStatement instance
:return: A dictionary with names and values
"""
result = {} result = {}
for attribute in statement.attribute: for attribute in statement.attribute:
if attribute.name_format and \
attribute.name_format != NAME_FORMAT_UNSPECIFIED:
continue
try: try:
name = attribute.friendly_name.strip() name = attribute.friendly_name.strip()
except AttributeError: except AttributeError:
@@ -281,7 +318,7 @@ class AttributeConverter(object):
return attr, val return attr, val
def fro(self, statement): def fro(self, statement):
""" Get the attributes and the attribute values """ Get the attributes and the attribute values.
:param statement: The AttributeStatement. :param statement: The AttributeStatement.
:return: A dictionary containing attributes and values :return: A dictionary containing attributes and values
@@ -294,14 +331,11 @@ class AttributeConverter(object):
for attribute in statement.attribute: for attribute in statement.attribute:
if attribute.name_format and self.name_format and \ if attribute.name_format and self.name_format and \
attribute.name_format != self.name_format: attribute.name_format != self.name_format:
raise UnknownNameFormat continue
(key, val) = self.ava_from(attribute) (key, val) = self.ava_from(attribute)
result[key] = val result[key] = val
if not result:
return self.fail_safe_fro(statement)
else:
return result return result
def to_format(self, attr): def to_format(self, attr):

View File

@@ -173,3 +173,13 @@ STATEMENT3 = """<?xml version='1.0' encoding='UTF-8'?>
<ns0:AttributeValue>Roland</ns0:AttributeValue> <ns0:AttributeValue>Roland</ns0:AttributeValue>
</ns0:Attribute> </ns0:Attribute>
</ns0:AttributeStatement>""" </ns0:AttributeStatement>"""
STATEMENT4 = """<?xml version='1.0' encoding='UTF-8'?>
<ns0:AttributeStatement xmlns:ns0="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Attribute Name="user_id" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<ns0:AttributeValue xsi:type="xs:string">bob</ns0:AttributeValue>
</ns0:Attribute>
<ns0:Attribute Name="NameID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<ns0:AttributeValue xsi:type="xs:string">bobsnameagain</ns0:AttributeValue>
</ns0:Attribute>
</ns0:AttributeStatement>"""

View File

@@ -5,7 +5,7 @@ from saml2 import attribute_converter, saml
from attribute_statement_data import * from attribute_statement_data import *
from pathutils import full_path from pathutils import full_path
from saml2.attribute_converter import AttributeConverterNOOP from saml2.attribute_converter import AttributeConverterNOOP, to_local
def _eq(l1,l2): def _eq(l1,l2):
@@ -15,10 +15,12 @@ BASIC_NF = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic'
URI_NF = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri' URI_NF = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
SAML1 = 'urn:mace:shibboleth:1.0:attributeNamespace:uri' SAML1 = 'urn:mace:shibboleth:1.0:attributeNamespace:uri'
def test_default(): def test_default():
acs = attribute_converter.ac_factory() acs = attribute_converter.ac_factory()
assert acs assert acs
class TestAC(): class TestAC():
def setup_class(self): def setup_class(self):
self.acs = attribute_converter.ac_factory(full_path("attributemaps")) self.acs = attribute_converter.ac_factory(full_path("attributemaps"))
@@ -51,13 +53,10 @@ class TestAC():
def test_ava_fro_2(self): def test_ava_fro_2(self):
ats = saml.attribute_statement_from_string(STATEMENT2) ats = saml.attribute_statement_from_string(STATEMENT2)
#print ats #print ats
ava = None ava = {}
for ac in self.acs: for ac in self.acs:
try: ava.update(ac.fro(ats))
ava = ac.fro(ats)
break
except attribute_converter.UnknownNameFormat:
pass
print ava.keys() print ava.keys()
assert _eq(ava.keys(),['uid', 'swissedupersonuniqueid', assert _eq(ava.keys(),['uid', 'swissedupersonuniqueid',
'swissedupersonhomeorganizationtype', 'swissedupersonhomeorganizationtype',
@@ -168,6 +167,11 @@ class TestAC():
assert _eq(ava.keys(), oava.keys()) assert _eq(ava.keys(), oava.keys())
def test_unspecified_name_format(self):
ats = saml.attribute_statement_from_string(STATEMENT4)
ava = to_local(self.acs, ats)
assert ava == {'user_id': ['bob'], 'NameID': ['bobsnameagain']}
def test_noop_attribute_conversion(): def test_noop_attribute_conversion():
ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg" } ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg" }
@@ -187,4 +191,7 @@ def test_noop_attribute_conversion():
if __name__ == "__main__": if __name__ == "__main__":
test_noop_attribute_conversion() t = TestAC()
t.setup_class()
t.test_ava_fro_2()
#test_noop_attribute_conversion()

View File

@@ -430,6 +430,6 @@ class TestClientWithDummy():
# tc.test_response() # tc.test_response()
if __name__ == "__main__": if __name__ == "__main__":
tc = TestClientWithDummy() tc = TestClient()
tc.setup_class() tc.setup_class()
tc.test_post_sso() tc.test_sign_auth_request_0()