View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  
21  package org.apache.directory.api.ldap.model.ldif.anonymizer;
22  
23  
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.directory.api.ldap.model.entry.Attribute;
30  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
31  import org.apache.directory.api.ldap.model.entry.StringValue;
32  import org.apache.directory.api.ldap.model.entry.Value;
33  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
34  
35  
36  /**
37   * A default anonymizer for attributes that is an Integer. the initial value is randomized
38   *
39   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
40   */
41  public class IntegerAnonymizer extends AbstractAnonymizer<String>
42  {
43      /** The latest anonymized Integer value map */
44      private Map<Integer, String> latestIntegerMap;
45  
46      /**
47       * Creates a new instance of IntegerAnonymizer.
48       */
49      public IntegerAnonymizer()
50      {
51          latestIntegerMap = new HashMap<>();
52      }
53  
54      
55      /**
56       * Creates a new instance of IntegerAnonymizer.
57       * 
58       * @param latestIntegerMap The map containing the latest integer value for each length 
59       */
60      public IntegerAnonymizer( Map<Integer, String> latestIntegerMap )
61      {
62          if ( latestIntegerMap == null ) 
63          {
64              this.latestIntegerMap = new HashMap<>();
65          }
66          else
67          {
68              this.latestIntegerMap = latestIntegerMap;
69          }
70      }
71  
72      /**
73       * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type)
74       */
75      @Override
76      public Attribute anonymize( Map<Value<String>, Value<String>> valueMap, Set<Value<String>> valueSet, Attribute attribute )
77      {
78          Attribute result = new DefaultAttribute( attribute.getAttributeType() );
79  
80          for ( Value<?> value : attribute )
81          {
82              if ( value instanceof StringValue )
83              {
84                  Value<String> anonymized =  valueMap.get( value );
85                  
86                  if ( anonymized != null )
87                  {
88                      try
89                      {
90                          result.add( anonymized );
91                      }
92                      catch ( LdapInvalidAttributeValueException e )
93                      {
94                          // Handle me...
95                      }
96                  }
97                  else
98                  {
99                      String strValue = value.getNormValue().toString();
100                     String newValue = computeNewIntegerValue( strValue );
101     
102                     try
103                     {
104                         result.add( newValue );
105                         Value<String> anonValue = new StringValue( attribute.getAttributeType(), newValue );
106                         valueMap.put( ( Value<String> ) value, anonValue );
107                         valueSet.add( anonValue );
108                     }
109                     catch ( LdapInvalidAttributeValueException e )
110                     {
111                         // TODO : handle that
112                     }
113                 }
114             }
115         }
116 
117         return result;
118     }
119     
120 
121     /**
122      * @return The Map containing the latest anonymized value for each integer
123      */
124     public Map<Integer, String> getLatestIntegerMap()
125     {
126         return latestIntegerMap;
127     }
128     
129     
130     /**
131      * Set the Map containing anonymized integers
132      * @param latestIntegerMap The Map containing the latest anonymized value for each integer
133      */
134     public void setLatestIntegerMap( Map<Integer, String> latestIntegerMap )
135     {
136         this.latestIntegerMap = latestIntegerMap;
137     }
138 
139     
140     /**
141      * Compute the next Integer value
142      *
143      * @param valStr The original value
144      * @return The anonymized value
145      */
146     private String computeNewIntegerValue( String valStr )
147     {
148         int length = valStr.length();
149         String latestInteger = latestIntegerMap.get( length );
150         
151         if ( latestInteger == null )
152         {
153             // No previous value : create a new one
154             char[] newValue = new char[length];
155             
156             Arrays.fill( newValue, '9' );
157             
158             String anonymizedValue = new String( newValue );
159             latestIntegerMap.put( length, anonymizedValue );
160             
161             return anonymizedValue;
162         }
163         else
164         {
165             // Compute a new value
166             char[] latest = latestInteger.toCharArray();
167             boolean overflow = true;
168             
169             for ( int i = length - 1; i >= 0; i-- )
170             {
171                 if ( latest[i] == '0' )
172                 {
173                     latest[i] = '9';
174                 }
175                 else
176                 {
177                     latest[i]--;
178                     overflow = false;
179                     break;
180                 }
181             }
182             
183             // Corner case : we can't have a value starting with '0' unless its length is 1
184             if ( ( length > 1 ) && ( latest[0] == '0' ) )
185             {
186                 throw new RuntimeException( "Overflow for " + valStr );
187             }
188             
189             String anonymizedValue = new String( latest );
190             
191             if ( overflow )
192             {
193                 // We have exhausted all the possible values...
194                 throw new RuntimeException( "Cannot compute a new value for " + anonymizedValue );
195             }
196             
197             latestIntegerMap.put( length, anonymizedValue );
198             
199             return anonymizedValue;
200         }
201     }
202 }