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