Prevent deletion of the last admin

Raise an error when trying to delete the last user in the
'admins' group, or remove the last member from the group,
or delete the group itself.

https://fedorahosted.org/freeipa/ticket/2564
This commit is contained in:
Petr Viktorin
2012-05-23 05:44:53 -04:00
committed by Rob Crittenden
parent cf72738b21
commit f8e7b516d9
5 changed files with 150 additions and 3 deletions

View File

@@ -1575,6 +1575,38 @@ class DependentEntry(ExecutionError):
format = _('%(key)s cannot be deleted because %(label)s %(dependent)s requires it')
class LastMemberError(ExecutionError):
"""
**4308** Raised when an entry being deleted is last member of a protected group
For example:
>>> raise LastMemberError(key=u'admin', label=u'group', container=u'admins')
Traceback (most recent call last):
...
LastMemberError: admin cannot be deleted because it is the last member of group admins
"""
errno = 4308
format = _('%(key)s cannot be deleted because it is the last member of %(label)s %(container)s')
class ProtectedEntryError(ExecutionError):
"""
**4309** Raised when an entry being deleted is protected
For example:
>>> raise ProtectedEntryError(label=u'group', key=u'admins', reason=u'privileged group')
Traceback (most recent call last):
...
ProtectedEntryError: group admins cannot be deleted: privileged group
"""
errno = 4309
format = _('%(label)s %(key)s cannot be deleted: %(reason)s')
##############################################################################
# 5000 - 5999: Generic errors

View File

@@ -72,6 +72,8 @@ EXAMPLES:
ipa group-show localadmins
""")
protected_group_name = u'admins'
class group(LDAPObject):
"""
Group object.
@@ -164,7 +166,9 @@ class group_del(LDAPDelete):
group_attrs = self.obj.methods.show(
self.obj.get_primary_key_from_dn(dn), all=True
)['result']
if keys[0] == protected_group_name:
raise errors.ProtectedEntryError(label=_(u'group'), key=keys[0],
reason=_(u'privileged group'))
if 'mepmanagedby' in group_attrs:
raise errors.ManagedGroupError()
return dn
@@ -276,6 +280,16 @@ api.register(group_add_member)
class group_remove_member(LDAPRemoveMember):
__doc__ = _('Remove members from a group.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
if keys[0] == protected_group_name:
result = api.Command.group_show(protected_group_name)
users_left = set(result['result'].get('member_user', []))
users_deleted = set(options['user'])
if users_left.issubset(users_deleted):
raise errors.LastMemberError(key=sorted(users_deleted)[0],
label=_(u'group'), container=protected_group_name)
return dn
api.register(group_remove_member)

View File

@@ -544,8 +544,13 @@ class user_del(LDAPDelete):
msg_summary = _('Deleted user "%(value)s"')
def post_callback(self, ldap, dn, *keys, **options):
return True
def pre_callback(self, ldap, dn, *keys, **options):
protected_group_name = u'admins'
result = api.Command.group_show(protected_group_name)
if result['result'].get('member_user', []) == [keys[-1]]:
raise errors.LastMemberError(key=keys[-1], label=_(u'group'),
container=protected_group_name)
return dn
api.register(user_del)

View File

@@ -797,4 +797,59 @@ class test_group(Declarative):
expected=errors.NotFound(reason=u'%s: group not found' % user1),
),
dict(
desc='Try to remove the admin user from the admins group',
command=('group_remove_member', [u'admins'], dict(user=[u'admin'])),
expected=errors.LastMemberError(key=u'admin', label=u'group',
container='admins'),
),
dict(
desc='Add %r to the admins group' % user1,
command=('group_add_member', [u'admins'], dict(user=user1)),
expected=dict(
completed=1,
failed=dict(
member=dict(
group=tuple(),
user=tuple(),
),
),
result={
'dn': lambda x: DN(x) == \
DN(('cn', 'admins'), ('cn', 'groups'),
('cn', 'accounts'), api.env.basedn),
'member_user': [u'admin', user1],
'gidnumber': [fuzzy_digits],
'cn': [u'admins'],
'description': [u'Account administrators group'],
},
),
),
dict(
desc='Try to remove admin and %r from the admins group' % user1,
command=('group_remove_member', [u'admins'],
dict(user=[u'admin', user1])),
expected=errors.LastMemberError(key=u'admin', label=u'group',
container='admins'),
),
dict(
desc='Try to delete the admins group',
command=('group_del', [u'admins'], {}),
expected=errors.ProtectedEntryError(label=u'group',
key='admins', reason='privileged group'),
),
dict(
desc='Delete %r' % user1,
command=('user_del', [user1], {}),
expected=dict(
result=dict(failed=u''),
summary=u'Deleted user "%s"' % user1,
value=user1,
),
),
]

View File

@@ -1330,4 +1330,45 @@ class test_user(Declarative):
),
expected=lambda x: True,
),
dict(
desc='Try to remove the admin user',
command=('user_del', [u'admin'], {}),
expected=errors.LastMemberError(key=u'admin', label=u'group',
container='admins'),
),
dict(
desc='Add %r to the admins group' % user2,
command=('group_add_member', [u'admins'], dict(user=user2)),
expected=dict(
completed=1,
failed=dict(
member=dict(
group=tuple(),
user=tuple(),
),
),
result={
'dn': lambda x: DN(x) == \
DN(('cn', 'admins'), ('cn', 'groups'),
('cn', 'accounts'), api.env.basedn),
'member_user': [u'admin', user2],
'gidnumber': [fuzzy_digits],
'cn': [u'admins'],
'description': [u'Account administrators group'],
},
),
),
dict(
desc='Delete %r' % user2,
command=('user_del', [user2], {}),
expected=dict(
result=dict(failed=u''),
summary=u'Deleted user "%s"' % user2,
value=user2,
),
),
]