Repository navigation
Description
Description
When using authenticator maps with attribute-based triggers, attributes that are lists are currently evaluated element by element and combined using the global join_condition. This produces unexpected results when the join condition is and.
Steps to Reproduce
Raw attribute data:
2025-08-26 14:17:15,287 DEBUG 21d8a01d-1fe8-4527-bb44-2e75329fd9b4 ansible_base.authentication.utils.claims aapadmA100's attrs: {
'objectclass': ['top', 'person', 'organizationalPerson', 'inetOrgPerson'],
'cn': ['aapadmA100'],
'sn': ['A100'],
'uid': ['aapadmA100'],
'userpassword': ['{SSHA}0idZBuDtewExj/0pcgLisBDHUr5Ih/tg'],
'employeetype': ['developer', 'executor'],
'auth_time': '2025-08-26T14:17:15.284587Z'
}
-
Configure a user with attributes:
cn: ['aapadmA100'] employeetype: ['developer', 'executor']
-
Configure an authenticator map with trigger condition:
if 'aapadm' in cn and 'developer' in employeetype -
Authenticate the user.
Expected Behavior
The condition should evaluate to true, because:
'aapadm' in ['aapadmA100'] → True
'developer' in ['developer', 'executor'] → True
Therefore, the overall expression should succeed.
Actual Behavior
In _process_user_value, list attributes are expanded and each element is evaluated individually.
Because has_access_with_join(..., join_condition) is applied inside the loop, the global join_condition (and/or) is incorrectly applied across list elements, not just across attributes.
Relevant code excerpt:
evaluate_fn = operators[operator]
for a_user_value in user_value:
user_str = f"{a_user_value}".casefold() if _is_case_insensitivity_enabled() else f"{a_user_value}"
result = evaluate_fn(user_str, trigger_value)
# <-- Problem: join_condition applied here for each element -->
has_access = has_access_with_join(has_access, result, join_condition)
header = f"Attr [{attribute}] value [{user_str}]"
message = _get_operator_messages(operator, result)
_prefixed_debug(map_id, tracking_id, f"{header} {message} [{trigger_value}], {_result_suffix(result)}")
return has_accessEvaluation steps:
-
For
cn:_evaluate_contains("aapadmA100", "aapadm") # → "aapadm" in "aapadmA100" → True has_access = True -
For
employeetype:_evaluate_contains("developer", "developer") # → "developer" in "developer" → True has_access = has_access_with_join(True, True, "and") → True _evaluate_contains("executor", "developer") # → "developer" in "executor" → False has_access = has_access_with_join(True, False, "and") → False
Final result: False, so the map is skipped/denied, even though the user clearly has "developer" in their employeetype.
Impact
Users with multi-valued attributes cannot be matched correctly when join_condition = 'and'.
This breaks common scenarios such as matching on employeetype or other attributes that often contain multiple values.
Suggestion
List attributes should be evaluated with any(...) semantics: if any element of the list matches, the condition for that attribute should be considered true.
Only after reducing each attribute’s evaluation to a single boolean should the global join_condition (and / or) be applied across attributes.
Activity