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 2016/02/18 21:35:56 UTC

svn commit: r1731136 - in /directory/shared/trunk/ldap: client/api/src/test/java/org/apache/directory/ldap/client/api/ model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/

Author: elecharny
Date: Thu Feb 18 20:35:55 2016
New Revision: 1731136

URL: http://svn.apache.org/viewvc?rev=1731136&view=rev
Log:
o Changed the way we anonymize values : we don't anymore pick random chars, the collision risk is way to high
o Added a case sensitive String anonymizer
o Added some more accurate tests

Added:
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/CaseSensitiveStringAnonymizer.java
Removed:
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/DefaultAnonymizer.java
Modified:
    directory/shared/trunk/ldap/client/api/src/test/java/org/apache/directory/ldap/client/api/LdifAnonymizerTest.java
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/AbstractAnonymizer.java
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/Anonymizer.java
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/BinaryAnonymizer.java
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/IntegerAnonymizer.java
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/StringAnonymizer.java
    directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/TelephoneNumberAnonymizer.java

Modified: directory/shared/trunk/ldap/client/api/src/test/java/org/apache/directory/ldap/client/api/LdifAnonymizerTest.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/test/java/org/apache/directory/ldap/client/api/LdifAnonymizerTest.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/test/java/org/apache/directory/ldap/client/api/LdifAnonymizerTest.java (original)
+++ directory/shared/trunk/ldap/client/api/src/test/java/org/apache/directory/ldap/client/api/LdifAnonymizerTest.java Thu Feb 18 20:35:55 2016
@@ -27,16 +27,17 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.Entry;
 import org.apache.directory.api.ldap.model.entry.Modification;
 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
 import org.apache.directory.api.ldap.model.entry.Value;
 import org.apache.directory.api.ldap.model.exception.LdapException;
-import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
 import org.apache.directory.api.ldap.model.ldif.LdifEntry;
 import org.apache.directory.api.ldap.model.ldif.LdifReader;
 import org.apache.directory.api.ldap.model.schema.SchemaManager;
 import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
-import org.junit.BeforeClass;
+import org.apache.directory.api.util.Strings;
+import org.junit.Before;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -53,13 +54,13 @@ import static org.junit.Assert.assertNot
  */
 public class LdifAnonymizerTest
 {
-    private static SchemaManager schemaManager;
+    private SchemaManager schemaManager;
     
-    private static LdifReader ldifReader;
+    private LdifReader ldifReader;
 
     
-    @BeforeClass
-    public static void setup()
+    @Before
+    public void setup()
     {
         schemaManager = null;
         
@@ -91,7 +92,7 @@ public class LdifAnonymizerTest
             "ObjectClass: top\n" +
             "objectClass: person\n" +
             "cn: emmanuel\n" +
-            "sn: elecharny\n"+
+            "sn: lecharnye\n"+
             "\n" +
             "dn: cn=emmanuel,dc=test,dc=example,dc=com\n" +
             "ObjectClass: top\n" +
@@ -118,7 +119,59 @@ public class LdifAnonymizerTest
         anonymizer.addNamingContext( "dc=acme,dc=com" );
         anonymizer.removeAnonAttributeType( schemaManager.getAttributeType( "sn" ) );
         
-        anonymizer.anonymize( ldif );
+        String result = anonymizer.anonymize( ldif );
+        
+        List<LdifEntry> entries = ldifReader.parseLdif( result );
+        
+        assertEquals( 3, entries.size() );
+        
+        // First entry
+        LdifEntry ldifEntry = entries.get( 0 );
+        assertTrue( ldifEntry.isEntry() );
+        
+        Entry entry = ldifEntry.getEntry();
+        assertEquals( 3, entry.size() );
+        
+        assertEquals( "cn=AAAA,dc=example,dc=com", entry.getDn().toString() );
+
+        Attribute cn = entry.get( "cn" );
+        assertEquals( "AAAA", cn.getString() );
+
+        Attribute sn = entry.get( "sn" );
+        assertEquals( "AAAA", sn.getString() );
+        
+        // Second entry
+        ldifEntry = entries.get( 1 );
+        assertTrue( ldifEntry.isEntry() );
+        
+        entry = ldifEntry.getEntry();
+        assertEquals( 3, entry.size() );
+        
+        assertEquals( "cn=AAAAAAAA,dc=acme,dc=com", entry.getDn().toString() );
+
+        cn = entry.get( "cn" );
+        assertEquals( "AAAAAAAA", cn.getString() );
+
+        sn = entry.get( "sn" );
+        assertEquals( "AAAAAAAAA", sn.getString() );
+        
+        // Third entry
+        ldifEntry = entries.get( 2 );
+        assertTrue( ldifEntry.isEntry() );
+        
+        entry = ldifEntry.getEntry();
+        assertEquals( 4, entry.size() );
+        
+        assertEquals( "cn=AAAAAAAA,dc=AAAA,dc=example,dc=com", entry.getDn().toString() );
+
+        cn = entry.get( "cn" );
+        assertEquals( "AAAAAAAA", cn.getString() );
+
+        sn = entry.get( "sn" );
+        assertEquals( "AAAAAAAAB", sn.getString() );
+
+        Attribute seeAlso = entry.get( "seeAlso" );
+        assertEquals( "cn=AAAAAAAA,dc=acme,dc=com", seeAlso.getString() );
     }
 
 
@@ -137,7 +190,31 @@ public class LdifAnonymizerTest
 
         LdifAnonymizer anonymizer = new LdifAnonymizer( schemaManager );
         anonymizer.addNamingContext( "dc=example,dc=com" );
-        anonymizer.anonymize( ldif );
+        String result = anonymizer.anonymize( ldif );
+        
+        List<LdifEntry> entries = ldifReader.parseLdif( result );
+        
+        assertEquals( 1, entries.size() );
+        
+        // Check the entry
+        LdifEntry ldifEntry = entries.get( 0 );
+        assertTrue( ldifEntry.isEntry() );
+        
+        Entry entry = ldifEntry.getEntry();
+        assertEquals( 4, entry.size() );
+        
+        // Here, we expect cn2 to be translated to AAA, because it was encountered in teh DN first
+        assertEquals( "cn=AAA+sn=AAAAAAAAA,dc=example,dc=com", entry.getDn().toString() );
+
+        Attribute cn = entry.get( "cn" );
+        assertEquals( 3, cn.size() );
+        assertTrue( cn.contains( "AAA", "AAB", "AAC" ) );
+
+        Attribute sn = entry.get( "sn" );
+        assertEquals( "AAAAAAAAA", sn.getString() );
+
+        Attribute givenname = entry.get( "givenname" );
+        assertEquals( "AAAA", givenname.getString() );
     }
 
 
@@ -152,12 +229,42 @@ public class LdifAnonymizerTest
                 "cn: cn2\n" +
                 "cn: cn3\n" +
                 "userPassword: test\n" +
+                "userPassword: tesu\n" +
                 "sn: elecharny\n" +
                 "givenname: test\n";
 
         LdifAnonymizer anonymizer = new LdifAnonymizer( schemaManager );
         anonymizer.addNamingContext( "dc=example,dc=com" );
-        anonymizer.anonymize( ldif );
+        String result = anonymizer.anonymize( ldif );
+        
+        List<LdifEntry> entries = ldifReader.parseLdif( result );
+        
+        assertEquals( 1, entries.size() );
+        
+        // Check the entry
+        LdifEntry ldifEntry = entries.get( 0 );
+        assertTrue( ldifEntry.isEntry() );
+        
+        Entry entry = ldifEntry.getEntry();
+        assertEquals( 5, entry.size() );
+        
+        // Here, we expect cn2 to be translated to AAA, because it was encountered in teh DN first
+        assertEquals( "cn=AAA+sn=AAAAAAAAA,dc=example,dc=com", entry.getDn().toString() );
+
+        Attribute cn = entry.get( "cn" );
+        assertEquals( 3, cn.size() );
+        assertTrue( cn.contains( "AAA", "AAB", "AAC" ) );
+
+        Attribute sn = entry.get( "sn" );
+        assertEquals( "AAAAAAAAA", sn.getString() );
+
+        Attribute givenname = entry.get( "givenname" );
+        assertEquals( "AAAA", givenname.getString() );
+
+        Attribute userPassword = entry.get( "userPassword" );
+        assertEquals( 2, userPassword.size() );
+        assertTrue( userPassword.contains( Strings.getBytesUtf8( "AAAA" ) ) );
+        assertTrue( userPassword.contains( Strings.getBytesUtf8( "AAAB" ) ) );
     }
 
 
@@ -193,19 +300,18 @@ public class LdifAnonymizerTest
         assertEquals( "member", attribute.getUpId() );
         assertEquals( 4, attribute.size() );
         
-        Set<String> originalValues = new HashSet<String>();
-        originalValues.add( "cn=acme1.com,ou=Servers,o=acme,dc=com" );
-        originalValues.add( "uid=john.doe@acme.com,ou=People,o=acme,dc=com" );
-        originalValues.add( "uid=jack.doe@acme.com,ou=People,o=acme,dc=com" );
-        originalValues.add( "uid=jim.gonzales@acme.com,ou=People,o=acme,dc=com" );
+        Set<String> values = new HashSet<String>();
+        values.add( "cn=AAAAAAAAA,ou=AAAAAAA,o=acme,dc=com" );
+        values.add( "uid=AAAAAAAAAAAAAAAAA,ou=AAAAAB,o=acme,dc=com" );
+        values.add( "uid=AAAAAAAAAAAAAAAAB,ou=AAAAAB,o=acme,dc=com" );
+        values.add( "uid=AAAAAAAAAAAAAAAAAAAAA,ou=AAAAAB,o=acme,dc=com" );
         
         for ( Value<?> value : attribute )
         {
             String str = value.getString();
             
             // We can only test the length and teh fact teh values are not equal (as the vale has been anonymized)
-            assertNotSame( 0, value.length() );
-            assertFalse( originalValues.contains( str ) );
+            assertTrue( values.contains( str ) );
             assertTrue( str.endsWith( ",o=acme,dc=com" ) );
         }
     }
@@ -241,9 +347,9 @@ public class LdifAnonymizerTest
         
         String value = attribute.getString();
         
-        // We can only test the length and teh fact teh values are not equal (as the vale has been anonymized)
-        assertEquals( "ACME Inc. Legal Team".length(), value.length() );
-        assertNotEquals( "ACME Inc. Legal Team", value );
+        // We can only test the length and the fact the values are not equal (as the vale has been anonymized)
+        assertEquals( "AAAAAAAAAAAAAAAAAAAA".length(), value.length() );
+        assertEquals( "AAAAAAAAAAAAAAAAAAAA", value );
     }
     
     

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/AbstractAnonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/AbstractAnonymizer.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/AbstractAnonymizer.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/AbstractAnonymizer.java Thu Feb 18 20:35:55 2016
@@ -20,6 +20,7 @@
 
 package org.apache.directory.api.ldap.model.ldif.anonymizer;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -37,6 +38,50 @@ public abstract class AbstractAnonymizer
     
     /** The map of AttributeType'sOID we want to anonymize. They are all associated with anonymizers */
     protected Map<String, Anonymizer> attributeAnonymizers = new HashMap<String, Anonymizer>();
+    
+    /** A flag set to <tt>true</tt> if the AttributeType is case sensitive */
+    protected boolean caseSensitive = false;
+    
+    /** Map of chars to use in the anonymized values 0    5    10   15   20   25   30   35   40*/
+    private static final char[] NOT_SENSITIVE_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'()-./".toCharArray();
+    private static final char[] SENSITIVE_MAP =     "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'()-./abcdefghijklmnopqrstuvwxyz".toCharArray();
+    
+    /** A table containing booleans when the corresponding char is printable */
+    private static final int[] CHAR_MAP =
+        {
+            // ---, ---, ---, ---, ---, ---, ---, ---
+                0,   0,   0,   0,   0,   0,   0,   0, 
+            // ---, ---, ---, ---, ---, ---, ---, ---
+                0,   0,   0,   0,   0,   0,   0,   0, 
+            // ---, ---, ---, ---, ---, ---, ---, ---
+                0,   0,   0,   0,   0,   0,   0,   0, 
+            // ---, ---, ---, ---, ---, ---, ---, ---
+                0,   0,   0,   0,   0,   0,   0,   0, 
+            // ---, ---, ---, ---, ---, ---, ---, "'"
+                0,   0,   0,   0,   0,   0,   0,  36, 
+            // '(', ')', ---, '+', ',', '-', '.', '/'
+               37,  38,   0,   0,   0,  39,  40,  41, 
+            // '0', '1', '2', '3', '4', '5', '6', '7',
+               26,  27,  28,  29,  30,  31,  32,  33, 
+            // '8', '9', ':', ---, ---, '=', ---, '?'
+               34,  35,   0,   0,   0,   0,   0,  26, 
+            // ---, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+                0,   0,   1,   2,   3,   4,   5,   6, 
+            // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'
+                7,   8,   9,  10,  11,  12,  13,  14, 
+            // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'
+               15,  16,  17,  18,  19,  20,  21,  22, 
+            // 'X', 'Y', 'Z', ---, ---, ---, ---, ---
+               23,  24,  25,   0,   0,   0,   0,   0, 
+            // ---, 'a', 'b', 'c', 'd', 'e', 'f', 'g'
+                0,  42,  43,  44,  45,  46,  47,  48, 
+            // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'
+               49,  50,  51,  52,  53,  54,  55,  56, 
+            // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w'
+               57,  58,  59,  60,  61,  62,  63,  64, 
+            // 'x', 'y', 'z', ---, ---, ---, ---, ---
+               65,  66,  67,   0,   0,   0,   0,   0, 
+    };
 
     /**
      * {@inheritDoc}
@@ -56,4 +101,162 @@ public abstract class AbstractAnonymizer
     {
         this.attributeAnonymizers = attributeAnonymizers;
     }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Map<Integer, String> getLatestStringMap()
+    {
+        return null;
+    }
+
+    
+    /**
+     * @param latestValueMap The latest String anonymized value map
+     */
+    public void setLatestStringMap( Map<Integer, String> latestStringMap )
+    {
+        // Do nothing
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Map<Integer, byte[]> getLatestBytesMap()
+    {
+        return null;
+    }
+    
+    
+    /**
+     * @param latestBytesMap The latest byte[] anonymized value map
+     */
+    public void setLatestBytesMap( Map<Integer, byte[]> latestBytesMap )
+    {
+        // Do nothing
+    }
+    
+    
+    /**
+     * Compute the next String value
+     *
+     * @param valStr The original value
+     * @return The anonymized value
+     */
+    protected String computeNewValue( String valStr )
+    {
+        int length = valStr.length();
+        String latestString = getLatestStringMap().get( length );
+        char[] charMap;
+        
+        if ( caseSensitive )
+        {
+            charMap = SENSITIVE_MAP;
+        }
+        else
+        {
+            charMap = NOT_SENSITIVE_MAP;
+        }
+        
+        int lastMapChar = charMap.length - 1;
+
+        if ( latestString == null )
+        {
+            // No previous value : create a new one
+            char[] newValue = new char[length];
+            
+            Arrays.fill( newValue, charMap[0] );
+            
+            String anonymizedValue = new String( newValue );
+            getLatestStringMap().put( length, anonymizedValue );
+            
+            return anonymizedValue;
+        }
+        else
+        {
+            // Compute a new value
+            char[] latest = latestString.toCharArray();
+            boolean overflow = true;
+            
+            for ( int i = length - 1; i >= 0; i-- )
+            {
+                if ( latest[i] == charMap[lastMapChar] )
+                {
+                    latest[i] = charMap[0];
+                }
+                else
+                {
+                    latest[i] = charMap[CHAR_MAP[latest[i]] + 1];
+                    overflow = false;
+                    break;
+                }
+            }
+            
+            String anonymizedValue = new String( latest );
+            
+            if ( overflow )
+            {
+                // We have exhausted all the possible values...
+                throw new RuntimeException( "Cannot compute a new value for " + anonymizedValue );
+            }
+            
+            getLatestStringMap().put( length, anonymizedValue );
+            
+            return anonymizedValue;
+        }
+    }
+    
+    
+    /**
+     * Compute the next byte[] value
+     *
+     * @param valBytes The original value
+     * @return The anonymized value
+     */
+    protected byte[] computeNewValue( byte[] valBytes )
+    {
+        int length = valBytes.length;
+        byte[] latestBytes = getLatestBytesMap().get( length );
+        
+        if ( latestBytes == null )
+        {
+            // No previous value : create a new one
+            byte[] newValue = new byte[length];
+            
+            Arrays.fill( newValue, ( byte ) 'A' );
+            
+            getLatestBytesMap().put( length, newValue );
+            
+            return newValue;
+        }
+        else
+        {
+            // Compute a new value
+            boolean overflow = true;
+            
+            for ( int i = length - 1; i >= 0; i-- )
+            {
+                if ( latestBytes[i] == ( byte ) 'Z' )
+                {
+                    latestBytes[i] = ( byte ) 'A';
+                }
+                else
+                {
+                    latestBytes[i]++;
+                    overflow = false;
+                    break;
+                }
+            }
+            
+            if ( overflow )
+            {
+                // We have exhausted all the possible values...
+                throw new RuntimeException( "Cannot compute a new value for " + latestBytes );
+            }
+            
+            return latestBytes;
+        }
+    }
 }

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/Anonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/Anonymizer.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/Anonymizer.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/Anonymizer.java Thu Feb 18 20:35:55 2016
@@ -61,4 +61,28 @@ public interface Anonymizer<K>
      * @param attributeAnonymizers The list of existing anonymizers
      */
     void setAnonymizers( Map<String, Anonymizer> attributeAnonymizers );
+    
+    
+    /**
+     * @return The latest String anonymized value map
+     */
+    Map<Integer, String> getLatestStringMap();
+    
+    
+    /**
+     * @param latestValueMap The latest String anonymized value map
+     */
+    void setLatestStringMap( Map<Integer, String> latestStringMap );
+    
+    
+    /**
+     * @return The latest byte[] anonymized value map
+     */
+    Map<Integer, byte[]> getLatestBytesMap();
+    
+    
+    /**
+     * @param latestBytesMap The latest byte[] anonymized value map
+     */
+    void setLatestBytesMap( Map<Integer, byte[]> latestBytesMap );
 }

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/BinaryAnonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/BinaryAnonymizer.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/BinaryAnonymizer.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/BinaryAnonymizer.java Thu Feb 18 20:35:55 2016
@@ -21,8 +21,8 @@
 package org.apache.directory.api.ldap.model.ldif.anonymizer;
 
 
+import java.util.HashMap;
 import java.util.Map;
-import java.util.Random;
 import java.util.Set;
 
 import org.apache.directory.api.ldap.model.entry.Attribute;
@@ -39,9 +39,34 @@ import org.apache.directory.api.ldap.mod
  */
 public class BinaryAnonymizer extends AbstractAnonymizer<byte[]>
 {
-    /** Create a random generator */
-    Random random = new Random( System.currentTimeMillis() );
+    /** The latest anonymized byte[] value map */
+    protected Map<Integer, byte[]> latestBytesMap = new HashMap<Integer, byte[]>();
 
+    /**
+     * Creates a new instance of BinaryAnonymizer.
+     */
+    public BinaryAnonymizer()
+    {
+        latestBytesMap = new HashMap<Integer, byte[]>();
+    }
+
+    
+    /**
+     * Creates a new instance of BinaryAnonymizer.
+     * 
+     * @param latestBytesMap The map containing the latest value for each length 
+     */
+    public BinaryAnonymizer( Map<Integer, byte[]> latestBytesMap )
+    {
+        if ( latestBytesMap == null )
+        {
+            this.latestBytesMap = new HashMap<Integer, byte[]>();
+        }
+        else
+        {
+            this.latestBytesMap = latestBytesMap;
+        }
+    }
 
     /**
      * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
@@ -49,56 +74,44 @@ public class BinaryAnonymizer extends Ab
     public Attribute anonymize( Map<Value<byte[]>, Value<byte[]>> valueMap, Set<Value<byte[]>> valueSet, Attribute attribute )
     {
         Attribute result = new DefaultAttribute( attribute.getAttributeType() );
-        random.setSeed( System.nanoTime() );
 
         for ( Value<?> value : attribute )
         {
-            if ( value instanceof BinaryValue )
+            byte[] bytesValue = ( byte[] ) value.getNormValue();
+            byte[] newValue = computeNewValue( bytesValue );
+            
+            try
             {
-                byte[] bytesValue = value.getBytes();
-
-                int length = bytesValue.length;
-
-                // Same size
-                byte[] newValue = new byte[length];
-
-                for ( int i = 0; i < length; i++ )
-                {
-                    newValue[i] = ( byte ) ( random.nextInt( 'Z' - 'A' ) + 'A' );
-                }
-
-                try
-                {
-                    result.add( newValue );
-                }
-                catch ( LdapInvalidAttributeValueException e )
-                {
-                    // TODO : handle that
-                }
+                result.add( newValue );
+                Value<byte[]> anonValue = new BinaryValue( attribute.getAttributeType(), newValue );
+                valueMap.put( ( Value<byte[]> ) value, anonValue );
+                valueSet.add( anonValue );
             }
-            else
+            catch ( LdapInvalidAttributeValueException e )
             {
-                byte[] byteValue = value.getBytes();
-
-                // Same size
-                byte[] newValue = new byte[byteValue.length];
-
-                for ( int i = 0; i < byteValue.length; i++ )
-                {
-                    newValue[i] = ( byte ) random.nextInt();
-                }
-
-                try
-                {
-                    result.add( newValue );
-                }
-                catch ( LdapInvalidAttributeValueException e )
-                {
-                    // TODO : handle that
-                }
+                // TODO Auto-generated catch block
+                throw new RuntimeException( "Error while anonymizing the value" + value );
             }
         }
 
         return result;
     }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Map<Integer, byte[]> getLatestBytesMap()
+    {
+        return latestBytesMap;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void setLatestBytesgMap( Map<Integer, byte[]> latestBytesMap )
+    {
+        this.latestBytesMap = latestBytesMap;
+    }
 }

Added: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/CaseSensitiveStringAnonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/CaseSensitiveStringAnonymizer.java?rev=1731136&view=auto
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/CaseSensitiveStringAnonymizer.java (added)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/CaseSensitiveStringAnonymizer.java Thu Feb 18 20:35:55 2016
@@ -0,0 +1,142 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   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
+ *
+ *     http://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.ldif.anonymizer;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.api.ldap.model.entry.StringValue;
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+
+
+/**
+ * A default anonymizer for attributes that are HR. It covers DirectoryString, Ia5String, ...
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class CaseSensitiveStringAnonymizer extends AbstractAnonymizer<String>
+{
+    /** The latest anonymized String value map */
+    private Map<Integer, String> latestStringMap;
+
+    /**
+     * Creates a new instance of StringAnonymizer.
+     */
+    public CaseSensitiveStringAnonymizer()
+    {
+        latestStringMap = new HashMap<Integer, String>();
+        caseSensitive = true;
+    }
+
+    
+    /**
+     * Creates a new instance of StringAnonymizer.
+     * 
+     * @param latestStringMap The map containing the latest value for each length 
+     */
+    public CaseSensitiveStringAnonymizer( Map<Integer, String> latestStringMap )
+    {
+        if ( latestStringMap == null ) 
+        {
+            this.latestStringMap = new HashMap<Integer, String>();
+        }
+        else
+        {
+            this.latestStringMap = latestStringMap;
+        }
+
+        caseSensitive = true ;
+    }
+    
+    
+    /**
+     * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
+     */
+    public Attribute anonymize( Map<Value<String>, Value<String>> valueMap, Set<Value<String>> valueSet, Attribute attribute )
+    {
+        AttributeType attributeType = attribute.getAttributeType();
+        Attribute result = new DefaultAttribute( attributeType );
+
+        for ( Value<?> value : attribute )
+        {
+            if ( value instanceof StringValue )
+            {
+                Value<String> anonymized =  valueMap.get( value );
+                
+                if ( anonymized != null )
+                {
+                    try
+                    {
+                        result.add( anonymized );
+                    }
+                    catch ( LdapInvalidAttributeValueException e )
+                    {
+                        // TODO : handle that
+                    }
+                }
+                else
+                {
+                    String strValue = value.getNormValue().toString();
+                    String newValue = computeNewValue( strValue );
+                    
+                    try
+                    {
+                        result.add( newValue );
+                        Value<String> anonValue = new StringValue( attribute.getAttributeType(), newValue );
+                        valueMap.put( ( Value<String> ) value, anonValue );
+                        valueSet.add( anonValue );
+                    }
+                    catch ( LdapInvalidAttributeValueException e )
+                    {
+                        // TODO Auto-generated catch block
+                        throw new RuntimeException( "Error while anonymizing the value" + strValue );
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Map<Integer, String> getLatestStringMap()
+    {
+        return latestStringMap;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void setLatestStringMap( Map<Integer, String> latestStringMap )
+    {
+        this.latestStringMap = latestStringMap;
+    }
+}

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/IntegerAnonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/IntegerAnonymizer.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/IntegerAnonymizer.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/IntegerAnonymizer.java Thu Feb 18 20:35:55 2016
@@ -21,8 +21,9 @@
 package org.apache.directory.api.ldap.model.ldif.anonymizer;
 
 
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
-import java.util.Random;
 import java.util.Set;
 
 import org.apache.directory.api.ldap.model.entry.Attribute;
@@ -39,9 +40,34 @@ import org.apache.directory.api.ldap.mod
  */
 public class IntegerAnonymizer extends AbstractAnonymizer<String>
 {
-    /** Create a random generator */
-    Random random = new Random( System.currentTimeMillis() );
+    /** The latest anonymized Integer value map */
+    private Map<Integer, String> latestIntegerMap;
 
+    /**
+     * Creates a new instance of IntegerAnonymizer.
+     */
+    public IntegerAnonymizer()
+    {
+        latestIntegerMap = new HashMap<Integer, String>();
+    }
+
+    
+    /**
+     * Creates a new instance of IntegerAnonymizer.
+     * 
+     * @param latestIntegerMap The map containing the latest integer value for each length 
+     */
+    public IntegerAnonymizer( Map<Integer, String> latestIntegerMap )
+    {
+        if ( latestIntegerMap == null ) 
+        {
+            this.latestIntegerMap = new HashMap<Integer, String>();
+        }
+        else
+        {
+            this.latestIntegerMap = latestIntegerMap;
+        }
+    }
 
     /**
      * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
@@ -49,46 +75,126 @@ public class IntegerAnonymizer extends A
     public Attribute anonymize( Map<Value<String>, Value<String>> valueMap, Set<Value<String>> valueSet, Attribute attribute )
     {
         Attribute result = new DefaultAttribute( attribute.getAttributeType() );
-        random.setSeed( System.nanoTime() );
 
         for ( Value<?> value : attribute )
         {
             if ( value instanceof StringValue )
             {
-                String strValue = value.getString();
-
-                int length = strValue.length();
-
-                // Same size
-                char[] newValue = new char[length];
-
-                boolean isFirst = true;
+                Value<String> anonymized =  valueMap.get( value );
                 
-                for ( int i = 0; i < length; i++ )
+                if ( anonymized != null )
                 {
-                    if ( isFirst && length > 1 ) 
+                    try
                     {
-                        newValue[i] = ( char ) ( random.nextInt( 9 ) + '1' );
+                        result.add( anonymized );
                     }
-                    else
+                    catch ( LdapInvalidAttributeValueException e )
                     {
-                        newValue[i] = ( char ) ( random.nextInt( 10 ) + '0' );
+                        // Handle me...
                     }
-
-                    isFirst = false;
-                }
-                
-                try
-                {
-                    result.add( new String( newValue ) );
                 }
-                catch ( LdapInvalidAttributeValueException e )
+                else
                 {
-                    // TODO : handle that
+                    String strValue = value.getNormValue().toString();
+                    String newValue = computeNewIntegerValue( strValue );
+    
+                    try
+                    {
+                        result.add( newValue );
+                        Value<String> anonValue = new StringValue( attribute.getAttributeType(), newValue );
+                        valueMap.put( ( Value<String> ) value, anonValue );
+                        valueSet.add( anonValue );
+                    }
+                    catch ( LdapInvalidAttributeValueException e )
+                    {
+                        // TODO : handle that
+                    }
                 }
             }
         }
 
         return result;
     }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Map<Integer, String> getLatestIntegerMap()
+    {
+        return latestIntegerMap;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void setLatestIntegerMap( Map<Integer, String> latestIntegerMap )
+    {
+        this.latestIntegerMap = latestIntegerMap;
+    }
+
+    
+    /**
+     * Compute the next Integer value
+     *
+     * @param valStr The original value
+     * @return The anonymized value
+     */
+    private String computeNewIntegerValue( String valStr )
+    {
+        int length = valStr.length();
+        String latestInteger = latestIntegerMap.get( length );
+        
+        if ( latestInteger == null )
+        {
+            // No previous value : create a new one
+            char[] newValue = new char[length];
+            
+            Arrays.fill( newValue, '9' );
+            
+            String anonymizedValue = new String( newValue );
+            latestIntegerMap.put( length, anonymizedValue );
+            
+            return anonymizedValue;
+        }
+        else
+        {
+            // Compute a new value
+            char[] latest = latestInteger.toCharArray();
+            boolean overflow = true;
+            
+            for ( int i = length - 1; i >= 0; i-- )
+            {
+                if ( latest[i] == '0' )
+                {
+                    latest[i] = '9';
+                }
+                else
+                {
+                    latest[i]--;
+                    overflow = false;
+                    break;
+                }
+            }
+            
+            // Corner case : we can't have a value starting with '0' unless its length is 1
+            if ( ( length > 1 ) && ( latest[0] == '0' ) )
+            {
+                throw new RuntimeException( "Overflow for " + valStr );
+            }
+            
+            String anonymizedValue = new String( latest );
+            
+            if ( overflow )
+            {
+                // We have exhausted all the possible values...
+                throw new RuntimeException( "Cannot compute a new value for " + anonymizedValue );
+            }
+            
+            latestIntegerMap.put( length, anonymizedValue );
+            
+            return anonymizedValue;
+        }
+    }
 }

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/StringAnonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/StringAnonymizer.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/StringAnonymizer.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/StringAnonymizer.java Thu Feb 18 20:35:55 2016
@@ -21,8 +21,8 @@
 package org.apache.directory.api.ldap.model.ldif.anonymizer;
 
 
+import java.util.HashMap;
 import java.util.Map;
-import java.util.Random;
 import java.util.Set;
 
 import org.apache.directory.api.ldap.model.entry.Attribute;
@@ -30,6 +30,7 @@ import org.apache.directory.api.ldap.mod
 import org.apache.directory.api.ldap.model.entry.StringValue;
 import org.apache.directory.api.ldap.model.entry.Value;
 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
 
 
 /**
@@ -39,99 +40,80 @@ import org.apache.directory.api.ldap.mod
  */
 public class StringAnonymizer extends AbstractAnonymizer<String>
 {
-    /** Create a random generator */
-    Random random = new Random( System.currentTimeMillis() );
+    /** The latest anonymized String value map */
+    private Map<Integer, String> latestStringMap;
 
+    /**
+     * Creates a new instance of StringAnonymizer.
+     */
+    public StringAnonymizer()
+    {
+        latestStringMap = new HashMap<Integer, String>();
+        caseSensitive = false;
+    }
 
+    
+    /**
+     * Creates a new instance of StringAnonymizer.
+     * 
+     * @param latestStringMap The map containing the latest value for each length 
+     */
+    public StringAnonymizer( Map<Integer, String> latestStringMap )
+    {
+        if ( latestStringMap == null ) 
+        {
+            this.latestStringMap = new HashMap<Integer, String>();
+        }
+        else
+        {
+            this.latestStringMap = latestStringMap;
+        }
+
+        caseSensitive = false;
+    }
+    
+    
     /**
      * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
      */
     public Attribute anonymize( Map<Value<String>, Value<String>> valueMap, Set<Value<String>> valueSet, Attribute attribute )
     {
-        Attribute result = new DefaultAttribute( attribute.getAttributeType() );
-        random.setSeed( System.nanoTime() );
+        AttributeType attributeType = attribute.getAttributeType();
+        Attribute result = new DefaultAttribute( attributeType );
 
         for ( Value<?> value : attribute )
         {
-            Value<String> anonymized =  valueMap.get( value );
-            
-            if ( anonymized != null )
-            {
-                try
-                {
-                    result.add( anonymized.getString() );
-                }
-                catch ( LdapInvalidAttributeValueException e )
-                {
-                    // TODO : handle that
-                }
-            }
-            else
+            if ( value instanceof StringValue )
             {
-                if ( value instanceof StringValue )
+                Value<String> anonymized =  valueMap.get( value );
+                
+                if ( anonymized != null )
                 {
-                    String strValue = value.getNormValue().toString();
-                    int length = strValue.length();
-    
-                    // Same size
-                    char[] newValue = new char[length];
-    
-                    int count = 1000;
-                    
-                    while ( count > 0 )
+                    try
                     {
-                        for ( int i = 0; i < length; i++ )
-                        {
-                            newValue[i] = ( char ) ( random.nextInt( 'Z' - 'A' ) + 'A' );
-                        }
-    
-                        try
-                        {
-                            String newValueStr = new String( newValue );
-                            
-                            Value<String> anonValue = new StringValue( attribute.getAttributeType(), newValueStr );
-                            
-                            if ( valueSet.contains( anonValue ) )
-                            {
-                                count--;
-                                continue;
-                            }
-                            
-                            result.add( newValueStr );
-                            valueMap.put( ( Value<String> ) value, anonValue );
-                            valueSet.add( anonValue );
-                            break;
-                        }
-                        catch ( LdapInvalidAttributeValueException e )
-                        {
-                            // TODO : handle that
-                        }
+                        result.add( anonymized );
                     }
-                    
-                    if ( count == 0 )
+                    catch ( LdapInvalidAttributeValueException e )
                     {
-                        throw new RuntimeException( "Error : too many collisions" );
+                        // TODO : handle that
                     }
                 }
                 else
                 {
-                    byte[] byteValue = value.getBytes();
-    
-                    // Same size
-                    byte[] newValue = new byte[byteValue.length];
-    
-                    for ( int i = 0; i < byteValue.length; i++ )
-                    {
-                        newValue[i] = ( byte ) random.nextInt();
-                    }
-    
+                    String strValue = value.getNormValue().toString();
+                    String newValue = computeNewValue( strValue );
+                    
                     try
                     {
                         result.add( newValue );
+                        Value<String> anonValue = new StringValue( attribute.getAttributeType(), newValue );
+                        valueMap.put( ( Value<String> ) value, anonValue );
+                        valueSet.add( anonValue );
                     }
                     catch ( LdapInvalidAttributeValueException e )
                     {
-                        // TODO : handle that
+                        // TODO Auto-generated catch block
+                        throw new RuntimeException( "Error while anonymizing the value" + strValue );
                     }
                 }
             }
@@ -139,4 +121,22 @@ public class StringAnonymizer extends Ab
 
         return result;
     }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Map<Integer, String> getLatestStringMap()
+    {
+        return latestStringMap;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void setLatestStringMap( Map<Integer, String> latestStringMap )
+    {
+        this.latestStringMap = latestStringMap;
+    }
 }

Modified: directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/TelephoneNumberAnonymizer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/TelephoneNumberAnonymizer.java?rev=1731136&r1=1731135&r2=1731136&view=diff
==============================================================================
--- directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/TelephoneNumberAnonymizer.java (original)
+++ directory/shared/trunk/ldap/model/src/main/java/org/apache/directory/api/ldap/model/ldif/anonymizer/TelephoneNumberAnonymizer.java Thu Feb 18 20:35:55 2016
@@ -22,14 +22,6 @@ package org.apache.directory.api.ldap.mo
 
 
 import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-
-import org.apache.directory.api.ldap.model.entry.Attribute;
-import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
-import org.apache.directory.api.ldap.model.entry.StringValue;
-import org.apache.directory.api.ldap.model.entry.Value;
-import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
 
 
 /**
@@ -37,19 +29,34 @@ import org.apache.directory.api.ldap.mod
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-public class TelephoneNumberAnonymizer extends AbstractAnonymizer<String>
+public class TelephoneNumberAnonymizer extends IntegerAnonymizer
 {
-    /** Create a random generator */
-    Random random = new Random( System.currentTimeMillis() );
 
+    /**
+     * Creates a new instance of TelephoneNumberAnonymizer.
+     */
+    public TelephoneNumberAnonymizer()
+    {
+        super();
+    }
 
+    
     /**
-     * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
+     * Creates a new instance of TelephoneNumberAnonymizer.
+     * 
+     * @param latestIntegerMap The map containing the latest integer value for each length 
      */
+    public TelephoneNumberAnonymizer( Map<Integer, String> latestIntegerMap )
+    {
+        super( latestIntegerMap );
+    }
+
+    /**
+     * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
+     *
     public Attribute anonymize( Map<Value<String>, Value<String>> valueMap, Set<Value<String>> valueSet, Attribute attribute )
     {
         Attribute result = new DefaultAttribute( attribute.getAttributeType() );
-        random.setSeed( System.nanoTime() );
 
         for ( Value<?> value : attribute )
         {
@@ -128,5 +135,5 @@ public class TelephoneNumberAnonymizer e
         }
 
         return result;
-    }
+    }*/
 }