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 * http://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 023import java.util.Arrays; 024import java.util.HashMap; 025import java.util.Map; 026 027import org.apache.directory.api.ldap.model.schema.SchemaManager; 028 029/** 030 * An abstract class implementing the default behavior of an Anonymizer instance 031 * 032 * @param <K> The type of object being anonymized 033 * 034 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 035 */ 036public abstract class AbstractAnonymizer<K> implements Anonymizer<K> 037{ 038 /** The SchemaManager instance */ 039 protected SchemaManager schemaManager; 040 041 /** The map of AttributeType'sOID we want to anonymize. They are all associated with anonymizers */ 042 protected Map<String, Anonymizer<K>> attributeAnonymizers = new HashMap<>(); 043 044 /** A flag set to <tt>true</tt> if the AttributeType is case sensitive */ 045 protected boolean caseSensitive = false; 046 047 /** Map of chars to use in the anonymized values 0 5 10 15 20 25 30 35 40*/ 048 private static final char[] NOT_SENSITIVE_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'()-./".toCharArray(); 049 private static final char[] SENSITIVE_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'()-./abcdefghijklmnopqrstuvwxyz".toCharArray(); 050 051 /** A table containing booleans when the corresponding char is printable */ 052 private static final int[] CHAR_MAP = 053 { 054 // ---, ---, ---, ---, ---, ---, ---, --- 055 0, 0, 0, 0, 0, 0, 0, 0, 056 // ---, ---, ---, ---, ---, ---, ---, --- 057 0, 0, 0, 0, 0, 0, 0, 0, 058 // ---, ---, ---, ---, ---, ---, ---, --- 059 0, 0, 0, 0, 0, 0, 0, 0, 060 // ---, ---, ---, ---, ---, ---, ---, --- 061 0, 0, 0, 0, 0, 0, 0, 0, 062 // ---, ---, ---, ---, ---, ---, ---, "'" 063 0, 0, 0, 0, 0, 0, 0, 36, 064 // '(', ')', ---, '+', ',', '-', '.', '/' 065 37, 38, 0, 0, 0, 39, 40, 41, 066 // '0', '1', '2', '3', '4', '5', '6', '7', 067 26, 27, 28, 29, 30, 31, 32, 33, 068 // '8', '9', ':', ---, ---, '=', ---, '?' 069 34, 35, 0, 0, 0, 0, 0, 26, 070 // ---, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 071 0, 0, 1, 2, 3, 4, 5, 6, 072 // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' 073 7, 8, 9, 10, 11, 12, 13, 14, 074 // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' 075 15, 16, 17, 18, 19, 20, 21, 22, 076 // 'X', 'Y', 'Z', ---, ---, ---, ---, --- 077 23, 24, 25, 0, 0, 0, 0, 0, 078 // ---, 'a', 'b', 'c', 'd', 'e', 'f', 'g' 079 0, 42, 43, 44, 45, 46, 47, 48, 080 // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' 081 49, 50, 51, 52, 53, 54, 55, 56, 082 // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' 083 57, 58, 59, 60, 61, 62, 63, 64, 084 // 'x', 'y', 'z', ---, ---, ---, ---, --- 085 65, 66, 67, 0, 0, 0, 0, 0, 086 }; 087 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 public void setSchemaManager( SchemaManager schemaManager ) 093 { 094 this.schemaManager = schemaManager; 095 } 096 097 098 /** 099 * Set the list of existing anonymizers 100 * 101 * @param attributeAnonymizers The list of existing anonymizers 102 */ 103 @Override 104 public void setAnonymizers( Map<String, Anonymizer<K>> attributeAnonymizers ) 105 { 106 this.attributeAnonymizers = attributeAnonymizers; 107 } 108 109 110 /** 111 * {@inheritDoc} 112 */ 113 @Override 114 public Map<Integer, String> getLatestStringMap() 115 { 116 return null; 117 } 118 119 120 /** 121 * {@inheritDoc} 122 */ 123 @Override 124 public void setLatestStringMap( Map<Integer, String> latestStringMap ) 125 { 126 // Do nothing 127 } 128 129 130 /** 131 * {@inheritDoc} 132 */ 133 @Override 134 public Map<Integer, byte[]> getLatestBytesMap() 135 { 136 return null; 137 } 138 139 140 /** 141 * {@inheritDoc} 142 */ 143 @Override 144 public void setLatestBytesMap( Map<Integer, byte[]> latestBytesMap ) 145 { 146 // Do nothing 147 } 148 149 150 /** 151 * Compute the next String value 152 * 153 * @param valStr The original value 154 * @return The anonymized value 155 */ 156 protected String computeNewValue( String valStr ) 157 { 158 int length = valStr.length(); 159 String latestString = getLatestStringMap().get( length ); 160 char[] charMap; 161 162 if ( caseSensitive ) 163 { 164 charMap = SENSITIVE_MAP; 165 } 166 else 167 { 168 charMap = NOT_SENSITIVE_MAP; 169 } 170 171 int lastMapChar = charMap.length - 1; 172 173 if ( latestString == null ) 174 { 175 // No previous value : create a new one 176 char[] newValue = new char[length]; 177 178 Arrays.fill( newValue, charMap[0] ); 179 180 String anonymizedValue = new String( newValue ); 181 getLatestStringMap().put( length, anonymizedValue ); 182 183 return anonymizedValue; 184 } 185 else 186 { 187 // Compute a new value 188 char[] latest = latestString.toCharArray(); 189 boolean overflow = true; 190 191 for ( int i = length - 1; i >= 0; i-- ) 192 { 193 if ( latest[i] == charMap[lastMapChar] ) 194 { 195 latest[i] = charMap[0]; 196 } 197 else 198 { 199 latest[i] = charMap[CHAR_MAP[latest[i]] + 1]; 200 overflow = false; 201 break; 202 } 203 } 204 205 String anonymizedValue = new String( latest ); 206 207 if ( overflow ) 208 { 209 // We have exhausted all the possible values... 210 throw new RuntimeException( "Cannot compute a new value for " + anonymizedValue ); 211 } 212 213 getLatestStringMap().put( length, anonymizedValue ); 214 215 return anonymizedValue; 216 } 217 } 218 219 220 /** 221 * Compute the next byte[] value 222 * 223 * @param valBytes The original value 224 * @return The anonymized value 225 */ 226 protected byte[] computeNewValue( byte[] valBytes ) 227 { 228 int length = valBytes.length; 229 byte[] latestBytes = getLatestBytesMap().get( length ); 230 231 if ( latestBytes == null ) 232 { 233 // No previous value : create a new one 234 byte[] newValue = new byte[length]; 235 236 Arrays.fill( newValue, ( byte ) 'A' ); 237 238 getLatestBytesMap().put( length, newValue ); 239 240 return newValue; 241 } 242 else 243 { 244 // Compute a new value 245 boolean overflow = true; 246 247 for ( int i = length - 1; i >= 0; i-- ) 248 { 249 if ( latestBytes[i] == ( byte ) 'Z' ) 250 { 251 latestBytes[i] = ( byte ) 'A'; 252 } 253 else 254 { 255 latestBytes[i]++; 256 overflow = false; 257 break; 258 } 259 } 260 261 if ( overflow ) 262 { 263 // We have exhausted all the possible values... 264 throw new RuntimeException( "Cannot compute a new value for " + latestBytes ); 265 } 266 267 return latestBytes; 268 } 269 } 270}