diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 40f9d6a58..78ce8e023 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -77,6 +77,7 @@ class LDAPObject(Object): rdn_attribute = '' uuid_attribute = '' attribute_members = {} + rdnattr = None container_not_found_msg = _('container entry (%(container)s) not found') parent_not_found_msg = _('%(parent)s: %(oname)s not found') @@ -541,14 +542,31 @@ class LDAPUpdate(LDAPQuery, crud.Update): _check_single_value_attrs(self.params, entry_attrs) + rdnupdate = False try: + if self.obj.rdnattr and self.obj.rdnattr in entry_attrs: + # RDN change + ldap.update_entry_rdn(dn, unicode('%s=%s' % (self.obj.rdnattr, + entry_attrs[self.obj.rdnattr]))) + dn = self.obj.get_dn(entry_attrs[self.obj.rdnattr]) + del entry_attrs[self.obj.rdnattr] + options['rdnupdate'] = True + rdnupdate = True + ldap.update_entry(dn, entry_attrs, normalize=self.obj.normalize_dn) except errors.ExecutionError, e: + # 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 + # perform. try: self._call_exc_callbacks( keys, options, e, ldap.update_entry, dn, entry_attrs, normalize=self.obj.normalize_dn ) + except errors.EmptyModlist, e: + if not rdnupdate: + raise e except errors.NotFound: self.obj.handle_not_found(*keys) diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py index 2b8dc1af1..975915b42 100644 --- a/ipalib/plugins/group.py +++ b/ipalib/plugins/group.py @@ -90,6 +90,7 @@ class group(LDAPObject): 'member': ['user', 'group'], 'memberof': ['group', 'netgroup', 'rolegroup', 'taskgroup'], } + rdnattr = 'cn' label = _('User Groups') diff --git a/ipalib/plugins/rolegroup.py b/ipalib/plugins/rolegroup.py index 99560c46c..feffa0d49 100644 --- a/ipalib/plugins/rolegroup.py +++ b/ipalib/plugins/rolegroup.py @@ -75,6 +75,7 @@ class rolegroup(LDAPObject): 'member': ['user', 'group', 'host', 'hostgroup'], 'memberof': ['taskgroup'], } + rdnattr='cn' label = _('Role Groups') diff --git a/ipalib/plugins/taskgroup.py b/ipalib/plugins/taskgroup.py index 0ee90474d..11bef4860 100644 --- a/ipalib/plugins/taskgroup.py +++ b/ipalib/plugins/taskgroup.py @@ -47,6 +47,7 @@ class taskgroup(LDAPObject): 'member': ['user', 'group', 'rolegroup'], # FIXME: taskgroup can be member of ??? } + rdnattr='cn' label = _('Task Groups') diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 68ca24a0c..fb0da4800 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -72,6 +72,7 @@ class user(LDAPObject): attribute_members = { 'memberof': ['group', 'netgroup', 'rolegroup', 'taskgroup'], } + rdnattr = 'uid' label = _('Users') diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 05292ce7c..4117e47b7 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -681,6 +681,8 @@ class ldap2(CrudBackend, Encoder): del_old -- delete old RDN value (default True) """ dn = self.normalize_dn(dn) + if dn.startswith(new_rdn + ","): + raise errors.EmptyModlist() try: self.conn.rename_s(dn, new_rdn, delold=int(del_old)) except _ldap.LDAPError, e: diff --git a/tests/test_xmlrpc/test_group_plugin.py b/tests/test_xmlrpc/test_group_plugin.py index 55bb9cde1..6218ebe59 100644 --- a/tests/test_xmlrpc/test_group_plugin.py +++ b/tests/test_xmlrpc/test_group_plugin.py @@ -27,6 +27,7 @@ from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid group1 = u'testgroup1' group2 = u'testgroup2' +renamedgroup1 = u'testgroup' user1 = u'tuser1' invalidgroup1=u'+tgroup1' @@ -65,6 +66,13 @@ class test_group(Declarative): ), + dict( + desc='Try to rename non-existent %r' % group1, + command=('group_mod', [group1], dict(setattr=u'cn=%s' % renamedgroup1)), + expected=errors.NotFound(reason='no such entry'), + ), + + dict( desc='Create non-POSIX %r' % group1, command=( @@ -327,6 +335,8 @@ class test_group(Declarative): 'gidnumber': [fuzzy_digits], 'cn': [u'admins'], 'description': [u'Account administrators group'], + 'memberof_rolegroup': [u'replicaadmin'], + 'memberof_taskgroup': [u'managereplica', u'deletereplica'], }, { 'dn': u'cn=ipausers,cn=groups,cn=accounts,%s' % api.env.basedn, @@ -453,6 +463,36 @@ class test_group(Declarative): ), + dict( + desc='Rename %r' % group1, + command=('group_mod', [group1], dict(setattr=u'cn=%s' % renamedgroup1)), + expected=dict( + value=group1, + result=dict( + cn=[renamedgroup1], + description=[u'New desc 1'], + gidnumber=[fuzzy_digits], + ), + summary=u'Modified group "%s"' % group1 + ) + ), + + + dict( + desc='Rename %r back' % renamedgroup1, + command=('group_mod', [renamedgroup1], dict(setattr=u'cn=%s' % group1)), + expected=dict( + value=renamedgroup1, + result=dict( + cn=[group1], + description=[u'New desc 1'], + gidnumber=[fuzzy_digits], + ), + summary=u'Modified group "%s"' % renamedgroup1 + ) + ), + + ################ # delete group1: diff --git a/tests/test_xmlrpc/test_host_plugin.py b/tests/test_xmlrpc/test_host_plugin.py index 6a07763c6..631a5de7d 100644 --- a/tests/test_xmlrpc/test_host_plugin.py +++ b/tests/test_xmlrpc/test_host_plugin.py @@ -233,6 +233,13 @@ class test_host(Declarative): ), + dict( + desc='Try to rename %r' % fqdn1, + command=('host_mod', [fqdn1], dict(setattr=u'fqdn=changed')), + expected=errors.NotAllowedOnRDN() + ), + + dict( desc='Delete %r' % fqdn1, command=('host_del', [fqdn1], {}), diff --git a/tests/test_xmlrpc/test_rolegroup_plugin.py b/tests/test_xmlrpc/test_rolegroup_plugin.py index c66bdc474..46922cd4e 100644 --- a/tests/test_xmlrpc/test_rolegroup_plugin.py +++ b/tests/test_xmlrpc/test_rolegroup_plugin.py @@ -31,6 +31,7 @@ rolegroup1 = u'test-rolegroup-1' rolegroup1_dn = u'cn=%s,cn=rolegroups,cn=accounts,%s' % ( rolegroup1, api.env.basedn ) +renamedrolegroup1 = u'test-rolegroup' rolegroup2 = u'test-rolegroup-2' rolegroup2_dn = u'cn=%s,cn=rolegroups,cn=accounts,%s' % ( @@ -72,6 +73,13 @@ class test_rolegroup(Declarative): ), + dict( + desc='Try to rename non-existent %r' % rolegroup1, + command=('rolegroup_del', [rolegroup1], dict(setattr=u'cn=%s' % renamedrolegroup1)), + expected=errors.NotFound(reason='no such entry'), + ), + + dict( desc='Search for non-existent %r' % rolegroup1, command=('rolegroup_find', [rolegroup1], {}), @@ -356,6 +364,34 @@ class test_rolegroup(Declarative): ), + dict( + desc='Rename %r' % rolegroup1, + command=('rolegroup_mod', [rolegroup1], dict(setattr=u'cn=%s' % renamedrolegroup1)), + expected=dict( + value=rolegroup1, + result=dict( + cn=[renamedrolegroup1], + description=[u'New desc 1'], + ), + summary=u'Modified rolegroup "%s"' % rolegroup1 + ) + ), + + + dict( + desc='Rename %r back' % renamedrolegroup1, + command=('rolegroup_mod', [renamedrolegroup1], dict(setattr=u'cn=%s' % rolegroup1)), + expected=dict( + value=renamedrolegroup1, + result=dict( + cn=[rolegroup1], + description=[u'New desc 1'], + ), + summary=u'Modified rolegroup "%s"' % renamedrolegroup1 + ) + ), + + dict( desc='Delete %r' % rolegroup1, command=('rolegroup_del', [rolegroup1], {}), diff --git a/tests/test_xmlrpc/test_taskgroup_plugin.py b/tests/test_xmlrpc/test_taskgroup_plugin.py index 8401fbdc3..ce3166020 100644 --- a/tests/test_xmlrpc/test_taskgroup_plugin.py +++ b/tests/test_xmlrpc/test_taskgroup_plugin.py @@ -32,6 +32,7 @@ taskgroup1 = u'test-taskgroup-1' taskgroup1_dn = u'cn=%s,cn=taskgroups,cn=accounts,%s' % ( taskgroup1, api.env.basedn ) +renamedtaskgroup1 = u'test-taskgroup1' taskgroup2 = u'test-taskgroup-2' taskgroup2_dn = u'cn=%s,cn=taskgroups,cn=accounts,%s' % ( @@ -79,6 +80,13 @@ class test_taskgroup(Declarative): ), + dict( + desc='Try to rename non-existent %r' % taskgroup1, + command=('taskgroup_del', [taskgroup1], dict(setattr=u'cn=%s' % renamedtaskgroup1)), + expected=errors.NotFound(reason='no such entry'), + ), + + dict( desc='Search for non-existent %r' % taskgroup1, command=('taskgroup_find', [taskgroup1], {}), @@ -361,6 +369,36 @@ class test_taskgroup(Declarative): ), + dict( + desc='Rename %r' % taskgroup1, + command=('taskgroup_mod', [taskgroup1], dict(setattr=u'cn=%s' % renamedtaskgroup1)), + expected=dict( + value=taskgroup1, + result=dict( + cn=[renamedtaskgroup1], + description=[u'New desc 1'], + member_rolegroup=[u'test-rolegroup-1'], + ), + summary=u'Modified taskgroup "%s"' % taskgroup1 + ) + ), + + + dict( + desc='Rename %r back' % renamedtaskgroup1, + command=('taskgroup_mod', [renamedtaskgroup1], dict(setattr=u'cn=%s' % taskgroup1)), + expected=dict( + value=renamedtaskgroup1, + result=dict( + cn=[taskgroup1], + description=[u'New desc 1'], + member_rolegroup=[u'test-rolegroup-1'], + ), + summary=u'Modified taskgroup "%s"' % renamedtaskgroup1 + ) + ), + + dict( desc='Delete %r' % taskgroup1, command=('taskgroup_del', [taskgroup1], {}), diff --git a/tests/test_xmlrpc/test_user_plugin.py b/tests/test_xmlrpc/test_user_plugin.py index ee02a0f81..8c2bae499 100644 --- a/tests/test_xmlrpc/test_user_plugin.py +++ b/tests/test_xmlrpc/test_user_plugin.py @@ -31,6 +31,7 @@ from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid user_memberof = (u'cn=ipausers,cn=groups,cn=accounts,%s' % api.env.basedn,) user1=u'tuser1' user2=u'tuser2' +renameduser1=u'tuser' invaliduser1=u'+tuser1' invaliduser2=u'tuser1234567890123456789012345678901234567890' @@ -65,6 +66,13 @@ class test_user(Declarative): ), + dict( + desc='Try to rename non-existent %r' % user1, + command=('user_mod', [user1], dict(setattr=u'uid=tuser')), + expected=errors.NotFound(reason='no such entry'), + ), + + dict( desc='Create %r' % user1, command=( @@ -300,6 +308,49 @@ class test_user(Declarative): ), + dict( + desc='Rename %r' % user1, + command=('user_mod', [user1], dict(setattr=u'uid=%s' % renameduser1)), + expected=dict( + result=dict( + givenname=[u'Finkle'], + homedirectory=[u'/home/tuser1'], + loginshell=[u'/bin/sh'], + sn=[u'User1'], + uid=[renameduser1], + memberof_group=[u'ipausers'], + ), + summary=u'Modified user "%s"' % user1, + value=user1, + ), + ), + + + dict( + desc='Rename %r to same value' % renameduser1, + command=('user_mod', [renameduser1], dict(setattr=u'uid=%s' % renameduser1)), + expected=errors.EmptyModlist(), + ), + + + dict( + desc='Rename back %r' % renameduser1, + command=('user_mod', [renameduser1], dict(setattr=u'uid=%s' % user1)), + expected=dict( + result=dict( + givenname=[u'Finkle'], + homedirectory=[u'/home/tuser1'], + loginshell=[u'/bin/sh'], + sn=[u'User1'], + uid=[user1], + memberof_group=[u'ipausers'], + ), + summary=u'Modified user "%s"' % renameduser1, + value=renameduser1, + ), + ), + + dict( desc='Delete %r' % user1, command=('user_del', [user1], {}),