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;
+    }
+
 }