Blob Blame History Raw
From 5731aa2850d150a90ad84ce5492cd5d8b154e413 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 23 Jul 2019 09:31:53 +0200
Subject: [PATCH] user-stage: transfer all attributes from preserved to stage
 user

The user-stage command is internally implemented as:
- user_show(all=True) in order to read the user attributes
- loop on the attributes defined as possible to add using stageuser-add and
transform them into new options for stageuser_add (for instance stageuser-add
provides the option --shell for the attribute loginshell, but there is no
option for the attribute businesscategory).
- call stageuser_add in order to create a new entry in the active users subtree
- user-del to remove the previous entry in the staged users subtree

The issue is in the 2nd step. Only the attributes with a stageuser-add option
are processed.
The logic of the code should be slightly modified, so that all the attributes
read in the first step are processed:
- if they correspond to an option of stageuser-add, process them like it's
currently done. For instance if the entry contains displayname, then it
should be processed as --displayName=value in the stageuser-add cmd
- if they do not correspond to an option of stageuser-add, add them with
--setattr=<attrname>=<attrvalue>

Note that some attributes may need to be filtered, for instance user-show
returns has_password or has_keytab, which do not correspond to attributes
in the LDAP entry.

Fixes: https://pagure.io/freeipa/issue/7597
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
 ipaserver/plugins/user.py | 44 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py
index 980385dc83e93ec4a65726077b34917e21115efa..fbf7b11789c58377366f187211c4e403d0cf7ffe 100644
--- a/ipaserver/plugins/user.py
+++ b/ipaserver/plugins/user.py
@@ -919,7 +919,29 @@ class user_stage(LDAPMultiQuery):
     has_output = output.standard_multi_delete
     msg_summary = _('Staged user account "%(value)s"')
 
+    # when moving from preserved to stage, some attributes may be
+    # present in the preserved entry but cannot be provided to
+    # stageuser_add
+    # For instance: dn and uid are derived from LOGIN argument
+    #    has_keytab, has_password, preserved are virtual attributes
+    #    ipauniqueid, krbcanonicalname, sshpubkeyfp, krbextradata
+    #    are automatically generated
+    #    ipacertmapdata can only be provided with user_add_certmapdata
+    ignore_attrs = [u'dn', u'uid',
+                    u'has_keytab', u'has_password', u'preserved',
+                    u'ipauniqueid', u'krbcanonicalname',
+                    u'sshpubkeyfp', u'krbextradata',
+                    u'ipacertmapdata',
+                    u'nsaccountlock']
+
     def execute(self, *keys, **options):
+
+        def _build_setattr_arg(key, val):
+            if isinstance(val, bytes):
+                return u"{}={}".format(key, val.decode('UTF-8'))
+            else:
+                return u"{}={}".format(key, val)
+
         staged = []
         failed = []
 
@@ -940,8 +962,30 @@ class user_stage(LDAPMultiQuery):
                     value = value[0]
                 new_options[param.name] = value
 
+            # Some attributes may not be accessible through the Command
+            # options and need to be added with --setattr
+            set_attr = []
+            for userkey in user.keys():
+                if userkey in new_options or userkey in self.ignore_attrs:
+                    continue
+                value = user[userkey]
+
+                if isinstance(value, (list, tuple)):
+                    for val in value:
+                        set_attr.append(_build_setattr_arg(userkey, val))
+                else:
+                    set_attr.append(_build_setattr_arg(userkey, val))
+            if set_attr:
+                new_options[u'setattr'] = set_attr
+
             try:
                 self.api.Command.stageuser_add(*single_keys, **new_options)
+                # special handling for certmapdata
+                certmapdata = user.get(u'ipacertmapdata')
+                if certmapdata:
+                    self.api.Command.stageuser_add_certmapdata(
+                        *single_keys,
+                        ipacertmapdata=certmapdata)
                 try:
                     self.api.Command.user_del(*multi_keys, preserve=False)
                 except errors.ExecutionError:
-- 
2.20.1