diff options
author | Thierry Bordaz <tbordaz@redhat.com> | 2015-04-16 11:56:11 +0200 |
---|---|---|
committer | Martin Kosek <mkosek@redhat.com> | 2015-05-18 09:37:21 +0200 |
commit | 4ef32967f76965e622780b9d6171b3e8896ec348 (patch) | |
tree | 63a9e1c4bc5a17cd9efabea33980c68d308914a6 /ipalib | |
parent | 699dd771022227644c631dcc17839dfe04077a8e (diff) | |
download | freeipa-4ef32967f76965e622780b9d6171b3e8896ec348.tar.gz freeipa-4ef32967f76965e622780b9d6171b3e8896ec348.tar.xz freeipa-4ef32967f76965e622780b9d6171b3e8896ec348.zip |
User life cycle: user-del supports --permanently, --preserve options and ability to delete deleted user
change user plugin commands : user-del
- --permanently: deletes permanently an Active user (DEL)
- --preserve: move an Active user to Delete user (MODRDN)
- allows to delete Active user and Delete user
https://fedorahosted.org/freeipa/ticket/3813
Reviewed-By: David Kupka <dkupka@redhat.com>
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/plugins/user.py | 135 |
1 files changed, 123 insertions, 12 deletions
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 4da6c5a1e..07effa5bc 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -335,6 +335,33 @@ class user(baseuser): ), ) + def get_dn(self, *keys, **options): + ''' + Returns the DN of a user + The user can be active (active container) or delete (delete container) + If the user does not exist, returns the Active user DN + ''' + ldap = self.backend + # Check that this value is a Active user + try: + active_dn = super(user, self).get_dn(*keys, **options) + ldap.get_entry(active_dn, ['dn']) + + # The Active user exists + dn = active_dn + except errors.NotFound: + # Check that this value is a Delete user + delete_dn = DN(active_dn[0], self.delete_container_dn, api.env.basedn) + try: + ldap.get_entry(delete_dn, ['dn']) + + # The Delete user exists + dn = delete_dn + except errors.NotFound: + # The user is neither Active/Delete -> returns that Active DN + dn = active_dn + + return dn def _normalize_manager(self, manager): """ @@ -360,6 +387,7 @@ class user_add(baseuser_add): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) + self.log.error("====> user-add pre_callback 1 %s " % dn) if not options.get('noprivate', False): try: # The Managed Entries plugin will allow a user to be created @@ -466,6 +494,7 @@ class user_add(baseuser_add): answer = self.api.Object['radiusproxy'].get_dn_if_exists(rcl) entry_attrs['ipatokenradiusconfiglink'] = answer + self.log.error("====> user-add pre_callback %s " % dn) return dn @@ -522,24 +551,106 @@ class user_del(baseuser_del): msg_summary = _('Deleted user "%(value)s"') + takes_options = baseuser_del.takes_options + ( + Flag('preserve?', + doc=_('Delete a user, keeping the entry available for future use'), + cli_name='preserve', + default=False, + ), + Flag('permanently?', + doc=_('Delete a user'), + cli_name='permanently', + default=False, + ), + ) + def pre_callback(self, ldap, dn, *keys, **options): assert isinstance(dn, DN) - check_protected_member(keys[-1]) - # Delete all tokens owned and managed by this user. - # Orphan all tokens owned but not managed by this user. - owner = self.api.Object.user.get_primary_key_from_dn(dn) - results = self.api.Command.otptoken_find(ipatokenowner=owner)['result'] - for token in results: - orphan = not [x for x in token.get('managedby_user', []) if x == owner] - token = self.api.Object.otptoken.get_primary_key_from_dn(token['dn']) - if orphan: - self.api.Command.otptoken_mod(token, ipatokenowner=None) - else: - self.api.Command.otptoken_del(token) + # For User life Cycle: user-del is a common plugin + # command to delete active user (active container) and + # delete user (delete container). + # If the target entry is a Delete entry, skip the updates + # protected member and otptoken owner + if not dn.endswith(DN(self.obj.delete_container_dn, api.env.basedn)): + check_protected_member(keys[-1]) + + # Delete all tokens owned and managed by this user. + # Orphan all tokens owned but not managed by this user. + owner = self.api.Object.user.get_primary_key_from_dn(dn) + results = self.api.Command.otptoken_find(ipatokenowner=owner)['result'] + for token in results: + orphan = not [x for x in token.get('managedby_user', []) if x == owner] + token = self.api.Object.otptoken.get_primary_key_from_dn(token['dn']) + if orphan: + self.api.Command.otptoken_mod(token, ipatokenowner=None) + else: + self.api.Command.otptoken_del(token) return dn + def execute(self, *keys, **options): + + dn = self.obj.get_dn(*keys, **options) + + if options['permanently'] or dn.endswith(DN(self.obj.delete_container_dn, api.env.basedn)): + # We are going to permanent delete or the user is already in the delete container. + # So we issue a true DEL on that entry + return super(user_del, self).execute(*keys, **options) + + # The user to delete is active and there is no 'permanently' option + if options['preserve']: + + ldap = self.obj.backend + + # need to handle multiple keys (e.g. keys[-1]=(u'tb8', u'tb9').. + active_dn = self.obj.get_dn(*keys, **options) + superior_dn = DN(self.obj.delete_container_dn, api.env.basedn) + delete_dn = DN(active_dn[0], self.obj.delete_container_dn, api.env.basedn) + self.log.debug("preserve move %s -> %s" % (active_dn, delete_dn)) + + # Check that this value is a Active user + try: + original_entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(active_dn, ['dn']) + except errors.NotFound: + raise + + # start to move the entry to Delete container + self._exc_wrapper(keys, options, ldap.update_entry_rdn)(active_dn, new_rdn=active_dn[0], new_superior=superior_dn, del_old=True) + + # Then clear the credential attributes + attrs_to_clear = ['krbPrincipalKey', 'krbLastPwdChange', 'krbPasswordExpiration', 'userPassword'] + try: + entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(delete_dn, attrs_to_clear) + except errors.NotFound: + raise + clearedCredential = False + for attr in attrs_to_clear: + if attr.lower() in entry_attrs: + del entry_attrs[attr] + clearedCredential = True + if clearedCredential: + self._exc_wrapper(keys, options, ldap.update_entry)(entry_attrs) + + # Then restore some original entry attributes + attrs_to_restore = [ 'secretary', 'managedby', 'manager', 'ipauniqueid', 'uidnumber', 'gidnumber', 'passwordHistory'] + try: + entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(delete_dn, attrs_to_restore) + except errors.NotFound: + raise + restoreAttr = False + for attr in attrs_to_restore: + if (attr.lower() in original_entry_attrs) and not (attr.lower() in entry_attrs): + restoreAttr = True + entry_attrs[attr.lower()] = original_entry_attrs[attr.lower()] + if restoreAttr: + self._exc_wrapper(keys, options, ldap.update_entry)(entry_attrs) + + val = dict(result=dict(failed=[]), value=[keys[-1][0]]) + return val + else: + return super(user_del, self).execute(*keys, **options) + @register() class user_mod(baseuser_mod): |