From 634644e94c33faafe1eaf008a3ed3eaca99aa5d6 Mon Sep 17 00:00:00 2001 From: Scott Koranda Date: Tue, 4 Jun 2019 12:41:13 -0500 Subject: Enable NameQualifier and SPNameQualifier attributes for ePTID The attribute value for eduPersonTargetedID (ePTID) is a NameID element. The SAML specification allows the NameID element to include the two optional attributes 'NameQualifier' and 'SPNameQualifier'. This patch enables specifying a dictionary as the internal or local attribute value instead of a string. When the local attribute value is a dictionary with keys 'value', 'NameQualifier', and 'SPNameQualifier' then the resulting XML NameID element will include the 'NameQualifier' and 'SPNameQualifier' attributes with values taken from the values of the dictionary. The value for the NameID element is taken from the value associated with tthe 'value' key. --- src/saml2/attribute_converter.py | 42 ++++++++++++++++++++++++++++++------ tests/test_19_attribute_converter.py | 18 ++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/saml2/attribute_converter.py b/src/saml2/attribute_converter.py index 4d411597..cd40fe79 100644 --- a/src/saml2/attribute_converter.py +++ b/src/saml2/attribute_converter.py @@ -432,12 +432,7 @@ class AttributeConverter(object): if name: if name == "urn:oid:1.3.6.1.4.1.5923.1.1.1.10": # special case for eduPersonTargetedID - attr_value = [] - for v in value: - extension_element = ExtensionElement("NameID", NAMESPACE, - attributes={'Format': NAMEID_FORMAT_PERSISTENT}, text=v) - attrval = saml.AttributeValue(extension_elements=[extension_element]) - attr_value.append(attrval) + attr_value = self.to_eptid_value(value) else: attr_value = do_ava(value) attributes.append(factory(saml.Attribute, @@ -452,6 +447,41 @@ class AttributeConverter(object): return attributes + def to_eptid_value(self, value): + """ + Special handling for the attribute with name + urn:oid:1.3.6.1.4.1.5923.1.1.1.10, usually known by the friendly + name eduPersonTargetedID. Create the AttributeValue instance(s) + for the attribute. + + value is either a string or a dictionary with keys 'value', + 'NameQualifier', and 'SPNameQualifier'. + + Returns a list of AttributeValue instances. + """ + attribute_values = [] + + for v in value: + if isinstance(v, dict): + element_attributes = { + 'Format': NAMEID_FORMAT_PERSISTENT, + 'NameQualifier': v['NameQualifier'], + 'SPNameQualifier': v['SPNameQualifier'] + } + text = v['value'] + else: + element_attributes = {'Format': NAMEID_FORMAT_PERSISTENT} + text = v + + element = ExtensionElement("NameID", NAMESPACE, element_attributes, + text=text) + + attrval = saml.AttributeValue(extension_elements=[element]) + + attribute_values.append(attrval) + + return attribute_values + class AttributeConverterNOOP(AttributeConverter): """ Does a NOOP conversion, that is no conversion is made """ diff --git a/tests/test_19_attribute_converter.py b/tests/test_19_attribute_converter.py index 5f60f6d5..99eef12a 100644 --- a/tests/test_19_attribute_converter.py +++ b/tests/test_19_attribute_converter.py @@ -221,6 +221,24 @@ class TestAC(): assert attributes[0].attribute_value[0].extension_elements[0].text == "test value1" assert attributes[0].attribute_value[1].extension_elements[0].text == "test value2" + def test_from_local_eduPersonTargetedID_with_qualifiers(self): + IDP_ENTITY_ID = 'https://some.org/idp' + SP_ENTITY_ID = 'https://some.org/sp' + + ava = {"edupersontargetedid": [{ + 'value': "test value1", + 'NameQualifier': IDP_ENTITY_ID, + 'SPNameQualifier': SP_ENTITY_ID}]} + attributes = from_local(self.acs, ava, URI_NF) + + assert len(attributes) == 1 + + element = attributes[0].attribute_value[0].extension_elements[0] + + assert element.text == "test value1" + assert element.attributes['NameQualifier'] == IDP_ENTITY_ID + assert element.attributes['SPNameQualifier'] == SP_ENTITY_ID + def test_noop_attribute_conversion(): ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg"} -- cgit v1.2.1 From 1432950d86c1941da0b6465804b94a3ca75075f8 Mon Sep 17 00:00:00 2001 From: Ivan Kanakarakis Date: Mon, 17 Jun 2019 01:27:56 +0300 Subject: Restructure to_eptid_value function Use "text" instead of "value" as the key that denotes the text-value of the NameID node. Signed-off-by: Ivan Kanakarakis --- src/saml2/attribute_converter.py | 58 +++++++++++++++++++----------------- tests/test_19_attribute_converter.py | 23 ++++++++------ 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/saml2/attribute_converter.py b/src/saml2/attribute_converter.py index cd40fe79..bc937702 100644 --- a/src/saml2/attribute_converter.py +++ b/src/saml2/attribute_converter.py @@ -447,39 +447,41 @@ class AttributeConverter(object): return attributes - def to_eptid_value(self, value): + def to_eptid_value(self, values): """ - Special handling for the attribute with name - urn:oid:1.3.6.1.4.1.5923.1.1.1.10, usually known by the friendly - name eduPersonTargetedID. Create the AttributeValue instance(s) - for the attribute. + Create AttributeValue instances of NameID from the given values. - value is either a string or a dictionary with keys 'value', - 'NameQualifier', and 'SPNameQualifier'. + Special handling for the "eptid" attribute + Name=urn:oid:1.3.6.1.4.1.5923.1.1.1.10 + FriendlyName=eduPersonTargetedID - Returns a list of AttributeValue instances. - """ - attribute_values = [] - - for v in value: - if isinstance(v, dict): - element_attributes = { - 'Format': NAMEID_FORMAT_PERSISTENT, - 'NameQualifier': v['NameQualifier'], - 'SPNameQualifier': v['SPNameQualifier'] - } - text = v['value'] - else: - element_attributes = {'Format': NAMEID_FORMAT_PERSISTENT} - text = v - - element = ExtensionElement("NameID", NAMESPACE, element_attributes, - text=text) + values is a list of items of type str or dict. When an item is a + dictionary it has the keys: "NameQualifier", "SPNameQualifier", and + "text". - attrval = saml.AttributeValue(extension_elements=[element]) - - attribute_values.append(attrval) + Returns a list of AttributeValue instances of NameID elements. + """ + def _create_nameid_ext_el(value): + text = value["text"] if isinstance(value, dict) else value + attributes = ( + { + "Format": NAMEID_FORMAT_PERSISTENT, + "NameQualifier": value["NameQualifier"], + "SPNameQualifier": value["SPNameQualifier"], + } + if isinstance(value, dict) + else {"Format": NAMEID_FORMAT_PERSISTENT} + ) + element = ExtensionElement( + "NameID", NAMESPACE, attributes=attributes, text=text + ) + return element + + attribute_values = [ + saml.AttributeValue(extension_elements=[_create_nameid_ext_el(v)]) + for v in values + ] return attribute_values diff --git a/tests/test_19_attribute_converter.py b/tests/test_19_attribute_converter.py index 99eef12a..fdb9cd96 100644 --- a/tests/test_19_attribute_converter.py +++ b/tests/test_19_attribute_converter.py @@ -222,13 +222,18 @@ class TestAC(): assert attributes[0].attribute_value[1].extension_elements[0].text == "test value2" def test_from_local_eduPersonTargetedID_with_qualifiers(self): - IDP_ENTITY_ID = 'https://some.org/idp' - SP_ENTITY_ID = 'https://some.org/sp' - - ava = {"edupersontargetedid": [{ - 'value': "test value1", - 'NameQualifier': IDP_ENTITY_ID, - 'SPNameQualifier': SP_ENTITY_ID}]} + IDP_ENTITY_ID = "https://some.org/idp" + SP_ENTITY_ID = "https://some.org/sp" + + ava = { + "edupersontargetedid": [ + { + "text": "test value1", + "NameQualifier": IDP_ENTITY_ID, + "SPNameQualifier": SP_ENTITY_ID, + } + ] + } attributes = from_local(self.acs, ava, URI_NF) assert len(attributes) == 1 @@ -236,8 +241,8 @@ class TestAC(): element = attributes[0].attribute_value[0].extension_elements[0] assert element.text == "test value1" - assert element.attributes['NameQualifier'] == IDP_ENTITY_ID - assert element.attributes['SPNameQualifier'] == SP_ENTITY_ID + assert element.attributes["NameQualifier"] == IDP_ENTITY_ID + assert element.attributes["SPNameQualifier"] == SP_ENTITY_ID def test_noop_attribute_conversion(): -- cgit v1.2.1