You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2008/06/14 11:22:49 UTC
svn commit: r667762 - in /directory/apacheds/trunk:
core/src/main/java/org/apache/directory/server/core/schema/
jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/
server-unit/src/test/java/org/apache/directory/server/
Author: seelmann
Date: Sat Jun 14 02:22:48 2008
New Revision: 667762
URL: http://svn.apache.org/viewvc?rev=667762&view=rev
Log:
Fix for DIRSERVER-1085 and DIRSERVER-1162
o Added support for multi-valued RDN in JdbmStore.rename()
o Fixed handling of deleteOldRdn in JdbmStore.rename()
o Added checks to SchemaIntercepter.rename() to prevent deletion of must or operational attributes
o Added tests to ModifyRdnTest
Modified:
directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java
directory/apacheds/trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java
directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java
Modified: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java?rev=667762&r1=667761&r2=667762&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java Sat Jun 14 02:22:48 2008
@@ -20,6 +20,21 @@
package org.apache.directory.server.core.schema;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SearchControls;
+
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.entry.DefaultServerAttribute;
@@ -77,6 +92,7 @@
import org.apache.directory.shared.ldap.filter.SubstringNode;
import org.apache.directory.shared.ldap.message.CascadeControl;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.name.Rdn;
import org.apache.directory.shared.ldap.schema.AttributeType;
@@ -91,21 +107,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.NoPermissionException;
-import javax.naming.directory.InvalidAttributeValueException;
-import javax.naming.directory.SearchControls;
-
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
/**
* An {@link org.apache.directory.server.core.interceptor.Interceptor} that manages and enforces schemas.
@@ -1134,9 +1135,44 @@
LdapDN name = opContext.getDn();
Rdn newRdn = opContext.getNewRdn();
boolean deleteOldRn = opContext.getDelOldDn();
-
ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+ if ( deleteOldRn )
+ {
+ ServerEntry tmpEntry = ( ServerEntry ) entry.clone();
+ Rdn oldRDN = name.getRdn();
+
+ // Delete the old RDN means we remove some attributes and values.
+ // We must make sure that after this operation all must attributes
+ // are still present in the entry.
+ for ( AttributeTypeAndValue atav : oldRDN )
+ {
+ AttributeType type = atRegistry.lookup( atav.getUpType() );
+ String value = ( String ) atav.getNormValue();
+ tmpEntry.remove( type, value );
+ }
+ for ( AttributeTypeAndValue atav : newRdn )
+ {
+ AttributeType type = atRegistry.lookup( atav.getUpType() );
+ String value = ( String ) atav.getNormValue();
+ if ( !tmpEntry.contains( type, value ) )
+ {
+ tmpEntry.add( new DefaultServerAttribute( type, value ) );
+ }
+ }
+ check( name, tmpEntry );
+
+ // Check that no operational attributes are removed
+ for ( AttributeTypeAndValue atav : oldRDN )
+ {
+ AttributeType attributeType = atRegistry.lookup( atav.getUpType() );
+ if ( !attributeType.isCanUserModify() )
+ {
+ throw new NoPermissionException( "Cannot modify the attribute '" + atav.getUpType() + "'" );
+ }
+ }
+ }
+
if ( name.startsWith( schemaBaseDN ) )
{
schemaManager.modifyRn( name, newRdn, deleteOldRn, entry, opContext
Modified: directory/apacheds/trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java?rev=667762&r1=667761&r2=667762&view=diff
==============================================================================
--- directory/apacheds/trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java (original)
+++ directory/apacheds/trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java Sat Jun 14 02:22:48 2008
@@ -48,6 +48,7 @@
import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.name.Rdn;
import org.apache.directory.shared.ldap.schema.AttributeType;
@@ -1588,8 +1589,6 @@
*/
public void rename( LdapDN dn, Rdn newRdn, boolean deleteOldRdn ) throws NamingException
{
- String newRdnAttr = newRdn.getNormType();
- String newRdnValue = ( String ) newRdn.getValue();
Long id = getEntryId( dn.getNormName() );
ServerEntry entry = lookup( id );
LdapDN updn = entry.getDn();
@@ -1603,31 +1602,23 @@
* new Rdn attribute within this entry.
*/
- AttributeType newRdnAttrType = attributeTypeRegistry.lookup( newRdn.getNormType() );
- EntryAttribute rdnAttr = entry.get( newRdnAttrType );
-
- if ( rdnAttr == null )
- {
- rdnAttr = new DefaultServerAttribute( newRdnAttrType );
- }
-
- // add the new Rdn value only if it is not already present in the entry
- if ( !rdnAttr.contains( newRdnValue ) )
- {
- rdnAttr.add( ( String ) newRdn.getUpValue() );
- }
-
- //entry.put( rdnAttr );
-
- if ( hasUserIndexOn( newRdn.getNormType() ) )
+ for ( AttributeTypeAndValue newAtav : newRdn )
{
- Index idx = getUserIndex( newRdn.getNormType() );
- idx.add( newRdnValue, id );
+ String newNormType = newAtav.getNormType();
+ String newNormValue = ( String ) newAtav.getNormValue();
+ AttributeType newRdnAttrType = attributeTypeRegistry.lookup( newNormType );
+ entry.add( newRdnAttrType, ( String ) newAtav.getUpValue() );
- // Make sure the altered entry shows the existance of the new attrib
- if ( !existanceIdx.hasValue( newRdn.getNormType(), id ) )
+ if ( hasUserIndexOn( newNormType ) )
{
- existanceIdx.add( newRdn.getNormType(), id );
+ Index idx = getUserIndex( newNormType );
+ idx.add( newNormValue, id );
+
+ // Make sure the altered entry shows the existance of the new attrib
+ if ( !existanceIdx.hasValue( newNormType, id ) )
+ {
+ existanceIdx.add( newNormType, id );
+ }
}
}
@@ -1643,28 +1634,49 @@
* lookup. If so that means we blew away the last value of the old
* Rdn attribute. In this case we need to remove the attrName/id
* tuple from the existance index.
+ *
+ * We only remove an ATAV of the old Rdn if it is not included in the
+ * new Rdn.
*/
if ( deleteOldRdn )
{
Rdn oldRdn = updn.getRdn();
- AttributeType oldRdnAttrType = attributeTypeRegistry.lookup( oldRdn.getNormType() );
-
- EntryAttribute oldRdnAttr = entry.get( oldRdnAttrType );
- oldRdnAttr.remove( ( String ) oldRdn.getUpValue() );
-
- if ( hasUserIndexOn( oldRdn.getNormType() ) )
+ for ( AttributeTypeAndValue oldAtav : oldRdn )
{
- Index idx = getUserIndex( oldRdn.getNormType() );
- idx.drop( oldRdn.getValue(), id );
+ // check if the new ATAV is part of the old Rdn
+ // if that is the case we do not remove the ATAV
+ boolean mustRemove = true;
+ for ( AttributeTypeAndValue newAtav : newRdn )
+ {
+ if ( oldAtav.equals( newAtav ) )
+ {
+ mustRemove = false;
+ break;
+ }
+ }
- /*
- * If there is no value for id in this index due to our
- * drop above we remove the oldRdnAttr from the existance idx
- */
- if ( null == idx.reverseLookup( id ) )
+ if ( mustRemove )
{
- existanceIdx.drop( oldRdn.getNormType(), id );
+ String oldNormType = oldAtav.getNormType();
+ String oldNormValue = ( String ) oldAtav.getNormValue();
+ AttributeType oldRdnAttrType = attributeTypeRegistry.lookup( oldNormType );
+ entry.remove( oldRdnAttrType, oldNormValue );
+
+ if ( hasUserIndexOn( oldNormType ) )
+ {
+ Index idx = getUserIndex( oldNormType );
+ idx.drop( oldNormValue, id );
+
+ /*
+ * If there is no value for id in this index due to our
+ * drop above we remove the oldRdnAttr from the existance idx
+ */
+ if ( null == idx.reverseLookup( id ) )
+ {
+ existanceIdx.drop( oldNormType, id );
+ }
+ }
}
}
}
Modified: directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java?rev=667762&r1=667761&r2=667762&view=diff
==============================================================================
--- directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java (original)
+++ directory/apacheds/trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java Sat Jun 14 02:22:48 2008
@@ -25,9 +25,11 @@
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
+import javax.naming.directory.SchemaViolationException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
@@ -338,7 +340,6 @@
@Test
public void testModifyRdnDifferentAttribute() throws NamingException
{
-
// Create a person, cn value is rdn
String cnVal = "Tori Amos";
String snVal = "Amos";
@@ -381,6 +382,40 @@
/**
+ * Modify DN of an entry, changing RDN from cn to sn,
+ * delete old RDn, must fail because cn can not be deleted.
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyRdnDifferentAttributeDeleteOldFails() throws NamingException
+ {
+ // Create a person, cn value is rdn
+ String cnVal = "Tori Amos";
+ String snVal = "Amos";
+ String oldRdn = "cn=" + cnVal;
+ Attributes attributes = this.getPersonAttributes( snVal, cnVal );
+ ctx.createSubcontext( oldRdn, attributes );
+
+ // modify Rdn from cn=... to sn=...
+ String newRdn = "sn=" + snVal;
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ try
+ {
+ ctx.rename( oldRdn, newRdn );
+ fail( "Rename must fail, mandatory attirbute cn can not be deleted." );
+ }
+ catch ( SchemaViolationException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Remove entry (use old rdn)
+ ctx.unbind( oldRdn );
+ }
+
+
+ /**
* Test for DIRSERVER-1086.
* Modify Rdn of an entry that has a child entry, delete its old rdn value.
* Ensure that the tree is not broken.
@@ -503,4 +538,476 @@
ctx.unbind( newRdn );
}
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify single valued RDN to a multi valued RDN.
+ * - Old Rdn: cn
+ * - New Rdn: cn+sn
+ * - Keep old Rdn
+ * - Attributes: cn, sn, description must exist
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant1() throws NamingException
+ {
+ Attributes attributes = createPerson( "cn" );
+ String oldRdn = getRdn( attributes, "cn" );
+ String newRdn = getRdn( attributes, "cn", "sn" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+ ctx.rename( oldRdn, newRdn );
+
+ // Check whether new Entry exists
+ DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+ assertNotNull( newCtx );
+
+ // Check attributes
+ Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify single valued RDN to a multi valued RDN.
+ * - Old Rdn: cn
+ * - New Rdn: cn+sn
+ * - Delete old Rdn
+ * - Attributes: cn, sn, description must exist
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant2() throws NamingException
+ {
+ Attributes attributes = createPerson( "cn" );
+ String oldRdn = getRdn( attributes, "cn" );
+ String newRdn = getRdn( attributes, "cn", "sn" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ ctx.rename( oldRdn, newRdn );
+
+ // Check whether new Entry exists
+ DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+ assertNotNull( newCtx );
+
+ // Check attributes
+ Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify single valued RDN to a multi valued RDN.
+ * - Old Rdn: description
+ * - New Rdn: cn+sn
+ * - Keep old Rdn
+ * - Attributes: cn, sn, description must exist
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant3() throws NamingException
+ {
+ Attributes attributes = createPerson( "description" );
+ String oldRdn = getRdn( attributes, "description" );
+ String newRdn = getRdn( attributes, "cn", "sn" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+ ctx.rename( oldRdn, newRdn );
+
+ // Check whether new Entry exists
+ DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+ assertNotNull( newCtx );
+
+ // Check attributes
+ Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify single valued RDN to a multi valued RDN.
+ * - Old Rdn: description
+ * - New Rdn: cn+sn
+ * - Delete old Rdn
+ * - Attributes: cn, sn must exist; descriptions must not exist
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant4() throws NamingException
+ {
+ Attributes attributes = createPerson( "description" );
+ String oldRdn = getRdn( attributes, "description" );
+ String newRdn = getRdn( attributes, "cn", "sn" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ ctx.rename( oldRdn, newRdn );
+
+ // Check whether new Entry exists
+ DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+ assertNotNull( newCtx );
+
+ // Check attributes
+ Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+ assertNull( descriptionAttr );
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify single valued RDN to a multi valued RDN.
+ * - Old Rdn: cn
+ * - New Rdn: sn+telephoneNumber
+ * - Keep old Rdn
+ * - Attributes: cn, sn, description, telephoneNumber must exist
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant5() throws NamingException
+ {
+ Attributes attributes = createPerson( "cn" );
+ attributes.put( "telephoneNumber", "12345" );
+ String oldRdn = getRdn( attributes, "cn" );
+ String newRdn = getRdn( attributes, "sn", "telephoneNumber" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+ ctx.rename( oldRdn, newRdn );
+
+ // Check whether new Entry exists
+ DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+ assertNotNull( newCtx );
+
+ // Check attributes
+ Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+ Attribute telephoneNumberAttr = newCtx.getAttributes( "" ).get( "telephoneNumber" );
+ assertEquals( 1, telephoneNumberAttr.size() );
+ assertTrue( telephoneNumberAttr.contains( "12345" ) );
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify single valued RDN to a multi valued RDN.
+ * - Old Rdn: cn
+ * - New Rdn: sn+telephoneNumber
+ * - Delete old Rdn
+ * - Must fail with schema violation, cn cannot be deleted
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant6() throws NamingException
+ {
+ Attributes attributes = createPerson( "cn" );
+ attributes.put( "telephoneNumber", "12345" );
+ String oldRdn = getRdn( attributes, "cn" );
+ String newRdn = getRdn( attributes, "sn", "telephoneNumber" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ try
+ {
+ ctx.rename( oldRdn, newRdn );
+ fail( "Rename must fail, cn can not be deleted from a person." );
+ }
+ catch ( SchemaViolationException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Check that entry was not changed
+ try
+ {
+ ctx.lookup( newRdn );
+ fail( "Previous rename failed as expected, entry must not exist" );
+ }
+ catch ( NameNotFoundException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Check that entry was not changed
+ DirContext oldCtx = ( DirContext ) ctx.lookup( oldRdn );
+ assertNotNull( oldCtx );
+ Attribute cnAttr = oldCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = oldCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = oldCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+
+ // Remove entry (use old rdn)
+ ctx.unbind( oldRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify multi valued RDN to a single valued RDN.
+ * - Old Rdn: cn+sn
+ * - New Rdn: cn
+ * - Keep old Rdn
+ * - Attributes: cn, sn, description must exist
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant7() throws NamingException
+ {
+ Attributes attributes = createPerson( "cn", "sn" );
+ String oldRdn = getRdn( attributes, "cn", "sn" );
+ String newRdn = getRdn( attributes, "cn" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+ ctx.rename( oldRdn, newRdn );
+
+ // Check whether new Entry exists
+ DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+ assertNotNull( newCtx );
+
+ // Check attributes
+ Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Modify multi valued RDN to a single valued RDN.
+ * - Old Rdn: cn+sn
+ * - New Rdn: cn
+ * - Delete old Rdn
+ * - Must fail with schema violation, cn cannot be deleted
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyMultiValuedRdnVariant8() throws NamingException
+ {
+ Attributes attributes = createPerson( "cn", "sn" );
+ String oldRdn = getRdn( attributes, "cn", "sn" );
+ String newRdn = getRdn( attributes, "cn" );
+
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ try
+ {
+ ctx.rename( oldRdn, newRdn );
+ fail( "Rename must fail, cn can not be deleted from a person." );
+ }
+ catch ( SchemaViolationException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Check that entry was not changed
+ try
+ {
+ ctx.lookup( newRdn );
+ fail( "Previous rename failed as expected, entry must not exist" );
+ }
+ catch ( NameNotFoundException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Check that entry was not changed
+ DirContext oldCtx = ( DirContext ) ctx.lookup( oldRdn );
+ assertNotNull( oldCtx );
+ Attribute cnAttr = oldCtx.getAttributes( "" ).get( "cn" );
+ assertEquals( 1, cnAttr.size() );
+ assertTrue( cnAttr.contains( "Tori Amos" ) );
+ Attribute snAttr = oldCtx.getAttributes( "" ).get( "sn" );
+ assertEquals( 1, snAttr.size() );
+ assertTrue( snAttr.contains( "Amos" ) );
+ Attribute descriptionAttr = oldCtx.getAttributes( "" ).get( "description" );
+ assertEquals( 1, descriptionAttr.size() );
+
+ // Remove entry (use old rdn)
+ ctx.unbind( oldRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Tries to rename+deleteOldRdn an entry that has an operational attribute
+ * in its RDN. Must fail because an operational attribute can not be
+ * deleted.
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyRdnOperationalAttribute() throws NamingException
+ {
+ // create the entry
+ Attributes attributes = createPerson( "cn" );
+ String oldRdn = getRdn( attributes, "cn" );
+
+ // read createTimestamp
+ String createTimestamp = ( String ) ctx.getAttributes( oldRdn, new String[]
+ { "createTimestamp" } ).get( "createTimestamp" ).get();
+
+ // rename to createTimstamp=YYYYMMDDHHMMSSZ
+ String newRdn = "createTimestamp=" + createTimestamp;
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+ ctx.rename( oldRdn, newRdn );
+
+ // rename back to old Rdn, enable deleteOldRdn,
+ // must fail with NoPermisionException
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ try
+ {
+ ctx.rename( newRdn, oldRdn );
+ fail( "Rename must fail, operational attribute createTimestamp can not be deleted." );
+ }
+ catch ( NoPermissionException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ /**
+ * Test for DIRSERVER-1162 and DIRSERVER-1085.
+ *
+ * Tries to rename+deleteOldRdn an entry that has the structural object class
+ * person in its RDN (objectClass=person,ou=system). Must fail because the
+ * structural object class can not be deleted.
+ *
+ * @throws NamingException
+ */
+ @Test
+ public void testModifyRdnObjectClassAttribute() throws NamingException
+ {
+ // create the entry
+ Attributes attributes = createPerson( "cn" );
+ String oldRdn = getRdn( attributes, "cn" );
+
+ // rename to objectClass=person
+ String newRdn = "objectClass=person";
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+ ctx.rename( oldRdn, newRdn );
+
+ // rename back to old Rdn, enable deleteOldRdn,
+ // must fail with NoPermisionException
+ ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+ try
+ {
+ ctx.rename( newRdn, oldRdn );
+ fail( "Rename must fail, structural objectClass person can not be deleted." );
+ }
+ catch ( SchemaViolationException ignored )
+ {
+ // expected behaviour
+ }
+
+ // Remove entry (use new rdn)
+ ctx.unbind( newRdn );
+ }
+
+
+ private String getRdn( Attributes attributes, String... rdnTypes ) throws NamingException
+ {
+ String rdn = "";
+ for ( String type : rdnTypes )
+ {
+ rdn += type + "=" + attributes.get( type ).get() + "+";
+ }
+ rdn = rdn.substring( 0, rdn.length() - 1 );
+ return rdn;
+ }
+
+
+ private Attributes createPerson( String... rdnTypes ) throws NamingException
+ {
+ Attributes attributes = new AttributesImpl();
+ Attribute attribute = new AttributeImpl( "objectClass" );
+ attribute.add( "top" );
+ attribute.add( "person" );
+ attributes.put( attribute );
+ attributes.put( "cn", "Tori Amos" );
+ attributes.put( "sn", "Amos" );
+ attributes.put( "description", "Tori Amos is a person." );
+
+ String rdn = getRdn( attributes, rdnTypes );
+
+ ctx.createSubcontext( rdn, attributes );
+
+ return attributes;
+ }
+
}