mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Allow decoupling of user-private groups.
To do this we need to break the link manually on both sides, the user and the group. We also have to verify in advance that the user performing this is allowed to do both. Otherwise the user could be decoupled but not the group leaving it in a quasi broken state that only ldapmodify could fix. ticket 75
This commit is contained in:
parent
719592a209
commit
5b894d1fb7
@ -154,10 +154,10 @@ add:aci: '(targetattr = "givenName || sn || cn || displayName || title || initia
|
||||
|| loginShell || gecos || homePhone || mobile || pager || facsimileTelephoneN
|
||||
umber || telephoneNumber || street || roomNumber || l || st || postalCode ||
|
||||
manager || secretary || description || carLicense || labeledURI || inetUserHT
|
||||
TPURL || seeAlso || employeeType || businessCategory || ou")(target = "ldap:/
|
||||
//uid=*,cn=users,cn=accounts,$SUFFIX")(version 3.0;acl "Modify User
|
||||
s";allow (write) groupdn = "ldap:///cn=modifyusers,cn=taskgroups,cn=accounts,
|
||||
$SUFFIX";)'
|
||||
TPURL || seeAlso || employeeType || businessCategory || ou || mepManagedEntry
|
||||
|| objectclass")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")
|
||||
(version 3.0;acl "Modify Users";allow (write) groupdn =
|
||||
"ldap:///cn=modifyusers,cn=taskgroups,cn=accounts,$SUFFIX";)'
|
||||
|
||||
# Add the taskgroups referenced by the ACIs for group administration
|
||||
|
||||
@ -204,10 +204,10 @@ add:aci: '(target = "ldap:///cn=*,cn=groups,cn=accounts,$SUFFIX")(version
|
||||
askgroups,cn=accounts,$SUFFIX";)'
|
||||
# we need objectclass and gidnumber in modify so a non-posix group can be
|
||||
# promoted
|
||||
add:aci: '(targetattr = "cn || description || gidnumber || objectclass")(target
|
||||
= "ldap:///cn=*,cn=groups,cn=accounts,$SUFFIX")(version 3.0;acl "Modify Group
|
||||
s";allow (write) groupdn = "ldap:///cn=modifygroups,cn=taskgroups,cn=accounts,
|
||||
$SUFFIX";)'
|
||||
add:aci: '(targetattr = "cn || description || gidnumber || objectclass ||
|
||||
mepManagedBy")(target = "ldap:///cn=*,cn=groups,cn=accounts,$SUFFIX")
|
||||
(version 3.0;acl "Modify Groups";allow (write) groupdn =
|
||||
"ldap:///cn=modifygroups,cn=taskgroups,cn=accounts,$SUFFIX";)'
|
||||
|
||||
# Add the taskgroups referenced by the ACIs for host administration
|
||||
|
||||
|
@ -1070,6 +1070,21 @@ class DNSNotARecordError(ExecutionError):
|
||||
errno = 4019
|
||||
format = _('Host does not have corresponding DNS A record')
|
||||
|
||||
class ManagedGroupError(ExecutionError):
|
||||
"""
|
||||
**4020** Raised when a managed group is deleted
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise ManagedGroupError()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ManagedGroupError: Deleting a managed group is not allowed. It must be detached first.
|
||||
"""
|
||||
|
||||
errno = 4020
|
||||
format = _('Deleting a managed group is not allowed. It must be detached first.')
|
||||
|
||||
class BuiltinError(ExecutionError):
|
||||
"""
|
||||
**4100** Base class for builtin execution errors (*4100 - 4199*).
|
||||
|
@ -158,6 +158,9 @@ class group_del(LDAPDelete):
|
||||
def_primary_group_dn = group_dn = self.obj.get_dn(def_primary_group)
|
||||
if dn == def_primary_group_dn:
|
||||
raise errors.DefaultGroup()
|
||||
(group_dn, group_attrs) = ldap.get_entry(dn)
|
||||
if 'mepmanagedby' in group_attrs:
|
||||
raise errors.ManagedGroupError()
|
||||
return dn
|
||||
|
||||
def post_callback(self, ldap, dn, *keys, **options):
|
||||
@ -235,3 +238,59 @@ class group_remove_member(LDAPRemoveMember):
|
||||
"""
|
||||
|
||||
api.register(group_remove_member)
|
||||
|
||||
|
||||
class group_detach(LDAPRemoveMember):
|
||||
"""
|
||||
Detach a managed group from a user
|
||||
"""
|
||||
has_output = output.standard_value
|
||||
msg_summary = _('Detached group "%(value)s" from user "%(value)s"')
|
||||
|
||||
def execute(self, *keys, **options):
|
||||
"""
|
||||
This requires updating both the user and the group. We first need to
|
||||
verify that both the user and group can be updated, then we go
|
||||
about our work. We don't want a situation where only the user or
|
||||
group can be modified and we're left in a bad state.
|
||||
"""
|
||||
ldap = self.obj.backend
|
||||
|
||||
group_dn = self.obj.get_dn(*keys, **options)
|
||||
user_dn = self.api.Object['user'].get_dn(*keys)
|
||||
|
||||
if (not ldap.can_write(user_dn, "objectclass") or
|
||||
not ldap.can_write(user_dn, "mepManagedEntry")):
|
||||
raise errors.ACIError(info=_('not allowed to modify user entries'))
|
||||
|
||||
if (not ldap.can_write(group_dn, "objectclass") or
|
||||
not ldap.can_write(group_dn, "mepManagedBy")):
|
||||
raise errors.ACIError(info=_('not allowed to modify group entries'))
|
||||
|
||||
(user_dn, user_attrs) = ldap.get_entry(user_dn)
|
||||
objectclasses = user_attrs['objectclass']
|
||||
try:
|
||||
i = objectclasses.index('mepOriginEntry')
|
||||
except ValueError:
|
||||
raise NotFound(reason=_('Not a managed group'))
|
||||
del objectclasses[i]
|
||||
update_attrs = {'objectclass': objectclasses, 'mepManagedEntry': None}
|
||||
ldap.update_entry(user_dn, update_attrs)
|
||||
|
||||
(group_dn, group_attrs) = ldap.get_entry(group_dn)
|
||||
objectclasses = group_attrs['objectclass']
|
||||
try:
|
||||
i = objectclasses.index('mepManagedEntry')
|
||||
except ValueError:
|
||||
# this should never happen
|
||||
raise NotFound(reason=_('Not a managed group'))
|
||||
del objectclasses[i]
|
||||
update_attrs = {'objectclass': objectclasses, 'mepManagedBy': None}
|
||||
ldap.update_entry(group_dn, update_attrs)
|
||||
|
||||
return dict(
|
||||
result=True,
|
||||
value=keys[0],
|
||||
)
|
||||
|
||||
api.register(group_detach)
|
||||
|
@ -27,6 +27,7 @@ from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid
|
||||
|
||||
group1 = u'testgroup1'
|
||||
group2 = u'testgroup2'
|
||||
user1 = u'tuser1'
|
||||
|
||||
invalidgroup1=u'+tgroup1'
|
||||
invalidgroup2=u'tgroup1234567890123456789012345678901234567890'
|
||||
@ -36,6 +37,7 @@ class test_group(Declarative):
|
||||
cleanup_commands = [
|
||||
('group_del', [group1], {}),
|
||||
('group_del', [group2], {}),
|
||||
('user_del', [user1], {}),
|
||||
]
|
||||
|
||||
tests = [
|
||||
@ -527,5 +529,81 @@ class test_group(Declarative):
|
||||
expected=errors.ValidationError(name='cn', error='can be at most 33 characters'),
|
||||
),
|
||||
|
||||
##### managed entry tests
|
||||
dict(
|
||||
desc='Create %r' % user1,
|
||||
command=(
|
||||
'user_add', [], dict(givenname=u'Test', sn=u'User1')
|
||||
),
|
||||
expected=dict(
|
||||
value=user1,
|
||||
summary=u'Added user "%s"' % user1,
|
||||
result=dict(
|
||||
gecos=[user1],
|
||||
givenname=[u'Test'],
|
||||
homedirectory=[u'/home/%s' % user1],
|
||||
krbprincipalname=[u'%s@%s' % (user1, api.env.realm)],
|
||||
loginshell=[u'/bin/sh'],
|
||||
objectclass=objectclasses.user,
|
||||
sn=[u'User1'],
|
||||
uid=[user1],
|
||||
uidnumber=[fuzzy_digits],
|
||||
ipauniqueid=[fuzzy_uuid],
|
||||
dn=u'uid=%s,cn=users,cn=accounts,%s' % (user1, api.env.basedn),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Verify the managed group %r was created' % user1,
|
||||
command=('group_show', [user1], {}),
|
||||
expected=dict(
|
||||
value=user1,
|
||||
summary=None,
|
||||
result=dict(
|
||||
cn=[user1],
|
||||
description=[u'User private group for %s' % user1],
|
||||
gidnumber=[fuzzy_digits],
|
||||
dn=u'cn=%s,cn=groups,cn=accounts,%s' % (user1, api.env.basedn),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Try to delete a managed group %r' % user1,
|
||||
command=('group_del', [user1], {}),
|
||||
expected=errors.ManagedGroupError(),
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Detach managed group %r' % user1,
|
||||
command=('group_detach', [user1], {}),
|
||||
expected=dict(
|
||||
result=True,
|
||||
value=user1,
|
||||
summary=u'Detached group "%s" from user "%s"' % (user1, user1),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Now delete the unmanaged group %r' % user1,
|
||||
command=('group_del', [user1], {}),
|
||||
expected=dict(
|
||||
result=True,
|
||||
value=user1,
|
||||
summary=u'Deleted group "%s"' % user1,
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
dict(
|
||||
desc='Verify that %r is really gone' % user1,
|
||||
command=('group_show', [user1], {}),
|
||||
expected=errors.NotFound(reason='no such entry'),
|
||||
),
|
||||
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user