sudorule-add-user: allow to reference users and groups from trusted domains directly

Allow specifying AD users and groups from trusted Active Directory
forests in `ipa sudorule-add/remove-user` family of commands.

SSSD uses single attribute 'externalUser' for IPA to pull 'external'
objects referenced in SUDO rules. This means both users and groups are
represented within the same attribute, with groups prefixed with '%',
as described in sudoers(5) man page.

Add member type validators to 'ipa sudorule-add/remove-user' family
commands and rely on member type validators from 'idviews' plugin to
resolve trusted objects.

Referencing fully qualified names for users and groups from trusted
Active Directory domains in 'externalUser' attribute of SUDO rules is
supported in SSSD 2.4 or later.

RN: IPA now supports adding users and groups from trusted Active
RN: Directory domains in SUDO rules without an intermediate non-POSIX
RN: group membership

Fixes: https://pagure.io/freeipa/issue/3226
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
This commit is contained in:
Alexander Bokovoy 2021-01-24 12:47:22 +02:00 committed by Rob Crittenden
parent 0ffdfc70f2
commit a37db297f0

View File

@ -24,13 +24,14 @@ from ipalib import api, errors
from ipalib import Str, StrEnum, Bool, Int from ipalib import Str, StrEnum, Bool, Int
from ipalib.plugable import Registry from ipalib.plugable import Registry
from .baseldap import (LDAPObject, LDAPCreate, LDAPDelete, from .baseldap import (LDAPObject, LDAPCreate, LDAPDelete,
LDAPUpdate, LDAPSearch, LDAPRetrieve, LDAPUpdate, LDAPSearch, LDAPRetrieve,
LDAPQuery, LDAPAddMember, LDAPRemoveMember, LDAPQuery, LDAPAddMember, LDAPRemoveMember,
add_external_pre_callback, add_external_pre_callback,
add_external_post_callback, pre_callback_process_external_objects,
remove_external_post_callback, add_external_post_callback,
output, entry_to_dict, pkey_to_value, remove_external_post_callback,
external_host_param) output, entry_to_dict, pkey_to_value,
external_host_param)
from .hbacrule import is_all from .hbacrule import is_all
from ipalib import _, ngettext from ipalib import _, ngettext
from ipalib.util import validate_hostmask from ipalib.util import validate_hostmask
@ -98,6 +99,11 @@ register = Registry()
topic = 'sudo' topic = 'sudo'
# used to process external object references in SUDO rules
USER_OBJ_SPEC = ('user', None)
GROUP_OBJ_SPEC = ('group', '%')
def deprecated(attribute): def deprecated(attribute):
raise errors.ValidationError( raise errors.ValidationError(
@ -592,17 +598,40 @@ class sudorule_add_user(LDAPAddMember):
raise errors.MutuallyExclusiveError( raise errors.MutuallyExclusiveError(
reason=_("users cannot be added when user category='all'")) reason=_("users cannot be added when user category='all'"))
return add_external_pre_callback('user', ldap, dn, keys, options) for o_desc in (USER_OBJ_SPEC, GROUP_OBJ_SPEC):
dn = pre_callback_process_external_objects(
'memberuser', o_desc,
ldap, dn, found, not_found, *keys, **options)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, def post_callback(self, ldap, completed, failed, dn, entry_attrs,
*keys, **options): *keys, **options):
assert isinstance(dn, DN) assert isinstance(dn, DN)
return add_external_post_callback(ldap, dn, entry_attrs,
failed=failed, completed_ex = {}
completed=completed, completed_ex['user'] = 0
memberattr='memberuser', completed_ex['group'] = 0
membertype='user',
externalattr='externaluser') # Since external_post_callback returns the total number of completed
# entries yet (that is, any external users it added plus the value of
# passed variable 'completed', we need to pass 0 as completed,
# so that the entries added by the framework are not counted twice
# (once in each call of add_external_post_callback)
for o_type in ('user', 'group'):
if o_type not in options:
continue
(completed_ex[o_type], dn) = \
add_external_post_callback(ldap, dn,
entry_attrs=entry_attrs,
failed=failed,
completed=0,
memberattr='memberuser',
membertype=o_type,
externalattr='externaluser',
)
return (completed + sum(completed_ex.values()), dn)
@register() @register()
@ -612,15 +641,41 @@ class sudorule_remove_user(LDAPRemoveMember):
member_attributes = ['memberuser'] member_attributes = ['memberuser']
member_count_out = ('%i object removed.', '%i objects removed.') member_count_out = ('%i object removed.', '%i objects removed.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
assert isinstance(dn, DN)
for o_desc in (USER_OBJ_SPEC, GROUP_OBJ_SPEC):
dn = pre_callback_process_external_objects(
'memberuser', o_desc,
ldap, dn, found, not_found, *keys, **options)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, def post_callback(self, ldap, completed, failed, dn, entry_attrs,
*keys, **options): *keys, **options):
assert isinstance(dn, DN) assert isinstance(dn, DN)
return remove_external_post_callback(ldap, dn, entry_attrs,
failed=failed, # Since external_post_callback returns the total number of completed
completed=completed, # entries yet (that is, any external users it removed plus the value of
memberattr='memberuser', # passed variable 'completed', we need to pass 0 as completed,
membertype='user', # so that the entries removed by the framework are not counted twice
externalattr='externaluser') # (once in each call of remove_external_post_callback)
(completed_ex_users, dn) = remove_external_post_callback(
ldap, dn, entry_attrs,
failed=failed,
completed=0,
memberattr='memberuser',
membertype='user',
externalattr='externaluser')
(completed_ex_groups, dn) = remove_external_post_callback(
ldap, dn, entry_attrs,
failed=failed,
completed=0,
memberattr='memberuser',
membertype='group',
externalattr='externaluser')
return (completed + completed_ex_users + completed_ex_groups, dn)
@register() @register()