diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py index 6f5e349..febc22f 100644 --- a/ipaserver/plugins/user.py +++ b/ipaserver/plugins/user.py @@ -144,8 +144,7 @@ PROTECTED_USERS = ('admin',) def check_protected_member(user, protected_group_name=u'admins'): ''' Ensure admin and the last enabled member of a protected group cannot - be deleted or disabled by raising ProtectedEntryError or - LastMemberError as appropriate. + be deleted. ''' if user in PROTECTED_USERS: @@ -155,6 +154,12 @@ def check_protected_member(user, protected_group_name=u'admins'): reason=_("privileged user"), ) + +def check_last_member(user, protected_group_name=u'admins'): + ''' + Ensure the last enabled member of a protected group cannot + be disabled. + ''' # Get all users in the protected group result = api.Command.user_find(in_group=protected_group_name) @@ -796,6 +801,7 @@ class user_del(baseuser_del): # If the target entry is a Delete entry, skip the orphaning/removal # of OTP tokens. check_protected_member(keys[-1]) + check_last_member(keys[-1]) preserve = options.get('preserve', False) @@ -1128,7 +1134,7 @@ class user_disable(LDAPQuery): def execute(self, *keys, **options): ldap = self.obj.backend - check_protected_member(keys[-1]) + check_last_member(keys[-1]) dn, _oc = self.obj.get_either_dn(*keys, **options) ldap.deactivate_entry(dn) diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py index c0cb4d0..c2a55b8 100644 --- a/ipatests/test_integration/test_commands.py +++ b/ipatests/test_integration/test_commands.py @@ -1530,6 +1530,30 @@ class TestIPACommand(IntegrationTest): assert 'Discovered server %s' % self.master.hostname in result + def test_delete_last_enabled_admin(self): + """ + The admin user may be disabled. Don't allow all other + members of admins to be removed if the admin user is + disabled which would leave the install with no + usable admins users + """ + user = 'adminuser2' + passwd = 'Secret123' + tasks.create_active_user(self.master, user, passwd) + tasks.kinit_admin(self.master) + self.master.run_command(['ipa', 'group-add-member', 'admins', + '--users', user]) + tasks.kinit_user(self.master, user, passwd) + self.master.run_command(['ipa', 'user-disable', 'admin']) + result = self.master.run_command( + ['ipa', 'user-del', user], + raiseonerr=False + ) + self.master.run_command(['ipa', 'user-enable', 'admin']) + tasks.kdestroy_all(self.master) + assert result.returncode == 1 + assert 'cannot be deleted or disabled' in result.stderr_text + class TestIPACommandWithoutReplica(IntegrationTest): """ diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py index 3c58845..68c6c48 100644 --- a/ipatests/test_xmlrpc/test_user_plugin.py +++ b/ipatests/test_xmlrpc/test_user_plugin.py @@ -1045,8 +1045,8 @@ class TestAdmins(XMLRPC_test): tracker = Tracker() command = tracker.make_command('user_disable', admin1) - with raises_exact(errors.ProtectedEntryError(label=u'user', - key=admin1, reason='privileged user')): + with raises_exact(errors.LastMemberError(label=u'group', + key=admin1, container=admin_group)): command() def test_create_admin2(self, admin2): @@ -1064,8 +1064,8 @@ class TestAdmins(XMLRPC_test): admin2.disable() tracker = Tracker() - with raises_exact(errors.ProtectedEntryError(label=u'user', - key=admin1, reason='privileged user')): + with raises_exact(errors.LastMemberError(label=u'group', + key=admin1, container=admin_group)): tracker.run_command('user_disable', admin1) admin2.delete() diff --git a/ipatests/test_webui/test_user.py b/ipatests/test_webui/test_user.py index a8a92d0..9083e50 100644 --- a/ipatests/test_webui/test_user.py +++ b/ipatests/test_webui/test_user.py @@ -50,6 +50,8 @@ INV_FIRSTNAME = ("invalid 'first': Leading and trailing spaces are " FIELD_REQ = 'Required field' ERR_INCLUDE = 'may only include letters, numbers, _, -, . and $' ERR_MISMATCH = 'Passwords must match' +ERR_ADMIN_DISABLE = ('admin cannot be deleted or disabled because ' + 'it is the last member of group admins') ERR_ADMIN_DEL = ('user admin cannot be deleted/modified: privileged user') USR_EXIST = 'user with name "{}" already exists' ENTRY_EXIST = 'This entry already exists' @@ -546,7 +548,7 @@ class test_user(user_tasks): self.select_record('admin') self.facet_button_click('disable') self.dialog_button_click('ok') - self.assert_last_error_dialog(ERR_ADMIN_DEL, details=True) + self.assert_last_error_dialog(ERR_ADMIN_DISABLE, details=True) self.dialog_button_click('ok') self.assert_record('admin')