You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2023/03/05 04:57:48 UTC

[directory-ldap-api] branch master updated: Fixed a multi-component DN parsing issue: the ordering was not correct

This is an automated email from the ASF dual-hosted git repository.

elecharny pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-ldap-api.git


The following commit(s) were added to refs/heads/master by this push:
     new 5df9f63c2 Fixed a multi-component DN parsing issue: the ordering was not correct
5df9f63c2 is described below

commit 5df9f63c22f3af73fb898cb7b9551e513c12b6a1
Author: emmanuel lecharny <el...@apache.org>
AuthorDate: Sun Mar 5 05:57:36 2023 +0100

    Fixed a multi-component DN parsing issue: the ordering was not correct
---
 .../directory/api/ldap/model/name/DnTest.java      |  88 ++++---
 .../api/ldap/model/name/FastParserRdnTest.java     |   2 +-
 .../directory/api/ldap/model/name/RdnTest.java     |  10 +-
 ...stParserRdnTest.java => SchemaAwareDnTest.java} |  85 +++----
 ldap/model/src/main/antlr/distinguishedName.g      | 252 +++++++++++++--------
 .../api/ldap/model/ldif/LdifRevertor.java          |   2 +-
 .../apache/directory/api/ldap/model/name/Ava.java  |  50 +++-
 .../apache/directory/api/ldap/model/name/Dn.java   |  59 ++++-
 .../api/ldap/model/name/FastDnParser.java          |   2 +-
 .../apache/directory/api/ldap/model/name/Rdn.java  |  21 +-
 .../api/ldap/model/ldif/LdifRevertorTest.java      |  16 +-
 .../api/ldap/model/name/DnParserTest.java          |   4 +-
 12 files changed, 356 insertions(+), 235 deletions(-)

diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java
index 8b21fe030..984ae2b2b 100644
--- a/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java
@@ -2040,13 +2040,11 @@ public class DnTest
 
         Dn result = new Dn( schemaManager, name );
 
-        assertTrue( 
-            result.equals( 
-                "0.9.2342.19200300.100.1.25=and some animals+ou=some people,0.9.2342.19200300.100.1.25=eXample,dc=cOm" ) );
-        assertTrue( ( result )
-            .getName()
-            .equals(
-                "ou= Some   People   + 0.9.2342.19200300.100.1.25=  And   Some anImAls,0.9.2342.19200300.100.1.25 = eXample,dc= cOm" ) );
+        assertEquals( result,
+                "0.9.2342.19200300.100.1.25=and some animals+ou=some people,0.9.2342.19200300.100.1.25=eXample,dc=cOm" );
+        assertEquals(
+                "ou= Some   People   + 0.9.2342.19200300.100.1.25=  And   Some anImAls,0.9.2342.19200300.100.1.25 = eXample,dc= cOm",
+                result.getName() );
     }
 
 
@@ -2674,27 +2672,27 @@ public class DnTest
         Dn dn = new Dn( "  ou  =  Example + cn = TEST ,  ou  =  COM " );
 
         new Dn( schemaManager, dn );
-        assertEquals( "ou=Example+cn=TEST,ou=COM", dn.getEscaped() );
+        assertEquals( "cn=TEST+ou=Example,ou=COM", dn.getEscaped() );
         assertEquals( "  ou  =  Example + cn = TEST ,  ou  =  COM ", dn.getName() );
 
         Rdn rdn = dn.getRdn();
-        assertEquals( "ou", rdn.getNormType() );
-        assertEquals( "ou=Example+cn=TEST", rdn.getEscaped() );
-        assertEquals( "ou", rdn.getType() );
-        assertEquals( "Example", rdn.getValue() );
+        assertEquals( "cn", rdn.getNormType() );
+        assertEquals( "cn=TEST+ou=Example", rdn.getEscaped() );
+        assertEquals( "cn", rdn.getType() );
+        assertEquals( "TEST", rdn.getValue() );
         assertEquals( "  ou  =  Example + cn = TEST ", rdn.getName() );
 
         // The first ATAV
         Ava atav = rdn.getAva();
 
-        assertEquals( "ou=Example", atav.getEscaped() );
-        assertEquals( "ou", atav.getNormType() );
-        assertEquals( "Example", atav.getValue().getString() );
+        assertEquals( "cn=TEST", atav.getEscaped() );
+        assertEquals( "cn", atav.getNormType() );
+        assertEquals( "TEST", atav.getValue().getString() );
 
-        assertEquals( "ou", atav.getType() );
-        assertEquals( "Example", atav.getValue().getString() );
+        assertEquals( "cn", atav.getType() );
+        assertEquals( "TEST", atav.getValue().getString() );
 
-        assertEquals( "  ou  =  Example ", atav.getName() );
+        assertEquals( " cn = TEST ", atav.getName() );
 
         assertEquals( 2, rdn.size() );
 
@@ -2775,29 +2773,29 @@ public class DnTest
         // ------------------------------------------------------------------
         // Before normalization
         assertEquals( "  OU  =  Ex\\+mple + cn = T\\+ST\\  ,  ou  =  COM ", dn.getName() );
-        assertEquals( "OU=Ex\\+mple+cn=T\\+ST\\ ,ou=COM", dn.getEscaped() );
+        assertEquals( "cn=T\\+ST\\ +OU=Ex\\+mple,ou=COM", dn.getEscaped() );
 
         // Check the first Rdn
         Rdn rdn = dn.getRdn();
         assertEquals( "  OU  =  Ex\\+mple + cn = T\\+ST\\  ", rdn.getName() );
-        assertEquals( "OU=Ex\\+mple+cn=T\\+ST\\ ", rdn.getEscaped() );
+        assertEquals( "cn=T\\+ST\\ +OU=Ex\\+mple", rdn.getEscaped() );
 
-        assertEquals( "OU", rdn.getType() );
-        assertEquals( "ou", rdn.getNormType() );
+        assertEquals( "cn", rdn.getType() );
+        assertEquals( "cn", rdn.getNormType() );
 
-        assertEquals( "Ex+mple", rdn.getValue() );
-        assertEquals( "Ex+mple", rdn.getAva().getValue().getString() );
+        assertEquals( "T+ST ", rdn.getValue() );
+        assertEquals( "T+ST ", rdn.getAva().getValue().getString() );
 
         // The first ATAV
         Ava atav = rdn.getAva();
 
-        assertEquals( "  OU  =  Ex\\+mple ", atav.getName() );
-        assertEquals( "OU=Ex\\+mple", atav.getEscaped() );
+        assertEquals( " cn = T\\+ST\\  ", atav.getName() );
+        assertEquals( "cn=T\\+ST\\ ", atav.getEscaped() );
 
-        assertEquals( "ou", atav.getNormType() );
-        assertEquals( "OU", atav.getType() );
+        assertEquals( "cn", atav.getNormType() );
+        assertEquals( "cn", atav.getType() );
 
-        assertEquals( "Ex+mple", atav.getValue().getString() );
+        assertEquals( "T+ST ", atav.getValue().getString() );
 
         assertEquals( 2, rdn.size() );
 
@@ -2824,47 +2822,47 @@ public class DnTest
         new Dn( schemaManager, dn );
 
         assertEquals( "  OU  =  Ex\\+mple + cn = T\\+ST\\  ,  ou  =  COM ", dn.getName() );
-        assertEquals( "OU=Ex\\+mple+cn=T\\+ST\\ ,ou=COM", dn.getEscaped() );
+        assertEquals( "cn=T\\+ST\\ +OU=Ex\\+mple,ou=COM", dn.getEscaped() );
 
         // Check the first Rdn
         rdn = dn.getRdn();
         assertEquals( "  OU  =  Ex\\+mple + cn = T\\+ST\\  ", rdn.getName() );
-        assertEquals( "OU=Ex\\+mple+cn=T\\+ST\\ ", rdn.getEscaped() );
+        assertEquals( "cn=T\\+ST\\ +OU=Ex\\+mple", rdn.getEscaped() );
 
-        assertEquals( "OU", rdn.getType() );
-        assertEquals( "ou", rdn.getNormType() );
+        assertEquals( "cn", rdn.getType() );
+        assertEquals( "cn", rdn.getNormType() );
 
-        assertEquals( "Ex+mple", rdn.getValue() );
+        assertEquals( "T+ST ", rdn.getValue() );
 
         // The first ATAV
         atav = rdn.getAva();
 
-        assertEquals( "  OU  =  Ex\\+mple ", atav.getName() );
-        assertEquals( "OU=Ex\\+mple", atav.getEscaped() );
+        assertEquals( " cn = T\\+ST\\  ", atav.getName() );
+        assertEquals( "cn=T\\+ST\\ ", atav.getEscaped() );
 
-        assertEquals( "ou", atav.getNormType() );
-        assertEquals( "OU", atav.getType() );
+        assertEquals( "cn", atav.getNormType() );
+        assertEquals( "cn", atav.getType() );
 
-        assertEquals( "Ex+mple", atav.getValue().getString() );
+        assertEquals( "T+ST ", atav.getValue().getString() );
 
         assertEquals( 2, rdn.size() );
 
         // The second ATAV
         for ( Ava ava : rdn )
         {
-            if ( "Ex+mple".equals( ava.getValue().getString() ) )
+            if ( "T+ST ".equals( ava.getValue().getString() ) )
             {
                 // Skip the first one
                 continue;
             }
 
-            assertEquals( " cn = T\\+ST\\  ", ava.getName() );
-            assertEquals( "cn=T\\+ST\\ ", ava.getEscaped() );
+            assertEquals( "  OU  =  Ex\\+mple ", ava.getName() );
+            assertEquals( "OU=Ex\\+mple", ava.getEscaped() );
 
-            assertEquals( "cn", ava.getType() );
-            assertEquals( "cn", ava.getNormType() );
+            assertEquals( "OU", ava.getType() );
+            assertEquals( "ou", ava.getNormType() );
 
-            assertEquals( "T+ST ", ava.getValue().getString() );
+            assertEquals( "Ex+mple", ava.getValue().getString() );
         }
     }
 
diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java
index 998491aed..ecdbd8362 100644
--- a/integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java
@@ -85,7 +85,7 @@ public class FastParserRdnTest
             errors += "\nRdn.getName fails '" + rdn.getName() + "'";
         }
         
-        if ( !"Cn=TEST".equals( rdn.getNormName() ) )
+        if ( !"cn=TEST".equals( rdn.getNormName() ) )
         {
             errors = "\nRdn.getNormName fails '" + rdn.getNormName() + "'";
         }
diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java
index 9ec5139ae..0db15aca5 100644
--- a/integ/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java
@@ -153,22 +153,22 @@ public class RdnTest
             errors += "\nRdn.getName fails '" + rdn.getName() + "'";
         }
         
-        if ( !"OU=Exemple \\+ Rdn\u00E4\\ +cn=TEST" .equals( rdn.getEscaped() ) )
+        if ( !"cn=TEST+OU=Exemple \\+ Rdn\u00E4\\ " .equals( rdn.getEscaped() ) )
         {
             errors = "\nRdn.getEscaped fails '" + rdn.getEscaped() + "'";
         }
         
-        if ( !"ou".equals( rdn.getNormType() ) )
+        if ( !"cn".equals( rdn.getNormType() ) )
         {
             errors += "\nRdn.getNormType fails '" + rdn.getNormType() + "'";
         }
         
-        if ( !"Exemple + Rdn\u00E4 ".equals( rdn.getValue() ) )
+        if ( !"TEST".equals( rdn.getValue() ) )
         {
-            errors += "\nRdn.getEscaped fails '" + rdn.getValue() + "'";
+            errors += "\nRdn.getValue fails '" + rdn.getValue() + "'";
         }
         
-        if ( !"OU".equals( rdn.getType() ) )
+        if ( !"cn".equals( rdn.getType() ) )
         {
             errors += "\nRdn.getUpType fails '" + rdn.getType() + "'";
         }
diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareDnTest.java
similarity index 51%
copy from integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java
copy to integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareDnTest.java
index 998491aed..a00abf6e0 100644
--- a/integ/src/test/java/org/apache/directory/api/ldap/model/name/FastParserRdnTest.java
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareDnTest.java
@@ -6,95 +6,68 @@
  *  to you under the Apache License, Version 2.0 (the
  *  "License"); you may not use this file except in compliance
  *  with the License.  You may obtain a copy of the License at
- * 
+ *
  *    https://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  *  Unless required by applicable law or agreed to in writing,
  *  software distributed under the License is distributed on an
  *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  *  KIND, either express or implied.  See the License for the
  *  specific language governing permissions and limitations
  *  under the License.
- * 
+ *
  */
 package org.apache.directory.api.ldap.model.name;
 
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import org.apache.directory.api.ldap.model.exception.LdapException;
 import org.apache.directory.api.ldap.model.schema.SchemaManager;
 import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.Execution;
 import org.junit.jupiter.api.parallel.ExecutionMode;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 /**
- * Tests for the schemaAware Rdn class when using teh FastParser
- * 
+ * Test the class Dn
+ *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 @Execution( ExecutionMode.CONCURRENT )
-public class FastParserRdnTest
+public class SchemaAwareDnTest
 {
     private static SchemaManager schemaManager;
 
 
+    /**
+     * Initialize OIDs maps for normalization
+     */
     @BeforeAll
     public static void setup() throws Exception
     {
         schemaManager = new DefaultSchemaManager();
     }
-    
-    
-    @Test
-    public void testSimpleRdnValueSchemaAware() throws LdapException
-    {
-        String errors = null;
-        
-        Rdn rdn = new Rdn( schemaManager, "CommonName = TEST " );
-        
-        if ( !"CommonName = TEST ".equals( rdn.getName() ) )
-        {
-            errors += "\nRdn.getName fails '" + rdn.getName() + "'";
-        }
-        
-        if ( !"2.5.4.3= test ".equals( rdn.getNormName() ) )
-        {
-            errors = "\nRdn.getNormName fails '" + rdn.getNormName() + "'";
-        }
-        
-        if ( !"TEST".equals( rdn.getValue( "cn" ) ) )
-        {
-            errors += "\nRdn.getNormValue( 'cn' ) fails '" + ( String ) rdn.getValue( "cn" ) + "'";
-        }
-        
-        assertEquals( null, errors );
-    }
-    
-    
+
+    /**
+     * test a simple Dn with multiple NameComponents : cn=Kate + sn=Bush,ou=system
+     */
     @Test
-    public void testSimpleRdnValueNullSchemaManager() throws LdapException
+    @Disabled
+    public void testDnSimpleMultivaluedAttribute() throws LdapException
     {
-        String errors = null;
-        
-        Rdn rdn = new Rdn( (SchemaManager)null, "Cn= TEST " );
-        
-        if ( !"Cn= TEST ".equals( rdn.getName() ) )
-        {
-            errors += "\nRdn.getName fails '" + rdn.getName() + "'";
-        }
-        
-        if ( !"Cn=TEST".equals( rdn.getNormName() ) )
-        {
-            errors = "\nRdn.getNormName fails '" + rdn.getNormName() + "'";
-        }
-        
-        if ( !"TEST".equals( rdn.getValue( "cn" ) ) )
-        {
-            errors += "\nRdn.getNormValue( 'cn' ) fails '" + ( String ) rdn.getValue( "cn" ) + "'";
-        }
+        Dn dn = new Dn( schemaManager, "cn=Kate+sn=Bush,ou=system" );
+
+        assertTrue( Dn.isValid( "cn=Kate+sn=Bush,ou=system" ) );
+        assertEquals( "2.5.4.3= kate +2.5.4.4= bush ,2.5.4.11= system ", dn.getNormName() );
+
+        Dn dn2 = new Dn( schemaManager, "sn=Bush+cn=Kate,ou=system" );
+
+        assertTrue( Dn.isValid( "sn=Bush+cn=Kate,ou=system" ) );
+        assertEquals( "2.5.4.3= kate +2.5.4.4= bush ,2.5.4.11= system ", dn2.getNormName() );
         
-        assertEquals( null, errors );
+        assertEquals( dn, dn2 );
     }
 }
diff --git a/ldap/model/src/main/antlr/distinguishedName.g b/ldap/model/src/main/antlr/distinguishedName.g
index 04b7d646a..c71c1eef7 100644
--- a/ldap/model/src/main/antlr/distinguishedName.g
+++ b/ldap/model/src/main/antlr/distinguishedName.g
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
 import javax.naming.NameParser;
 import org.apache.directory.api.ldap.model.entry.Value;
 import org.apache.directory.api.ldap.model.schema.SchemaManager;
@@ -164,6 +165,32 @@ options    {
         // A flag set to false if we have a binary value
         boolean isHR = true;
     }
+
+
+    private String createNormAva( Ava ava )
+    {
+        StringBuilder rdnNormStr = new StringBuilder();
+        Value value = ava.getValue(); 
+        AttributeType attributeType = ava.getAttributeType();
+        rdnNormStr.append( ava.getNormType() );
+        rdnNormStr.append( '=' );
+
+        if ( value != null )
+        {
+            if ( value.getNormalized() != null )
+            {
+                rdnNormStr.append( value.getNormalized() );
+            }
+            else
+            {
+                // We can't tell if the value is HR or not. 
+                // Use the Value User Provided value
+                rdnNormStr.append( value.getUpValue() );
+            }
+        }
+
+        return rdnNormStr.toString();
+    }
 }
 
 
@@ -302,6 +329,7 @@ relativeDistinguishedNames [SchemaManager schemaManager, List<Rdn> rdns] returns
     }
     ;
 
+
 /**
  * Parses a Rdn string.
  *
@@ -335,120 +363,123 @@ relativeDistinguishedName [SchemaManager schemaManager, Rdn rdn]
 
         // The rdnStr variable is used to gather the full RDN string
         // as provided
-        StringBuilder rdnStr = new StringBuilder();
+        StringBuilder rdnUpStr = new StringBuilder();
         StringBuilder rdnNormStr = new StringBuilder();
         int avaPos = 0;
-        Ava currentAva;
         AttributeType attributeType;
         Value val;
+        Ava ava = new Ava( schemaManager);
+        String upAva;
+
+        // The list of parsed Ava for a later post-processing
+        List<Ava> avas = new ArrayList<>();
     }
     :
     (
-        tmp = attributeTypeAndValue[schemaManager, rdn] 
+        // The first AVA
+        upAva = attributeTypeAndValue[schemaManager, ava] 
         {
-            rdnStr.append( tmp );
-            currentAva = rdn.getAva( avaPos );
-            
-            attributeType = currentAva.getAttributeType();
-            
-            if ( schemaManager == null )
+            ava.hashCode();
+            rdnUpStr.append( upAva );
+            avas.add( ava );
+        }
+        (
+            PLUS 
             {
-                rdnNormStr.append( currentAva.getNormType() );
-                rdnNormStr.append( '=' );
+                ava = new Ava( schemaManager);
             }
-            
-            val = currentAva.getValue();
-            
-            if ( ( schemaManager == null ) && ( val != null ) )
+            upAva = attributeTypeAndValue[schemaManager, ava] 
             {
-                if ( val.getNormalized() != null )
-                {
-                    rdnNormStr.append( val.getNormalized() );
-                }
-                else
+                ava.hashCode();
+                rdnUpStr.append( '+' ).append( upAva );
+
+                try 
                 {
-                    // We can't tell if the value is HR or not. 
-                    // Use the Value User Provided value
-                    rdnNormStr.append( val.getUpValue() );
+                    Rdn.addOrdered( avas, ava );
                 }
-            }
-        }
-        (
-            PLUS 
-            { 
-                rdnStr.append( '+' ); 
-                
-                if ( schemaManager == null )
+                catch ( LdapInvalidDnException lide )
                 {
-                    rdnNormStr.append( '+' );
+                    throw new SemanticException( lide.getMessage() );
                 }
-                
-                avaPos++;
             }
+        )*
+    )
+    {
+        // Now, build the Rdn
+        switch ( avas.size() )
+        {
+            case 0:
+                // Can't be...
+            case 1:
+                // One single Ava
+                rdn.upName = rdnUpStr.toString();
+                rdn.normName = createNormAva( ava );
+                rdn.ava = ava;
+                rdn.avaType = ava.getType();
+                rdn.nbAvas = 1; 
+                break;
 
-            tmp = attributeTypeAndValue[schemaManager, rdn] 
-            {
-                rdnStr.append( tmp );
-                currentAva = rdn.getAva( avaPos );
-                attributeType = currentAva.getAttributeType();
-            
-                if ( schemaManager == null )
+            default:
+                rdn.nbAvas = avas.size();
+                rdn.avaTypes = new HashMap<String, List<Ava>>();
+                boolean isFirst = true;
+
+                for ( Ava parsedAva : avas )
                 {
-                    rdnNormStr.append( tmp );
+                    if ( isFirst  )
+                    {
+                        isFirst = false;
+                    }
+                    else
+                    {
+                        rdnNormStr.append( '+' );
+                    }
+
+                    String type;
+
+                    if ( schemaManager != null )
+                    {
+                        type = parsedAva.getAttributeType().getOid();
+                        rdnNormStr.append( type );
+                    }
+                    else
+                    {
+                        type = parsedAva.normType;
+                        rdnNormStr.append( type );
+                    }
+
                     rdnNormStr.append( '=' );
-                }
-            
-                val = currentAva.getValue();
-            
-                if ( ( schemaManager == null ) && ( val != null ) )
-                {
-                    if ( val.getNormalized() != null )
+
+                    val = parsedAva.getValue();
+
+                    if ( ( val != null ) && ( val.getNormalized() != null ) )
                     {
                         rdnNormStr.append( val.getNormalized() );
                     }
                     else
                     {
-                        // We can't tell if the value is HR or not. 
-                        // Use the Value User Provided value
                         rdnNormStr.append( val.getUpValue() );
                     }
-                }
-            }
-        )*
-    )
-    {
-        rdn.hashCode();
-        rdn.setUpName( rdnStr.toString() );
-        
-        if ( schemaManager != null )
-        {
-            // process the multi-value RDN, ordering them by attributes
-            boolean isFirst = true;
-            
-            for ( Ava ava : rdn )
-            {
-                if ( isFirst )
-                {
-                    isFirst = false;
-                }
-                else
-                {
-                    rdnNormStr.append( '+' );
-                }
-                
-                rdnNormStr.append( ava.getAttributeType().getOid() );
-                rdnNormStr.append( '=' );
-                
-                val = ava.getValue();
 
-                if ( ( val != null ) && ( val.getNormalized() != null ) )
-                {
-                    rdnNormStr.append( val.getNormalized() );
+                    List<Ava> avaList = rdn.avaTypes.get( type );
+
+                    if ( avaList == null )
+                    {
+                        avaList = new ArrayList<>();
+                    }
+
+                    avaList.add( parsedAva );
+                    rdn.avaTypes.put( type, avaList );
                 }
-            }
+
+                rdn.upName = rdnUpStr.toString();
+                rdn.normName = rdnNormStr.toString();
+                rdn.avas = avas;
+
+                break;
         }
-        
-        rdn.setNormName( rdnNormStr.toString() );
+
+        rdn.hashCode();
     }
     ;
     
@@ -465,12 +496,12 @@ relativeDistinguishedName [SchemaManager schemaManager, Rdn rdn]
  * </pre>
  *
  * @param schemaManager The SchemaManager
- * @param rdn The Rdn to update
+ * @param ava The parsed Ava
  * @return The user provided Ava
  * @throws RecognitionException If the token is invalid
  * @throws TokenStreamException When we weren't able to fetch a token
  */
-attributeTypeAndValue [SchemaManager schemaManager, Rdn rdn] returns [String upNameStr]
+attributeTypeAndValue [SchemaManager schemaManager, Ava ava] returns [String upNameStr]
     {
         matchedProduction( "attributeTypeAndValue()" );
         String type = null;
@@ -492,7 +523,6 @@ attributeTypeAndValue [SchemaManager schemaManager, Rdn rdn] returns [String upN
                 // cannot be processed
                 rdnUpName.append( value.upValue );
                 AttributeType attributeType = null;
-                Ava ava = null;
 
                 if ( schemaManager != null )
                 {
@@ -502,6 +532,14 @@ attributeTypeAndValue [SchemaManager schemaManager, Rdn rdn] returns [String upN
                     }
 
                     attributeType = schemaManager.getAttributeType( type );
+                    ava.attributeType = attributeType;
+                    ava.upType = type;
+                    ava.normType = attributeType.getOid();
+                }
+                else
+                {
+                    ava.upType = type;
+                    ava.normType = Strings.lowerCaseAscii( Strings.trim( type ) );
                 }
 
                 if ( ( ( attributeType != null ) && attributeType.isHR() ) || value.isHR )
@@ -538,17 +576,45 @@ attributeTypeAndValue [SchemaManager schemaManager, Rdn rdn] returns [String upN
                         }
                     }
 
-                    if ( ava == null )
+                    if ( attributeType != null )
+                    {
+                        try 
+                        {
+                            if ( ava == null )
+                            {
+                                ava.upName = rdnUpName.toString();
+                                ava.value = new Value( attributeType, Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
+                            }
+                            else
+                            {
+                                ava.upName = rdnUpName.toString();
+                                ava.value = new Value( attributeType, Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
+                            }
+                        }
+                        catch ( LdapInvalidAttributeValueException liave )
+                        {
+                            throw new SemanticException( liave.getMessage() );
+                        }
+                    }
+                    else
                     {
-                        ava = new Ava( schemaManager, type, rdnUpName.toString(), Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
+                        if ( ava == null )
+                        {
+                            ava.upName = rdnUpName.toString();
+                            ava.value = new Value( Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
+                        }
+                        else
+                        {
+                            ava.upName = rdnUpName.toString();
+                            ava.value = new Value( Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
+                        }
                     }
                 }
                 else
                 {
-                    ava = new Ava( schemaManager, type, rdnUpName.toString(), value.bytes.copyOfUsedBytes() );
+                    ava.upName = rdnUpName.toString();
+                    ava.value = new Value( value.bytes.copyOfUsedBytes() );
                 }
-           
-                rdn.addAVA( schemaManager, ava );
             }
             catch ( LdapInvalidDnException e )
             {
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java
index 34721b31f..1604dad3a 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/LdifRevertor.java
@@ -426,7 +426,7 @@ public final class LdifRevertor
             reverted.setDn( newDn );
         }
 
-        reverted.setNewRdn( oldRdn.getName() );
+        reverted.setNewRdn( oldRdn.getNormName() );
 
         if ( newSuperior != null )
         {
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java
index 8feafbff8..03133d625 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java
@@ -77,19 +77,19 @@ public class Ava implements Externalizable, Cloneable, Comparable<Ava>
     private static final Logger LOG = LoggerFactory.getLogger( Ava.class );
 
     /** The normalized Name type */
-    private String normType;
+    /* package protected */ String normType;
 
     /** The user provided Name type */
-    private String upType;
+    /* package protected */ String upType;
 
     /** The value. It can be a String or a byte array */
-    private Value value;
+    /* package protected */ Value value;
 
     /** The user provided Ava */
-    private String upName;
+    /* package protected */ String upName;
 
     /** The attributeType if the Ava is schemaAware */
-    private AttributeType attributeType;
+    /* package protected */ AttributeType attributeType;
 
     /** the schema manager */
     private transient SchemaManager schemaManager;
@@ -1295,6 +1295,46 @@ public class Ava implements Externalizable, Cloneable, Comparable<Ava>
     {
         return attributeType;
     }
+    
+    
+    /* Package protected */ void setAttributeType( SchemaManager schemaManager, String upType )
+        throws LdapInvalidDnException
+    {
+        if ( schemaManager != null )
+        {
+            this.schemaManager = schemaManager;
+
+            try
+            {
+                attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
+            }
+            catch ( LdapException le )
+            {
+                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
+                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
+            }
+
+            normType = attributeType.getOid();
+        }
+        else
+        {
+            String upTypeTrimmed = Strings.trim( upType );
+
+            if ( Strings.isEmpty( upTypeTrimmed ) )
+            {
+                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
+                // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files.
+                // Let the caller log the exception if needed.
+                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
+            }
+            else
+            {
+                // In this case, we will use the upType instead
+                this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
+                this.upType = upType;
+            }
+        }
+    }
 
 
     private int compareValues( Ava that )
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java
index ecdb23bb0..40685134a 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java
@@ -107,6 +107,10 @@ public class Dn implements Iterable<Rdn>, Externalizable
 
     /** the schema manager */
     private transient SchemaManager schemaManager;
+    
+    /** Two constants used to trim the DN UpName */
+    private static final boolean LEFT = true;
+    private static final boolean RIGHT = false;
 
     /**
      * An iterator over RDNs
@@ -197,7 +201,7 @@ public class Dn implements Iterable<Rdn>, Externalizable
             this.rdns.add( new Rdn( schemaManager, rdn ) );
         }
 
-        toUpName();
+        upName = toUpName();
     }
 
 
@@ -755,7 +759,7 @@ public class Dn implements Iterable<Rdn>, Externalizable
 
 
     /**
-     * Get the descendant of a given DN, using the ancestr DN. Assuming that
+     * Get the descendant of a given DN, using the ancestor DN. Assuming that
      * a DN has two parts :<br>
      * DN = [descendant DN][ancestor DN]<br>
      * To get back the descendant from the full DN, you just pass the ancestor DN
@@ -807,6 +811,12 @@ public class Dn implements Iterable<Rdn>, Externalizable
             }
         }
 
+        // Short cut: if the last RDNs are equal, return ""
+        if ( rdns.get( 0 ).equals( rdnsAncestor.get( 0 ) ) )
+        {
+            return newDn;
+        }
+        
         for ( int i = 0; i < rdns.size() - length; i++ )
         {
             newDn.rdns.add( rdns.get( i ) );
@@ -862,25 +872,25 @@ public class Dn implements Iterable<Rdn>, Externalizable
      */
     public Dn getAncestorOf( Dn descendant ) throws LdapInvalidDnException
     {
-        if ( ( descendant == null ) || ( descendant.size() == 0 ) )
+        int length = descendant.size();
+
+        if ( ( descendant == null ) || ( length == 0 ) )
         {
             return this;
         }
 
-        if ( rdns.isEmpty() )
+        if ( rdns.isEmpty() || length == rdns.size() )
         {
             return EMPTY_DN;
         }
 
-        int length = descendant.size();
-
         if ( length > rdns.size() )
         {
             String message = I18n.err( I18n.ERR_13612_POSITION_NOT_IN_RANGE, length, rdns.size() );
             LOG.error( message );
             throw new ArrayIndexOutOfBoundsException( message );
         }
-
+        
         Dn newDn = new Dn( schemaManager );
         List<Rdn> rdnsDescendant = descendant.getRdns();
 
@@ -900,7 +910,7 @@ public class Dn implements Iterable<Rdn>, Externalizable
             newDn.rdns.add( rdns.get( i ) );
         }
 
-        newDn.toUpName();
+        newDn.upName = upName.substring( descendant.upName.length() + 1 );
 
         return newDn;
     }
@@ -993,6 +1003,7 @@ public class Dn implements Iterable<Rdn>, Externalizable
 
         clonedDn.rdns.add( 0, new Rdn( schemaManager, newRdn ) );
         clonedDn.toUpName();
+        clonedDn.upName = newRdn.upName + ',' + upName;
 
         return clonedDn;
     }
@@ -1021,10 +1032,40 @@ public class Dn implements Iterable<Rdn>, Externalizable
             newDn.rdns.add( rdns.get( i ) );
         }
 
-        newDn.toUpName();
+        newDn.upName = removeUpName( rdns.get( 0 ).upName, LEFT );
 
         return newDn;
     }
+    
+    
+    private String removeUpName( String removedUpName, boolean fromLeft )
+    {
+        int removedSize = removedUpName.length();
+        
+        if ( fromLeft )
+        {
+            for ( int i = removedSize; i < upName.length(); i++ )
+            {
+                if ( upName.charAt( i ) == ',' )
+                {
+                    return upName.substring( i + 1 );
+                }
+            }
+        }
+        else
+        {
+             for ( int i = upName.length() - removedSize; i > 0; i-- )
+             {
+                 if ( upName.charAt( i ) == ',' )
+                 {
+                     return upName.substring( 0, i - 1 );
+                 }
+             }
+        }
+        
+        // Nothing left
+        return Strings.EMPTY_STRING;
+    }
 
 
     /**
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java
index fd356123a..2f9c8d95b 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java
@@ -215,7 +215,7 @@ import org.apache.directory.api.util.Strings;
             }
             else
             {
-                sbNormName.append( type );
+                sbNormName.append( Strings.toLowerCaseAscii( type ) );
                 sbNormName.append( '=' );
                 sbNormName.append( upValue );
             }
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
index 842b652be..7c94b53e6 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
@@ -129,10 +129,10 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
     private static final long serialVersionUID = 1L;
 
     /** The User Provided Rdn */
-    private String upName = null;
+    /* package protected */ String upName = null;
     
     /** The normalized Rdn */
-    private String normName;
+    /* package protected */ String normName;
 
     /**
      * Stores all couple type = value. We may have more than one type, if the
@@ -140,31 +140,31 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
      * because we want the Avas to be sorted. An Ava may contain more than one
      * value. In this case, the values are String stored in a List.
      */
-    private transient List<Ava> avas = null;
+    /* package protected */ transient List<Ava> avas = null;
 
     /**
      * We also keep a set of types, in order to use manipulations. A type is
      * connected with the Ava it represents.
      */
-    private transient Map<String, List<Ava>> avaTypes;
+    /* package protected */ transient Map<String, List<Ava>> avaTypes;
 
     /**
      * We keep the type for a single valued Rdn, to avoid the creation of an HashMap
      */
-    private String avaType = null;
+    /* package protected */ String avaType = null;
 
     /**
      * A simple Ava is used to store the Rdn for the simple
      * case where we only have a single type=value. This will be 99.99% the
      * case. This avoids the creation of a HashMap.
      */
-    protected Ava ava = null;
+    /* package protected */ Ava ava = null;
 
     /**
      * The number of Avas. We store this number here to avoid complex
      * manipulation of Ava and Avas
      */
-    private int nbAvas = 0;
+    /* package protected */ int nbAvas = 0;
 
     /** CompareTo() results */
     public static final int UNDEFINED = Integer.MAX_VALUE;
@@ -225,6 +225,8 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
      */
     public Rdn( SchemaManager schemaManager, String rdn ) throws LdapInvalidDnException
     {
+        this.schemaManager = schemaManager;
+        
         if ( Strings.isNotEmpty( rdn ) )
         {
             // Parse the string. The Rdn will be updated.
@@ -530,7 +532,7 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
      * @param newAva The Ava to add
      * @return The list of Ava with the new Ava at the right position
      */
-    /*package protected*/ static List<Ava> addOrdered( List<Ava> avaList, Ava newAva )
+    /*package protected*/ static List<Ava> addOrdered( List<Ava> avaList, Ava newAva ) throws LdapInvalidDnException
     {
         if ( avaList == null )
         {
@@ -561,7 +563,8 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
             else if ( comp == 0 )
             {
                 found = true;
-                break;
+             
+                throw new LdapInvalidDnException( I18n.err( I18n.ERR_13626_INVALID_RDN_DUPLICATE_AVA, avaElem.normType ) );
             }
             else 
             {
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifRevertorTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifRevertorTest.java
index 788e5b1cc..bf92ee58a 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifRevertorTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/ldif/LdifRevertorTest.java
@@ -1575,7 +1575,7 @@ public class LdifRevertorTest
         assertEquals( "cn=joe+sn=plumber,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertTrue( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
     }
 
@@ -1624,7 +1624,7 @@ public class LdifRevertorTest
         assertEquals( "sn=joe+cn=big,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertFalse( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
 
         reversed = reverseds.get( 1 );
@@ -1686,7 +1686,7 @@ public class LdifRevertorTest
         assertEquals( "cn=joe+sn=plumber,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertTrue( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
     }
 
@@ -1735,7 +1735,7 @@ public class LdifRevertorTest
         assertEquals( "cn=joe+sn=big,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertFalse( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
 
         reversed = reverseds.get( 1 );
@@ -1797,7 +1797,7 @@ public class LdifRevertorTest
         assertEquals( "sn=joe+cn=test,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertTrue( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
     }
 
@@ -1846,7 +1846,7 @@ public class LdifRevertorTest
         assertEquals( "sn=big+cn=test,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertFalse( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
     }
 
@@ -1895,7 +1895,7 @@ public class LdifRevertorTest
         assertEquals( "sn=joe+cn=test,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertTrue( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
     }
 
@@ -1944,7 +1944,7 @@ public class LdifRevertorTest
         assertEquals( "sn=big+cn=test,ou=system", reversed.getDn().getName() );
         assertEquals( ChangeType.ModRdn, reversed.getChangeType() );
         assertFalse( reversed.isDeleteOldRdn() );
-        assertEquals( oldRdn.getName(), reversed.getNewRdn() );
+        assertEquals( oldRdn.getNormName(), reversed.getNewRdn() );
         assertNull( reversed.getNewSuperior() );
     }
 }
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java
index c82dc95ef..b5cc0d3a6 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java
@@ -401,7 +401,7 @@ public class DnParserTest
 
         assertEquals( "OU = Sales + CN =   J. Smith , O = Widget Inc. , C = US",
             nameRFC2253_2.getName(), "RFC2253_2 : " );
-        assertEquals( "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US", nameRFC2253_2.getEscaped(), "RFC2253_2 : " );
+        assertEquals( "CN=J. Smith+OU=Sales,O=Widget Inc.,C=US", nameRFC2253_2.getEscaped(), "RFC2253_2 : " );
     }
 
 
@@ -658,7 +658,7 @@ public class DnParserTest
         
         try
         {
-            String dnStr = "workforceID=2# + a=b";
+            String dnStr = "workforceID=2# + z=b";
             assertTrue( Dn.isValid( dnStr ) );
             
             Dn dn = new Dn( dnStr );