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