590d18
From d5e7ef94033660aed43edb0f2dc4e1eb096579aa Mon Sep 17 00:00:00 2001
590d18
From: Martin Basti <mbasti@redhat.com>
590d18
Date: Mon, 17 Aug 2015 20:11:21 +0200
590d18
Subject: [PATCH] Add user-stage command
590d18
590d18
This patch replaces 'stageuser-add --from-delete' with new command
590d18
user-stage.
590d18
590d18
Original way always required  to specify first and last name, and
590d18
overall combination of options was hard to manage. The new command
590d18
requires only login of deleted user (user-del --preserve).
590d18
590d18
https://fedorahosted.org/freeipa/ticket/5041
590d18
590d18
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
590d18
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
590d18
---
590d18
 API.txt                     | 10 ++++++++-
590d18
 VERSION                     |  4 ++--
590d18
 ipalib/plugins/stageuser.py | 44 +++++++-------------------------------
590d18
 ipalib/plugins/user.py      | 51 +++++++++++++++++++++++++++++++++++++++++++++
590d18
 4 files changed, 70 insertions(+), 39 deletions(-)
590d18
590d18
diff --git a/API.txt b/API.txt
590d18
index f23d9a40c647a3c4d209419631794cd36e8e5e2f..b0f456e725a6c3d24c1071b282de5a28c3b5a671 100644
590d18
--- a/API.txt
590d18
+++ b/API.txt
590d18
@@ -4211,7 +4211,7 @@ option: Str('displayname', attribute=True, autofill=True, cli_name='displayname'
590d18
 option: Str('employeenumber', attribute=True, cli_name='employeenumber', multivalue=False, required=False)
590d18
 option: Str('employeetype', attribute=True, cli_name='employeetype', multivalue=False, required=False)
590d18
 option: Str('facsimiletelephonenumber', attribute=True, cli_name='fax', multivalue=True, required=False)
590d18
-option: Flag('from_delete?', autofill=True, cli_name='from_delete', default=False)
590d18
+option: DeprecatedParam('from_delete?', cli_name='from_delete', default=False)
590d18
 option: Str('gecos', attribute=True, autofill=True, cli_name='gecos', multivalue=False, required=False)
590d18
 option: Int('gidnumber', attribute=True, cli_name='gidnumber', minvalue=1, multivalue=False, required=False)
590d18
 option: Str('givenname', attribute=True, cli_name='first', multivalue=False, required=True)
590d18
@@ -5371,6 +5371,14 @@ option: Str('version?', exclude='webui')
590d18
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
590d18
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
590d18
 output: PrimaryKey('value', None, None)
590d18
+command: user_stage
590d18
+args: 1,2,3
590d18
+arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, query=True, required=True)
590d18
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
590d18
+option: Str('version?', exclude='webui')
590d18
+output: Output('result', <type 'dict'>, None)
590d18
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
590d18
+output: ListOfPrimaryKeys('value', None, None)
590d18
 command: user_status
590d18
 args: 1,4,4
590d18
 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, query=True, required=True)
590d18
diff --git a/VERSION b/VERSION
590d18
index 31a4af4a819415740e5c8db9259f934e13418cb5..9fe2f4d4f9ff6ffd42c2ee7493c385b0a432a6a0 100644
590d18
--- a/VERSION
590d18
+++ b/VERSION
590d18
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
590d18
 #                                                      #
590d18
 ########################################################
590d18
 IPA_API_VERSION_MAJOR=2
590d18
-IPA_API_VERSION_MINOR=151
590d18
-# Last change: cheimes - Add flag to list all service and user vaults
590d18
+IPA_API_VERSION_MINOR=152
590d18
+# Last change: mbasti - add 'user-stage' command
590d18
diff --git a/ipalib/plugins/stageuser.py b/ipalib/plugins/stageuser.py
590d18
index 41844712042c4456fc515afd316af60b612f164f..b6d98005314ab671af345aacb2e3c9f08193e155 100644
590d18
--- a/ipalib/plugins/stageuser.py
590d18
+++ b/ipalib/plugins/stageuser.py
590d18
@@ -23,7 +23,8 @@ import posixpath
590d18
 import os
590d18
 from copy import deepcopy
590d18
 from ipalib import api, errors
590d18
-from ipalib import Flag, Int, Password, Str, Bool, StrEnum, DateTime
590d18
+from ipalib import (Flag, Int, Password, Str, Bool, StrEnum, DateTime,
590d18
+                    DeprecatedParam)
590d18
 from ipalib.plugable import Registry
590d18
 from ipalib.plugins.baseldap import LDAPCreate, LDAPQuery, LDAPSearch, DN, entry_to_dict, pkey_to_value
590d18
 from ipalib.plugins import baseldap
590d18
@@ -260,7 +261,7 @@ class stageuser_add(baseuser_add):
590d18
     has_output_params = baseuser_add.has_output_params + stageuser_output_params
590d18
 
590d18
     takes_options = LDAPCreate.takes_options + (
590d18
-        Flag('from_delete?',
590d18
+        DeprecatedParam('from_delete?',
590d18
             doc=_('Create Stage user in from a delete user'),
590d18
             cli_name='from_delete',
590d18
             default=False,
590d18
@@ -270,13 +271,12 @@ class stageuser_add(baseuser_add):
590d18
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
590d18
         assert isinstance(dn, DN)
590d18
 
590d18
-        if not options.get('from_delete'):
590d18
-            # then givenname and sn are required attributes
590d18
-            if 'givenname' not in entry_attrs:
590d18
-                raise errors.RequirementError(name='givenname', error=_('givenname is required'))
590d18
+        # then givenname and sn are required attributes
590d18
+        if 'givenname' not in entry_attrs:
590d18
+            raise errors.RequirementError(name='givenname', error=_('givenname is required'))
590d18
 
590d18
-            if 'sn' not in entry_attrs:
590d18
-                raise errors.RequirementError(name='sn', error=_('sn is required'))
590d18
+        if 'sn' not in entry_attrs:
590d18
+            raise errors.RequirementError(name='sn', error=_('sn is required'))
590d18
 
590d18
         # we don't want an user private group to be created for this user
590d18
         # add NO_UPG_MAGIC description attribute to let the DS plugin know
590d18
@@ -367,34 +367,6 @@ class stageuser_add(baseuser_add):
590d18
 
590d18
         return dn
590d18
 
590d18
-    def execute(self, *keys, **options):
590d18
-        '''
590d18
-        A stage entry may be taken from the Delete container.
590d18
-        In that case we rather do 'MODRDN' than 'ADD'.
590d18
-        '''
590d18
-        if options.get('from_delete'):
590d18
-            ldap = self.obj.backend
590d18
-
590d18
-            staging_dn = self.obj.get_dn(*keys, **options)
590d18
-            delete_dn = DN(staging_dn[0], self.obj.delete_container_dn, api.env.basedn)
590d18
-            new_dn = DN(staging_dn[0], self.obj.stage_container_dn, api.env.basedn)
590d18
-            # Check that this value is a Active user
590d18
-            try:
590d18
-                entry_attrs = self._exc_wrapper(keys, options, ldap.get_entry)(delete_dn, ['dn'])
590d18
-            except errors.NotFound:
590d18
-                self.obj.handle_not_found(*keys)
590d18
-
590d18
-            self._exc_wrapper(keys, options, ldap.move_entry)(
590d18
-                delete_dn, new_dn)
590d18
-            entry_attrs = entry_to_dict(entry_attrs, **options)
590d18
-            entry_attrs['dn'] = new_dn
590d18
-
590d18
-            if self.obj.primary_key and keys[-1] is not None:
590d18
-                return dict(result=entry_attrs, value=keys[-1])
590d18
-            return dict(result=entry_attrs, value=u'')
590d18
-        else:
590d18
-            return super(stageuser_add, self).execute(*keys, **options)
590d18
-
590d18
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
590d18
         assert isinstance(dn, DN)
590d18
         config = ldap.get_ipa_config()
590d18
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
590d18
index 418c51bdafc4e462e2decfe1e8541aaf49705cb0..d7cf9666d43b69b0cab91cfa0c98a8373f095ab5 100644
590d18
--- a/ipalib/plugins/user.py
590d18
+++ b/ipalib/plugins/user.py
590d18
@@ -859,6 +859,57 @@ class user_undel(LDAPQuery):
590d18
             value=pkey_to_value(keys[0], options),
590d18
         )
590d18
 
590d18
+
590d18
+@register()
590d18
+class user_stage(LDAPMultiQuery):
590d18
+    __doc__ = _('Move deleted user into staged area')
590d18
+
590d18
+    has_output = output.standard_multi_delete
590d18
+    msg_summary = _('Staged user account "%(value)s"')
590d18
+
590d18
+    def execute(self, *keys, **options):
590d18
+        staged = []
590d18
+        failed = []
590d18
+
590d18
+        for key in keys[-1]:
590d18
+            single_keys = keys[:-1] + (key,)
590d18
+            multi_keys = keys[:-1] + ((key,),)
590d18
+
590d18
+            user = self.api.Command.user_show(*single_keys, all=True)['result']
590d18
+            new_options = {}
590d18
+            for param in self.api.Command.stageuser_add.options():
590d18
+                try:
590d18
+                    value = user[param.name]
590d18
+                except KeyError:
590d18
+                    continue
590d18
+                if param.multivalue and not isinstance(value, (list, tuple)):
590d18
+                    value = [value]
590d18
+                elif not param.multivalue and isinstance(value, (list, tuple)):
590d18
+                    value = value[0]
590d18
+                new_options[param.name] = value
590d18
+
590d18
+            try:
590d18
+                self.api.Command.stageuser_add(*single_keys, **new_options)
590d18
+                try:
590d18
+                    self.api.Command.user_del(*multi_keys, preserve=False)
590d18
+                except errors.ExecutionError:
590d18
+                    self.api.Command.stageuser_del(*multi_keys)
590d18
+                    raise
590d18
+            except errors.ExecutionError:
590d18
+                if not options['continue']:
590d18
+                    raise
590d18
+                failed.append(key)
590d18
+            else:
590d18
+                staged.append(key)
590d18
+
590d18
+        return dict(
590d18
+            result=dict(
590d18
+                failed=pkey_to_value(failed, options),
590d18
+            ),
590d18
+            value=pkey_to_value(staged, options),
590d18
+        )
590d18
+
590d18
+
590d18
 @register()
590d18
 class user_disable(LDAPQuery):
590d18
     __doc__ = _('Disable a user account.')
590d18
-- 
590d18
2.4.3
590d18