On 27.10.2015 14:59, Martin Basti wrote:


On 20.10.2015 18:46, Martin Basti wrote:


On 20.10.2015 16:07, Martin Basti wrote:


On 20.10.2015 15:57, Martin Basti wrote:
https://fedorahosted.org/freeipa/ticket/5344

Patch attached.

Test are failing, a fix in UserTracker has to be done (partially in my patch 329)


SelfNACK, I forgot to add stageuser tests


Updated patch attached.

I extracted tests to the separate patch, tests do not work, I had issues with user and stageuser trackers.



Patch to fix issues with --addattr and managers added and attached.



The new one patch 0331 attached, patch 0337 is not needed anymore.

This patch also fixes https://fedorahosted.org/freeipa/ticket/5387
From a85d8d2d0e1ab0aabe57c560bbd7a45527f5a7f7 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Thu, 5 Nov 2015 17:11:23 +0100
Subject: [PATCH] Allow multiple managers per user - CLI part

Added commands:
* user-add-manager
* user-remove-manager
* stageuser-add-manager
* stageuser-remove-manager

Commit contains override of convert_attribute_members method in baseuser
class that ensures the managers will be returned in 'manager' attribute
due to backward compatibility instead of 'manager_user' as would be
expected.

https://fedorahosted.org/freeipa/ticket/5344

This patch also fixes: https://fedorahosted.org/freeipa/ticket/5387
---
 API.txt                     | 44 ++++++++++++++++++++++++++++++++++++
 VERSION                     |  4 ++--
 ipalib/plugins/baseuser.py  | 55 +++++++++++++++++++++++++++++++--------------
 ipalib/plugins/stageuser.py | 22 +++++++++++++-----
 ipalib/plugins/user.py      | 24 ++++++++++++++------
 5 files changed, 117 insertions(+), 32 deletions(-)

diff --git a/API.txt b/API.txt
index 873c6d54221a0c1657b5457bd9dceedb4adf06b3..0976c97213775d79da43ee382a0badbe029b7960 100644
--- a/API.txt
+++ b/API.txt
@@ -4248,6 +4248,17 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: stageuser_add_manager
+args: 1,5,3
+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)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: stageuser_del
 args: 1,2,3
 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)
@@ -4367,6 +4378,17 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: stageuser_remove_manager
+args: 1,5,3
+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)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: stageuser_show
 args: 1,5,3
 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)
@@ -5208,6 +5230,17 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: user_add_manager
+args: 1,5,3
+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)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: user_del
 args: 1,4,3
 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)
@@ -5358,6 +5391,17 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: user_remove_manager
+args: 1,5,3
+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)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: user_show
 args: 1,6,3
 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)
diff --git a/VERSION b/VERSION
index cdda198c6ce3148dcf785149dc3ce050782e8caa..064e98cb46a084b9f87f4fb52c8cd6033be171a4 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=157
-# Last change: mbabinsk - hide segment direction from topology commands
+IPA_API_VERSION_MINOR=158
+# Last change: mbasti - allow multiple managers: (stage)user-{add|remove}-manager commands
diff --git a/ipalib/plugins/baseuser.py b/ipalib/plugins/baseuser.py
index cf0fd88d9b9ee83ee046852e0e4709362947b291..7af398c4efe18f97dd2809cd8ac7401e524a6760 100644
--- a/ipalib/plugins/baseuser.py
+++ b/ipalib/plugins/baseuser.py
@@ -27,8 +27,9 @@ import six
 from ipalib import api, errors
 from ipalib import Flag, Int, Password, Str, Bool, StrEnum, DateTime, Bytes
 from ipalib.plugable import Registry
-from ipalib.plugins.baseldap import DN, LDAPObject, \
-    LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete, LDAPRetrieve
+from ipalib.plugins.baseldap import (
+    DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete,
+    LDAPRetrieve, LDAPAddMember, LDAPRemoveMember)
 from ipalib.plugins.service import validate_certificate
 from ipalib.plugins import baseldap
 from ipalib.request import context
@@ -201,6 +202,7 @@ class baseuser(LDAPObject):
     ]
     uuid_attribute = 'ipauniqueid'
     attribute_members = {
+        'manager': ['user'],
         'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
         'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
     }
@@ -338,9 +340,14 @@ class baseuser(LDAPObject):
         Str('title?',
             label=_('Job Title'),
         ),
+        # keep backward compatibility using single value manager option
         Str('manager?',
             label=_('Manager'),
         ),
+        Str('manager_user*',
+            label=_('Manager'),
+            flags=['no_create', 'no_update', 'no_search'],
+        ),
         Str('carlicense*',
             label=_('Car License'),
         ),
@@ -426,8 +433,11 @@ class baseuser(LDAPObject):
         if not manager:
             return None
 
-        if not isinstance(manager, list):
+        if isinstance(manager, tuple):
+            manager = list(manager)
+        elif not isinstance(manager, list):
             manager = [manager]
+
         try:
             container_dn = DN(container, api.env.basedn)
             for i, mgr in enumerate(manager):
@@ -443,17 +453,6 @@ class baseuser(LDAPObject):
 
         return manager
 
-    def convert_manager(self, entry_attrs, **options):
-        """
-        Convert a manager dn into a userid
-        """
-        if options.get('raw', False):
-             return
-
-        if 'manager' in entry_attrs:
-            for i, mgr in enumerate(entry_attrs['manager']):
-                entry_attrs['manager'][i] = self.get_primary_key_from_dn(mgr)
-
     def _user_status(self, user, container):
         assert isinstance(user, DN)
         return user.endswith(container)
@@ -480,6 +479,23 @@ class baseuser(LDAPObject):
             entry_attrs['usercertificate'] = entry_attrs.pop(
                 'usercertificate;binary')
 
+    def convert_attribute_members(self, entry_attrs, *keys, **options):
+        super(baseuser, self).convert_attribute_members(
+            entry_attrs, keys, options)
+
+        # due the backward compatibility, managers have to be returned in
+        # 'manager' attribute instead of 'manager_user'
+        try:
+            entry_attrs['failed_manager'] = entry_attrs.pop('manager')
+        except KeyError:
+            pass
+
+        try:
+            entry_attrs['manager'] = entry_attrs.pop('manager_user')
+        except KeyError:
+            pass
+
+
 class baseuser_add(LDAPCreate):
     """
     Prototype command plugin to be implemented by real plugin
@@ -578,7 +594,6 @@ class baseuser_mod(LDAPUpdate):
                 # if both randompassword and userpassword options were used
                 pass
         convert_nsaccountlock(entry_attrs)
-        self.obj.convert_manager(entry_attrs, **options)
         self.obj.get_password_attributes(ldap, dn, entry_attrs)
         self.obj.convert_usercertificate_post(entry_attrs, **options)
         convert_sshpubkey_post(ldap, dn, entry_attrs)
@@ -609,7 +624,6 @@ class baseuser_find(LDAPSearch):
 
     def post_common_callback(self, ldap, entries, lockout=False, **options):
         for attrs in entries:
-            self.obj.convert_manager(attrs, **options)
             self.obj.get_password_attributes(ldap, attrs.dn, attrs)
             self.obj.convert_usercertificate_post(attrs, **options)
             if (lockout):
@@ -624,8 +638,15 @@ class baseuser_show(LDAPRetrieve):
     """
     def post_common_callback(self, ldap, dn, entry_attrs, **options):
         assert isinstance(dn, DN)
-        self.obj.convert_manager(entry_attrs, **options)
         self.obj.get_password_attributes(ldap, dn, entry_attrs)
         self.obj.convert_usercertificate_post(entry_attrs, **options)
         convert_sshpubkey_post(ldap, dn, entry_attrs)
         radius_dn2pk(self.api, entry_attrs)
+
+
+class baseuser_add_manager(LDAPAddMember):
+    member_attributes = ['manager']
+
+
+class baseuser_remove_manager(LDAPRemoveMember):
+    member_attributes = ['manager']
diff --git a/ipalib/plugins/stageuser.py b/ipalib/plugins/stageuser.py
index 47c96bb715fcf8293f4ce257566ab914644fa489..995f679235a4ceaa398fb7ee0f7ffcd98f833058 100644
--- a/ipalib/plugins/stageuser.py
+++ b/ipalib/plugins/stageuser.py
@@ -31,12 +31,12 @@ from ipalib import (Flag, Int, Password, Str, Bool, StrEnum, DateTime,
 from ipalib.plugable import Registry
 from ipalib.plugins.baseldap import LDAPCreate, LDAPQuery, LDAPSearch, DN, entry_to_dict, pkey_to_value
 from ipalib.plugins import baseldap
-from ipalib.plugins.baseuser import baseuser, baseuser_add, baseuser_del, \
-    baseuser_mod, baseuser_find, baseuser_show, \
-    NO_UPG_MAGIC, radius_dn2pk, \
-    baseuser_pwdchars, fix_addressbook_permission_bindrule, normalize_principal, validate_principal, \
-    baseuser_output_params, status_baseuser_output_params
-
+from ipalib.plugins.baseuser import (
+    baseuser, baseuser_add, baseuser_del, baseuser_mod, baseuser_find,
+    baseuser_show, NO_UPG_MAGIC, radius_dn2pk, baseuser_pwdchars,
+    fix_addressbook_permission_bindrule, normalize_principal,
+    validate_principal, baseuser_output_params, status_baseuser_output_params,
+    baseuser_add_manager, baseuser_remove_manager)
 from ipalib.request import context
 from ipalib import _, ngettext
 from ipalib import output
@@ -716,3 +716,13 @@ class stageuser_activate(LDAPQuery):
         return dict(result=result_entry,
                     summary=unicode(_('Stage user %s activated' % staging_dn[0].value)),
                     value=pkey_to_value(args[-1], options))
+
+
+@register()
+class stageuser_add_manager(baseuser_add_manager):
+    __doc__ = _("Add a manager to the stage user entry")
+
+
+@register()
+class stageuser_remove_manager(baseuser_remove_manager):
+    __doc__ = _("Remove a manager to the stage user entry")
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index 81a7070359253262f30e65298c42759cd055e67d..2b792af6289bae3644e627afff8e8ddc662068b2 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -27,12 +27,13 @@ import six
 
 from ipalib import api, errors, util
 from ipalib import Flag, Int, Password, Str, Bool, StrEnum, DateTime
-from ipalib.plugins.baseuser import baseuser, baseuser_add, baseuser_del, \
-    baseuser_mod, baseuser_find, baseuser_show, \
-    NO_UPG_MAGIC, UPG_DEFINITION_DN, baseuser_output_params, \
-    status_baseuser_output_params, baseuser_pwdchars, \
-    validate_nsaccountlock, radius_dn2pk, convert_nsaccountlock, split_principal, validate_principal, \
-    normalize_principal, fix_addressbook_permission_bindrule
+from ipalib.plugins.baseuser import (
+    baseuser, baseuser_add, baseuser_del, baseuser_mod, baseuser_find,
+    baseuser_show, NO_UPG_MAGIC, UPG_DEFINITION_DN, baseuser_output_params,
+    status_baseuser_output_params, baseuser_pwdchars, validate_nsaccountlock,
+    radius_dn2pk, convert_nsaccountlock, split_principal, validate_principal,
+    normalize_principal, fix_addressbook_permission_bindrule,
+    baseuser_add_manager, baseuser_remove_manager)
 from ipalib.plugins.idviews import remove_ipaobject_overrides
 from ipalib.plugable import Registry
 from ipalib.plugins.baseldap import *
@@ -542,7 +543,6 @@ class user_add(baseuser_add):
         except errors.AlreadyGroupMember:
             pass
 
-        self.obj.convert_manager(entry_attrs, **options)
         # delete description attribute NO_UPG_MAGIC if present
         if options.get('noprivate', False):
             if not options.get('all', False):
@@ -1157,3 +1157,13 @@ class user_remove_cert(LDAPRemoveAttribute):
         self.obj.convert_usercertificate_post(entry_attrs, **options)
 
         return dn
+
+
+@register()
+class user_add_manager(baseuser_add_manager):
+    __doc__ = _("Add a manager to the user entry")
+
+
+@register()
+class user_remove_manager(baseuser_remove_manager):
+    __doc__ = _("Remove a manager to the user entry")
-- 
2.4.3

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to