URL: https://github.com/freeipa/freeipa/pull/617
Author: stlaz
 Title: #617: Allow renaming of sudo and HBAC rules
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/617/head:pr617
git checkout pr617
From 862ec59543d57ffa073e9831e1ae5a17278595e1 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Mon, 27 Mar 2017 08:18:29 +0200
Subject: [PATCH 1/3] Reworked the renaming mechanism

The rename operation on *_mod commands was only allowed when
the primary key of an entry was also its RDN. With these changes,
it should be possible to rename the rest of the entries as well.

An attribute to the base LDAPObject was added to whitelist the
objects we want to allow to be renamed. It replaced an old
attribute rdn_is_primary_key which was used for the very same
purpose but the name was confusing because it was not set
correctly for certain objects.

https://pagure.io/freeipa/issue/2466
https://pagure.io/freeipa/issue/6784
---
 ipaserver/plugins/automount.py         |  2 +-
 ipaserver/plugins/baseldap.py          | 32 ++++++++++++++++++++------------
 ipaserver/plugins/baseuser.py          |  2 +-
 ipaserver/plugins/ca.py                |  2 +-
 ipaserver/plugins/dns.py               |  2 +-
 ipaserver/plugins/group.py             |  2 +-
 ipaserver/plugins/idviews.py           |  6 +++---
 ipaserver/plugins/otptoken.py          |  2 +-
 ipaserver/plugins/permission.py        |  2 +-
 ipaserver/plugins/privilege.py         |  2 +-
 ipaserver/plugins/radiusproxy.py       |  2 +-
 ipaserver/plugins/role.py              |  2 +-
 ipaserver/plugins/servicedelegation.py |  2 +-
 13 files changed, 34 insertions(+), 26 deletions(-)

diff --git a/ipaserver/plugins/automount.py b/ipaserver/plugins/automount.py
index c4cf2d6..03f994c 100644
--- a/ipaserver/plugins/automount.py
+++ b/ipaserver/plugins/automount.py
@@ -456,7 +456,7 @@ class automountkey(LDAPObject):
     default_attributes = [
         'automountkey', 'automountinformation', 'description'
     ]
-    rdn_is_primary_key = True
+    allow_rename = True
     rdn_separator = ' '
 
     takes_params = (
diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py
index 79ba7fc..dbe3cbd 100644
--- a/ipaserver/plugins/baseldap.py
+++ b/ipaserver/plugins/baseldap.py
@@ -36,7 +36,7 @@
 from ipalib.util import json_serialize, validate_hostname
 from ipalib.capabilities import client_has_capability
 from ipalib.messages import add_message, SearchResultTruncated
-from ipapython.dn import DN
+from ipapython.dn import DN, RDN
 from ipapython.version import API_VERSION
 
 if six.PY3:
@@ -549,7 +549,7 @@ class LDAPObject(Object):
     rdn_attribute = ''
     uuid_attribute = ''
     attribute_members = {}
-    rdn_is_primary_key = False # Do we need RDN change to do a rename?
+    allow_rename = False
     password_attributes = []
     # Can bind as this entry (has userPassword or krbPrincipalKey)
     bindable = False
@@ -1384,7 +1384,7 @@ def _get_rename_option(self):
     def get_options(self):
         for option in super(LDAPUpdate, self).get_options():
             yield option
-        if self.obj.rdn_is_primary_key:
+        if self.obj.allow_rename:
             yield self._get_rename_option()
 
     def execute(self, *keys, **options):
@@ -1419,15 +1419,19 @@ def execute(self, *keys, **options):
         _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), list(entry_attrs), allow_only=False)
 
         rdnupdate = False
-        try:
-            if self.obj.rdn_is_primary_key and 'rename' in options:
-                if not options['rename']:
-                    raise errors.ValidationError(name='rename', error=u'can\'t be empty')
-                entry_attrs[self.obj.primary_key.name] = options['rename']
-
-            if self.obj.rdn_is_primary_key and self.obj.primary_key.name in entry_attrs:
+        if 'rename' in options:
+            if not options['rename']:
+                raise errors.ValidationError(
+                    name='rename', error=u'can\'t be empty')
+            entry_attrs[self.obj.primary_key.name] = options['rename']
+
+        # if setattr was used to change the RDN, the primary_key.name is
+        # already in entry_attrs
+        if self.obj.allow_rename and self.obj.primary_key.name in entry_attrs:
+            # perform RDN change if the primary key is also RDN
+            if (RDN((self.obj.primary_key.name, keys[-1])) ==
+                    entry_attrs.dn[0]):
                 try:
-                    # RDN change
                     new_dn = DN((self.obj.primary_key.name,
                                  entry_attrs[self.obj.primary_key.name]),
                                 *entry_attrs.dn[1:])
@@ -1435,17 +1439,21 @@ def execute(self, *keys, **options):
                         entry_attrs.dn,
                         new_dn)
 
-                    rdnkeys = keys[:-1] + (entry_attrs[self.obj.primary_key.name], )
+                    rdnkeys = (keys[:-1] +
+                               (entry_attrs[self.obj.primary_key.name], ))
                     entry_attrs.dn = self.obj.get_dn(*rdnkeys)
                     options['rdnupdate'] = True
                     rdnupdate = True
                 except errors.EmptyModlist:
                     # Attempt to rename to the current name, ignore
                     pass
+                except errors.NotFound:
+                    self.obj.handle_not_found(*keys)
                 finally:
                     # Delete the primary_key from entry_attrs either way
                     del entry_attrs[self.obj.primary_key.name]
 
+        try:
             # Exception callbacks will need to test for options['rdnupdate']
             # to decide what to do. An EmptyModlist in this context doesn't
             # mean an error occurred, just that there were no other updates to
diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py
index 44adc76..bf24dbf 100644
--- a/ipaserver/plugins/baseuser.py
+++ b/ipaserver/plugins/baseuser.py
@@ -164,7 +164,7 @@ class baseuser(LDAPObject):
         'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
         'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
     }
-    rdn_is_primary_key = True
+    allow_rename = True
     bindable = True
     password_attributes = [('userpassword', 'has_password'),
                            ('krbprincipalkey', 'has_keytab')]
diff --git a/ipaserver/plugins/ca.py b/ipaserver/plugins/ca.py
index f774f78..9bb163d 100644
--- a/ipaserver/plugins/ca.py
+++ b/ipaserver/plugins/ca.py
@@ -68,7 +68,7 @@ class ca(LDAPObject):
         'cn', 'description', 'ipacaid', 'ipacaissuerdn', 'ipacasubjectdn',
     ]
     rdn_attribute = 'cn'
-    rdn_is_primary_key = True
+    allow_rename = True
     label = _('Certificate Authorities')
     label_singular = _('Certificate Authority')
 
diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
index 7007928..47ac963 100644
--- a/ipaserver/plugins/dns.py
+++ b/ipaserver/plugins/dns.py
@@ -3000,7 +3000,7 @@ class dnsrecord(LDAPObject):
     possible_objectclasses = ['idnsTemplateObject']
     permission_filter_objectclasses = ['idnsrecord']
     default_attributes = ['idnsname'] + _record_attributes
-    rdn_is_primary_key = True
+    allow_rename = True
 
     label = _('DNS Resource Records')
     label_singular = _('DNS Resource Record')
diff --git a/ipaserver/plugins/group.py b/ipaserver/plugins/group.py
index 218da3c..1fb092d 100644
--- a/ipaserver/plugins/group.py
+++ b/ipaserver/plugins/group.py
@@ -173,7 +173,7 @@ class group(LDAPObject):
         'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule',
         'sudorule'],
     }
-    rdn_is_primary_key = True
+    allow_rename = True
     managed_permissions = {
         'System: Read Groups': {
             'replaces_global_anonymous_aci': True,
diff --git a/ipaserver/plugins/idviews.py b/ipaserver/plugins/idviews.py
index 6d4ac75..b5ee32c 100644
--- a/ipaserver/plugins/idviews.py
+++ b/ipaserver/plugins/idviews.py
@@ -97,7 +97,7 @@ class idview(LDAPObject):
     object_class = ['ipaIDView', 'top']
     possible_objectclasses = ['ipaNameResolutionData']
     default_attributes = ['cn', 'description', 'ipadomainresolutionorder']
-    rdn_is_primary_key = True
+    allow_rename = True
 
     label = _('ID Views')
     label_singular = _('ID View')
@@ -848,7 +848,7 @@ class idoverrideuser(baseidoverride):
 
     label = _('User ID overrides')
     label_singular = _('User ID override')
-    rdn_is_primary_key = True
+    allow_rename = True
 
     # ID user overrides are bindable because we map SASL GSSAPI
     # authentication of trusted users to ID user overrides in the
@@ -964,7 +964,7 @@ class idoverridegroup(baseidoverride):
 
     label = _('Group ID overrides')
     label_singular = _('Group ID override')
-    rdn_is_primary_key = True
+    allow_rename = True
 
     permission_filter_objectclasses = ['ipaGroupOverride']
     managed_permissions = {
diff --git a/ipaserver/plugins/otptoken.py b/ipaserver/plugins/otptoken.py
index 98ecbe5..c66f098 100644
--- a/ipaserver/plugins/otptoken.py
+++ b/ipaserver/plugins/otptoken.py
@@ -143,7 +143,7 @@ class otptoken(LDAPObject):
     relationships = {
         'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
     }
-    rdn_is_primary_key = True
+    allow_rename = True
 
     label = _('OTP Tokens')
     label_singular = _('OTP Token')
diff --git a/ipaserver/plugins/permission.py b/ipaserver/plugins/permission.py
index dd2a018..977c6fe 100644
--- a/ipaserver/plugins/permission.py
+++ b/ipaserver/plugins/permission.py
@@ -188,7 +188,7 @@ class permission(baseldap.LDAPObject):
         'member': ['privilege'],
         'memberindirect': ['role'],
     }
-    rdn_is_primary_key = True
+    allow_rename = True
     managed_permissions = {
         'System: Read Permissions': {
             'replaces_global_anonymous_aci': True,
diff --git a/ipaserver/plugins/privilege.py b/ipaserver/plugins/privilege.py
index b3afbd2..01d5396 100644
--- a/ipaserver/plugins/privilege.py
+++ b/ipaserver/plugins/privilege.py
@@ -101,7 +101,7 @@ class privilege(LDAPObject):
     reverse_members = {
         'member': ['permission'],
     }
-    rdn_is_primary_key = True
+    allow_rename = True
     managed_permissions = {
         'System: Read Privileges': {
             'replaces_global_anonymous_aci': True,
diff --git a/ipaserver/plugins/radiusproxy.py b/ipaserver/plugins/radiusproxy.py
index 3391b8a..be77c62 100644
--- a/ipaserver/plugins/radiusproxy.py
+++ b/ipaserver/plugins/radiusproxy.py
@@ -101,7 +101,7 @@ class radiusproxy(LDAPObject):
         'ipatokenradiustimeout', 'ipatokenradiusretries', 'ipatokenusermapattribute'
     ]
     search_attributes = ['cn', 'description', 'ipatokenradiusserver']
-    rdn_is_primary_key = True
+    allow_rename = True
     label = _('RADIUS Servers')
     label_singular = _('RADIUS Server')
 
diff --git a/ipaserver/plugins/role.py b/ipaserver/plugins/role.py
index 5d0d1f8..e7f115c 100644
--- a/ipaserver/plugins/role.py
+++ b/ipaserver/plugins/role.py
@@ -92,7 +92,7 @@ class role(LDAPObject):
     reverse_members = {
         'member': ['privilege'],
     }
-    rdn_is_primary_key = True
+    allow_rename = True
     managed_permissions = {
         'System: Read Roles': {
             'replaces_global_anonymous_aci': True,
diff --git a/ipaserver/plugins/servicedelegation.py b/ipaserver/plugins/servicedelegation.py
index c8052e9..4f94924 100644
--- a/ipaserver/plugins/servicedelegation.py
+++ b/ipaserver/plugins/servicedelegation.py
@@ -138,7 +138,7 @@ class servicedelegation(LDAPObject):
         },
     }
 
-    rdn_is_primary_key = True
+    allow_rename = True
 
     takes_params = (
         Str(

From b0c365e9b33f68363abbb981da096864e80cb58f Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Mon, 27 Mar 2017 08:25:04 +0200
Subject: [PATCH 2/3] Allow renaming of the HBAC rule objects

The recent changes allow HBAC rule objects to be renamed.

https://pagure.io/freeipa/issue/6784
---
 API.txt                                  |  3 ++-
 VERSION.m4                               |  4 ++--
 ipaserver/plugins/hbacrule.py            |  1 +
 ipatests/test_xmlrpc/test_hbac_plugin.py | 15 +++++++++++++++
 4 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/API.txt b/API.txt
index f0bd1b6..2a63c98 100644
--- a/API.txt
+++ b/API.txt
@@ -2163,7 +2163,7 @@ output: ListOfEntries('result')
 output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
 output: Output('truncated', type=[<type 'bool'>])
 command: hbacrule_mod/1
-args: 1,16,3
+args: 1,17,3
 arg: Str('cn', cli_name='name')
 option: StrEnum('accessruletype?', autofill=False, cli_name='type', default=u'allow', values=[u'allow', u'deny'])
 option: Str('addattr*', cli_name='addattr')
@@ -2175,6 +2175,7 @@ option: StrEnum('hostcategory?', autofill=False, cli_name='hostcat', values=[u'a
 option: Bool('ipaenabledflag?', autofill=False)
 option: Flag('no_members', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False)
+option: Str('rename?', cli_name='rename')
 option: Flag('rights', autofill=True, default=False)
 option: StrEnum('servicecategory?', autofill=False, cli_name='servicecat', values=[u'all'])
 option: Str('setattr*', cli_name='setattr')
diff --git a/VERSION.m4 b/VERSION.m4
index cfac2a9..75c0897 100644
--- a/VERSION.m4
+++ b/VERSION.m4
@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000)
 #                                                      #
 ########################################################
 define(IPA_API_VERSION_MAJOR, 2)
-define(IPA_API_VERSION_MINOR, 223)
-# Last change: Add domain resolution order to ID views
+define(IPA_API_VERSION_MINOR, 224)
+# Last change: Add rename option to HBAC rule objects
 
 
 ########################################################
diff --git a/ipaserver/plugins/hbacrule.py b/ipaserver/plugins/hbacrule.py
index 60e5e60..2495702 100644
--- a/ipaserver/plugins/hbacrule.py
+++ b/ipaserver/plugins/hbacrule.py
@@ -141,6 +141,7 @@ class hbacrule(LDAPObject):
     ]
     uuid_attribute = 'ipauniqueid'
     rdn_attribute = 'ipauniqueid'
+    allow_rename = True
     attribute_members = {
         'memberuser': ['user', 'group'],
         'memberhost': ['host', 'hostgroup'],
diff --git a/ipatests/test_xmlrpc/test_hbac_plugin.py b/ipatests/test_xmlrpc/test_hbac_plugin.py
index 75c15c5..b495fe3 100644
--- a/ipatests/test_xmlrpc/test_hbac_plugin.py
+++ b/ipatests/test_xmlrpc/test_hbac_plugin.py
@@ -34,6 +34,7 @@ class test_hbac(XMLRPC_test):
     Test the `hbacrule` plugin.
     """
     rule_name = u'testing_rule1234'
+    rule_renamed = u'mega_testing_rule'
     rule_type = u'allow'
     rule_type_fail = u'value not allowed'
     rule_service = u'ssh'
@@ -459,6 +460,20 @@ def test_n_hbacrule_links(self):
         assert_attr_equal(entry, 'cn', self.rule_name)
         assert_attr_equal(entry, 'memberservice_hbacsvc', self.test_service)
 
+    def test_o_hbacrule_rename(self):
+        """
+        Test renaming an HBAC rule, rename it back afterwards
+        """
+        api.Command['hbacrule_mod'](
+            self.rule_name, rename=self.rule_renamed
+        )
+        entry = api.Command['hbacrule_show'](self.rule_renamed)['result']
+        assert_attr_equal(entry, 'cn', self.rule_renamed)
+        # clean up by renaming the rule back
+        api.Command['hbacrule_mod'](
+            self.rule_renamed, rename=self.rule_name
+        )
+
     def test_y_hbacrule_zap_testing_data(self):
         """
         Clear data for HBAC plugin testing.

From ff2669bfbf1d8b3c14b88457d0da2886116ef97b Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Mon, 27 Mar 2017 08:26:03 +0200
Subject: [PATCH 3/3] Allow renaming of the sudorule objects

The recent changes allow the sudorule objects to be renamed.

https://pagure.io/freeipa/issue/2466
---
 API.txt                                      |  3 ++-
 VERSION.m4                                   |  2 +-
 ipaserver/plugins/sudorule.py                |  1 +
 ipatests/test_xmlrpc/test_sudorule_plugin.py | 14 ++++++++++++++
 4 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/API.txt b/API.txt
index 2a63c98..7594157 100644
--- a/API.txt
+++ b/API.txt
@@ -5403,7 +5403,7 @@ output: ListOfEntries('result')
 output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
 output: Output('truncated', type=[<type 'bool'>])
 command: sudorule_mod/1
-args: 1,20,3
+args: 1,21,3
 arg: Str('cn', cli_name='sudorule_name')
 option: Str('addattr*', cli_name='addattr')
 option: Flag('all', autofill=True, cli_name='all', default=False)
@@ -5420,6 +5420,7 @@ option: StrEnum('ipasudorunasgroupcategory?', autofill=False, cli_name='runasgro
 option: StrEnum('ipasudorunasusercategory?', autofill=False, cli_name='runasusercat', values=[u'all'])
 option: Flag('no_members', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False)
+option: Str('rename?', cli_name='rename')
 option: Flag('rights', autofill=True, default=False)
 option: Str('setattr*', cli_name='setattr')
 option: Int('sudoorder?', autofill=False, cli_name='order', default=0)
diff --git a/VERSION.m4 b/VERSION.m4
index 75c0897..368f964 100644
--- a/VERSION.m4
+++ b/VERSION.m4
@@ -74,7 +74,7 @@ define(IPA_DATA_VERSION, 20100614120000)
 ########################################################
 define(IPA_API_VERSION_MAJOR, 2)
 define(IPA_API_VERSION_MINOR, 224)
-# Last change: Add rename option to HBAC rule objects
+# Last change: Add rename option to sudorule objects
 
 
 ########################################################
diff --git a/ipaserver/plugins/sudorule.py b/ipaserver/plugins/sudorule.py
index 9077107..28c3f21 100644
--- a/ipaserver/plugins/sudorule.py
+++ b/ipaserver/plugins/sudorule.py
@@ -145,6 +145,7 @@ class sudorule(LDAPObject):
     ]
     uuid_attribute = 'ipauniqueid'
     rdn_attribute = 'ipauniqueid'
+    allow_rename = True
     attribute_members = {
         'memberuser': ['user', 'group'],
         'memberhost': ['host', 'hostgroup'],
diff --git a/ipatests/test_xmlrpc/test_sudorule_plugin.py b/ipatests/test_xmlrpc/test_sudorule_plugin.py
index c37262a..75dbfbe 100644
--- a/ipatests/test_xmlrpc/test_sudorule_plugin.py
+++ b/ipatests/test_xmlrpc/test_sudorule_plugin.py
@@ -42,6 +42,7 @@ class test_sudorule(XMLRPC_test):
     """
     rule_name = u'testing_sudorule1'
     rule_name2 = u'testing_sudorule2'
+    rule_renamed = u'testing_mega_sudorule'
     rule_command = u'/usr/bin/testsudocmd1'
     rule_desc = u'description'
     rule_desc_mod = u'description modified'
@@ -782,6 +783,19 @@ def test_l_sudorule_order(self):
         api.Command['sudorule_mod'](self.rule_name, sudoorder=None)
         api.Command['sudorule_mod'](self.rule_name2, sudoorder=None)
 
+    def test_l_1_sudorule_rename(self):
+        """
+        Test renaming an HBAC rule, rename it back afterwards
+        """
+        api.Command['sudorule_mod'](
+            self.rule_name, rename=self.rule_renamed
+        )
+        entry = api.Command['sudorule_show'](self.rule_renamed)['result']
+        assert_attr_equal(entry, 'cn', self.rule_renamed)
+        # clean up by renaming the rule back
+        api.Command['sudorule_mod'](
+            self.rule_renamed, rename=self.rule_name
+        )
 
     def test_m_sudorule_del(self):
         """
-- 
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