Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

NameID input from attributes for LDAP attribute store #103

Merged
merged 1 commit into from
Jun 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
34 changes: 33 additions & 1 deletion src/satosa/micro_services/ldap_attribute_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably nitpicking :) but userId = "".join(value) should be more efficient than the for loop + string concat and still be readable

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)

Expand Down