|
|
590d18 |
From 8208a3b4a642b19ebf112d29f4b3f4feae1a6011 Mon Sep 17 00:00:00 2001
|
|
|
590d18 |
From: Martin Babinsky <mbabinsk@redhat.com>
|
|
|
590d18 |
Date: Wed, 19 Aug 2015 14:43:14 +0200
|
|
|
590d18 |
Subject: [PATCH] improve the usability of `ipa user-del --preserve` command
|
|
|
590d18 |
|
|
|
590d18 |
`ipa user-del` with `--preserve` option will now process multiple entries and
|
|
|
590d18 |
handle `--continue` option in a manner analogous to `ipa user-del` in normal
|
|
|
590d18 |
mode.
|
|
|
590d18 |
|
|
|
590d18 |
In addition, it is now no longer possible to permanently delete a user by
|
|
|
590d18 |
accidentally running `ipa user-del --preserve` twice.
|
|
|
590d18 |
|
|
|
590d18 |
https://fedorahosted.org/freeipa/ticket/5234
|
|
|
590d18 |
https://fedorahosted.org/freeipa/ticket/5236
|
|
|
590d18 |
|
|
|
590d18 |
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
|
|
|
590d18 |
Reviewed-By: Martin Basti <mbasti@redhat.com>
|
|
|
590d18 |
---
|
|
|
590d18 |
ipalib/plugins/user.py | 123 ++++++++++++++++++++++++++-----------------------
|
|
|
590d18 |
1 file changed, 66 insertions(+), 57 deletions(-)
|
|
|
590d18 |
|
|
|
590d18 |
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
|
|
|
590d18 |
index d7cf9666d43b69b0cab91cfa0c98a8373f095ab5..cb47cbb4869cb978f87603817033580647cc2d17 100644
|
|
|
590d18 |
--- a/ipalib/plugins/user.py
|
|
|
590d18 |
+++ b/ipalib/plugins/user.py
|
|
|
590d18 |
@@ -593,6 +593,60 @@ class user_del(baseuser_del):
|
|
|
590d18 |
),
|
|
|
590d18 |
)
|
|
|
590d18 |
|
|
|
590d18 |
+ def _preserve_user(self, pkey, delete_container, **options):
|
|
|
590d18 |
+ assert isinstance(delete_container, DN)
|
|
|
590d18 |
+
|
|
|
590d18 |
+ dn = self.obj.get_either_dn(pkey, **options)
|
|
|
590d18 |
+ delete_dn = DN(dn[0], delete_container)
|
|
|
590d18 |
+ ldap = self.obj.backend
|
|
|
590d18 |
+ self.log.debug("preserve move %s -> %s" % (dn, delete_dn))
|
|
|
590d18 |
+
|
|
|
590d18 |
+ if dn.endswith(delete_container):
|
|
|
590d18 |
+ raise errors.ExecutionError(
|
|
|
590d18 |
+ _('%s: user is already preserved' % pkey)
|
|
|
590d18 |
+ )
|
|
|
590d18 |
+ # Check that this value is a Active user
|
|
|
590d18 |
+ try:
|
|
|
590d18 |
+ original_entry_attrs = self._exc_wrapper(
|
|
|
590d18 |
+ pkey, options, ldap.get_entry)(dn, ['dn'])
|
|
|
590d18 |
+ except errors.NotFound:
|
|
|
590d18 |
+ self.obj.handle_not_found(pkey)
|
|
|
590d18 |
+
|
|
|
590d18 |
+ # start to move the entry to Delete container
|
|
|
590d18 |
+ self._exc_wrapper(pkey, options, ldap.move_entry)(dn, delete_dn,
|
|
|
590d18 |
+ del_old=True)
|
|
|
590d18 |
+
|
|
|
590d18 |
+ # Then clear the credential attributes
|
|
|
590d18 |
+ attrs_to_clear = ['krbPrincipalKey', 'krbLastPwdChange',
|
|
|
590d18 |
+ 'krbPasswordExpiration', 'userPassword']
|
|
|
590d18 |
+
|
|
|
590d18 |
+ entry_attrs = self._exc_wrapper(pkey, options, ldap.get_entry)(
|
|
|
590d18 |
+ delete_dn, attrs_to_clear)
|
|
|
590d18 |
+
|
|
|
590d18 |
+ clearedCredential = False
|
|
|
590d18 |
+ for attr in attrs_to_clear:
|
|
|
590d18 |
+ if attr.lower() in entry_attrs:
|
|
|
590d18 |
+ del entry_attrs[attr]
|
|
|
590d18 |
+ clearedCredential = True
|
|
|
590d18 |
+ if clearedCredential:
|
|
|
590d18 |
+ self._exc_wrapper(pkey, options, ldap.update_entry)(entry_attrs)
|
|
|
590d18 |
+
|
|
|
590d18 |
+ # Then restore some original entry attributes
|
|
|
590d18 |
+ attrs_to_restore = ['secretary', 'managedby', 'manager', 'ipauniqueid',
|
|
|
590d18 |
+ 'uidnumber', 'gidnumber', 'passwordHistory']
|
|
|
590d18 |
+
|
|
|
590d18 |
+ entry_attrs = self._exc_wrapper(
|
|
|
590d18 |
+ pkey, options, ldap.get_entry)(delete_dn, attrs_to_restore)
|
|
|
590d18 |
+
|
|
|
590d18 |
+ restoreAttr = False
|
|
|
590d18 |
+ for attr in attrs_to_restore:
|
|
|
590d18 |
+ if ((attr.lower() in original_entry_attrs) and
|
|
|
590d18 |
+ not (attr.lower() in entry_attrs)):
|
|
|
590d18 |
+ restoreAttr = True
|
|
|
590d18 |
+ entry_attrs[attr.lower()] = original_entry_attrs[attr.lower()]
|
|
|
590d18 |
+ if restoreAttr:
|
|
|
590d18 |
+ self._exc_wrapper(pkey, options, ldap.update_entry)(entry_attrs)
|
|
|
590d18 |
+
|
|
|
590d18 |
def forward(self, *keys, **options):
|
|
|
590d18 |
if self.api.env.context == 'cli':
|
|
|
590d18 |
if options['no_preserve'] and options['preserve']:
|
|
|
590d18 |
@@ -633,68 +687,23 @@ class user_del(baseuser_del):
|
|
|
590d18 |
|
|
|
590d18 |
def execute(self, *keys, **options):
|
|
|
590d18 |
|
|
|
590d18 |
- dn = self.obj.get_either_dn(*keys, **options)
|
|
|
590d18 |
-
|
|
|
590d18 |
# We are going to permanent delete or the user is already in the delete container.
|
|
|
590d18 |
delete_container = DN(self.obj.delete_container_dn, self.api.env.basedn)
|
|
|
590d18 |
- user_from_delete_container = dn.endswith(delete_container)
|
|
|
590d18 |
-
|
|
|
590d18 |
- if not options.get('preserve', True) or user_from_delete_container:
|
|
|
590d18 |
- # Remove any ID overrides tied with this user
|
|
|
590d18 |
- remove_ipaobject_overrides(self.obj.backend, self.obj.api, dn)
|
|
|
590d18 |
-
|
|
|
590d18 |
- # Issue a true DEL on that entry
|
|
|
590d18 |
- return super(user_del, self).execute(*keys, **options)
|
|
|
590d18 |
|
|
|
590d18 |
# The user to delete is active and there is no 'no_preserve' option
|
|
|
590d18 |
if options.get('preserve', False):
|
|
|
590d18 |
-
|
|
|
590d18 |
- ldap = self.obj.backend
|
|
|
590d18 |
-
|
|
|
590d18 |
- # need to handle multiple keys (e.g. keys[-1]=(u'tb8', u'tb9')..
|
|
|
590d18 |
- active_dn = self.obj.get_either_dn(*keys, **options)
|
|
|
590d18 |
- superior_dn = DN(self.obj.delete_container_dn, api.env.basedn)
|
|
|
590d18 |
- delete_dn = DN(active_dn[0], self.obj.delete_container_dn, api.env.basedn)
|
|
|
590d18 |
- self.log.debug("preserve move %s -> %s" % (active_dn, delete_dn))
|
|
|
590d18 |
-
|
|
|
590d18 |
- # Check that this value is a Active user
|
|
|
590d18 |
- try:
|
|
|
590d18 |
- original_entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(active_dn, ['dn'])
|
|
|
590d18 |
- except errors.NotFound:
|
|
|
590d18 |
- raise
|
|
|
590d18 |
-
|
|
|
590d18 |
- # start to move the entry to Delete container
|
|
|
590d18 |
- self._exc_wrapper(keys, options, ldap.move_entry)(active_dn, delete_dn, del_old=True)
|
|
|
590d18 |
-
|
|
|
590d18 |
- # Then clear the credential attributes
|
|
|
590d18 |
- attrs_to_clear = ['krbPrincipalKey', 'krbLastPwdChange', 'krbPasswordExpiration', 'userPassword']
|
|
|
590d18 |
- try:
|
|
|
590d18 |
- entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(delete_dn, attrs_to_clear)
|
|
|
590d18 |
- except errors.NotFound:
|
|
|
590d18 |
- raise
|
|
|
590d18 |
- clearedCredential = False
|
|
|
590d18 |
- for attr in attrs_to_clear:
|
|
|
590d18 |
- if attr.lower() in entry_attrs:
|
|
|
590d18 |
- del entry_attrs[attr]
|
|
|
590d18 |
- clearedCredential = True
|
|
|
590d18 |
- if clearedCredential:
|
|
|
590d18 |
- self._exc_wrapper(keys, options, ldap.update_entry)(entry_attrs)
|
|
|
590d18 |
-
|
|
|
590d18 |
- # Then restore some original entry attributes
|
|
|
590d18 |
- attrs_to_restore = [ 'secretary', 'managedby', 'manager', 'ipauniqueid', 'uidnumber', 'gidnumber', 'passwordHistory']
|
|
|
590d18 |
- try:
|
|
|
590d18 |
- entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(delete_dn, attrs_to_restore)
|
|
|
590d18 |
- except errors.NotFound:
|
|
|
590d18 |
- raise
|
|
|
590d18 |
- restoreAttr = False
|
|
|
590d18 |
- for attr in attrs_to_restore:
|
|
|
590d18 |
- if (attr.lower() in original_entry_attrs) and not (attr.lower() in entry_attrs):
|
|
|
590d18 |
- restoreAttr = True
|
|
|
590d18 |
- entry_attrs[attr.lower()] = original_entry_attrs[attr.lower()]
|
|
|
590d18 |
- if restoreAttr:
|
|
|
590d18 |
- self._exc_wrapper(keys, options, ldap.update_entry)(entry_attrs)
|
|
|
590d18 |
-
|
|
|
590d18 |
- val = dict(result=dict(failed=[]), value=[keys[-1][0]])
|
|
|
590d18 |
+ failed = []
|
|
|
590d18 |
+ preserved = []
|
|
|
590d18 |
+ for pkey in keys[-1]:
|
|
|
590d18 |
+ try:
|
|
|
590d18 |
+ self._preserve_user(pkey, delete_container, **options)
|
|
|
590d18 |
+ preserved.append(pkey_to_value(pkey, options))
|
|
|
590d18 |
+ except:
|
|
|
590d18 |
+ if not options.get('continue', False):
|
|
|
590d18 |
+ raise
|
|
|
590d18 |
+ failed.append(pkey_to_value(pkey, options))
|
|
|
590d18 |
+
|
|
|
590d18 |
+ val = dict(result=dict(failed=failed), value=preserved)
|
|
|
590d18 |
return val
|
|
|
590d18 |
else:
|
|
|
590d18 |
return super(user_del, self).execute(*keys, **options)
|
|
|
590d18 |
--
|
|
|
590d18 |
2.4.3
|
|
|
590d18 |
|