Skip to content

Commit 4b9c83b

Browse files
Merge pull request #398 from shaardie/ldap_multiple_entries
Add option to process all ldap results
2 parents 4da0823 + af4820c commit 4b9c83b

File tree

2 files changed

+73
-66
lines changed

2 files changed

+73
-66
lines changed

example/plugins/microservices/ldap_attribute_store.yaml.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ config:
103103
user_id_from_attrs:
104104
- employeeNumber
105105

106+
# If true, do not only process the first ldap result, but iterate over
107+
# the result and process all of them.
108+
use_all_results: false
109+
106110
# Where to redirect the browser if no record is returned
107111
# from LDAP. The default is not to redirect.
108112
on_ldap_search_result_empty: https://my.vo.org/please/go/enroll

src/satosa/micro_services/ldap_attribute_store.py

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -550,13 +550,13 @@ def process(self, context, data):
550550

551551
# For now consider only the first record found (if any).
552552
if len(responses) > 0:
553-
if len(responses) > 1:
553+
if len(responses) > 1 and not config.get("use_all_results", False):
554554
msg = "LDAP server returned {} records using search filter"
555555
msg = msg + " value {}"
556556
msg = msg.format(len(responses), filter_val)
557557
logline = lu.LOG_FMT.format(id=session_id, message=msg)
558558
logger.warning(logline)
559-
record = responses[0]
559+
responses = responses[0:1]
560560
break
561561

562562
# Before using a found record, if any, to populate attributes
@@ -568,73 +568,76 @@ def process(self, context, data):
568568
logger.debug(logline)
569569
data.attributes = {}
570570

571-
# This adapts records with different search and connection strategy
572-
# (sync without pool), it should be tested with anonimous bind with
573-
# message_id.
574-
if isinstance(results, bool) and record:
575-
record = {
576-
"dn": record.entry_dn if hasattr(record, "entry_dn") else "",
577-
"attributes": (
578-
record.entry_attributes_as_dict
579-
if hasattr(record, "entry_attributes_as_dict")
580-
else {}
581-
),
582-
}
583-
584-
# Use a found record, if any, to populate attributes and input for
585-
# NameID
586-
if record:
587-
msg = {
588-
"message": "Using record with DN and attributes",
589-
"DN": record["dn"],
590-
"attributes": record["attributes"],
591-
}
592-
logline = lu.LOG_FMT.format(id=session_id, message=msg)
593-
logger.debug(logline)
571+
for record in responses:
572+
# This adapts records with different search and connection strategy
573+
# (sync without pool), it should be tested with anonimous bind with
574+
# message_id.
575+
if isinstance(results, bool) and record:
576+
record = {
577+
"dn": record.entry_dn if hasattr(record, "entry_dn") else "",
578+
"attributes": (
579+
record.entry_attributes_as_dict
580+
if hasattr(record, "entry_attributes_as_dict")
581+
else {}
582+
),
583+
}
584+
585+
# Use a found record, if any, to populate attributes and input for
586+
# NameID
587+
if record:
588+
msg = {
589+
"message": "Using record with DN and attributes",
590+
"DN": record["dn"],
591+
"attributes": record["attributes"],
592+
}
593+
logline = lu.LOG_FMT.format(id=session_id, message=msg)
594+
logger.debug(logline)
594595

595-
# Populate attributes as configured.
596-
new_attrs = self._populate_attributes(config, record)
597-
598-
overwrite = config["overwrite_existing_attributes"]
599-
for attr, values in new_attrs.items():
600-
if not overwrite:
601-
values = list(set(data.attributes.get(attr, []) + values))
602-
data.attributes[attr] = values
603-
604-
# Populate input for NameID if configured. SATOSA core does the
605-
# hashing of input to create a persistent NameID.
606-
user_ids = self._populate_input_for_name_id(config, record, data)
607-
if user_ids:
608-
data.subject_id = "".join(user_ids)
609-
msg = "NameID value is {}".format(data.subject_id)
610-
logger.debug(msg)
596+
# Populate attributes as configured.
597+
new_attrs = self._populate_attributes(config, record)
598+
599+
overwrite = config["overwrite_existing_attributes"]
600+
for attr, values in new_attrs.items():
601+
if not overwrite:
602+
values = list(map(str, set(data.attributes.get(attr, []) + values)))
603+
else:
604+
values = list(map(str, set(values)))
605+
data.attributes[attr] = values
606+
607+
# Populate input for NameID if configured. SATOSA core does the
608+
# hashing of input to create a persistent NameID.
609+
user_ids = self._populate_input_for_name_id(config, record, data)
610+
if user_ids:
611+
data.subject_id = "".join(user_ids)
612+
msg = "NameID value is {}".format(data.subject_id)
613+
logger.debug(msg)
611614

612-
# Add the record to the context so that later microservices
613-
# may use it if required.
614-
context.decorate(KEY_FOUND_LDAP_RECORD, record)
615-
msg = "Added record {} to context".format(record)
616-
logline = lu.LOG_FMT.format(id=session_id, message=msg)
617-
logger.debug(logline)
618-
else:
619-
msg = "No record found in LDAP so no attributes will be added"
620-
logline = lu.LOG_FMT.format(id=session_id, message=msg)
621-
logger.warning(logline)
622-
on_ldap_search_result_empty = config["on_ldap_search_result_empty"]
623-
if on_ldap_search_result_empty:
624-
# Redirect to the configured URL with
625-
# the entityIDs for the target SP and IdP used by the user
626-
# as query string parameters (URL encoded).
627-
encoded_sp_entity_id = urllib.parse.quote_plus(requester)
628-
encoded_idp_entity_id = urllib.parse.quote_plus(issuer)
629-
url = "{}?sp={}&idp={}".format(
630-
on_ldap_search_result_empty,
631-
encoded_sp_entity_id,
632-
encoded_idp_entity_id,
633-
)
634-
msg = "Redirecting to {}".format(url)
615+
# Add the record to the context so that later microservices
616+
# may use it if required.
617+
context.decorate(KEY_FOUND_LDAP_RECORD, record)
618+
msg = "Added record {} to context".format(record)
635619
logline = lu.LOG_FMT.format(id=session_id, message=msg)
636-
logger.info(logline)
637-
return Redirect(url)
620+
logger.debug(logline)
621+
else:
622+
msg = "No record found in LDAP so no attributes will be added"
623+
logline = lu.LOG_FMT.format(id=session_id, message=msg)
624+
logger.warning(logline)
625+
on_ldap_search_result_empty = config["on_ldap_search_result_empty"]
626+
if on_ldap_search_result_empty:
627+
# Redirect to the configured URL with
628+
# the entityIDs for the target SP and IdP used by the user
629+
# as query string parameters (URL encoded).
630+
encoded_sp_entity_id = urllib.parse.quote_plus(requester)
631+
encoded_idp_entity_id = urllib.parse.quote_plus(issuer)
632+
url = "{}?sp={}&idp={}".format(
633+
on_ldap_search_result_empty,
634+
encoded_sp_entity_id,
635+
encoded_idp_entity_id,
636+
)
637+
msg = "Redirecting to {}".format(url)
638+
logline = lu.LOG_FMT.format(id=session_id, message=msg)
639+
logger.info(logline)
640+
return Redirect(url)
638641

639642
msg = "Returning data.attributes {}".format(data.attributes)
640643
logline = lu.LOG_FMT.format(id=session_id, message=msg)

0 commit comments

Comments
 (0)