From 89d0359efefe0dbc17745ccdecef8eded29fad63 Mon Sep 17 00:00:00 2001 From: "J.J. Guy" Date: Wed, 29 Apr 2015 14:14:27 -0700 Subject: [PATCH 1/3] Allow user to configure which field used as attribute name attribute names are currently identified by their OASIS spec oid. while these are specific, they're not easy to read. Most IdPs also include a "FriendlyName" field such as: That's a lot easier to use. Added support to advanced configuration to allow the user to define the field name used. Defaults to Name if not defined. --- src/onelogin/saml2/response.py | 2 +- src/onelogin/saml2/settings.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) 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..c7897da8 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. From 8191a89ad1c4c31b8dd5b04d7a8ccbe0d0ce2fbe Mon Sep 17 00:00:00 2001 From: "J.J. Guy" Date: Wed, 29 Apr 2015 14:24:37 -0700 Subject: [PATCH 2/3] updating README to include added function and field --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) 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#### From c8ca233729d9dc4558a50c1bd5b2c168140e45a8 Mon Sep 17 00:00:00 2001 From: "J.J. Guy" Date: Wed, 29 Apr 2015 18:29:21 -0700 Subject: [PATCH 3/3] fixing pep8 test failures --- src/onelogin/saml2/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index c7897da8..6020407c 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -434,7 +434,7 @@ def check_settings(self, settings): if 'attributes' in settings: defined_types = settings["attributes"].keys() - valid_types = ["nameField",] + 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) @@ -556,9 +556,9 @@ def get_organization(self): def get_attribute_name_field(self): """ - Gets the name of the field that represents the + Gets the name of the field that represents the attribute name. Usually Name or FriendlyName - + :returns: Field name :rtype: string """