On 06/05/2012 11:43 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
Raise an error when trying to delete the last user from the 'admins'
group

The 'admin' group name seems like something that shouldn't be hardcoded,
but that's how it's done in the webui and some of our ACIs, and I don't
see another solution short of adding a new attribute.


https://fedorahosted.org/freeipa/ticket/2564


This looks ok, I think it should go further and prevent the last member
to be removed from the admins group too.

rob

This updated patch prevents that, plus removing the admins group itself.


--
PetrĀ³
From 631f197cfaeac908baeec9873dedc091cf09c3c1 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pvikt...@redhat.com>
Date: Wed, 23 May 2012 05:44:53 -0400
Subject: [PATCH] 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
---
 ipalib/errors.py                       |   32 +++++++++++++++++++
 ipalib/plugins/group.py                |   16 +++++++++-
 ipalib/plugins/user.py                 |    9 ++++--
 tests/test_xmlrpc/test_group_plugin.py |   55 ++++++++++++++++++++++++++++++++
 tests/test_xmlrpc/test_user_plugin.py  |   41 ++++++++++++++++++++++++
 5 files changed, 150 insertions(+), 3 deletions(-)

diff --git a/ipalib/errors.py b/ipalib/errors.py
index df4ab4167cb5eee1ab940518746bf5c4109a009f..407d9f7dbcf79c47193a3087fe6efbc50728c903 100644
--- a/ipalib/errors.py
+++ b/ipalib/errors.py
@@ -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
 
diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py
index 13208542ce72c3bd9d26ccff9c86bb1836bc39bf..65657363a463fb0ccb07133c9c84e17b15ffee42 100644
--- a/ipalib/plugins/group.py
+++ b/ipalib/plugins/group.py
@@ -72,6 +72,8 @@
    ipa group-show localadmins
 """)
 
+protected_group_name = u'admins'
+
 class group(LDAPObject):
     """
     Group object.
@@ -164,7 +166,9 @@ def pre_callback(self, ldap, dn, *keys, **options):
         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 @@ class group_add_member(LDAPAddMember):
 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)
 
 
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index b48e68022c88f5b5f777c8d10e0775d566fd9480..7e98bba4c48436588ff3baffad538a426b9f5edb 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -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)
 
diff --git a/tests/test_xmlrpc/test_group_plugin.py b/tests/test_xmlrpc/test_group_plugin.py
index c0abcb955616c685703aed271fedb90b4b8d5926..abee7bac7ef194a53ba1fb7ff4e6bbd834fbe3af 100644
--- a/tests/test_xmlrpc/test_group_plugin.py
+++ b/tests/test_xmlrpc/test_group_plugin.py
@@ -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,
+            ),
+        ),
+
     ]
diff --git a/tests/test_xmlrpc/test_user_plugin.py b/tests/test_xmlrpc/test_user_plugin.py
index 4b2be5c325ab8d5a73a62f9c979ca5f40f3498bb..355a4cbbbbd1a758885c50b8f2450444cff23fd6 100644
--- a/tests/test_xmlrpc/test_user_plugin.py
+++ b/tests/test_xmlrpc/test_user_plugin.py
@@ -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,
+            ),
+        ),
+
     ]
-- 
1.7.10.2

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to