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.ldap.client.api; 022 023 024import java.io.BufferedReader; 025import java.io.File; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.InputStreamReader; 029import java.io.PrintStream; 030import java.io.Writer; 031import java.nio.charset.Charset; 032import java.nio.file.Files; 033import java.nio.file.Paths; 034import java.util.ArrayList; 035import java.util.HashMap; 036import java.util.HashSet; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040 041import org.apache.directory.api.ldap.model.constants.SchemaConstants; 042import org.apache.directory.api.ldap.model.entry.Attribute; 043import org.apache.directory.api.ldap.model.entry.DefaultAttribute; 044import org.apache.directory.api.ldap.model.entry.DefaultEntry; 045import org.apache.directory.api.ldap.model.entry.DefaultModification; 046import org.apache.directory.api.ldap.model.entry.Entry; 047import org.apache.directory.api.ldap.model.entry.Modification; 048import org.apache.directory.api.ldap.model.entry.StringValue; 049import org.apache.directory.api.ldap.model.entry.Value; 050import org.apache.directory.api.ldap.model.exception.LdapException; 051import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 052import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 053import org.apache.directory.api.ldap.model.ldif.ChangeType; 054import org.apache.directory.api.ldap.model.ldif.LdifEntry; 055import org.apache.directory.api.ldap.model.ldif.LdifReader; 056import org.apache.directory.api.ldap.model.ldif.LdifUtils; 057import org.apache.directory.api.ldap.model.ldif.anonymizer.Anonymizer; 058import org.apache.directory.api.ldap.model.ldif.anonymizer.BinaryAnonymizer; 059import org.apache.directory.api.ldap.model.ldif.anonymizer.CaseSensitiveStringAnonymizer; 060import org.apache.directory.api.ldap.model.ldif.anonymizer.IntegerAnonymizer; 061import org.apache.directory.api.ldap.model.ldif.anonymizer.StringAnonymizer; 062import org.apache.directory.api.ldap.model.ldif.anonymizer.TelephoneNumberAnonymizer; 063import org.apache.directory.api.ldap.model.name.Ava; 064import org.apache.directory.api.ldap.model.name.Dn; 065import org.apache.directory.api.ldap.model.name.Rdn; 066import org.apache.directory.api.ldap.model.schema.AttributeType; 067import org.apache.directory.api.ldap.model.schema.LdapSyntax; 068import org.apache.directory.api.ldap.model.schema.SchemaManager; 069import org.apache.directory.api.ldap.model.schema.syntaxCheckers.DnSyntaxChecker; 070import org.apache.directory.api.ldap.model.schema.syntaxCheckers.NameAndOptionalUIDSyntaxChecker; 071import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager; 072 073 074/** 075 * Anonymize the content of a LDIF file. 076 * 077 * We will replace the values of the defined attributes with random chars. There are a default 078 * list of attributes that are going to be anonymized : 079 * <ul> 080 * <li>userPassword</li> 081 * <li>displayName</li> 082 * <li>givenName</li> 083 * <li>surName</li> 084 * <li>homePhone</li> 085 * <li>homePostalAddress</li> 086 * <li>jpegPhoto</li> 087 * <li>labeledURI</li> 088 * <li>mail</li> 089 * <li>manager</li> 090 * <li>mobile</li> 091 * <li>organizationName</li> 092 * <li>pager</li> 093 * <li>photo</li> 094 * <li>secretary</li> 095 * <li>uid</li> 096 * <li>userCertificate</li> 097 * <li>userPKCS12</li> 098 * <li>userSMIMECertificate</li> 099 * <li>x500UniqueIdentifier</li> 100 * <li>carLicense</li> 101 * <li>host</li> 102 * <li>locality</li> 103 * <li>organizationName</li> 104 * <li>organizationalUnitName</li> 105 * <li>seelAlso</li> 106 * <li>homeDirectory</li> 107 * <li>uidNumber</li> 108 * <li>gidNumber</li> 109 * <li>commonName</li> 110 * <li>gecos</li> 111 * <li>description</li> 112 * <li>memberUid</li> 113 * </ul> 114 * 115 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 116 */ 117public class LdifAnonymizer 118{ 119 /** The map that stores the anonymized values associated to the original value */ 120 private Map<Value<?>, Value<?>> valueMap = new HashMap<>(); 121 122 /** The set that contains all the values we already have anonymized */ 123 private Set<Value<?>> valueSet = new HashSet<>(); 124 125 /** The latest anonymized String value Map */ 126 private Map<Integer, String> latestStringMap; 127 128 /** The latest anonymized byte[] value Map */ 129 private Map<Integer, byte[]> latestBytesMap; 130 131 /** The map of AttributeType'sOID we want to anonymize. They are all associated with anonymizers */ 132 private Map<String, Anonymizer> attributeAnonymizers = new HashMap<>(); 133 134 /** The list of existing NamingContexts */ 135 private Set<Dn> namingContexts = new HashSet<>(); 136 137 /** The schemaManager */ 138 private SchemaManager schemaManager; 139 140 /** The PrintStream used to write informations about the processing */ 141 private PrintStream out = null; 142 143 /** 144 * Creates a default instance of LdifAnonymizer. The list of anonymized attribute 145 * is set to a default value. 146 * 147 */ 148 public LdifAnonymizer() 149 { 150 try 151 { 152 schemaManager = new DefaultSchemaManager(); 153 } 154 catch ( Exception e ) 155 { 156 // Todo : we need a schemaManager 157 println( "Missing a SchemaManager !" ); 158 System.exit( -1 ); 159 } 160 161 init( null, null, null, null ); 162 } 163 164 165 /** 166 * Creates a default instance of LdifAnonymizer. The list of anonymized attribute 167 * is set to a default value. 168 * 169 * @param schemaManager The SchemaManager instance we will use 170 */ 171 public LdifAnonymizer( SchemaManager schemaManager ) 172 { 173 this.schemaManager = schemaManager; 174 175 init( null, null, null, null ); 176 } 177 178 179 /** 180 * Set the PrintStream to use to print information about the processing 181 * 182 * @param out The PrintStream to use 183 */ 184 public void setOut( PrintStream out ) 185 { 186 this.out = out; 187 } 188 189 190 /** 191 * Print the string into the PrintStream 192 */ 193 private void print( String str ) 194 { 195 if ( out != null ) 196 { 197 out.print( str ); 198 } 199 } 200 201 202 /** 203 * Print the string into the PrintStream, with a NL at the end 204 */ 205 private void println( String str ) 206 { 207 if ( out != null ) 208 { 209 out.println( str ); 210 } 211 } 212 213 214 /** 215 * Print a nl into the PrintStream 216 */ 217 private void println() 218 { 219 if ( out != null ) 220 { 221 out.println(); 222 } 223 } 224 225 226 /** 227 * Initialize the anonymizer, filling the maps we use. 228 */ 229 private void init( Map<Integer, String> stringLatestValueMap, Map<Integer, byte[]> binaryLatestValueMap, 230 Map<Integer, String> integerLatestValueMap, Map<Integer, String> telephoneNumberLatestValueMap ) 231 { 232 // Load the anonymizers 233 attributeAnonymizers.put( SchemaConstants.CAR_LICENSE_AT_OID, 234 new StringAnonymizer( stringLatestValueMap ) ); 235 attributeAnonymizers.put( SchemaConstants.DOMAIN_COMPONENT_AT_OID, 236 new StringAnonymizer( stringLatestValueMap ) ); 237 attributeAnonymizers.put( SchemaConstants.CN_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 238 attributeAnonymizers.put( SchemaConstants.DESCRIPTION_AT_OID, 239 new StringAnonymizer( stringLatestValueMap ) ); 240 attributeAnonymizers.put( SchemaConstants.DISPLAY_NAME_AT_OID, 241 new StringAnonymizer( stringLatestValueMap ) ); 242 attributeAnonymizers.put( SchemaConstants.GECOS_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 243 attributeAnonymizers.put( SchemaConstants.GID_NUMBER_AT_OID, 244 new IntegerAnonymizer( integerLatestValueMap ) ); 245 attributeAnonymizers.put( SchemaConstants.GIVENNAME_AT_OID, 246 new StringAnonymizer( stringLatestValueMap ) ); 247 attributeAnonymizers.put( SchemaConstants.HOME_DIRECTORY_AT_OID, 248 new CaseSensitiveStringAnonymizer( stringLatestValueMap ) ); 249 attributeAnonymizers.put( SchemaConstants.HOME_PHONE_AT_OID, 250 new TelephoneNumberAnonymizer() ); 251 attributeAnonymizers.put( SchemaConstants.HOME_POSTAL_ADDRESS_AT_OID, 252 new StringAnonymizer( stringLatestValueMap ) ); 253 attributeAnonymizers.put( SchemaConstants.HOST_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 254 attributeAnonymizers.put( SchemaConstants.HOUSE_IDENTIFIER_AT_OID, 255 new StringAnonymizer( stringLatestValueMap ) ); 256 attributeAnonymizers.put( SchemaConstants.JPEG_PHOTO_AT_OID, 257 new BinaryAnonymizer( binaryLatestValueMap ) ); 258 attributeAnonymizers.put( SchemaConstants.LABELED_URI_AT_OID, 259 new CaseSensitiveStringAnonymizer( stringLatestValueMap ) ); 260 attributeAnonymizers.put( SchemaConstants.LOCALITY_NAME_AT_OID, 261 new StringAnonymizer( stringLatestValueMap ) ); 262 attributeAnonymizers.put( SchemaConstants.MAIL_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 263 attributeAnonymizers.put( SchemaConstants.MANAGER_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 264 attributeAnonymizers.put( SchemaConstants.MEMBER_UID_AT_OID, 265 new StringAnonymizer( stringLatestValueMap ) ); 266 attributeAnonymizers.put( SchemaConstants.MOBILE_AT_OID, new TelephoneNumberAnonymizer() ); 267 attributeAnonymizers.put( SchemaConstants.ORGANIZATION_NAME_AT_OID, 268 new StringAnonymizer( stringLatestValueMap ) ); 269 attributeAnonymizers.put( SchemaConstants.ORGANIZATIONAL_UNIT_NAME_AT_OID, 270 new StringAnonymizer( stringLatestValueMap ) ); 271 attributeAnonymizers.put( SchemaConstants.PAGER_AT_OID, new TelephoneNumberAnonymizer() ); 272 attributeAnonymizers.put( SchemaConstants.POSTAL_ADDRESS_AT_OID, 273 new StringAnonymizer( stringLatestValueMap ) ); 274 attributeAnonymizers.put( SchemaConstants.PHOTO_AT_OID, new BinaryAnonymizer( binaryLatestValueMap ) ); 275 attributeAnonymizers.put( SchemaConstants.SECRETARY_AT_OID, 276 new StringAnonymizer( stringLatestValueMap ) ); 277 attributeAnonymizers 278 .put( SchemaConstants.SEE_ALSO_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 279 attributeAnonymizers.put( SchemaConstants.SN_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 280 attributeAnonymizers.put( SchemaConstants.TELEPHONE_NUMBER_AT_OID, 281 new TelephoneNumberAnonymizer( telephoneNumberLatestValueMap ) ); 282 attributeAnonymizers.put( SchemaConstants.UID_AT_OID, new StringAnonymizer( stringLatestValueMap ) ); 283 attributeAnonymizers.put( SchemaConstants.UID_NUMBER_AT_OID, 284 new IntegerAnonymizer( integerLatestValueMap ) ); 285 attributeAnonymizers.put( SchemaConstants.USER_CERTIFICATE_AT_OID, 286 new BinaryAnonymizer( binaryLatestValueMap ) ); 287 attributeAnonymizers.put( SchemaConstants.USER_PASSWORD_AT_OID, 288 new BinaryAnonymizer( binaryLatestValueMap ) ); 289 attributeAnonymizers.put( SchemaConstants.USER_PKCS12_AT_OID, 290 new BinaryAnonymizer( binaryLatestValueMap ) ); 291 attributeAnonymizers.put( SchemaConstants.USER_SMIME_CERTIFICATE_AT_OID, 292 new BinaryAnonymizer( binaryLatestValueMap ) ); 293 attributeAnonymizers.put( SchemaConstants.X500_UNIQUE_IDENTIFIER_AT_OID, 294 new BinaryAnonymizer( binaryLatestValueMap ) ); 295 attributeAnonymizers.put( SchemaConstants.FACSIMILE_TELEPHONE_NUMBER_AT_OID, 296 new TelephoneNumberAnonymizer( telephoneNumberLatestValueMap ) ); 297 } 298 299 300 /** 301 * Set the latest value map to a defined anonymizer - if it exists -. 302 * 303 * @param attributeType The AttributeType we are targetting 304 * @param latestValueMap The latest value map for this attribute 305 */ 306 public void setAttributeLatestValueMap( AttributeType attributeType, Map<Integer, ?> latestValueMap ) 307 { 308 Anonymizer anonymizer = attributeAnonymizers.get( attributeType.getOid() ); 309 310 if ( anonymizer != null ) 311 { 312 if ( attributeType.getSyntax().isHumanReadable() ) 313 { 314 anonymizer.setLatestStringMap( latestValueMap ); 315 } 316 else 317 { 318 anonymizer.setLatestBytesMap( latestValueMap ); 319 } 320 } 321 } 322 323 324 /** 325 * Add an attributeType that has to be anonymized 326 * 327 * @param attributeType the AttributeType that has to be anonymized 328 * @throws LdapException If the attributeType cannot be added 329 */ 330 public void addAnonAttributeType( AttributeType attributeType ) throws LdapException 331 { 332 schemaManager.add( attributeType ); 333 LdapSyntax syntax = attributeType.getSyntax(); 334 335 if ( syntax.isHumanReadable() ) 336 { 337 if ( syntax.getOid().equals( SchemaConstants.INTEGER_SYNTAX ) ) 338 { 339 attributeAnonymizers.put( attributeType.getOid(), new IntegerAnonymizer() ); 340 } 341 else if ( syntax.getOid().equals( SchemaConstants.DIRECTORY_STRING_SYNTAX ) ) 342 { 343 attributeAnonymizers.put( attributeType.getOid(), new StringAnonymizer() ); 344 } 345 else if ( syntax.getOid().equals( SchemaConstants.TELEPHONE_NUMBER_SYNTAX ) ) 346 { 347 attributeAnonymizers.put( attributeType.getOid(), new TelephoneNumberAnonymizer() ); 348 } 349 } 350 else 351 { 352 attributeAnonymizers.put( attributeType.getOid(), new BinaryAnonymizer() ); 353 } 354 } 355 356 357 /** 358 * Add an attributeType that has to be anonymized, with its associated anonymizer. 359 * 360 * @param attributeType the AttributeType that has to be anonymized 361 * @param anonymizer the instance of anonymizer to use with this AttributeType 362 * @throws LdapException If the attributeType cannot be added 363 */ 364 public void addAnonAttributeType( AttributeType attributeType, Anonymizer<?> anonymizer ) throws LdapException 365 { 366 schemaManager.add( attributeType ); 367 attributeAnonymizers.put( attributeType.getOid(), anonymizer ); 368 } 369 370 371 /** 372 * Remove an attributeType that has to be anonymized 373 * 374 * @param attributeType the AttributeType that we don't want to be anonymized 375 * @throws LdapException If the attributeType cannot be removed 376 */ 377 public void removeAnonAttributeType( AttributeType attributeType ) throws LdapException 378 { 379 attributeAnonymizers.remove( attributeType ); 380 } 381 382 383 /** 384 * @return The list of configured anonymizers 385 */ 386 public Map<String, Anonymizer> getAttributeAnonymizers() 387 { 388 return attributeAnonymizers; 389 } 390 391 /** 392 * Add a new NamingContext 393 * 394 * @param dn The naming context to add 395 * @throws LdapInvalidDnException if it's an invalid naming context 396 */ 397 public void addNamingContext( String dn ) throws LdapInvalidDnException 398 { 399 Dn namingContext = new Dn( schemaManager, dn ); 400 namingContexts.add( namingContext ); 401 } 402 403 404 /** 405 * Anonymize an AVA 406 */ 407 private Ava anonymizeAva( Ava ava ) throws LdapInvalidDnException, LdapInvalidAttributeValueException 408 { 409 Value<?> value = ava.getValue(); 410 AttributeType attributeType = ava.getAttributeType(); 411 Value<?> anonymizedValue = valueMap.get( value ); 412 Ava anonymizedAva; 413 414 if ( anonymizedValue == null ) 415 { 416 Attribute attribute = new DefaultAttribute( attributeType ); 417 attribute.add( value ); 418 Anonymizer anonymizer = attributeAnonymizers.get( attribute.getAttributeType().getOid() ); 419 420 if ( value.isHumanReadable() ) 421 { 422 if ( anonymizer == null ) 423 { 424 anonymizedAva = new Ava( schemaManager, ava.getType(), value.getString() ); 425 } 426 else 427 { 428 Attribute anonymizedAttribute = anonymizer.anonymize( valueMap, valueSet, attribute ); 429 anonymizedAva = new Ava( schemaManager, ava.getType(), anonymizedAttribute.getString() ); 430 } 431 } 432 else 433 { 434 if ( anonymizer == null ) 435 { 436 anonymizedAva = new Ava( schemaManager, ava.getType(), value.getBytes() ); 437 } 438 else 439 { 440 Attribute anonymizedAttribute = anonymizer.anonymize( valueMap, valueSet, attribute ); 441 442 anonymizedAva = new Ava( schemaManager, ava.getType(), anonymizedAttribute.getBytes() ); 443 } 444 } 445 } 446 else 447 { 448 if ( value.isHumanReadable() ) 449 { 450 anonymizedAva = new Ava( schemaManager, ava.getType(), anonymizedValue.getString() ); 451 } 452 else 453 { 454 anonymizedAva = new Ava( schemaManager, ava.getType(), anonymizedValue.getBytes() ); 455 } 456 } 457 458 return anonymizedAva; 459 } 460 461 462 /** 463 * Anonymize the entry's DN 464 */ 465 private Dn anonymizeDn( Dn entryDn ) throws LdapException 466 { 467 // Search for the naming context 468 Dn descendant = entryDn; 469 Dn namingContext = null; 470 471 for ( Dn nc : namingContexts ) 472 { 473 if ( entryDn.isDescendantOf( nc ) ) 474 { 475 descendant = entryDn.getDescendantOf( nc ); 476 namingContext = nc; 477 break; 478 } 479 } 480 481 Rdn[] anonymizedRdns = new Rdn[entryDn.size()]; 482 int rdnPos = entryDn.size() - 1; 483 484 if ( namingContext != null ) 485 { 486 // Copy the naming contex 487 for ( Rdn ncRdn : namingContext ) 488 { 489 anonymizedRdns[rdnPos] = ncRdn; 490 rdnPos--; 491 } 492 } 493 494 // Iterate on all the RDN 495 for ( Rdn rdn : descendant ) 496 { 497 Ava[] anonymizedAvas = new Ava[rdn.size()]; 498 int pos = 0; 499 500 // Iterate on the AVAs 501 for ( Ava ava : rdn ) 502 { 503 Ava anonymizedAva = anonymizeAva( ava ); 504 anonymizedAvas[pos] = anonymizedAva; 505 pos++; 506 } 507 508 Rdn anonymizedRdn = new Rdn( schemaManager, anonymizedAvas ); 509 anonymizedRdns[rdnPos] = anonymizedRdn; 510 rdnPos--; 511 } 512 513 return new Dn( schemaManager, anonymizedRdns ); 514 } 515 516 517 /** 518 * Anonymize a LDIF 519 * 520 * @param ldifFile The ldif file to anonymize 521 * @param writer The Writer to use to write the result 522 * @throws LdapException If we got some LDAP related exception 523 * @throws IOException If we had some issue during some IO operations 524 */ 525 public void anonymizeFile( String ldifFile, Writer writer ) throws LdapException, IOException 526 { 527 File inputFile = new File( ldifFile ); 528 529 if ( !inputFile.exists() ) 530 { 531 println( "Cannot open file " + ldifFile ); 532 return; 533 } 534 535 LdifReader ldifReader = new LdifReader( inputFile, schemaManager ); 536 int count = 0; 537 List<LdifEntry> errors = new ArrayList<>(); 538 List<String> errorTexts = new ArrayList<>(); 539 540 try 541 { 542 for ( LdifEntry ldifEntry : ldifReader ) 543 { 544 count++; 545 546 try 547 { 548 if ( ldifEntry.isEntry() && !ldifEntry.isChangeAdd() ) 549 { 550 // process a full entry. Add changes aren't processed here. 551 Entry newEntry = anonymizeEntry( ldifEntry ); 552 553 writer.write( LdifUtils.convertToLdif( newEntry ) ); 554 writer.write( "\n" ); 555 } 556 else if ( ldifEntry.isChangeDelete() ) 557 { 558 // A Delete operation 559 LdifEntry newLdifEntry = anonymizeChangeDelete( ldifEntry ); 560 561 if ( ldifEntry != null ) 562 { 563 writer.write( newLdifEntry.toString() ); 564 writer.write( "\n" ); 565 } 566 } 567 else if ( ldifEntry.isChangeAdd() ) 568 { 569 // A Add operation 570 LdifEntry newLdifEntry = anonymizeChangeAdd( ldifEntry ); 571 572 if ( ldifEntry != null ) 573 { 574 writer.write( newLdifEntry.toString() ); 575 writer.write( "\n" ); 576 } 577 } 578 else if ( ldifEntry.isChangeModify() ) 579 { 580 // A Modify operation 581 LdifEntry newLdifEntry = anonymizeChangeModify( ldifEntry ); 582 583 if ( ldifEntry != null ) 584 { 585 writer.write( newLdifEntry.toString() ); 586 writer.write( "\n" ); 587 } 588 } 589 else if ( ldifEntry.isChangeModDn() || ldifEntry.isChangeModRdn() ) 590 { 591 // A MODDN operation 592 LdifEntry newLdifEntry = anonymizeChangeModDn( ldifEntry ); 593 594 if ( ldifEntry != null ) 595 { 596 writer.write( newLdifEntry.toString() ); 597 writer.write( "\n" ); 598 } 599 } 600 601 System.out.print( '.' ); 602 603 if ( count % 100 == 0 ) 604 { 605 println(); 606 } 607 } 608 catch ( Exception e ) 609 { 610 e.printStackTrace(); 611 System.out.print( '*' ); 612 613 if ( count % 100 == 0 ) 614 { 615 println(); 616 } 617 618 errors.add( ldifEntry ); 619 errorTexts.add( e.getMessage() ); 620 } 621 } 622 623 println(); 624 625 if ( !errors.isEmpty() ) 626 { 627 println( "There are " + errors.size() + " bad entries" ); 628 int i = 0; 629 630 for ( LdifEntry ldifEntry : errors ) 631 { 632 println( "---------------------------------------------------" ); 633 println( "error : " + errorTexts.get( i ) ); 634 println( ldifEntry.getDn().toString() ); 635 i++; 636 } 637 } 638 } 639 finally 640 { 641 println(); 642 643 if ( !errors.isEmpty() ) 644 { 645 println( "There are " + errors.size() + " bad entries" ); 646 } 647 648 println( "Nb entries : " + count ); 649 ldifReader.close(); 650 } 651 } 652 653 654 /** 655 * Anonymize a Modify change 656 */ 657 private LdifEntry anonymizeChangeModify( LdifEntry ldifEntry ) throws LdapException 658 { 659 Dn entryDn = ldifEntry.getDn(); 660 LdifEntry newLdifEntry = new LdifEntry( schemaManager ); 661 newLdifEntry.setChangeType( ChangeType.Modify ); 662 663 // Process the DN first 664 Dn anonymizedDn = anonymizeDn( entryDn ); 665 666 newLdifEntry.setDn( anonymizedDn ); 667 668 // Now, process the entry's attributes 669 for ( Modification modification : ldifEntry.getModifications() ) 670 { 671 Attribute attribute = modification.getAttribute(); 672 AttributeType attributeType = schemaManager.getAttributeType( attribute.getId() ); 673 674 if ( attributeType == null ) 675 { 676 System.out.println( "\nUnknown AttributeType : " + attribute.getId() + " for entry " + entryDn ); 677 678 return null; 679 } 680 681 attribute.apply( attributeType ); 682 683 // Deal with the special case of a DN syntax 684 if ( attributeType.getSyntax().getSyntaxChecker() instanceof DnSyntaxChecker ) 685 { 686 Value<?>[] anonymizedValues = new Value<?>[ attribute.size()]; 687 int pos = 0; 688 689 for ( Value<?> dnValue : modification.getAttribute() ) 690 { 691 Dn dn = new Dn( schemaManager, dnValue.getString() ); 692 Dn newdDn = anonymizeDn( dn ); 693 anonymizedValues[pos++] = new StringValue( newdDn.toString() ); 694 } 695 696 Modification anonymizedModification = new DefaultModification( modification.getOperation(), attributeType, anonymizedValues ); 697 newLdifEntry.addModification( anonymizedModification ); 698 } 699 else 700 { 701 Anonymizer anonymizer = attributeAnonymizers.get( attributeType.getOid() ); 702 703 if ( anonymizer == null ) 704 { 705 newLdifEntry.addModification( modification ); 706 } 707 else 708 { 709 Attribute anonymizedAttribute = anonymizer.anonymize( valueMap, valueSet, attribute ); 710 711 Modification anonymizedModification = new DefaultModification( modification.getOperation(), anonymizedAttribute ); 712 newLdifEntry.addModification( anonymizedModification ); 713 } 714 } 715 } 716 717 return newLdifEntry; 718 } 719 720 721 /** 722 * Anonymize a Add change 723 */ 724 private LdifEntry anonymizeChangeAdd( LdifEntry ldifEntry ) throws LdapException 725 { 726 Dn entryDn = ldifEntry.getDn(); 727 LdifEntry newLdifEntry = new LdifEntry( schemaManager ); 728 newLdifEntry.setChangeType( ChangeType.Add ); 729 730 // Process the DN first 731 Dn anonymizedDn = anonymizeDn( entryDn ); 732 733 newLdifEntry.setDn( anonymizedDn ); 734 735 // Now, process the entry's attributes 736 for ( Attribute attribute : ldifEntry ) 737 { 738 AttributeType attributeType = attribute.getAttributeType(); 739 Attribute anonymizedAttribute = new DefaultAttribute( attributeType ); 740 741 // Deal with the special case of a DN syntax 742 743 if ( attributeType.getSyntax().getSyntaxChecker() instanceof DnSyntaxChecker ) 744 { 745 for ( Value<?> dnValue : attribute ) 746 { 747 Dn dn = new Dn( schemaManager, dnValue.getString() ); 748 Dn newdDn = anonymizeDn( dn ); 749 anonymizedAttribute.add( newdDn.toString() ); 750 } 751 752 newLdifEntry.addAttribute( attribute ); 753 } 754 else 755 { 756 Anonymizer anonymizer = attributeAnonymizers.get( attribute.getAttributeType().getOid() ); 757 758 if ( anonymizer == null ) 759 { 760 newLdifEntry.addAttribute( attribute ); 761 } 762 else 763 { 764 anonymizedAttribute = anonymizer.anonymize( valueMap, valueSet, attribute ); 765 766 if ( anonymizedAttribute != null ) 767 { 768 newLdifEntry.addAttribute( anonymizedAttribute ); 769 } 770 } 771 } 772 } 773 774 return newLdifEntry; 775 } 776 777 778 /** 779 * Anonymize a Delete change 780 */ 781 private LdifEntry anonymizeChangeDelete( LdifEntry ldifEntry ) throws LdapException 782 { 783 Dn entryDn = ldifEntry.getDn(); 784 785 // Process the DN, there is nothing more in the entry 786 Dn anonymizedDn = anonymizeDn( entryDn ); 787 788 ldifEntry.setDn( anonymizedDn ); 789 790 return ldifEntry; 791 } 792 793 794 /** 795 * Anonymize a Delete change 796 */ 797 private LdifEntry anonymizeChangeModDn( LdifEntry ldifEntry ) throws LdapException 798 { 799 Dn entryDn = ldifEntry.getDn(); 800 801 // Process the DN 802 Dn anonymizedDn = anonymizeDn( entryDn ); 803 804 ldifEntry.setDn( anonymizedDn ); 805 806 // Anonymize the newRdn if any 807 String newRdnStr = ldifEntry.getNewRdn(); 808 809 if ( newRdnStr != null ) 810 { 811 Dn newRdn = new Dn( schemaManager, newRdnStr ); 812 Dn anonymizedRdn = anonymizeDn( newRdn ); 813 814 ldifEntry.setNewRdn( anonymizedRdn.toString() ); 815 } 816 817 // Anonymize the neSuperior if any 818 String newSuperiorStr = ldifEntry.getNewSuperior(); 819 820 if ( newSuperiorStr != null ) 821 { 822 Dn newSuperior = new Dn( schemaManager, newSuperiorStr ); 823 824 Dn anonymizedSuperior = anonymizeDn( newSuperior ); 825 826 ldifEntry.setNewSuperior( anonymizedSuperior.toString() ); 827 } 828 829 return ldifEntry; 830 } 831 832 833 /** 834 * Anonymize the full entry 835 */ 836 private Entry anonymizeEntry( LdifEntry ldifEntry ) throws LdapException 837 { 838 Entry entry = ldifEntry.getEntry(); 839 Entry newEntry = new DefaultEntry( schemaManager ); 840 841 // Process the DN first 842 Dn entryDn = entry.getDn(); 843 844 Dn anonymizedDn = anonymizeDn( entryDn ); 845 846 // Now, process the entry's attributes 847 for ( Attribute attribute : entry ) 848 { 849 AttributeType attributeType = attribute.getAttributeType(); 850 851 // Deal with the special case of DN 852 if ( attributeType.getSyntax().getSyntaxChecker() instanceof DnSyntaxChecker ) 853 { 854 for ( Value<?> dnValue : attribute ) 855 { 856 Dn dn = new Dn( schemaManager, dnValue.getString() ); 857 Dn newdDn = anonymizeDn( dn ); 858 newEntry.add( attributeType, newdDn.toString() ); 859 } 860 } 861 // Deal with the special case of a NameAndOptionalUID 862 else if ( attributeType.getSyntax().getSyntaxChecker() instanceof NameAndOptionalUIDSyntaxChecker ) 863 { 864 for ( Value<?> dnValue : attribute ) 865 { 866 // Get rid of the # part (UID) 867 String valueStr = dnValue.getString(); 868 int uidPos = valueStr.indexOf( '#' ); 869 String uid = null; 870 871 if ( uidPos != -1 ) 872 { 873 uid = valueStr.substring( uidPos + 1 ); 874 valueStr = valueStr.substring( 0, uidPos ); 875 } 876 877 Dn dn = new Dn( schemaManager, valueStr ); 878 Dn newDn = anonymizeDn( dn ); 879 String newDnStr = newDn.toString(); 880 881 if ( uid != null ) 882 { 883 newDnStr = newDnStr + '#' + uid; 884 } 885 886 newEntry.add( attributeType, newDnStr ); 887 } 888 } 889 else 890 { 891 Anonymizer anonymizer = attributeAnonymizers.get( attribute.getAttributeType().getOid() ); 892 893 if ( anonymizer == null ) 894 { 895 newEntry.add( attribute ); 896 } 897 else 898 { 899 Attribute anonymizedAttribute = anonymizer.anonymize( valueMap, valueSet, attribute ); 900 901 if ( anonymizedAttribute != null ) 902 { 903 newEntry.add( anonymizedAttribute ); 904 } 905 } 906 } 907 } 908 909 newEntry.setDn( anonymizedDn ); 910 911 return newEntry; 912 } 913 914 915 /** 916 * Anonymize a LDIF 917 * 918 * @param ldif The ldif content to anonymize 919 * @return an anonymized version of the given ldif 920 * @throws LdapException If we got some LDAP related exception 921 * @throws IOException If we had some issue during some IO operations 922 */ 923 public String anonymize( String ldif ) throws LdapException, IOException 924 { 925 LdifReader ldifReader = new LdifReader( schemaManager ); 926 927 try 928 { 929 List<LdifEntry> entries = ldifReader.parseLdif( ldif ); 930 StringBuilder result = new StringBuilder(); 931 932 for ( LdifEntry ldifEntry : entries ) 933 { 934 if ( ldifEntry.isEntry() && !ldifEntry.isChangeAdd() ) 935 { 936 // process a full entry. Add changes aren't preocessed ghere. 937 Entry newEntry = anonymizeEntry( ldifEntry ); 938 939 result.append( LdifUtils.convertToLdif( newEntry ) ); 940 result.append( "\n" ); 941 } 942 else if ( ldifEntry.isChangeDelete() ) 943 { 944 // A Delete operation 945 LdifEntry newLdifEntry = anonymizeChangeDelete( ldifEntry ); 946 947 if ( newLdifEntry != null ) 948 { 949 result.append( newLdifEntry ); 950 result.append( "\n" ); 951 } 952 } 953 else if ( ldifEntry.isChangeAdd() ) 954 { 955 // A Add operation 956 LdifEntry newLdifEntry = anonymizeChangeAdd( ldifEntry ); 957 958 if ( newLdifEntry != null ) 959 { 960 result.append( newLdifEntry ); 961 result.append( "\n" ); 962 } 963 } 964 else if ( ldifEntry.isChangeModify() ) 965 { 966 // A Modify operation 967 LdifEntry newLdifEntry = anonymizeChangeModify( ldifEntry ); 968 969 if ( newLdifEntry != null ) 970 { 971 result.append( newLdifEntry ); 972 result.append( "\n" ); 973 } 974 } 975 else if ( ldifEntry.isChangeModDn() || ldifEntry.isChangeModRdn() ) 976 { 977 // A MODDN operation 978 LdifEntry newLdifEntry = anonymizeChangeModDn( ldifEntry ); 979 980 if ( newLdifEntry != null ) 981 { 982 result.append( newLdifEntry ); 983 result.append( "\n" ); 984 } 985 } 986 } 987 988 return result.toString(); 989 } 990 catch ( Exception e ) 991 { 992 println( "Error :" + e.getMessage() ); 993 return null; 994 } 995 finally 996 { 997 ldifReader.close(); 998 } 999 } 1000 1001 1002 /** 1003 * @return the valueMap 1004 */ 1005 public Map<Value<?>, Value<?>> getValueMap() 1006 { 1007 return valueMap; 1008 } 1009 1010 1011 /** 1012 * @param valueMap the valueMap to set 1013 */ 1014 public void setValueMap( Map<Value<?>, Value<?>> valueMap ) 1015 { 1016 this.valueMap = valueMap; 1017 } 1018 1019 1020 /** 1021 * @return the latest String Value Map 1022 */ 1023 public Map<Integer, String> getLatestStringMap() 1024 { 1025 return latestStringMap; 1026 } 1027 1028 1029 /** 1030 * @param latestStringMap the latest String Value Map to set 1031 */ 1032 public void setLatestStringMap( Map<Integer, String> latestStringMap ) 1033 { 1034 this.latestStringMap = latestStringMap; 1035 } 1036 1037 1038 /** 1039 * @return the latest byte[] Value Map 1040 */ 1041 public Map<Integer, byte[]> getLatestBytesMap() 1042 { 1043 return latestBytesMap; 1044 } 1045 1046 1047 /** 1048 * @param latestBytesMap the latest byte[] Value Map to set 1049 */ 1050 public void setLatestBytesMap( Map<Integer, byte[]> latestBytesMap ) 1051 { 1052 this.latestBytesMap = latestBytesMap; 1053 } 1054 1055 1056 /** 1057 * The entry point, when used as a standalone application. 1058 * 1059 * @param args Contains the arguments : the file to convert. The anonymized 1060 * LDIF will be printed on stdout 1061 * @throws IOException If we had an issue opening the file to anonymise ot writing the result 1062 * @throws LdapException If we had some issue while processing the LDAP data 1063 */ 1064 public static void main( String[] args ) throws IOException, LdapException 1065 { 1066 if ( ( args == null ) || ( args.length < 1 ) ) 1067 { 1068 System.out.println( "No file to anonymize" ); 1069 return; 1070 } 1071 1072 LdifAnonymizer anonymizer = new LdifAnonymizer(); 1073 1074 String ldifString = null; 1075 1076 try ( InputStream fis = Files.newInputStream( Paths.get( args[0] ) ) ) 1077 { 1078 1079 try ( BufferedReader br = new BufferedReader( new InputStreamReader( fis, Charset.defaultCharset() ) ) ) 1080 { 1081 StringBuilder sb = new StringBuilder(); 1082 String line = br.readLine(); 1083 1084 while ( line != null ) 1085 { 1086 sb.append( line ); 1087 sb.append( System.lineSeparator() ); 1088 line = br.readLine(); 1089 } 1090 1091 ldifString = sb.toString(); 1092 } 1093 } 1094 1095 String result = anonymizer.anonymize( ldifString ); 1096 1097 System.out.println( result ); 1098 } 1099}