From ffc2edf61efccbcbd4294fbc8a8613decea299a3 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Mon, 25 Jan 2021 17:41:02 +0200 Subject: [PATCH] baseldap: when adding external objects, differentiate between them and failures It was possible to add external members without any validation. Any object that was not found in IPA LDAP was considered an external object and a command such as sudorule could have added it to the list of values for externalUser attribute. With member validator support, real external members from trusted domains can be differentiated from the objects that were not found in IPA and in trusted domains. Use information from the ID Views plugin to treat external objects accordingly. Not found objects will be part of the error messaging instead. Fixes: https://pagure.io/freeipa/issue/3226 Signed-off-by: Alexander Bokovoy Reviewed-By: Christian Heimes Reviewed-By: Rob Crittenden Reviewed-By: Florence Blanc-Renaud --- ipaserver/plugins/baseldap.py | 62 ++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py index d711c1ee3..684ab8304 100644 --- a/ipaserver/plugins/baseldap.py +++ b/ipaserver/plugins/baseldap.py @@ -380,6 +380,60 @@ def add_external_pre_callback(membertype, ldap, dn, keys, options): return dn +EXTERNAL_OBJ_PREFIX = 'external ' + + +def pre_callback_process_external_objects(member_attr, object_desc, + ldap, dn, found, not_found, + *keys, **options): + """ + Takes the following arguments: + member_attr - member attribute to process external members for + object_desc - a tuple (type, prefix) to identify a type of an object + ('user', 'group', ...) to associate and a prefix to skip + when comparing with an external object. Prefix should be + None for objects that do not have prefixes. + found - the dictionary with all members that were found + not_found - the dictionary with all members which weren't found + keys - list of arguments to the command where this callback is used + options - list of options to the command where this callback is used. + + The callback performs validation of objects as external (not existing in + IPA LDAP) and then adds them to a list of not found objects with a mark + 'external ..' object if they were resolved as an object from a trusted + domain. + + Returns a DN object used for processing dn. + """ + + (o_type, o_prefix) = object_desc + + if o_type not in options: + return dn + + dn = add_external_pre_callback(o_type, ldap, dn, keys, options) + if 'trusted_objects' in options: + trusted_objects = options.pop('trusted_objects') + found_members = found.get(member_attr, {}) + found_objects = found_members.get(o_type, []) + filtered_objects = [] + for o_id in found_objects: + for obj in trusted_objects: + m = obj + if o_prefix is not None and obj.startswith(o_prefix): + m = obj[len(o_prefix):] + if o_id[0].value.lower() == m.lower(): + filtered_objects.append(o_id) + found[member_attr][o_type] = list( + set(found_objects) - set(filtered_objects)) + notfound_members = not_found.get(member_attr, {}) + notfound_objects = notfound_members.get(o_type, []) + notfound_objects.extend( + [(m, EXTERNAL_OBJ_PREFIX + o_type) for m in trusted_objects]) + notfound_members[o_type] = notfound_objects + + return dn + def add_external_post_callback(ldap, dn, entry_attrs, failed, completed, memberattr, membertype, externalattr, normalize=True): @@ -414,13 +468,19 @@ def add_external_post_callback(ldap, dn, entry_attrs, failed, completed, failed_entries = [] for entry in failed[memberattr][membertype]: + # entry is a tuple (name, error) membername = entry[0].lower() member_dn = api.Object[membertype].get_dn(membername) assert isinstance(member_dn, DN) if (membername not in lc_external_entries and member_dn not in members): - # Not an IPA entry, assume external + # Not an IPA entry, only add if it has been marked + # as an external entry during the pre-callback validation + if not entry[1].startswith(EXTERNAL_OBJ_PREFIX): + # Really a failure + failed_entries.append(membername) + continue if normalize: external_entries.append(membername) else: