diff --git a/doc/README.md b/doc/README.md index 3f2e16dbe..100136f74 100644 --- a/doc/README.md +++ b/doc/README.md @@ -527,7 +527,8 @@ correct functionality. An identifier such as eduPersonPrincipalName asserted by an IdP can be used to look up a person record in an LDAP directory to find attributes to assert about the authenticated user to the SP. The identifier to consume from the IdP, the LDAP directory details, and the mapping of attributes found in the -directory may all be confingured on a per-SP basis. To use the +directory may all be confingured on a per-SP basis. The input to use when hashing to create a +persistent NameID may also be obtained from attributes returned from the LDAP directory. To use the LDAP microservice install the extra necessary dependencies with `pip install satosa[ldap]` and then see the [example config](../example/plugins/microservices/ldap_attribute_store.yaml.example). diff --git a/example/plugins/microservices/ldap_attribute_store.yaml.example b/example/plugins/microservices/ldap_attribute_store.yaml.example index 49565c942..531875a1d 100644 --- a/example/plugins/microservices/ldap_attribute_store.yaml.example +++ b/example/plugins/microservices/ldap_attribute_store.yaml.example @@ -18,6 +18,10 @@ config: # Whether to clear values for attributes incoming # to this microservice. Default is no or false. clear_input_attributes: no + # List of LDAP attributes to use as input to hashing to create + # NameID. + user_id_from_attrs: + - employeeNumber # Configuration may also be done per-SP with any # missing parameters taken from the default if any. # The configuration key is the entityID of the SP. @@ -26,3 +30,5 @@ config: https://sp.myserver.edu/shibboleth-sp search_base: ou=People,o=MyVO,dc=example,dc=org eduPersonPrincipalName: employeenumber + user_id_from_attrs: + - uid diff --git a/src/satosa/micro_services/ldap_attribute_store.py b/src/satosa/micro_services/ldap_attribute_store.py index 8d2c37546..84500fecb 100644 --- a/src/satosa/micro_services/ldap_attribute_store.py +++ b/src/satosa/micro_services/ldap_attribute_store.py @@ -91,6 +91,12 @@ def process(self, context, data): clear_input_attributes = self.config['clear_input_attributes'] else: clear_input_attributes = False + if 'user_id_from_attrs' in config: + user_id_from_attrs = config['user_id_from_attrs'] + elif 'user_id_from_attrs' in self.config: + user_id_from_attrs = self.config['user_id_from_attrs'] + else: + user_id_from_attrs = [] except KeyError as err: satosa_logging(logger, logging.ERROR, "{} Configuration '{}' is missing".format(logprefix, err), context.state) @@ -153,15 +159,41 @@ def process(self, context, data): satosa_logging(logger, logging.DEBUG, "{} Clearing values for these input attributes: {}".format(logprefix, data.attributes), context.state) data.attributes = {} - # Use a found record, if any, to populate attributes + # Use a found record, if any, to populate attributes and input for NameID if record: satosa_logging(logger, logging.DEBUG, "{} Using record with DN {}".format(logprefix, record["dn"]), context.state) satosa_logging(logger, logging.DEBUG, "{} Record with DN {} has attributes {}".format(logprefix, record["dn"], record["attributes"]), context.state) + + # Populate attributes as configured. for attr in search_return_attributes.keys(): if attr in record["attributes"]: data.attributes[search_return_attributes[attr]] = record["attributes"][attr] satosa_logging(logger, logging.DEBUG, "{} Setting internal attribute {} with values {}".format(logprefix, search_return_attributes[attr], record["attributes"][attr]), context.state) + # Populate input for NameID if configured. SATOSA core does the hashing of input + # to create a persistent NameID. + if user_id_from_attrs: + userId = "" + for attr in user_id_from_attrs: + if attr in record["attributes"]: + value = record["attributes"][attr] + if isinstance(value, list): + # Use a default sort to ensure some predictability since the + # LDAP directory server may return multi-valued attributes + # in any order. + value.sort() + for v in value: + userId += v + satosa_logging(logger, logging.DEBUG, "{} Added attribute {} with value {} to input for NameID".format(logprefix, attr, v), context.state) + else: + userId += value + satosa_logging(logger, logging.DEBUG, "{} Added attribute {} with value {} to input for NameID".format(logprefix, attr, value), context.state) + if not userId: + satosa_logging(logger, logging.WARNING, "{} Input for NameID is empty so not overriding default".format(logprefix), context.state) + else: + data.user_id = userId + satosa_logging(logger, logging.DEBUG, "{} Input for NameID is {}".format(logprefix, data.user_id), context.state) + else: satosa_logging(logger, logging.WARN, "{} No record found in LDAP so no attributes will be added".format(logprefix), context.state)