001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021package org.apache.directory.api.ldap.model.ldif.anonymizer; 022 023 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.Map; 027import java.util.Set; 028 029import org.apache.directory.api.i18n.I18n; 030import org.apache.directory.api.ldap.model.entry.Attribute; 031import org.apache.directory.api.ldap.model.entry.DefaultAttribute; 032import org.apache.directory.api.ldap.model.entry.Value; 033import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 034 035 036/** 037 * A default anonymizer for attributes that is an Integer. the initial value is randomized 038 * 039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 040 */ 041public class IntegerAnonymizer extends AbstractAnonymizer<String> 042{ 043 /** The latest anonymized Integer value map */ 044 private Map<Integer, String> latestIntegerMap; 045 046 /** 047 * Creates a new instance of IntegerAnonymizer. 048 */ 049 public IntegerAnonymizer() 050 { 051 latestIntegerMap = new HashMap<>(); 052 } 053 054 055 /** 056 * Creates a new instance of IntegerAnonymizer. 057 * 058 * @param latestIntegerMap The map containing the latest integer value for each length 059 */ 060 public IntegerAnonymizer( Map<Integer, String> latestIntegerMap ) 061 { 062 if ( latestIntegerMap == null ) 063 { 064 this.latestIntegerMap = new HashMap<>(); 065 } 066 else 067 { 068 this.latestIntegerMap = latestIntegerMap; 069 } 070 } 071 072 /** 073 * Anonymize an attribute using pure random values (either chars of bytes, depending on the Attribute type) 074 */ 075 @Override 076 public Attribute anonymize( Map<Value, Value> valueMap, Set<Value> valueSet, Attribute attribute ) 077 { 078 Attribute result = new DefaultAttribute( attribute.getAttributeType() ); 079 080 for ( Value value : attribute ) 081 { 082 if ( value.isHumanReadable() ) 083 { 084 Value anonymized = valueMap.get( value ); 085 086 if ( anonymized != null ) 087 { 088 try 089 { 090 result.add( anonymized ); 091 } 092 catch ( LdapInvalidAttributeValueException e ) 093 { 094 // Handle me... 095 } 096 } 097 else 098 { 099 String strValue = value.getString(); 100 String newValue = computeNewIntegerValue( strValue ); 101 102 try 103 { 104 result.add( newValue ); 105 Value anonValue = new Value( attribute.getAttributeType(), newValue ); 106 valueMap.put( ( Value ) 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( I18n.err( I18n.ERR_13437_OVERFLOW, valStr ) ); 187 } 188 189 String anonymizedValue = new String( latest ); 190 191 if ( overflow ) 192 { 193 // We have exhausted all the possible values... 194 String msg = I18n.err( I18n.ERR_13435_CANNOT_COMPUTE_NEW_VALUE, anonymizedValue ); 195 196 throw new RuntimeException( msg ); 197 } 198 199 latestIntegerMap.put( length, anonymizedValue ); 200 201 return anonymizedValue; 202 } 203 } 204}