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

Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
Stanislav Laznicka
2017-03-27 08:18:29 +02:00
committed by Pavel Vomacka
parent b7ae3363fd
commit 8e4408e678
13 changed files with 33 additions and 25 deletions

View File

@@ -456,7 +456,7 @@ class automountkey(LDAPObject):
default_attributes = [ default_attributes = [
'automountkey', 'automountinformation', 'description' 'automountkey', 'automountinformation', 'description'
] ]
rdn_is_primary_key = True allow_rename = True
rdn_separator = ' ' rdn_separator = ' '
takes_params = ( takes_params = (

View File

@@ -36,7 +36,7 @@ from ipalib.text import _
from ipalib.util import json_serialize, validate_hostname from ipalib.util import json_serialize, validate_hostname
from ipalib.capabilities import client_has_capability from ipalib.capabilities import client_has_capability
from ipalib.messages import add_message, SearchResultTruncated from ipalib.messages import add_message, SearchResultTruncated
from ipapython.dn import DN from ipapython.dn import DN, RDN
from ipapython.version import API_VERSION from ipapython.version import API_VERSION
if six.PY3: if six.PY3:
@@ -549,7 +549,7 @@ class LDAPObject(Object):
rdn_attribute = '' rdn_attribute = ''
uuid_attribute = '' uuid_attribute = ''
attribute_members = {} attribute_members = {}
rdn_is_primary_key = False # Do we need RDN change to do a rename? allow_rename = False
password_attributes = [] password_attributes = []
# Can bind as this entry (has userPassword or krbPrincipalKey) # Can bind as this entry (has userPassword or krbPrincipalKey)
bindable = False bindable = False
@@ -1384,7 +1384,7 @@ class LDAPUpdate(LDAPQuery, crud.Update):
def get_options(self): def get_options(self):
for option in super(LDAPUpdate, self).get_options(): for option in super(LDAPUpdate, self).get_options():
yield option yield option
if self.obj.rdn_is_primary_key: if self.obj.allow_rename:
yield self._get_rename_option() yield self._get_rename_option()
def execute(self, *keys, **options): def execute(self, *keys, **options):
@@ -1419,15 +1419,19 @@ class LDAPUpdate(LDAPQuery, crud.Update):
_check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), list(entry_attrs), allow_only=False) _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), list(entry_attrs), allow_only=False)
rdnupdate = False rdnupdate = False
try: if 'rename' in options:
if self.obj.rdn_is_primary_key and 'rename' in options:
if not options['rename']: if not options['rename']:
raise errors.ValidationError(name='rename', error=u'can\'t be empty') raise errors.ValidationError(
name='rename', error=u'can\'t be empty')
entry_attrs[self.obj.primary_key.name] = options['rename'] 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 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: try:
# RDN change
new_dn = DN((self.obj.primary_key.name, new_dn = DN((self.obj.primary_key.name,
entry_attrs[self.obj.primary_key.name]), entry_attrs[self.obj.primary_key.name]),
*entry_attrs.dn[1:]) *entry_attrs.dn[1:])
@@ -1435,17 +1439,21 @@ class LDAPUpdate(LDAPQuery, crud.Update):
entry_attrs.dn, entry_attrs.dn,
new_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) entry_attrs.dn = self.obj.get_dn(*rdnkeys)
options['rdnupdate'] = True options['rdnupdate'] = True
rdnupdate = True rdnupdate = True
except errors.EmptyModlist: except errors.EmptyModlist:
# Attempt to rename to the current name, ignore # Attempt to rename to the current name, ignore
pass pass
except errors.NotFound:
self.obj.handle_not_found(*keys)
finally: finally:
# Delete the primary_key from entry_attrs either way # Delete the primary_key from entry_attrs either way
del entry_attrs[self.obj.primary_key.name] del entry_attrs[self.obj.primary_key.name]
try:
# Exception callbacks will need to test for options['rdnupdate'] # Exception callbacks will need to test for options['rdnupdate']
# to decide what to do. An EmptyModlist in this context doesn't # to decide what to do. An EmptyModlist in this context doesn't
# mean an error occurred, just that there were no other updates to # mean an error occurred, just that there were no other updates to

View File

@@ -164,7 +164,7 @@ class baseuser(LDAPObject):
'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], 'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
} }
rdn_is_primary_key = True allow_rename = True
bindable = True bindable = True
password_attributes = [('userpassword', 'has_password'), password_attributes = [('userpassword', 'has_password'),
('krbprincipalkey', 'has_keytab')] ('krbprincipalkey', 'has_keytab')]

View File

@@ -68,7 +68,7 @@ class ca(LDAPObject):
'cn', 'description', 'ipacaid', 'ipacaissuerdn', 'ipacasubjectdn', 'cn', 'description', 'ipacaid', 'ipacaissuerdn', 'ipacasubjectdn',
] ]
rdn_attribute = 'cn' rdn_attribute = 'cn'
rdn_is_primary_key = True allow_rename = True
label = _('Certificate Authorities') label = _('Certificate Authorities')
label_singular = _('Certificate Authority') label_singular = _('Certificate Authority')

View File

@@ -3000,7 +3000,7 @@ class dnsrecord(LDAPObject):
possible_objectclasses = ['idnsTemplateObject'] possible_objectclasses = ['idnsTemplateObject']
permission_filter_objectclasses = ['idnsrecord'] permission_filter_objectclasses = ['idnsrecord']
default_attributes = ['idnsname'] + _record_attributes default_attributes = ['idnsname'] + _record_attributes
rdn_is_primary_key = True allow_rename = True
label = _('DNS Resource Records') label = _('DNS Resource Records')
label_singular = _('DNS Resource Record') label_singular = _('DNS Resource Record')

View File

@@ -173,7 +173,7 @@ class group(LDAPObject):
'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule',
'sudorule'], 'sudorule'],
} }
rdn_is_primary_key = True allow_rename = True
managed_permissions = { managed_permissions = {
'System: Read Groups': { 'System: Read Groups': {
'replaces_global_anonymous_aci': True, 'replaces_global_anonymous_aci': True,

View File

@@ -97,7 +97,7 @@ class idview(LDAPObject):
object_class = ['ipaIDView', 'top'] object_class = ['ipaIDView', 'top']
possible_objectclasses = ['ipaNameResolutionData'] possible_objectclasses = ['ipaNameResolutionData']
default_attributes = ['cn', 'description', 'ipadomainresolutionorder'] default_attributes = ['cn', 'description', 'ipadomainresolutionorder']
rdn_is_primary_key = True allow_rename = True
label = _('ID Views') label = _('ID Views')
label_singular = _('ID View') label_singular = _('ID View')
@@ -848,7 +848,7 @@ class idoverrideuser(baseidoverride):
label = _('User ID overrides') label = _('User ID overrides')
label_singular = _('User ID override') label_singular = _('User ID override')
rdn_is_primary_key = True allow_rename = True
# ID user overrides are bindable because we map SASL GSSAPI # ID user overrides are bindable because we map SASL GSSAPI
# authentication of trusted users to ID user overrides in the # authentication of trusted users to ID user overrides in the
@@ -964,7 +964,7 @@ class idoverridegroup(baseidoverride):
label = _('Group ID overrides') label = _('Group ID overrides')
label_singular = _('Group ID override') label_singular = _('Group ID override')
rdn_is_primary_key = True allow_rename = True
permission_filter_objectclasses = ['ipaGroupOverride'] permission_filter_objectclasses = ['ipaGroupOverride']
managed_permissions = { managed_permissions = {

View File

@@ -143,7 +143,7 @@ class otptoken(LDAPObject):
relationships = { relationships = {
'managedby': ('Managed by', 'man_by_', 'not_man_by_'), 'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
} }
rdn_is_primary_key = True allow_rename = True
label = _('OTP Tokens') label = _('OTP Tokens')
label_singular = _('OTP Token') label_singular = _('OTP Token')

View File

@@ -188,7 +188,7 @@ class permission(baseldap.LDAPObject):
'member': ['privilege'], 'member': ['privilege'],
'memberindirect': ['role'], 'memberindirect': ['role'],
} }
rdn_is_primary_key = True allow_rename = True
managed_permissions = { managed_permissions = {
'System: Read Permissions': { 'System: Read Permissions': {
'replaces_global_anonymous_aci': True, 'replaces_global_anonymous_aci': True,

View File

@@ -101,7 +101,7 @@ class privilege(LDAPObject):
reverse_members = { reverse_members = {
'member': ['permission'], 'member': ['permission'],
} }
rdn_is_primary_key = True allow_rename = True
managed_permissions = { managed_permissions = {
'System: Read Privileges': { 'System: Read Privileges': {
'replaces_global_anonymous_aci': True, 'replaces_global_anonymous_aci': True,

View File

@@ -101,7 +101,7 @@ class radiusproxy(LDAPObject):
'ipatokenradiustimeout', 'ipatokenradiusretries', 'ipatokenusermapattribute' 'ipatokenradiustimeout', 'ipatokenradiusretries', 'ipatokenusermapattribute'
] ]
search_attributes = ['cn', 'description', 'ipatokenradiusserver'] search_attributes = ['cn', 'description', 'ipatokenradiusserver']
rdn_is_primary_key = True allow_rename = True
label = _('RADIUS Servers') label = _('RADIUS Servers')
label_singular = _('RADIUS Server') label_singular = _('RADIUS Server')

View File

@@ -92,7 +92,7 @@ class role(LDAPObject):
reverse_members = { reverse_members = {
'member': ['privilege'], 'member': ['privilege'],
} }
rdn_is_primary_key = True allow_rename = True
managed_permissions = { managed_permissions = {
'System: Read Roles': { 'System: Read Roles': {
'replaces_global_anonymous_aci': True, 'replaces_global_anonymous_aci': True,

View File

@@ -138,7 +138,7 @@ class servicedelegation(LDAPObject):
}, },
} }
rdn_is_primary_key = True allow_rename = True
takes_params = ( takes_params = (
Str( Str(