From 973e0c04e460c99f601b0292ff9c64dd0882432e Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Wed, 3 Jun 2020 12:52:55 +0300 Subject: [PATCH] idviews: handle unqualified ID override lookups from Web UI First part of the required changes to merge a plugin to manage IPA as a trusted Active Directory user. It is not possible to omit ID view in IPA API but a client might specify empty ID view. Up right now the empty view was considered an error. This prevented Web UI from resolving ID overrides in a group member adder dialog. Default to 'Default Trust View' if the ID view is None or empty string (''). Do this only for user ID overrides, as we do not support adding group ID overrides as group members in a plugin to manage IPA as a trusted Active Directory user[1]. Being a group member means an object in LDAP must have an object class that allows 'memberOf' attribute because 389-ds 'memberof' plugin will attempt to link back to the object from the group. Allow use of 'nsMemberOf' object class in ID overrides. Fixes: https://pagure.io/freeipa/issue/7255 [1] https://github.com/abbra/freeipa-adusers-admins Signed-off-by: Alexander Bokovoy Reviewed-By: Rob Crittenden --- ipaserver/plugins/idviews.py | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/ipaserver/plugins/idviews.py b/ipaserver/plugins/idviews.py index 096e8b49d..27b418f6e 100644 --- a/ipaserver/plugins/idviews.py +++ b/ipaserver/plugins/idviews.py @@ -91,6 +91,28 @@ ANCHOR_REGEX = re.compile( ) +def normalize_idview_name(value): + if value in (None, '',): + return DEFAULT_TRUST_VIEW_NAME + return value + + +def handle_idoverride_memberof(self, ldap, dn, found, not_found, + *keys, **options): + if ('idoverrideuser' in options) and ('member' in found): + for id in found['member'].get('idoverrideuser', []): + try: + e = ldap.get_entry(id, ['*']) + if not self.obj.has_objectclass(e['objectclass'], + 'nsmemberof'): + add_missing_object_class(ldap, 'nsmemberof', + id, entry_attrs=e, update=True) + except errors.NotFound: + # We are not adding an object here, only modifying existing + continue + return dn + + @register() class idview(LDAPObject): """ @@ -113,6 +135,7 @@ class idview(LDAPObject): cli_name='name', label=_('ID View Name'), primary_key=True, + normalizer=normalize_idview_name, ), Str('description?', cli_name='desc', @@ -733,8 +756,12 @@ class baseidoverride(LDAPObject): self.backend, self.override_object, keys[-1], - fallback_to_ldap=options['fallback_to_ldap'] + fallback_to_ldap=options.get('fallback_to_ldap', False) ) + if all([len(keys[:-1]) == 0, + self.override_object == 'user', + anchor.startswith(SID_ANCHOR_PREFIX)]): + keys = (DEFAULT_TRUST_VIEW_NAME, ) + keys keys = keys[:-1] + (anchor, ) return super(baseidoverride, self).get_dn(*keys, **options) @@ -810,6 +837,12 @@ class baseidoverride(LDAPObject): rules=ldap.MATCH_ALL ) + def get_primary_key_from_dn(self, dn): + return resolve_anchor_to_object_name(self.backend, + self.override_object, + dn[0].value) + + class baseidoverride_add(LDAPCreate): __doc__ = _('Add a new ID override.') @@ -918,16 +951,18 @@ class idoverrideuser(baseidoverride): 'ipapermdefaultattr': { 'objectClass', 'ipaAnchorUUID', 'uidNumber', 'description', 'homeDirectory', 'uid', 'ipaOriginalUid', 'loginShell', 'gecos', - 'gidNumber', 'ipaSshPubkey', 'usercertificate' + 'gidNumber', 'ipaSshPubkey', 'usercertificate', 'memberof' }, }, } object_class = baseidoverride.object_class + ['ipaUserOverride'] - possible_objectclasses = ['ipasshuser', 'ipaSshGroupOfPubKeys'] + possible_objectclasses = ['ipasshuser', 'ipaSshGroupOfPubKeys', + 'nsmemberof'] default_attributes = baseidoverride.default_attributes + [ 'homeDirectory', 'uidNumber', 'uid', 'ipaOriginalUid', 'loginShell', 'ipaSshPubkey', 'gidNumber', 'gecos', 'usercertificate;binary', + 'memberofindirect', 'memberof' ] search_display_attributes = baseidoverride.default_attributes + [ @@ -935,6 +970,11 @@ class idoverrideuser(baseidoverride): 'ipaSshPubkey', 'gidNumber', 'gecos', ] + attribute_members = { + 'memberof': ['group', 'role'], + 'memberofindirect': ['group', 'role'], + } + takes_params = baseidoverride.takes_params + ( Str('uid?', pattern=PATTERN_GROUPUSER_NAME,