diff --git a/README.md b/README.md index 2fe50fe0..fa363972 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,14 @@ In addition to the required settings data (idp, sp), there is extra information "displayname": "SP test", "url": "http://sp.example.com" } + }, + // When parsing user attributes in the SAMLResponse + // which field should be used to identify the + // attribute name. All IdPs return Name, usually + // as an oid, but most also return the easier + // FriendlyName + "attributes": { + "nameField": "Name" } } ``` @@ -803,6 +811,7 @@ Configuration of the OneLogin Python Toolkit * ***set_strict*** Activates or deactivates the strict mode. * ***is_strict*** Returns if the 'strict' mode is active. * ***is_debug_active*** Returns if the debug is active. +* ***get_attribute_name_field*** Returns the field that best identifies the attribute name ####OneLogin_Saml2_Metadata - metadata.py#### diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index c41e3cf0..b613a1e9 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -344,7 +344,7 @@ def get_attributes(self): attributes = {} attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') for attribute_node in attribute_nodes: - attr_name = attribute_node.get('Name') + attr_name = attribute_node.get(self.__settings.get_attribute_name_field()) values = [] for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): values.append(attr.text) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 213d078e..6020407c 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -79,6 +79,7 @@ def __init__(self, settings=None, custom_base_path=None): self.__contacts = {} self.__organization = {} self.__errors = [] + self.__attributes = {} self.__load_paths(base_path=custom_base_path) self.__update_paths(settings) @@ -209,6 +210,8 @@ def __load_settings_from_dict(self, settings): self.__contacts = settings['contactPerson'] if 'organization' in settings: self.__organization = settings['organization'] + if 'attributes' in settings: + self.__attributes = settings['attributes'] self.__add_default_values() return True @@ -303,6 +306,10 @@ def __add_default_values(self): if 'requestedAuthnContext' not in self.__security.keys(): self.__security['requestedAuthnContext'] = True + # Name attributes based on the required Name field + if 'nameField' not in self.__attributes.keys(): + self.__attributes['nameField'] = "Name" + def check_settings(self, settings): """ Checks the settings info. @@ -424,6 +431,14 @@ def check_settings(self, settings): ('url' not in organization or len(organization['url']) == 0): errors.append('organization_not_enought_data') break + + if 'attributes' in settings: + defined_types = settings["attributes"].keys() + valid_types = ["nameField", ] + if not all([type in valid_types for type in defined_types]): + invalid_types = ", ".join(set(defined_types).difference(set(valid_types))) + errors.append('unexpected attribute fields: %s' % invalid_types) + # Restores the value that had the self.__sp if 'old_sp' in locals(): self.__sp = old_sp @@ -539,6 +554,16 @@ def get_organization(self): """ return self.__organization + def get_attribute_name_field(self): + """ + Gets the name of the field that represents the + attribute name. Usually Name or FriendlyName + + :returns: Field name + :rtype: string + """ + return self.__attributes["nameField"] + def get_sp_metadata(self): """ Gets the SP metadata. The XML representation.