001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.server.types; 018 019import static org.opends.messages.UtilityMessages.*; 020import static org.opends.server.util.StaticUtils.*; 021 022import java.io.BufferedWriter; 023import java.io.Closeable; 024import java.io.File; 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.io.OutputStream; 028import java.io.OutputStreamWriter; 029import java.util.ArrayList; 030import java.util.HashSet; 031import java.util.List; 032import java.util.Set; 033import java.util.zip.GZIPOutputStream; 034 035import org.forgerock.i18n.LocalizableMessage; 036import org.forgerock.opendj.ldap.DN; 037import org.forgerock.opendj.ldap.schema.AttributeType; 038import org.opends.server.util.StaticUtils; 039 040/** 041 * This class defines a data structure for holding configuration 042 * information to use when performing an LDIF export. 043 */ 044@org.opends.server.types.PublicAPI( 045 stability=org.opends.server.types.StabilityLevel.VOLATILE, 046 mayInstantiate=true, 047 mayExtend=false, 048 mayInvoke=true) 049public final class LDIFExportConfig extends OperationConfig 050 implements Closeable 051{ 052 /** Indicates whether the data should be compressed as it is written. */ 053 private boolean compressData; 054 /** Indicates whether the data should be encrypted as it is written. */ 055 private boolean encryptData; 056 /** Indicates whether to generate a cryptographic hash of the data as it is written. */ 057 private boolean hashData; 058 /** Indicates whether to include the objectclasses in the entries written in the export. */ 059 private boolean includeObjectClasses; 060 /** Indicates whether to include operational attributes in the export. */ 061 private boolean includeOperationalAttributes; 062 /** Indicates whether to include virtual attributes in the export. */ 063 private boolean includeVirtualAttributes; 064 /** Indicates whether to invoke LDIF export plugins on entries being exported. */ 065 private boolean invokeExportPlugins; 066 067 /** 068 * Indicates whether to digitally sign the hash when the export is complete. 069 */ 070 private boolean signHash; 071 072 /** Indicates whether to include attribute types (i.e., names) only or both types and values. */ 073 private boolean typesOnly; 074 075 /** The path to the LDIF file that should be written. */ 076 private final String ldifFile; 077 /** The buffered writer to which the LDIF data should be written. */ 078 private BufferedWriter writer; 079 /** The output stream to which the LDIF data should be written. */ 080 private OutputStream ldifOutputStream; 081 082 /** 083 * The behavior that should be used when writing an LDIF file and a file with 084 * the same name already exists. 085 */ 086 private final ExistingFileBehavior existingFileBehavior; 087 088 /** The column number at which long lines should be wrapped. */ 089 private int wrapColumn; 090 091 /** The set of base DNs to exclude from the export. */ 092 private List<DN> excludeBranches; 093 /** The set of base DNs to include from the export. */ 094 private List<DN> includeBranches; 095 096 /** The set of search filters for entries to exclude from the export. */ 097 private List<SearchFilter> excludeFilters; 098 /** The set of search filters for entries to include in the export. */ 099 private List<SearchFilter> includeFilters; 100 101 /** The set of attribute types that should be excluded from the export. */ 102 private Set<AttributeType> excludeAttributes; 103 /** The set of attribute types that should be included in the export. */ 104 private Set<AttributeType> includeAttributes; 105 106 /** 107 * Creates a new LDIF export configuration that will write to the 108 * specified LDIF file. 109 * 110 * @param ldifFile The path to the LDIF file to 111 * export. 112 * @param existingFileBehavior Indicates how to proceed if the 113 * specified file already exists. 114 */ 115 public LDIFExportConfig(String ldifFile, 116 ExistingFileBehavior existingFileBehavior) 117 { 118 this.ldifFile = ldifFile; 119 this.existingFileBehavior = existingFileBehavior; 120 ldifOutputStream = null; 121 122 excludeBranches = new ArrayList<>(); 123 includeBranches = new ArrayList<>(); 124 excludeFilters = new ArrayList<>(); 125 includeFilters = new ArrayList<>(); 126 compressData = false; 127 encryptData = false; 128 hashData = false; 129 includeObjectClasses = true; 130 includeOperationalAttributes = true; 131 includeVirtualAttributes = false; 132 invokeExportPlugins = false; 133 signHash = false; 134 typesOnly = false; 135 writer = null; 136 excludeAttributes = new HashSet<>(); 137 includeAttributes = new HashSet<>(); 138 wrapColumn = -1; 139 } 140 141 /** 142 * Creates a new LDIF export configuration that will write to the 143 * provided output stream. 144 * 145 * @param ldifOutputStream The output stream to which the LDIF 146 * data should be written. 147 */ 148 public LDIFExportConfig(OutputStream ldifOutputStream) 149 { 150 this.ldifOutputStream = ldifOutputStream; 151 ldifFile = null; 152 existingFileBehavior = ExistingFileBehavior.FAIL; 153 154 excludeBranches = new ArrayList<>(); 155 includeBranches = new ArrayList<>(); 156 excludeFilters = new ArrayList<>(); 157 includeFilters = new ArrayList<>(); 158 compressData = false; 159 encryptData = false; 160 hashData = false; 161 includeObjectClasses = true; 162 includeOperationalAttributes = true; 163 includeVirtualAttributes = false; 164 invokeExportPlugins = false; 165 signHash = false; 166 typesOnly = false; 167 writer = null; 168 excludeAttributes = new HashSet<>(); 169 includeAttributes = new HashSet<>(); 170 wrapColumn = -1; 171 } 172 173 /** 174 * Retrieves the writer that should be used to write the LDIF data. 175 * If compression or encryption are to be used, then they must be 176 * enabled before the first call to this method. 177 * 178 * @return The writer that should be used to write the LDIF data. 179 * 180 * @throws IOException If a problem occurs while preparing the 181 * writer. 182 */ 183 public BufferedWriter getWriter() 184 throws IOException 185 { 186 if (writer == null) 187 { 188 if (ldifOutputStream == null) 189 { 190 File f = new File(ldifFile); 191 boolean mustSetPermissions = false; 192 193 switch (existingFileBehavior) 194 { 195 case APPEND: 196 // Create new file if it doesn't exist ensuring that we can 197 // set its permissions. 198 if (!f.exists()) 199 { 200 f.createNewFile(); 201 mustSetPermissions = true; 202 } 203 ldifOutputStream = new FileOutputStream(ldifFile, true); 204 break; 205 case OVERWRITE: 206 // Create new file if it doesn't exist ensuring that we can 207 // set its permissions. 208 if (!f.exists()) 209 { 210 f.createNewFile(); 211 mustSetPermissions = true; 212 } 213 ldifOutputStream = new FileOutputStream(ldifFile, false); 214 break; 215 case FAIL: 216 if (f.exists()) 217 { 218 LocalizableMessage message = ERR_LDIF_FILE_EXISTS.get(ldifFile); 219 throw new IOException(message.toString()); 220 } 221 // Create new file ensuring that we can set its permissions. 222 f.createNewFile(); 223 mustSetPermissions = true; 224 ldifOutputStream = new FileOutputStream(ldifFile); 225 break; 226 } 227 228 if (mustSetPermissions) 229 { 230 try 231 { 232 // Ignore 233 FilePermission.setSafePermissions(f, 0600); 234 } 235 catch (Exception e) 236 { 237 // The file could not be created with the correct permissions. 238 LocalizableMessage message = WARN_EXPORT_LDIF_SET_PERMISSION_FAILED 239 .get(f, stackTraceToSingleLineString(e)); 240 throw new IOException(message.toString()); 241 } 242 } 243 } 244 245 // See if we should compress the output. 246 OutputStream outputStream; 247 if (compressData) 248 { 249 outputStream = new GZIPOutputStream(ldifOutputStream); 250 } 251 else 252 { 253 outputStream = ldifOutputStream; 254 } 255 256 257 // See if we should encrypt the output. 258 if (encryptData) 259 { 260 // FIXME -- To be implemented: See OPENDJ-448. 261 } 262 263 264 // Create the writer. 265 writer = new BufferedWriter(new OutputStreamWriter(outputStream)); 266 } 267 268 return writer; 269 } 270 271 /** 272 * Indicates whether the LDIF export plugins should be invoked for 273 * entries as they are exported. 274 * 275 * @return <CODE>true</CODE> if LDIF export plugins should be 276 * invoked for entries as they are exported, or 277 * <CODE>false</CODE> if not. 278 */ 279 public boolean invokeExportPlugins() 280 { 281 return invokeExportPlugins; 282 } 283 284 /** 285 * Specifies whether the LDIF export plugins should be invoked for 286 * entries as they are exported. 287 * 288 * @param invokeExportPlugins Specifies whether the LDIF export 289 * plugins should be invoked for 290 * entries as they are exported. 291 */ 292 public void setInvokeExportPlugins(boolean invokeExportPlugins) 293 { 294 this.invokeExportPlugins = invokeExportPlugins; 295 } 296 297 /** 298 * Specifies whether the LDIF data should be compressed as it is 299 * written. If compression should be used, then this must be set 300 * before calling <CODE>getWriter</CODE> for the first time. 301 * 302 * @param compressData Indicates whether the LDIF data should be 303 * compressed as it is written. 304 */ 305 public void setCompressData(boolean compressData) 306 { 307 this.compressData = compressData; 308 } 309 310 311 312 /** 313 * Indicates whether the LDIF data should be encrypted as it is 314 * written. 315 * 316 * @return <CODE>true</CODE> if the LDIF data should be encrypted 317 * as it is written, or <CODE>false</CODE> if not. 318 */ 319 public boolean encryptData() 320 { 321 return encryptData; 322 } 323 324 325 326 /** 327 * Specifies whether the LDIF data should be encrypted as it is 328 * written. If encryption should be used, then this must be set 329 * before calling <CODE>getWriter</CODE> for the first time. 330 * 331 * @param encryptData Indicates whether the LDIF data should be 332 * encrypted as it is written. 333 */ 334 public void setEncryptData(boolean encryptData) 335 { 336 this.encryptData = encryptData; 337 } 338 339 340 341 /** 342 * Indicates whether to generate a cryptographic hash of the data 343 * that is written. 344 * 345 * @return <CODE>true</CODE> if a hash should be generated as the 346 * data is written, or <CODE>false</CODE> if not. 347 */ 348 public boolean hashData() 349 { 350 return hashData; 351 } 352 353 354 355 /** 356 * Specifies whether to generate a cryptographic hash of the data 357 * that is written. If hashing is to be used, then this must be set 358 * before calling <CODE>getWriter</CODE> for the first time. 359 * 360 * @param hashData Indicates whether to generate a hash of the 361 * data as it is written. 362 */ 363 public void setHashData(boolean hashData) 364 { 365 this.hashData = hashData; 366 } 367 368 369 370 /** 371 * Indicates whether to sign the cryptographic hash of the data that 372 * is written when the export is complete. 373 * 374 * @return <CODE>true</CODE> if the hash should be signed when the 375 * export is complete, or <CODE>false</CODE> if not. 376 */ 377 public boolean signHash() 378 { 379 return signHash; 380 } 381 382 383 384 /** 385 * Specifies whether to sign the cryptographic hash of the data that 386 * is written when the export is complete. If the export is not 387 * configured to generate a hash, then this will be ignored. If 388 * hashing is to be used and the hash should be signed, then this 389 * must be set before calling <CODE>getWriter</CODE> for the first 390 * time. 391 * 392 * @param signHash Indicates whether to generate a hash of the 393 * data as it is written. 394 */ 395 public void setSignHash(boolean signHash) 396 { 397 this.signHash = signHash; 398 } 399 400 401 402 /** 403 * Indicates whether the LDIF generated should include attribute 404 * types (i.e., attribute names) only or both attribute types and 405 * values. 406 * 407 * @return <CODE>true</CODE> if only attribute types should be 408 * included in the resulting LDIF, or <CODE>false</CODE> if 409 * both types and values should be included. 410 */ 411 public boolean typesOnly() 412 { 413 return typesOnly; 414 } 415 416 /** 417 * Specifies whether the LDIF generated should include attribute 418 * types (i.e., attribute names) only or both attribute types and 419 * values. 420 * 421 * @param typesOnly Specifies whether the LDIF generated should 422 * include attribute types only or both attribute 423 * types and values. 424 */ 425 public void setTypesOnly(boolean typesOnly) 426 { 427 this.typesOnly = typesOnly; 428 } 429 430 /** 431 * Retrieves the column at which long lines should be wrapped. 432 * 433 * @return The column at which long lines should be wrapped, or a 434 * value less than or equal to zero to indicate that no 435 * wrapping should be performed. 436 */ 437 public int getWrapColumn() 438 { 439 return wrapColumn; 440 } 441 442 /** 443 * Specifies the column at which long lines should be wrapped. A 444 * value less than or equal to zero indicates that no wrapping 445 * should be performed. 446 * 447 * @param wrapColumn The column at which long lines should be 448 * wrapped. 449 */ 450 public void setWrapColumn(int wrapColumn) 451 { 452 this.wrapColumn = wrapColumn; 453 } 454 455 /** 456 * Retrieves the set of base DNs that specify the set of entries to 457 * exclude from the export. The list that is returned may be 458 * altered by the caller. 459 * 460 * @return The set of base DNs that specify the set of entries to 461 * exclude from the export. 462 */ 463 public List<DN> getExcludeBranches() 464 { 465 return excludeBranches; 466 } 467 468 /** 469 * Specifies the set of base DNs that specify the set of entries to 470 * exclude from the export. 471 * 472 * @param excludeBranches The set of base DNs that specify the set 473 * of entries to exclude from the export. 474 */ 475 public void setExcludeBranches(List<DN> excludeBranches) 476 { 477 if (excludeBranches != null) 478 { 479 this.excludeBranches = excludeBranches; 480 } 481 else 482 { 483 this.excludeBranches = new ArrayList<>(0); 484 } 485 } 486 487 /** 488 * Retrieves the set of base DNs that specify the set of entries to 489 * include in the export. The list that is returned may be altered 490 * by the caller. 491 * 492 * @return The set of base DNs that specify the set of entries to 493 * include in the export. 494 */ 495 public List<DN> getIncludeBranches() 496 { 497 return includeBranches; 498 } 499 500 /** 501 * Specifies the set of base DNs that specify the set of entries to 502 * include in the export. 503 * 504 * @param includeBranches The set of base DNs that specify the set 505 * of entries to include in the export. 506 */ 507 public void setIncludeBranches(List<DN> includeBranches) 508 { 509 if (includeBranches != null) 510 { 511 this.includeBranches = includeBranches; 512 } 513 else 514 { 515 this.includeBranches = new ArrayList<>(0); 516 } 517 } 518 519 /** 520 * Indicates whether the set of objectclasses should be included in 521 * the entries written to LDIF. 522 * 523 * @return <CODE>true</CODE> if the set of objectclasses should be 524 * included in the entries written to LDIF, or 525 * <CODE>false</CODE> if not. 526 */ 527 public boolean includeObjectClasses() 528 { 529 return includeObjectClasses; 530 } 531 532 /** 533 * Indicates whether the set of operational attributes should be 534 * included in the export. 535 * 536 * @return <CODE>true</CODE> if the set of operational attributes 537 * should be included in the export. 538 */ 539 public boolean includeOperationalAttributes() 540 { 541 return includeOperationalAttributes; 542 } 543 544 /** 545 * Specifies whether the objectclasss attribute should be 546 * included in the export. 547 * 548 * @param includeObjectClasses Specifies whether the 549 * objectclass attribute 550 * should be included in the 551 * export. 552 */ 553 public void setIncludeObjectClasses(boolean includeObjectClasses) 554 { 555 this.includeObjectClasses = includeObjectClasses; 556 } 557 558 /** 559 * Specifies whether the set of operational attributes should be 560 * included in the export. 561 * 562 * @param includeOperationalAttributes Specifies whether the set 563 * of operational attributes 564 * should be included in the 565 * export. 566 */ 567 public void setIncludeOperationalAttributes( 568 boolean includeOperationalAttributes) 569 { 570 this.includeOperationalAttributes = includeOperationalAttributes; 571 } 572 573 /** 574 * Indicates whether virtual attributes should be included in the 575 * export. 576 * 577 * @return {@code true} if virtual attributes should be included in 578 * the export, or {@code false} if not. 579 */ 580 public boolean includeVirtualAttributes() 581 { 582 return includeVirtualAttributes; 583 } 584 585 /** 586 * Specifies whether virtual attributes should be included in the 587 * export. 588 * 589 * @param includeVirtualAttributes Specifies whether virtual 590 * attributes should be included 591 * in the export. 592 */ 593 public void setIncludeVirtualAttributes( 594 boolean includeVirtualAttributes) 595 { 596 this.includeVirtualAttributes = includeVirtualAttributes; 597 } 598 599 /** 600 * Retrieves the set of attributes that should be excluded from the 601 * entries written to LDIF. The set that is returned may be altered 602 * by the caller. 603 * 604 * @return The set of attributes that should be excluded from the 605 * entries written to LDIF. 606 */ 607 public Set<AttributeType> getExcludeAttributes() 608 { 609 return excludeAttributes; 610 } 611 612 /** 613 * Specifies the set of attributes that should be excluded from the 614 * entries written to LDIF. 615 * 616 * @param excludeAttributes The set of attributes that should be 617 * excluded from the entries written to 618 * LDIF. 619 */ 620 public void setExcludeAttributes( 621 Set<AttributeType> excludeAttributes) 622 { 623 if (excludeAttributes != null) 624 { 625 this.excludeAttributes = excludeAttributes; 626 } 627 else 628 { 629 this.excludeAttributes = new HashSet<>(0); 630 } 631 } 632 633 /** 634 * Retrieves the set of attributes that should be included in the 635 * entries written to LDIF. The set that is returned may be altered 636 * by the caller. 637 * 638 * @return The set of attributes that should be included in the 639 * entries written to LDIF. 640 */ 641 public Set<AttributeType> getIncludeAttributes() 642 { 643 return includeAttributes; 644 } 645 646 /** 647 * Specifies the set of attributes that should be included in the 648 * entries written to LDIF. 649 * 650 * @param includeAttributes The set of attributes that should be 651 * included in the entries written to 652 * LDIF. 653 */ 654 public void setIncludeAttributes(Set<AttributeType> includeAttributes) 655 { 656 if (includeAttributes != null) 657 { 658 this.includeAttributes = includeAttributes; 659 } 660 else 661 { 662 this.includeAttributes = new HashSet<>(0); 663 } 664 } 665 666 /** 667 * Indicates whether the specified attribute should be included in 668 * the entries written to LDIF. 669 * 670 * @param attributeType The attribute type for which to make the 671 * determination. 672 * 673 * @return <CODE>true</CODE> if the specified attribute should be 674 * included in the entries written to LDIF, or 675 * <CODE>false</CODE> if not. 676 */ 677 public boolean includeAttribute(AttributeType attributeType) 678 { 679 return (excludeAttributes.isEmpty() 680 || !excludeAttributes.contains(attributeType)) 681 && (includeAttributes.isEmpty() 682 || includeAttributes.contains(attributeType)); 683 } 684 685 /** 686 * Retrieves the set of search filters that should be used to 687 * determine which entries to exclude from the LDIF. The list that 688 * is returned may be altered by the caller. 689 * 690 * @return The set of search filters that should be used to 691 * determine which entries to exclude from the LDIF. 692 */ 693 public List<SearchFilter> getExcludeFilters() 694 { 695 return excludeFilters; 696 } 697 698 /** 699 * Specifies the set of search filters that should be used to 700 * determine which entries to exclude from the LDIF. 701 * 702 * @param excludeFilters The set of search filters that should be 703 * used to determine which entries to 704 * exclude from the LDIF. 705 */ 706 public void setExcludeFilters(List<SearchFilter> excludeFilters) 707 { 708 if (excludeFilters != null) 709 { 710 this.excludeFilters = excludeFilters; 711 } 712 else 713 { 714 this.excludeFilters = new ArrayList<>(0); 715 } 716 } 717 718 /** 719 * Retrieves the set of search filters that should be used to 720 * determine which entries to include in the LDIF. The list that is 721 * returned may be altered by the caller. 722 * 723 * @return The set of search filters that should be used to 724 * determine which entries to include in the LDIF. 725 */ 726 public List<SearchFilter> getIncludeFilters() 727 { 728 return includeFilters; 729 } 730 731 /** 732 * Specifies the set of search filters that should be used to 733 * determine which entries to include in the LDIF. 734 * 735 * @param includeFilters The set of search filters that should be 736 * used to determine which entries to 737 * include in the LDIF. 738 */ 739 public void setIncludeFilters(List<SearchFilter> includeFilters) 740 { 741 if (includeFilters != null) 742 { 743 this.includeFilters = includeFilters; 744 } 745 else 746 { 747 this.includeFilters = new ArrayList<>(0); 748 } 749 } 750 751 /** 752 * Indicates whether the specified entry should be included in the 753 * export based on the configured set of include and exclude 754 * filters. 755 * 756 * @param entry The entry for which to make the determination. 757 * 758 * @return <CODE>true</CODE> if the specified entry should be 759 * included in the export, or <CODE>false</CODE> if not. 760 * 761 * @throws DirectoryException If there is a problem with any of 762 * the search filters used to make the 763 * determination. 764 */ 765 public boolean includeEntry(Entry entry) 766 throws DirectoryException 767 { 768 DN dn = entry.getName(); 769 if (! excludeBranches.isEmpty()) 770 { 771 for (DN excludeBranch : excludeBranches) 772 { 773 if (excludeBranch.isSuperiorOrEqualTo(dn)) 774 { 775 return false; 776 } 777 } 778 } 779 780 checkIncludeBranches: if (! includeBranches.isEmpty()) 781 { 782 for (DN includeBranch : includeBranches) 783 { 784 if (includeBranch.isSuperiorOrEqualTo(dn)) 785 { 786 break checkIncludeBranches; 787 } 788 } 789 790 return false; 791 } 792 793 if (! excludeFilters.isEmpty()) 794 { 795 for (SearchFilter filter : excludeFilters) 796 { 797 if (filter.matchesEntry(entry)) 798 { 799 return false; 800 } 801 } 802 } 803 804 if (! includeFilters.isEmpty()) 805 { 806 for (SearchFilter filter : includeFilters) 807 { 808 if (filter.matchesEntry(entry)) 809 { 810 return true; 811 } 812 } 813 return false; 814 } 815 816 return true; 817 } 818 819 /** Closes any resources that this export config might have open. */ 820 @Override 821 public void close() 822 { 823 // FIXME -- Need to add code to generate a signed hash of the LDIF content. 824 StaticUtils.close(writer); 825 } 826}