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-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011 profiq s.r.o. 016 * Portions Copyright 2011-2016 ForgeRock AS. 017 */ 018package org.opends.server.tools; 019 020import static org.forgerock.util.Utils.*; 021import static org.opends.messages.AdminToolMessages.*; 022import static org.opends.messages.QuickSetupMessages.*; 023import static org.opends.messages.ToolMessages.*; 024import static org.opends.messages.UtilityMessages.*; 025 026import static com.forgerock.opendj.cli.Utils.*; 027import static com.forgerock.opendj.util.OperatingSystem.*; 028 029import java.io.BufferedReader; 030import java.io.File; 031import java.io.IOException; 032import java.io.InputStream; 033import java.io.InputStreamReader; 034import java.io.OutputStream; 035import java.io.PrintStream; 036import java.security.KeyStoreException; 037import java.util.ArrayList; 038import java.util.Arrays; 039import java.util.Collection; 040import java.util.Collections; 041import java.util.LinkedList; 042import java.util.List; 043 044import javax.naming.ldap.LdapName; 045 046import org.forgerock.i18n.LocalizableMessage; 047import org.forgerock.i18n.LocalizableMessageDescriptor.Arg0; 048import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; 049import org.forgerock.i18n.slf4j.LocalizedLogger; 050import org.forgerock.opendj.config.ManagedObjectDefinition; 051import org.forgerock.opendj.server.config.client.BackendCfgClient; 052import org.forgerock.opendj.server.config.server.BackendCfg; 053import org.opends.messages.QuickSetupMessages; 054import org.opends.messages.ToolMessages; 055import org.opends.quicksetup.ApplicationException; 056import org.opends.quicksetup.Constants; 057import org.opends.quicksetup.CurrentInstallStatus; 058import org.opends.quicksetup.Installation; 059import org.opends.quicksetup.LicenseFile; 060import org.opends.quicksetup.QuickSetupLog; 061import org.opends.quicksetup.SecurityOptions; 062import org.opends.quicksetup.UserData; 063import org.opends.quicksetup.UserDataException; 064import org.opends.quicksetup.event.ProgressUpdateEvent; 065import org.opends.quicksetup.event.ProgressUpdateListener; 066import org.opends.quicksetup.installer.NewSuffixOptions; 067import org.opends.quicksetup.installer.offline.OfflineInstaller; 068import org.opends.quicksetup.util.IncompatibleVersionException; 069import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; 070import org.opends.quicksetup.util.Utils; 071import org.opends.server.types.InitializationException; 072import org.opends.server.types.NullOutputStream; 073import org.opends.server.util.CertificateManager; 074import org.opends.server.util.SetupUtils; 075import org.opends.server.util.StaticUtils; 076 077import com.forgerock.opendj.cli.ArgumentException; 078import com.forgerock.opendj.cli.ClientException; 079import com.forgerock.opendj.cli.ConsoleApplication; 080import com.forgerock.opendj.cli.IntegerArgument; 081import com.forgerock.opendj.cli.Menu; 082import com.forgerock.opendj.cli.MenuBuilder; 083import com.forgerock.opendj.cli.MenuResult; 084import com.forgerock.opendj.cli.StringArgument; 085 086/** 087 * This class provides a very simple mechanism for installing the OpenDS 088 * Directory Service. It performs the following tasks: 089 * <UL> 090 * <LI>Checks if the server is already installed and running</LI> 091 * <LI>Ask the user what base DN should be used for the data</LI> 092 * <LI>Ask the user whether to create the base entry, or to import LDIF</LI> 093 * <LI>Ask the user for the administration port and make sure it's available 094 * </LI> 095 * <LI>Ask the user for the LDAP port and make sure it's available</LI> 096 * <LI>Ask the user for the default root DN and password</LI> 097 * <LI>Ask the user to enable SSL or not and for the type of certificate that 098 * the server must use</LI> 099 * <LI>Ask the user if they want to start the server when done installing</LI> 100 * </UL> 101 */ 102public class InstallDS extends ConsoleApplication 103{ 104 105 private final PlainTextProgressMessageFormatter formatter = new PlainTextProgressMessageFormatter(); 106 107 /** Prefix for log files. */ 108 public static final String TMP_FILE_PREFIX = "opendj-setup-"; 109 110 /** Suffix for log files. */ 111 public static final String LOG_FILE_SUFFIX = ".log"; 112 113 /** 114 * The enumeration containing the different return codes that the command-line 115 * can have. 116 */ 117 private enum InstallReturnCode 118 { 119 SUCCESSFUL(0), 120 121 /** We did no have an error but the setup was not executed (displayed version or usage). */ 122 SUCCESSFUL_NOP(0), 123 124 /** Unexpected error (potential bug). */ 125 ERROR_UNEXPECTED(1), 126 127 /** Cannot parse arguments or data provided by user is not valid. */ 128 ERROR_USER_DATA(2), 129 130 /** Error server already installed. */ 131 ERROR_SERVER_ALREADY_INSTALLED(3), 132 133 /** Error initializing server. */ 134 ERROR_INITIALIZING_SERVER(4), 135 136 /** The user failed providing password (for the keystore for instance). */ 137 ERROR_PASSWORD_LIMIT(5), 138 139 /** The user cancelled the setup. */ 140 ERROR_USER_CANCELLED(6), 141 142 /** The user doesn't accept the license. */ 143 ERROR_LICENSE_NOT_ACCEPTED(7), 144 145 /** Incompatible java version. */ 146 JAVA_VERSION_INCOMPATIBLE(8); 147 148 private int returnCode; 149 private InstallReturnCode(int returnCode) 150 { 151 this.returnCode = returnCode; 152 } 153 154 /** 155 * Get the corresponding return code value. 156 * 157 * @return The corresponding return code value. 158 */ 159 public int getReturnCode() 160 { 161 return returnCode; 162 } 163 } 164 165 /** 166 * Enumeration describing the different answer that the user can provide when 167 * we ask to finalize the setup. Note that the code associated correspond to 168 * the order in the confirmation menu that is displayed at the end of the 169 * setup in interactive mode. 170 */ 171 private enum ConfirmCode 172 { 173 CONTINUE(1), 174 PROVIDE_INFORMATION_AGAIN(2), 175 PRINT_EQUIVALENT_COMMAND_LINE(3), 176 CANCEL(3); 177 178 private int returnCode; 179 private ConfirmCode(int returnCode) 180 { 181 this.returnCode = returnCode; 182 } 183 184 /** 185 * Get the corresponding return code value. 186 * 187 * @return The corresponding return code value. 188 */ 189 public int getReturnCode() 190 { 191 return returnCode; 192 } 193 } 194 195 /** 196 * The maximum number of times that we should ask the user to provide the 197 * password to access to a keystore. 198 */ 199 public static final int LIMIT_KEYSTORE_PASSWORD_PROMPT = 7; 200 201 private final BackendTypeHelper backendTypeHelper = new BackendTypeHelper(); 202 203 /** The argument parser. */ 204 private InstallDSArgumentParser argParser; 205 206 /** Different variables we use when the user decides to provide data again. */ 207 private NewSuffixOptions.Type lastResetPopulateOption; 208 private ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> lastResetBackendType; 209 private String lastResetImportFile; 210 private String lastResetRejectedFile; 211 private String lastResetSkippedFile; 212 private Integer lastResetNumEntries; 213 private Boolean lastResetEnableSSL; 214 private Boolean lastResetEnableStartTLS; 215 private SecurityOptions.CertificateType lastResetCertType; 216 private String lastResetKeyStorePath; 217 private Boolean lastResetEnableWindowsService; 218 private Boolean lastResetStartServer; 219 private String lastResetBaseDN = Installation.DEFAULT_INTERACTIVE_BASE_DN; 220 private String lastResetDirectoryManagerDN; 221 private Integer lastResetLdapPort; 222 private Integer lastResetLdapsPort; 223 private Integer lastResetAdminConnectorPort; 224 private Integer lastResetJmxPort; 225 226 227 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 228 229 230 /** 231 * Constructor for the InstallDS object. 232 * 233 * @param out 234 * the print stream to use for standard output. 235 * @param err 236 * the print stream to use for standard error. 237 * @param in 238 * the input stream to use for standard input. 239 */ 240 public InstallDS(PrintStream out, PrintStream err, InputStream in) 241 { 242 super(out, err); 243 } 244 245 /** 246 * The main method for the InstallDS CLI tool. 247 * 248 * @param args 249 * the command-line arguments provided to this program. 250 */ 251 public static void main(String[] args) 252 { 253 final int retCode = mainCLI(args, System.out, System.err, System.in); 254 255 System.exit(retCode); 256 } 257 258 /** 259 * Parses the provided command-line arguments and uses that information to run 260 * the setup tool. 261 * 262 * @param args 263 * the command-line arguments provided to this program. 264 * @return The error code. 265 */ 266 public static int mainCLI(String[] args) 267 { 268 return mainCLI(args, System.out, System.err, System.in); 269 } 270 271 /** 272 * Parses the provided command-line arguments and uses that information to run 273 * the setup tool. 274 * 275 * @param args 276 * The command-line arguments provided to this program. 277 * @param outStream 278 * The output stream to use for standard output, or <CODE>null</CODE> 279 * if standard output is not needed. 280 * @param errStream 281 * The output stream to use for standard error, or <CODE>null</CODE> 282 * if standard error is not needed. 283 * @param inStream 284 * The input stream to use for standard input. 285 * @return The error code. 286 */ 287 public static int mainCLI(String[] args, OutputStream outStream, OutputStream errStream, InputStream inStream) 288 { 289 final PrintStream out = NullOutputStream.wrapOrNullStream(outStream); 290 291 System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); 292 293 final PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 294 295 try { 296 QuickSetupLog.initLogFileHandler( 297 QuickSetupLog.isInitialized() ? null : 298 File.createTempFile(TMP_FILE_PREFIX, LOG_FILE_SUFFIX)); 299 } catch (final Throwable t) { 300 System.err.println("Unable to initialize log"); 301 t.printStackTrace(); 302 } 303 304 final InstallDS install = new InstallDS(out, err, inStream); 305 306 int retCode = install.execute(args); 307 if (retCode == 0) 308 { 309 QuickSetupLog.closeAndDeleteLogFile(); 310 } 311 return retCode; 312 } 313 314 /** 315 * Parses the provided command-line arguments and uses that information to run 316 * the setup CLI. 317 * 318 * @param args 319 * the command-line arguments provided to this program. 320 * @return the return code (SUCCESSFUL, USER_DATA_ERROR or BUG). 321 */ 322 public int execute(String[] args) 323 { 324 argParser = new InstallDSArgumentParser(InstallDS.class.getName()); 325 try 326 { 327 argParser.initializeArguments(); 328 } 329 catch (final ArgumentException ae) 330 { 331 println(ToolMessages.ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 332 return InstallReturnCode.ERROR_UNEXPECTED.getReturnCode(); 333 } 334 335 lastResetDirectoryManagerDN = argParser.directoryManagerDNArg.getDefaultValue(); 336 lastResetLdapPort = Integer.parseInt(argParser.ldapPortArg.getDefaultValue()); 337 lastResetLdapsPort = Integer.parseInt(argParser.ldapsPortArg.getDefaultValue()); 338 lastResetAdminConnectorPort = Integer.parseInt(argParser.adminConnectorPortArg.getDefaultValue()); 339 lastResetJmxPort = Integer.parseInt(argParser.jmxPortArg.getDefaultValue()); 340 341 // Validate user provided data 342 try 343 { 344 argParser.parseArguments(args); 345 } 346 catch (final ArgumentException ae) 347 { 348 argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 349 return InstallReturnCode.ERROR_USER_DATA.getReturnCode(); 350 } 351 352 if (argParser.testOnlyArg.isPresent() && !testJVM()) 353 { 354 return InstallReturnCode.JAVA_VERSION_INCOMPATIBLE.getReturnCode(); 355 } 356 357 if (argParser.usageOrVersionDisplayed() || argParser.testOnlyArg.isPresent()) 358 { 359 return InstallReturnCode.SUCCESSFUL_NOP.getReturnCode(); 360 } 361 362 try 363 { 364 checkInstallStatus(); 365 } 366 catch (final InitializationException ie) 367 { 368 println(ie.getMessageObject()); 369 return InstallReturnCode.ERROR_SERVER_ALREADY_INSTALLED.getReturnCode(); 370 } 371 372 if (!checkLicense()) 373 { 374 return InstallReturnCode.ERROR_LICENSE_NOT_ACCEPTED.getReturnCode(); 375 } 376 377 final UserData uData = new UserData(); 378 InstallReturnCode fillUserDataRC; 379 try 380 { 381 fillUserDataRC = fillUserData(uData, args); 382 if (fillUserDataRC != InstallReturnCode.SUCCESSFUL) 383 { 384 return fillUserDataRC.getReturnCode(); 385 } 386 } 387 catch (final UserDataException e) 388 { 389 return printAndReturnErrorCode(e.getMessageObject()).getReturnCode(); 390 } 391 392 393 System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); 394 final OfflineInstaller installer = new OfflineInstaller(); 395 installer.setUserData(uData); 396 installer.setProgressMessageFormatter(formatter); 397 installer.addProgressUpdateListener( 398 new ProgressUpdateListener() { 399 @Override 400 public void progressUpdate(ProgressUpdateEvent ev) { 401 if (ev.getNewLogs() != null) 402 { 403 print(ev.getNewLogs()); 404 } 405 } 406 }); 407 println(); 408 409 installer.run(); 410 printStatusCommand(); 411 412 final ApplicationException ue = installer.getRunError(); 413 if (ue != null) 414 { 415 return ue.getType().getReturnCode(); 416 } 417 418 return InstallReturnCode.SUCCESSFUL.getReturnCode(); 419 } 420 421 private InstallReturnCode fillUserData(UserData uData, String[] args) throws UserDataException 422 { 423 if (!isInteractive()) 424 { 425 initializeNonInteractiveUserDataWithParser(uData); 426 return InstallReturnCode.SUCCESSFUL; 427 } 428 429 boolean userApproved = false; 430 while (!userApproved) 431 { 432 try 433 { 434 promptIfRequired(uData); 435 } 436 catch (final ClientException ce) 437 { 438 return printAndReturnErrorCode(ce.getMessageObject()); 439 } 440 441 boolean promptAgain = true; 442 printSummary(uData); 443 while (promptAgain) 444 { 445 promptAgain = false; 446 final ConfirmCode confirm = askForConfirmation(); 447 switch (confirm) 448 { 449 case CONTINUE: 450 userApproved = true; 451 break; 452 453 case CANCEL: 454 logger.debug(LocalizableMessage.raw("User cancelled setup.")); 455 return InstallReturnCode.ERROR_USER_CANCELLED; 456 457 case PRINT_EQUIVALENT_COMMAND_LINE: 458 printEquivalentCommandLine(uData); 459 promptAgain = true; 460 break; 461 462 case PROVIDE_INFORMATION_AGAIN: 463 // Reset the arguments 464 try 465 { 466 resetArguments(uData); 467 argParser.parseArguments(args); 468 } 469 catch (final Throwable t) 470 { 471 logger.warn(LocalizableMessage.raw("Error resetting arg parser: "+t, t)); 472 } 473 userApproved = false; 474 } 475 } 476 } 477 478 return InstallReturnCode.SUCCESSFUL; 479 } 480 481 private boolean testJVM() 482 { 483 // Delete the log file that does not contain any information. The test only 484 // mode is called several times by the setup script and if we do not remove 485 // it we have a lot of empty log files. 486 try 487 { 488 QuickSetupLog.getLogFile().deleteOnExit(); 489 } 490 catch (final Throwable t) 491 { 492 logger.warn(LocalizableMessage.raw("Error while trying to update the contents of " 493 + "the set-java-home file in test only mode: " + t, t)); 494 } 495 try 496 { 497 Utils.checkJavaVersion(); 498 return true; 499 } 500 catch (final IncompatibleVersionException ive) 501 { 502 println(ive.getMessageObject()); 503 return false; 504 } 505 } 506 507 private boolean checkLicense() 508 { 509 if (!LicenseFile.exists()) { 510 return true; 511 } 512 513 println(LocalizableMessage.raw(LicenseFile.getText())); 514 // If the user asks for acceptLicense, license is displayed 515 // and automatically accepted. 516 if (!argParser.acceptLicense.isPresent()) 517 { 518 final String yes = INFO_LICENSE_CLI_ACCEPT_YES.get().toString(); 519 final String no = INFO_LICENSE_CLI_ACCEPT_NO.get().toString(); 520 final String yesShort = INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString(); 521 final String noShort = INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString(); 522 println(QuickSetupMessages.INFO_LICENSE_DETAILS_CLI_LABEL.get()); 523 524 final BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream())); 525 526 // No-prompt arg automatically rejects the license. 527 if (!argParser.noPromptArg.isPresent()) 528 { 529 while (true) 530 { 531 print(INFO_LICENSE_CLI_ACCEPT_QUESTION.get(yes, no, no)); 532 try 533 { 534 final String response = in.readLine(); 535 if (response == null 536 || response.equalsIgnoreCase(no) 537 || response.equalsIgnoreCase(noShort) 538 || response.length() == 0) 539 { 540 return false; 541 } 542 else if (response.equalsIgnoreCase(yes) 543 || response.equalsIgnoreCase(yesShort)) 544 { 545 LicenseFile.setApproval(true); 546 break; 547 } 548 println(QuickSetupMessages.INFO_LICENSE_CLI_ACCEPT_INVALID_RESPONSE.get()); 549 } 550 catch (final IOException e) 551 { 552 println(QuickSetupMessages.INFO_LICENSE_CLI_ACCEPT_INVALID_RESPONSE.get()); 553 } 554 } 555 } 556 else 557 { 558 return false; 559 } 560 } 561 else 562 { 563 print(INFO_LICENSE_ACCEPT.get()); 564 print(INFO_PROMPT_YES_COMPLETE_ANSWER.get()); 565 LicenseFile.setApproval(true); 566 } 567 568 return true; 569 } 570 571 private void printStatusCommand() 572 { 573 // Use this instead a call to Installation to avoid to launch a new JVM just to retrieve a path. 574 final String binariesRelativePath = isWindows() ? Installation.WINDOWS_BINARIES_PATH_RELATIVE 575 : Installation.UNIX_BINARIES_PATH_RELATIVE; 576 final String statusCliFileName = isWindows() ? Installation.WINDOWS_STATUSCLI_FILE_NAME 577 : Installation.UNIX_STATUSCLI_FILE_NAME; 578 final String binDir = Utils.getPath(Utils.getInstallPathFromClasspath(), binariesRelativePath); 579 final String cmd = Utils.getPath(binDir, statusCliFileName); 580 println(); 581 println(INFO_INSTALLDS_STATUS_COMMAND_LINE.get(cmd)); 582 println(); 583 } 584 585 586 private InstallReturnCode printAndReturnErrorCode(LocalizableMessage message) 587 { 588 println(message); 589 if (StaticUtils.hasDescriptor(message, ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES)) 590 { 591 return InstallReturnCode.ERROR_PASSWORD_LIMIT; 592 } 593 594 return InstallReturnCode.ERROR_USER_DATA; 595 } 596 597 /** 598 * Checks if the server is installed or not. 599 * 600 * @throws InitializationException 601 * if the server is already installed and configured or if the user 602 * did not accept to overwrite the existing databases. 603 */ 604 private void checkInstallStatus() throws InitializationException 605 { 606 final CurrentInstallStatus installStatus = new CurrentInstallStatus(); 607 if (installStatus.canOverwriteCurrentInstall()) 608 { 609 if (isInteractive()) 610 { 611 println(installStatus.getInstallationMsg()); 612 try 613 { 614 if (!confirmAction(INFO_CLI_DO_YOU_WANT_TO_CONTINUE.get(), true)) 615 { 616 throw new InitializationException(LocalizableMessage.EMPTY); 617 } 618 } 619 catch (final ClientException ce) 620 { 621 logger.error(LocalizableMessage.raw("Unexpected error: "+ce, ce)); 622 throw new InitializationException(LocalizableMessage.EMPTY, ce); 623 } 624 } 625 else 626 { 627 println(installStatus.getInstallationMsg()); 628 } 629 } 630 else if (installStatus.isInstalled()) 631 { 632 throw new InitializationException(installStatus.getInstallationMsg()); 633 } 634 } 635 636 /** {@inheritDoc} */ 637 @Override 638 public boolean isQuiet() 639 { 640 return argParser.quietArg.isPresent(); 641 } 642 643 /** {@inheritDoc} */ 644 @Override 645 public boolean isInteractive() 646 { 647 return !argParser.noPromptArg.isPresent(); 648 } 649 650 /** {@inheritDoc} */ 651 @Override 652 public boolean isMenuDrivenMode() { 653 return true; 654 } 655 656 /** {@inheritDoc} */ 657 @Override 658 public boolean isScriptFriendly() { 659 return false; 660 } 661 662 /** {@inheritDoc} */ 663 @Override 664 public boolean isAdvancedMode() { 665 return false; 666 } 667 668 669 /** {@inheritDoc} */ 670 @Override 671 public boolean isVerbose() { 672 return argParser.verboseArg.isPresent(); 673 } 674 675 /** 676 * This method updates the contents of a UserData object with what the user 677 * specified in the command-line. It assumes that it is being called in no 678 * prompt mode. 679 * 680 * @param uData 681 * the UserData object. 682 * @throws UserDataException 683 * if something went wrong checking the data. 684 */ 685 private void initializeNonInteractiveUserDataWithParser(UserData uData) throws UserDataException 686 { 687 uData.setQuiet(isQuiet()); 688 uData.setVerbose(isVerbose()); 689 uData.setConnectTimeout(getConnectTimeout()); 690 691 final List<LocalizableMessage> errorMessages = new LinkedList<>(); 692 setBackendType(uData, errorMessages); 693 final List<String> baseDNs = checkBaseDNs(errorMessages); 694 setDirectoryManagerData(uData, errorMessages); 695 setPorts(uData, errorMessages); 696 setImportData(baseDNs, uData, errorMessages); 697 setSecurityData(uData, errorMessages); 698 699 if (!errorMessages.isEmpty()) 700 { 701 throw new UserDataException(null, 702 Utils.getMessageFromCollection(errorMessages, formatter.getLineBreak().toString())); 703 } 704 } 705 706 private void setBackendType(final UserData uData, final List<LocalizableMessage> errorMessages) 707 { 708 final String filledBackendType = argParser.backendTypeArg.getValue(); 709 final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backend = 710 backendTypeHelper.retrieveBackendTypeFromName(filledBackendType); 711 if (backend != null) 712 { 713 uData.setBackendType(backend); 714 } 715 else 716 { 717 errorMessages.add( 718 ERR_INSTALLDS_NO_SUCH_BACKEND_TYPE.get(filledBackendType, backendTypeHelper.getPrintableBackendTypeNames())); 719 } 720 } 721 722 private List<String> checkBaseDNs(List<LocalizableMessage> errorMessages) 723 { 724 final List<String> baseDNs = argParser.baseDNArg.getValues(); 725 if (baseDNs.isEmpty() && argParser.baseDNArg.getDefaultValue() != null) 726 { 727 baseDNs.add(argParser.baseDNArg.getDefaultValue()); 728 } 729 730 for (final String baseDN : baseDNs) 731 { 732 checkBaseDN(baseDN, errorMessages); 733 } 734 735 return baseDNs; 736 } 737 738 private void setDirectoryManagerData(UserData uData, List<LocalizableMessage> errorMessages) 739 { 740 final String dmDN = argParser.directoryManagerDNArg.getValue(); 741 if (dmDN.trim().length() == 0) 742 { 743 errorMessages.add(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get()); 744 } 745 checkBaseDN(dmDN, errorMessages); 746 uData.setDirectoryManagerDn(argParser.directoryManagerDNArg.getValue()); 747 748 // Check the validity of the directory manager password 749 if (argParser.getDirectoryManagerPassword().isEmpty()) { 750 errorMessages.add(INFO_EMPTY_PWD.get()); 751 } 752 uData.setDirectoryManagerPwd(argParser.getDirectoryManagerPassword()); 753 } 754 755 private void checkBaseDN(String baseDN, List<LocalizableMessage> errorMessages) 756 { 757 try 758 { 759 new LdapName(baseDN); 760 } 761 catch (final Exception e) 762 { 763 errorMessages.add(ERR_INSTALLDS_CANNOT_PARSE_DN.get(baseDN, e.getMessage())); 764 } 765 } 766 767 private void setPorts(UserData uData, List<LocalizableMessage> errorMessages) 768 { 769 try 770 { 771 final int ldapPort = argParser.ldapPortArg.getIntValue(); 772 uData.setServerPort(ldapPort); 773 774 final int adminConnectorPort = argParser.adminConnectorPortArg.getIntValue(); 775 uData.setAdminConnectorPort(adminConnectorPort); 776 777 if (!argParser.skipPortCheckArg.isPresent()) 778 { 779 checkCanUsePort(ldapPort, errorMessages); 780 checkCanUsePort(adminConnectorPort, errorMessages); 781 } 782 if (argParser.jmxPortArg.isPresent()) 783 { 784 final int jmxPort = argParser.jmxPortArg.getIntValue(); 785 uData.setServerJMXPort(jmxPort); 786 if (!argParser.skipPortCheckArg.isPresent()) 787 { 788 checkCanUsePort(jmxPort, errorMessages); 789 } 790 } 791 } 792 catch (final ArgumentException ae) 793 { 794 errorMessages.add(ae.getMessageObject()); 795 } 796 } 797 798 private void setImportData(List<String> baseDNs, UserData uData, List<LocalizableMessage> errorMessages) 799 { 800 NewSuffixOptions dataOptions; 801 if (argParser.importLDIFArg.isPresent()) 802 { 803 // Check that the files exist 804 final List<String> nonExistingFiles = new LinkedList<>(); 805 for (final String file : argParser.importLDIFArg.getValues()) 806 { 807 if (!Utils.fileExists(file)) 808 { 809 nonExistingFiles.add(file); 810 } 811 } 812 813 if (!nonExistingFiles.isEmpty()) 814 { 815 errorMessages.add(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(joinAsString(", ", nonExistingFiles))); 816 } 817 818 final String rejectedFile = argParser.rejectedImportFileArg.getValue(); 819 if (rejectedFile != null && !canWrite(rejectedFile)) 820 { 821 errorMessages.add(ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile)); 822 } 823 824 final String skippedFile = argParser.skippedImportFileArg.getValue(); 825 if (skippedFile != null && !canWrite(skippedFile)) 826 { 827 errorMessages.add(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(skippedFile)); 828 } 829 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, argParser.importLDIFArg.getValues(), 830 rejectedFile, skippedFile); 831 } 832 else if (argParser.addBaseEntryArg.isPresent()) 833 { 834 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); 835 } 836 else if (argParser.sampleDataArg.isPresent()) 837 { 838 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, 839 Integer.valueOf(argParser.sampleDataArg.getValue())); 840 } 841 else 842 { 843 dataOptions = NewSuffixOptions.createEmpty(baseDNs); 844 } 845 uData.setNewSuffixOptions(dataOptions); 846 } 847 848 private void setSecurityData(UserData uData, List<LocalizableMessage> errorMessages) 849 { 850 final boolean enableSSL = argParser.ldapsPortArg.isPresent(); 851 int sslPort = -1; 852 853 try 854 { 855 sslPort = enableSSL ? argParser.ldapsPortArg.getIntValue() : -1; 856 } 857 catch (final ArgumentException ae) 858 { 859 errorMessages.add(ae.getMessageObject()); 860 } 861 862 if (enableSSL && !argParser.skipPortCheckArg.isPresent()) 863 { 864 checkCanUsePort(sslPort, errorMessages); 865 } 866 867 checkCertificate(sslPort, enableSSL, uData, errorMessages); 868 uData.setEnableWindowsService(argParser.enableWindowsServiceArg.isPresent()); 869 uData.setStartServer(!argParser.doNotStartArg.isPresent()); 870 } 871 872 private void checkCertificate(int sslPort, boolean enableSSL, UserData uData, List<LocalizableMessage> errorMessages) 873 { 874 final LinkedList<String> keystoreAliases = new LinkedList<>(); 875 uData.setHostName(argParser.hostNameArg.getValue()); 876 877 final boolean enableStartTLS = argParser.enableStartTLSArg.isPresent(); 878 final String pwd = argParser.getKeyStorePassword(); 879 SecurityOptions.CertificateType certType = null; 880 String pathToCertificat = null; 881 if (argParser.generateSelfSignedCertificateArg.isPresent()) 882 { 883 certType = SecurityOptions.CertificateType.SELF_SIGNED_CERTIFICATE; 884 } 885 else if (argParser.useJavaKeyStoreArg.isPresent()) 886 { 887 certType = SecurityOptions.CertificateType.JKS; 888 pathToCertificat = argParser.useJavaKeyStoreArg.getValue(); 889 } 890 else if (argParser.useJCEKSArg.isPresent()) 891 { 892 certType = SecurityOptions.CertificateType.JCEKS; 893 pathToCertificat = argParser.useJCEKSArg.getValue(); 894 } 895 else if (argParser.usePkcs11Arg.isPresent()) 896 { 897 certType = SecurityOptions.CertificateType.PKCS11; 898 pathToCertificat = argParser.usePkcs11Arg.getValue(); 899 } 900 else if (argParser.usePkcs12Arg.isPresent()) 901 { 902 certType = SecurityOptions.CertificateType.PKCS12; 903 pathToCertificat = argParser.usePkcs12Arg.getValue(); 904 } 905 else 906 { 907 certType = SecurityOptions.CertificateType.NO_CERTIFICATE; 908 } 909 910 Collection<String> certNicknames = argParser.certNicknameArg.getValues(); 911 if (pathToCertificat != null) 912 { 913 checkCertificateInKeystore(certType, pathToCertificat, pwd, certNicknames, errorMessages, keystoreAliases); 914 if (certNicknames.isEmpty() && !keystoreAliases.isEmpty()) 915 { 916 certNicknames = Arrays.asList(keystoreAliases.getFirst()); 917 } 918 } 919 920 final SecurityOptions securityOptions = SecurityOptions.createOptionsForCertificatType( 921 certType, pathToCertificat, pwd, enableSSL, enableStartTLS, sslPort, certNicknames); 922 uData.setSecurityOptions(securityOptions); 923 } 924 925 private void checkCanUsePort(int port, List<LocalizableMessage> errorMessages) 926 { 927 if (!SetupUtils.canUseAsPort(port)) 928 { 929 errorMessages.add(getCannotBindErrorMessage(port)); 930 } 931 } 932 933 private LocalizableMessage getCannotBindErrorMessage(int port) 934 { 935 if (SetupUtils.isPrivilegedPort(port)) 936 { 937 return ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(port); 938 } 939 return ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(port); 940 } 941 942 /** 943 * This method updates the contents of a UserData object with what the user 944 * specified in the command-line. If the user did not provide explicitly some 945 * data or if the provided data is not valid, it prompts the user to provide 946 * it. 947 * 948 * @param uData 949 * the UserData object to be updated. 950 * @throws UserDataException 951 * if the user did not manage to provide the keystore password after 952 * a certain number of tries. 953 * @throws ClientException 954 * if something went wrong when reading inputs. 955 */ 956 private void promptIfRequired(UserData uData) throws UserDataException, ClientException 957 { 958 uData.setQuiet(isQuiet()); 959 uData.setVerbose(isVerbose()); 960 uData.setConnectTimeout(getConnectTimeout()); 961 962 promptIfRequiredForDirectoryManager(uData); 963 promptIfRequiredForPortData(uData); 964 uData.setNewSuffixOptions(promptIfRequiredForImportData(uData)); 965 uData.setSecurityOptions(promptIfRequiredForSecurityData(uData)); 966 uData.setEnableWindowsService(promptIfRequiredForWindowsService()); 967 uData.setStartServer(promptIfRequiredForStartServer()); 968 } 969 970 /** 971 * This method updates the contents of a UserData object with what the user 972 * specified in the command-line for the Directory Manager parameters. If the 973 * user did not provide explicitly some data or if the provided data is not 974 * valid, it prompts the user to provide it. 975 * 976 * @param uData 977 * the UserData object to be updated. 978 * @throws UserDataException 979 * if something went wrong checking the data. 980 * @throws ClientException 981 * if something went wrong checking passwords. 982 */ 983 private void promptIfRequiredForDirectoryManager(UserData uData) throws UserDataException, ClientException 984 { 985 final LinkedList<String> dns = promptIfRequiredForDNs( 986 argParser.directoryManagerDNArg, lastResetDirectoryManagerDN, INFO_INSTALLDS_PROMPT_ROOT_DN.get(), true); 987 uData.setDirectoryManagerDn(dns.getFirst()); 988 989 int nTries = 0; 990 String pwd = argParser.getDirectoryManagerPassword(); 991 while (pwd == null) 992 { 993 if (nTries >= CONFIRMATION_MAX_TRIES) 994 { 995 throw new UserDataException(null, ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES)); 996 } 997 998 // Prompt for password and confirm. 999 char[] pwd1 = readPassword(INFO_INSTALLDS_PROMPT_ROOT_PASSWORD.get()); 1000 while (pwd1 == null || pwd1.length == 0) 1001 { 1002 println(); 1003 println(INFO_EMPTY_PWD.get()); 1004 println(); 1005 pwd1 = readPassword(INFO_INSTALLDS_PROMPT_ROOT_PASSWORD.get()); 1006 } 1007 1008 final char[] pwd2 = readPassword(INFO_INSTALLDS_PROMPT_CONFIRM_ROOT_PASSWORD.get()); 1009 if (Arrays.equals(pwd1, pwd2)) 1010 { 1011 pwd = String.valueOf(pwd1); 1012 } 1013 else 1014 { 1015 println(); 1016 println(ERR_INSTALLDS_PASSWORDS_DONT_MATCH.get()); 1017 } 1018 1019 nTries++; 1020 } 1021 uData.setDirectoryManagerPwd(pwd); 1022 } 1023 1024 /** 1025 * This method returns a list of DNs. It checks that the provided list of DNs 1026 * actually contain some values. If no valid values are found it prompts the 1027 * user to provide a valid DN. 1028 * 1029 * @param arg 1030 * the Argument that the user provided to specify the DNs. 1031 * @param valueToSuggest 1032 * the value to suggest by default on prompt. 1033 * @param promptMsg 1034 * the prompt message to be displayed. 1035 * @param includeLineBreak 1036 * whether to include a line break before the first prompt or not. 1037 * @return a list of valid DNs. 1038 * @throws UserDataException 1039 * if something went wrong checking the data. 1040 */ 1041 private LinkedList<String> promptIfRequiredForDNs(StringArgument arg, String valueToSuggest, 1042 LocalizableMessage promptMsg, boolean includeLineBreak) throws UserDataException 1043 { 1044 final LinkedList<String> dns = new LinkedList<>(); 1045 1046 boolean usedProvided = false; 1047 boolean firstPrompt = true; 1048 int nTries = 0; 1049 while (dns.isEmpty()) 1050 { 1051 if (nTries >= CONFIRMATION_MAX_TRIES) 1052 { 1053 throw new UserDataException(null, ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES)); 1054 } 1055 boolean prompted = false; 1056 if (usedProvided || !arg.isPresent()) 1057 { 1058 if (firstPrompt && includeLineBreak) 1059 { 1060 println(); 1061 } 1062 try 1063 { 1064 final String dn = readInput(promptMsg, valueToSuggest); 1065 firstPrompt = false; 1066 dns.add(dn); 1067 prompted = true; 1068 } 1069 catch (final ClientException ce) 1070 { 1071 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1072 } 1073 } 1074 else 1075 { 1076 dns.addAll(arg.getValues()); 1077 usedProvided = true; 1078 } 1079 final List<String> toRemove = new LinkedList<>(); 1080 for (final String dn : dns) 1081 { 1082 try 1083 { 1084 new LdapName(dn); 1085 if (dn.trim().length() == 0) 1086 { 1087 toRemove.add(dn); 1088 println(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get()); 1089 } 1090 } 1091 catch (final Exception e) 1092 { 1093 toRemove.add(dn); 1094 final LocalizableMessage message = prompted ? ERR_INSTALLDS_INVALID_DN_RESPONSE.get() : 1095 ERR_INSTALLDS_CANNOT_PARSE_DN.get(dn, e.getMessage()); 1096 println(message); 1097 } 1098 } 1099 if (!toRemove.isEmpty()) 1100 { 1101 println(); 1102 } 1103 dns.removeAll(toRemove); 1104 nTries++; 1105 } 1106 return dns; 1107 } 1108 1109 /** 1110 * This method updates the contents of a UserData object with what the user 1111 * specified in the command-line for the administration connector, LDAP and 1112 * JMX port parameters. If the user did not provide explicitly some data or 1113 * if the provided data is not valid, it prompts the user to provide it. 1114 * Note: this method does not update nor check the LDAPS port. 1115 * 1116 * @param uData 1117 * the UserData object to be updated. 1118 */ 1119 private void promptIfRequiredForPortData(UserData uData) 1120 { 1121 uData.setHostName(promptForHostNameIfRequired()); 1122 1123 final List<Integer> usedPorts = new LinkedList<>(); 1124 // Determine the LDAP port number. 1125 final int ldapPort = promptIfRequiredForPortData( 1126 argParser.ldapPortArg, lastResetLdapPort, INFO_INSTALLDS_PROMPT_LDAPPORT.get(), usedPorts, true); 1127 uData.setServerPort(ldapPort); 1128 usedPorts.add(ldapPort); 1129 1130 // Determine the Admin Connector port number. 1131 final int adminConnectorPort = promptIfRequiredForPortData(argParser.adminConnectorPortArg, 1132 lastResetAdminConnectorPort, INFO_INSTALLDS_PROMPT_ADMINCONNECTORPORT.get(), usedPorts, true); 1133 uData.setAdminConnectorPort(adminConnectorPort); 1134 usedPorts.add(adminConnectorPort); 1135 1136 if (argParser.jmxPortArg.isPresent()) 1137 { 1138 final int jmxPort = promptIfRequiredForPortData(argParser.jmxPortArg, lastResetJmxPort, 1139 INFO_INSTALLDS_PROMPT_JMXPORT.get(), usedPorts, true); 1140 uData.setServerJMXPort(jmxPort); 1141 } 1142 else 1143 { 1144 uData.setServerJMXPort(-1); 1145 } 1146 } 1147 1148 /** 1149 * This method returns a valid port value. It checks that the provided 1150 * argument contains a valid port. If a valid port is not found it prompts the 1151 * user to provide a valid port. 1152 * 1153 * @param portArg 1154 * the Argument that the user provided to specify the port. 1155 * @param valueToSuggest 1156 * the value to suggest by default on prompt. 1157 * @param promptMsg 1158 * the prompt message to be displayed. 1159 * @param usedPorts 1160 * the list of ports the user provided before for other connection 1161 * handlers. 1162 * @param includeLineBreak 1163 * whether to include a line break before the first prompt or not. 1164 * @return a valid port number. 1165 */ 1166 private int promptIfRequiredForPortData(IntegerArgument portArg, Integer valueToSuggest, LocalizableMessage promptMsg, 1167 Collection<Integer> usedPorts, boolean includeLineBreak) 1168 { 1169 int portNumber = -1; 1170 boolean usedProvided = false; 1171 boolean firstPrompt = true; 1172 while (portNumber == -1) 1173 { 1174 try 1175 { 1176 boolean prompted = false; 1177 if (usedProvided || !portArg.isPresent()) 1178 { 1179 if (firstPrompt && includeLineBreak) 1180 { 1181 println(); 1182 } 1183 portNumber = -1; 1184 while (portNumber == -1) 1185 { 1186 try 1187 { 1188 portNumber = readPort(promptMsg, valueToSuggest); 1189 } 1190 catch (final ClientException ce) 1191 { 1192 portNumber = -1; 1193 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1194 } 1195 } 1196 prompted = true; 1197 firstPrompt = false; 1198 } 1199 else 1200 { 1201 portNumber = portArg.getIntValue(); 1202 usedProvided = true; 1203 } 1204 1205 if (!argParser.skipPortCheckArg.isPresent() && !SetupUtils.canUseAsPort(portNumber)) 1206 { 1207 final LocalizableMessage message = getCannotBindErrorMessage(portNumber); 1208 if (prompted || includeLineBreak) 1209 { 1210 println(); 1211 } 1212 println(message); 1213 if (!SetupUtils.isPrivilegedPort(portNumber)) 1214 { 1215 println(); 1216 } 1217 portNumber = -1; 1218 } 1219 if (portNumber != -1 && usedPorts.contains(portNumber)) 1220 { 1221 println(ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(portNumber)); 1222 println(); 1223 portNumber = -1; 1224 } 1225 } 1226 catch (final ArgumentException ae) 1227 { 1228 println(ae.getMessageObject()); 1229 } 1230 } 1231 return portNumber; 1232 } 1233 1234 /** 1235 * This method returns what the user specified in the command-line for the 1236 * base DN and data import parameters. If the user did not provide explicitly 1237 * some data or if the provided data is not valid, it prompts the user to 1238 * provide it. 1239 * 1240 * @param uData 1241 * The UserData object to be updated. 1242 * @return the NewSuffixOptions telling how to import data 1243 * @throws UserDataException 1244 * if something went wrong checking the data. 1245 */ 1246 private NewSuffixOptions promptIfRequiredForImportData(final UserData uData) throws UserDataException 1247 { 1248 boolean prompt = true; 1249 if (!argParser.baseDNArg.isPresent()) 1250 { 1251 println(); 1252 try 1253 { 1254 prompt = confirmAction(INFO_INSTALLDS_PROVIDE_BASE_DN_PROMPT.get(), true); 1255 } 1256 catch (final ClientException ce) 1257 { 1258 prompt = true; 1259 logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce)); 1260 } 1261 } 1262 1263 if (!prompt) 1264 { 1265 return NewSuffixOptions.createEmpty(new LinkedList<String>()); 1266 } 1267 1268 uData.setBackendType(getOrPromptForBackendType()); 1269 // Check the validity of the base DNs 1270 final List<String> baseDNs = promptIfRequiredForDNs( 1271 argParser.baseDNArg, lastResetBaseDN, INFO_INSTALLDS_PROMPT_BASEDN.get(), true); 1272 return promptIfRequiredForDataOptions(baseDNs); 1273 1274 } 1275 1276 private ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> getOrPromptForBackendType() 1277 { 1278 if (argParser.backendTypeArg.isPresent()) 1279 { 1280 final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backend = 1281 backendTypeHelper.retrieveBackendTypeFromName(argParser.backendTypeArg.getValue().toLowerCase()); 1282 if ( backend != null) 1283 { 1284 return backend; 1285 } 1286 println(); 1287 println(ERR_INSTALLDS_NO_SUCH_BACKEND_TYPE.get( 1288 argParser.backendTypeArg.getValue(), backendTypeHelper.getPrintableBackendTypeNames())); 1289 } 1290 1291 return promptForBackendType(); 1292 } 1293 1294 private ManagedObjectDefinition<? extends BackendCfgClient,? extends BackendCfg> promptForBackendType() 1295 { 1296 println(); 1297 int backendTypeIndex = 1; 1298 final List<ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg>> backendTypes = 1299 backendTypeHelper.getBackendTypes(); 1300 if (backendTypes.size() == 1) { 1301 final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType = backendTypes.get(0); 1302 println(INFO_INSTALLDS_BACKEND_TYPE_USED.get(backendType.getUserFriendlyName())); 1303 return backendType; 1304 } 1305 1306 try 1307 { 1308 final MenuResult<Integer> m = getBackendTypeMenu().run(); 1309 if (m.isSuccess()) 1310 { 1311 backendTypeIndex = m.getValue(); 1312 } 1313 } 1314 catch (final ClientException ce) 1315 { 1316 logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce)); 1317 } 1318 1319 return backendTypes.get(backendTypeIndex - 1); 1320 } 1321 1322 private Menu<Integer> getBackendTypeMenu() 1323 { 1324 final MenuBuilder<Integer> builder = new MenuBuilder<>(this); 1325 builder.setPrompt(INFO_INSTALLDS_PROMPT_BACKEND_TYPE.get()); 1326 int index = 1; 1327 for (final ManagedObjectDefinition<?, ?> backendType : backendTypeHelper.getBackendTypes()) 1328 { 1329 builder.addNumberedOption(backendType.getUserFriendlyName(), MenuResult.success(index++)); 1330 } 1331 1332 final int printableIndex = getPromptedBackendTypeIndex(); 1333 builder.setDefault(LocalizableMessage.raw(Integer.toString(printableIndex)), MenuResult.success(printableIndex)); 1334 return builder.toMenu(); 1335 } 1336 1337 private int getPromptedBackendTypeIndex() 1338 { 1339 if (lastResetBackendType != null) 1340 { 1341 return backendTypeHelper.getBackendTypes().indexOf(lastResetBackendType) + 1; 1342 } 1343 return 1; 1344 } 1345 1346 private NewSuffixOptions promptIfRequiredForDataOptions(List<String> baseDNs) 1347 { 1348 NewSuffixOptions dataOptions; 1349 if (argParser.importLDIFArg.isPresent()) 1350 { 1351 // Check that the files exist 1352 final List<String> nonExistingFiles = new LinkedList<>(); 1353 final List<String> importLDIFFiles = new LinkedList<>(); 1354 for (final String file : argParser.importLDIFArg.getValues()) 1355 { 1356 if (!Utils.fileExists(file)) 1357 { 1358 nonExistingFiles.add(file); 1359 } 1360 else 1361 { 1362 importLDIFFiles.add(file); 1363 } 1364 } 1365 if (!nonExistingFiles.isEmpty()) 1366 { 1367 println(); 1368 println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(joinAsString(", ", nonExistingFiles))); 1369 } 1370 1371 readImportLdifFile(importLDIFFiles, lastResetImportFile); 1372 String rejectedFile = readValidFilePath(argParser.rejectedImportFileArg, lastResetRejectedFile, 1373 ERR_INSTALLDS_CANNOT_WRITE_REJECTED, INFO_INSTALLDS_PROMPT_REJECTED_FILE); 1374 String skippedFile = readValidFilePath(argParser.skippedImportFileArg, lastResetSkippedFile, 1375 ERR_INSTALLDS_CANNOT_WRITE_SKIPPED, INFO_INSTALLDS_PROMPT_SKIPPED_FILE); 1376 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, 1377 importLDIFFiles, rejectedFile, skippedFile); 1378 } 1379 else if (argParser.addBaseEntryArg.isPresent()) 1380 { 1381 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); 1382 } 1383 else if (argParser.sampleDataArg.isPresent()) 1384 { 1385 int numUsers; 1386 try 1387 { 1388 numUsers = argParser.sampleDataArg.getIntValue(); 1389 } 1390 catch (final ArgumentException ae) 1391 { 1392 println(); 1393 println(ae.getMessageObject()); 1394 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get(); 1395 numUsers = promptForInteger(message, 2000, 0, Integer.MAX_VALUE); 1396 } 1397 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, numUsers); 1398 } 1399 else 1400 { 1401 final int POPULATE_TYPE_LEAVE_EMPTY = 1; 1402 final int POPULATE_TYPE_BASE_ONLY = 2; 1403 final int POPULATE_TYPE_IMPORT_FROM_LDIF = 3; 1404 final int POPULATE_TYPE_GENERATE_SAMPLE_DATA = 4; 1405 1406 final int[] indexes = {POPULATE_TYPE_LEAVE_EMPTY, POPULATE_TYPE_BASE_ONLY, 1407 POPULATE_TYPE_IMPORT_FROM_LDIF, POPULATE_TYPE_GENERATE_SAMPLE_DATA}; 1408 final LocalizableMessage[] msgs = new LocalizableMessage[] { 1409 INFO_INSTALLDS_POPULATE_OPTION_LEAVE_EMPTY.get(), 1410 INFO_INSTALLDS_POPULATE_OPTION_BASE_ONLY.get(), 1411 INFO_INSTALLDS_POPULATE_OPTION_IMPORT_LDIF.get(), 1412 INFO_INSTALLDS_POPULATE_OPTION_GENERATE_SAMPLE.get() 1413 }; 1414 1415 final MenuBuilder<Integer> builder = new MenuBuilder<>(this); 1416 builder.setPrompt(INFO_INSTALLDS_HEADER_POPULATE_TYPE.get()); 1417 1418 for (int i=0; i<indexes.length; i++) 1419 { 1420 builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); 1421 } 1422 1423 if (lastResetPopulateOption == null) 1424 { 1425 builder.setDefault(LocalizableMessage.raw( 1426 String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)), 1427 MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY)); 1428 } 1429 else 1430 { 1431 switch (lastResetPopulateOption) 1432 { 1433 case LEAVE_DATABASE_EMPTY: 1434 builder.setDefault(LocalizableMessage.raw( 1435 String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)), 1436 MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY)); 1437 break; 1438 case IMPORT_FROM_LDIF_FILE: 1439 builder.setDefault(LocalizableMessage.raw( 1440 String.valueOf(POPULATE_TYPE_IMPORT_FROM_LDIF)), 1441 MenuResult.success(POPULATE_TYPE_IMPORT_FROM_LDIF)); 1442 break; 1443 case IMPORT_AUTOMATICALLY_GENERATED_DATA: 1444 builder.setDefault(LocalizableMessage.raw( 1445 String.valueOf(POPULATE_TYPE_GENERATE_SAMPLE_DATA)), 1446 MenuResult.success(POPULATE_TYPE_GENERATE_SAMPLE_DATA)); 1447 break; 1448 default: 1449 builder.setDefault(LocalizableMessage.raw( 1450 String.valueOf(POPULATE_TYPE_BASE_ONLY)), 1451 MenuResult.success(POPULATE_TYPE_BASE_ONLY)); 1452 } 1453 } 1454 1455 final Menu<Integer> menu = builder.toMenu(); 1456 int populateType; 1457 try 1458 { 1459 final MenuResult<Integer> m = menu.run(); 1460 if (m.isSuccess()) 1461 { 1462 populateType = m.getValue(); 1463 } 1464 else 1465 { 1466 // Should never happen. 1467 throw new RuntimeException(); 1468 } 1469 } 1470 catch (final ClientException ce) 1471 { 1472 populateType = POPULATE_TYPE_BASE_ONLY; 1473 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1474 } 1475 1476 if (populateType == POPULATE_TYPE_IMPORT_FROM_LDIF) 1477 { 1478 final List<String> importLDIFFiles = new LinkedList<>(); 1479 readImportLdifFile(importLDIFFiles, null); 1480 String rejectedFile = readValidFilePath(argParser.rejectedImportFileArg, null, 1481 ERR_INSTALLDS_CANNOT_WRITE_REJECTED, INFO_INSTALLDS_PROMPT_REJECTED_FILE); 1482 String skippedFile = readValidFilePath(argParser.skippedImportFileArg, null, 1483 ERR_INSTALLDS_CANNOT_WRITE_SKIPPED, INFO_INSTALLDS_PROMPT_SKIPPED_FILE); 1484 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, 1485 importLDIFFiles, rejectedFile, skippedFile); 1486 } 1487 else if (populateType == POPULATE_TYPE_GENERATE_SAMPLE_DATA) 1488 { 1489 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get(); 1490 int defaultValue = lastResetNumEntries != null ? lastResetNumEntries : 2000; 1491 final int numUsers = promptForInteger(message, defaultValue, 0, Integer.MAX_VALUE); 1492 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, numUsers); 1493 } 1494 else if (populateType == POPULATE_TYPE_LEAVE_EMPTY) 1495 { 1496 dataOptions = NewSuffixOptions.createEmpty(baseDNs); 1497 } 1498 else if (populateType == POPULATE_TYPE_BASE_ONLY) 1499 { 1500 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); 1501 } 1502 else 1503 { 1504 throw new IllegalStateException("Unexpected populateType: " + populateType); 1505 } 1506 } 1507 return dataOptions; 1508 } 1509 1510 private void readImportLdifFile(final List<String> importLDIFFiles, String defaultValue) 1511 { 1512 while (importLDIFFiles.isEmpty()) 1513 { 1514 println(); 1515 try 1516 { 1517 final String path = readInput(INFO_INSTALLDS_PROMPT_IMPORT_FILE.get(), defaultValue); 1518 if (Utils.fileExists(path)) 1519 { 1520 importLDIFFiles.add(path); 1521 } 1522 else 1523 { 1524 println(); 1525 println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path)); 1526 } 1527 } 1528 catch (final ClientException ce) 1529 { 1530 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1531 } 1532 } 1533 } 1534 1535 private String readValidFilePath(StringArgument arg, String defaultValue, Arg1<Object> errCannotWriteFile, 1536 Arg0 infoPromptFile) 1537 { 1538 String file = arg.getValue(); 1539 if (file != null) 1540 { 1541 while (!canWrite(file)) 1542 { 1543 println(); 1544 println(errCannotWriteFile.get(file)); 1545 println(); 1546 try 1547 { 1548 file = readInput(infoPromptFile.get(), defaultValue); 1549 } 1550 catch (final ClientException ce) 1551 { 1552 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1553 } 1554 } 1555 } 1556 return file; 1557 } 1558 1559 /** 1560 * This method returns what the user specified in the command-line for the 1561 * security parameters. If the user did not provide explicitly some data or if 1562 * the provided data is not valid, it prompts the user to provide it. 1563 * 1564 * @param uData 1565 * the current UserData object. 1566 * @return the {@link SecurityOptions} to be used when starting the server 1567 * @throws UserDataException 1568 * if the user did not manage to provide the keystore password after 1569 * a certain number of tries. 1570 * @throws ClientException 1571 * If an error occurs when reading inputs. 1572 */ 1573 private SecurityOptions promptIfRequiredForSecurityData(UserData uData) throws UserDataException, ClientException 1574 { 1575 // Check that the security data provided is valid. 1576 boolean enableSSL = false; 1577 boolean enableStartTLS = false; 1578 int ldapsPort = -1; 1579 1580 final List<Integer> usedPorts = new LinkedList<>(); 1581 usedPorts.add(uData.getServerPort()); 1582 if (uData.getServerJMXPort() != -1) 1583 { 1584 usedPorts.add(uData.getServerJMXPort()); 1585 } 1586 1587 // Ask to enable SSL 1588 if (!argParser.ldapsPortArg.isPresent()) 1589 { 1590 println(); 1591 try 1592 { 1593 final boolean defaultValue = lastResetEnableSSL != null ? lastResetEnableSSL : false; 1594 enableSSL = confirmAction(INFO_INSTALLDS_PROMPT_ENABLE_SSL.get(), defaultValue); 1595 if (enableSSL) 1596 { 1597 ldapsPort = promptIfRequiredForPortData( 1598 argParser.ldapsPortArg, lastResetLdapsPort, INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, false); 1599 } 1600 } 1601 catch (final ClientException ce) 1602 { 1603 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1604 } 1605 } 1606 else 1607 { 1608 ldapsPort = promptIfRequiredForPortData( 1609 argParser.ldapsPortArg, lastResetLdapsPort, INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, true); 1610 enableSSL = true; 1611 } 1612 1613 // Ask to enable Start TLS 1614 if (!argParser.enableStartTLSArg.isPresent()) 1615 { 1616 println(); 1617 try 1618 { 1619 final boolean defaultValue = lastResetEnableStartTLS != null ? 1620 lastResetEnableStartTLS : false; 1621 enableStartTLS = confirmAction(INFO_INSTALLDS_ENABLE_STARTTLS.get(), 1622 defaultValue); 1623 } 1624 catch (final ClientException ce) 1625 { 1626 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1627 } 1628 } 1629 else 1630 { 1631 enableStartTLS = true; 1632 } 1633 1634 SecurityOptions securityOptions; 1635 if (argParser.generateSelfSignedCertificateArg.isPresent()) 1636 { 1637 securityOptions = SecurityOptions.createSelfSignedCertificateOptions( 1638 enableSSL, enableStartTLS, ldapsPort); 1639 } 1640 else if (argParser.useJavaKeyStoreArg.isPresent()) 1641 { 1642 securityOptions = 1643 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS, 1644 enableSSL, enableStartTLS, ldapsPort); 1645 } 1646 else if (argParser.useJCEKSArg.isPresent()) 1647 { 1648 securityOptions = 1649 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JCEKS, 1650 enableSSL, enableStartTLS, ldapsPort); 1651 } 1652 else if (argParser.usePkcs12Arg.isPresent()) 1653 { 1654 securityOptions = 1655 createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS12, 1656 enableSSL, enableStartTLS, ldapsPort); 1657 } 1658 else if (argParser.usePkcs11Arg.isPresent()) 1659 { 1660 securityOptions = 1661 createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS11, 1662 enableSSL, enableStartTLS, ldapsPort); 1663 } 1664 else if (!enableSSL && !enableStartTLS) 1665 { 1666 // If the user did not want to enable SSL or start TLS do not ask 1667 // to create a certificate. 1668 securityOptions = SecurityOptions.createNoCertificateOptions(); 1669 } 1670 else 1671 { 1672 final int SELF_SIGNED = 1; 1673 final int JKS = 2; 1674 final int JCEKS = 3; 1675 final int PKCS12 = 4; 1676 final int PKCS11 = 5; 1677 final int[] indexes = {SELF_SIGNED, JKS, JCEKS, PKCS12, PKCS11}; 1678 final LocalizableMessage[] msgs = { 1679 INFO_INSTALLDS_CERT_OPTION_SELF_SIGNED.get(), 1680 INFO_INSTALLDS_CERT_OPTION_JKS.get(), 1681 INFO_INSTALLDS_CERT_OPTION_JCEKS.get(), 1682 INFO_INSTALLDS_CERT_OPTION_PKCS12.get(), 1683 INFO_INSTALLDS_CERT_OPTION_PKCS11.get() 1684 }; 1685 1686 1687 final MenuBuilder<Integer> builder = new MenuBuilder<>(this); 1688 builder.setPrompt(INFO_INSTALLDS_HEADER_CERT_TYPE.get()); 1689 1690 for (int i=0; i<indexes.length; i++) 1691 { 1692 builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); 1693 } 1694 1695 if (lastResetCertType == null) 1696 { 1697 builder.setDefault(LocalizableMessage.raw(String.valueOf(SELF_SIGNED)), 1698 MenuResult.success(SELF_SIGNED)); 1699 } 1700 else 1701 { 1702 switch (lastResetCertType) 1703 { 1704 case JKS: 1705 builder.setDefault(LocalizableMessage.raw(String.valueOf(JKS)), 1706 MenuResult.success(JKS)); 1707 break; 1708 case JCEKS: 1709 builder.setDefault(LocalizableMessage.raw(String.valueOf(JCEKS)), 1710 MenuResult.success(JCEKS)); 1711 break; 1712 case PKCS11: 1713 builder.setDefault(LocalizableMessage.raw(String.valueOf(PKCS11)), 1714 MenuResult.success(PKCS11)); 1715 break; 1716 case PKCS12: 1717 builder.setDefault(LocalizableMessage.raw(String.valueOf(PKCS12)), 1718 MenuResult.success(PKCS12)); 1719 break; 1720 default: 1721 builder.setDefault(LocalizableMessage.raw(String.valueOf(SELF_SIGNED)), 1722 MenuResult.success(SELF_SIGNED)); 1723 } 1724 } 1725 1726 final Menu<Integer> menu = builder.toMenu(); 1727 int certType; 1728 try 1729 { 1730 final MenuResult<Integer> m = menu.run(); 1731 if (m.isSuccess()) 1732 { 1733 certType = m.getValue(); 1734 } 1735 else 1736 { 1737 // Should never happen. 1738 throw new RuntimeException(); 1739 } 1740 } 1741 catch (final ClientException ce) 1742 { 1743 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1744 certType = SELF_SIGNED; 1745 } 1746 if (certType == SELF_SIGNED) 1747 { 1748 securityOptions = SecurityOptions.createSelfSignedCertificateOptions( 1749 enableSSL, enableStartTLS, ldapsPort); 1750 } 1751 else if (certType == JKS) 1752 { 1753 securityOptions = 1754 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS, 1755 enableSSL, enableStartTLS, ldapsPort); 1756 } 1757 else if (certType == JCEKS) 1758 { 1759 securityOptions = 1760 createSecurityOptionsPrompting( 1761 SecurityOptions.CertificateType.JCEKS, 1762 enableSSL, enableStartTLS, ldapsPort); 1763 } 1764 else if (certType == PKCS12) 1765 { 1766 securityOptions = 1767 createSecurityOptionsPrompting( 1768 SecurityOptions.CertificateType.PKCS12, enableSSL, 1769 enableStartTLS, ldapsPort); 1770 } 1771 else if (certType == PKCS11) 1772 { 1773 securityOptions = 1774 createSecurityOptionsPrompting( 1775 SecurityOptions.CertificateType.PKCS11, enableSSL, 1776 enableStartTLS, ldapsPort); 1777 } 1778 else 1779 { 1780 throw new IllegalStateException("Unexpected cert type: "+ certType); 1781 } 1782 } 1783 return securityOptions; 1784 } 1785 1786 /** 1787 * This method returns what the user specified in the command-line for the 1788 * Windows Service parameters. If the user did not provide explicitly the 1789 * data, it prompts the user to provide it. 1790 * 1791 * @return whether windows service should be enabled 1792 */ 1793 private boolean promptIfRequiredForWindowsService() 1794 { 1795 boolean enableService = false; 1796 // If we are in Windows ask if the server must run as a windows service. 1797 if (isWindows()) 1798 { 1799 if (argParser.enableWindowsServiceArg.isPresent()) 1800 { 1801 enableService = true; 1802 } 1803 else 1804 { 1805 println(); 1806 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_ENABLE_SERVICE.get(); 1807 try 1808 { 1809 final boolean defaultValue = (lastResetEnableWindowsService == null) ? 1810 false : lastResetEnableWindowsService; 1811 enableService = confirmAction(message, defaultValue); 1812 } 1813 catch (final ClientException ce) 1814 { 1815 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1816 } 1817 } 1818 } 1819 return enableService; 1820 } 1821 1822 /** 1823 * This method returns what the user specified in the command-line for the 1824 * Directory Manager parameters. If the user did not provide explicitly the 1825 * data, it prompts the user to provide it. 1826 * 1827 * @return whether server should be started 1828 */ 1829 private boolean promptIfRequiredForStartServer() 1830 { 1831 boolean startServer = false; 1832 if (!argParser.doNotStartArg.isPresent()) 1833 { 1834 println(); 1835 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_START_SERVER.get(); 1836 try 1837 { 1838 final boolean defaultValue = (lastResetStartServer == null) ? 1839 true : lastResetStartServer; 1840 startServer = confirmAction(message, defaultValue); 1841 } 1842 catch (final ClientException ce) 1843 { 1844 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1845 startServer = true; 1846 } 1847 } 1848 return startServer; 1849 } 1850 1851 /** 1852 * Checks that the provided parameters are valid to access an existing key 1853 * store. This method adds the encountered errors to the provided list of 1854 * LocalizableMessage. It also adds the alias (nicknames) found to the 1855 * provided list of String. 1856 * 1857 * @param type 1858 * the type of key store. 1859 * @param path 1860 * the path of the key store. 1861 * @param pwd 1862 * the password (PIN) to access the key store. 1863 * @param certNicknames 1864 * the certificate nicknames that we are looking for (or null if we 1865 * just one to get the one that is in the key store). 1866 * @param errorMessages 1867 * the list that will be updated with the errors encountered. 1868 * @param nicknameList 1869 * the list that will be updated with the nicknames found in the key 1870 * store. 1871 */ 1872 public static void checkCertificateInKeystore(SecurityOptions.CertificateType type, String path, String pwd, 1873 Collection<String> certNicknames, Collection<LocalizableMessage> errorMessages, Collection<String> nicknameList) 1874 { 1875 boolean errorWithPath = false; 1876 if (type != SecurityOptions.CertificateType.PKCS11) 1877 { 1878 final File f = new File(path); 1879 if (!f.exists()) 1880 { 1881 errorMessages.add(INFO_KEYSTORE_PATH_DOES_NOT_EXIST.get()); 1882 errorWithPath = true; 1883 } 1884 else if (!f.isFile()) 1885 { 1886 errorMessages.add(INFO_KEYSTORE_PATH_NOT_A_FILE.get()); 1887 errorWithPath = true; 1888 } 1889 } 1890 if (!errorWithPath) 1891 { 1892 try 1893 { 1894 CertificateManager certManager; 1895 switch (type) 1896 { 1897 case JKS: 1898 certManager = new CertificateManager( 1899 path, 1900 CertificateManager.KEY_STORE_TYPE_JKS, 1901 pwd); 1902 break; 1903 1904 case JCEKS: 1905 certManager = new CertificateManager( 1906 path, 1907 CertificateManager.KEY_STORE_TYPE_JCEKS, 1908 pwd); 1909 break; 1910 1911 case PKCS12: 1912 certManager = new CertificateManager( 1913 path, 1914 CertificateManager.KEY_STORE_TYPE_PKCS12, 1915 pwd); 1916 break; 1917 1918 case PKCS11: 1919 certManager = new CertificateManager( 1920 CertificateManager.KEY_STORE_PATH_PKCS11, 1921 CertificateManager.KEY_STORE_TYPE_PKCS11, 1922 pwd); 1923 break; 1924 1925 default: 1926 throw new IllegalArgumentException("Invalid type: "+type); 1927 } 1928 final String[] aliases = certManager.getCertificateAliases(); 1929 if (aliases == null || aliases.length == 0) 1930 { 1931 // Could not retrieve any certificate 1932 switch (type) 1933 { 1934 case JKS: 1935 errorMessages.add(INFO_JKS_KEYSTORE_DOES_NOT_EXIST.get()); 1936 break; 1937 case JCEKS: 1938 errorMessages.add(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST.get()); 1939 break; 1940 case PKCS12: 1941 errorMessages.add(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST.get()); 1942 break; 1943 case PKCS11: 1944 errorMessages.add(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST.get()); 1945 break; 1946 default: 1947 throw new IllegalArgumentException("Invalid type: "+type); 1948 } 1949 } 1950 else if (certManager.hasRealAliases()) 1951 { 1952 Collections.addAll(nicknameList, aliases); 1953 final String aliasString = joinAsString(", ", nicknameList); 1954 if (certNicknames.isEmpty() && aliases.length > 1) 1955 { 1956 errorMessages.add(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME.get(aliasString)); 1957 } 1958 for (String certNickname : certNicknames) 1959 { 1960 // Check if the certificate alias is in the list. 1961 boolean found = false; 1962 for (int i = 0; i < aliases.length && !found; i++) 1963 { 1964 found = aliases[i].equalsIgnoreCase(certNickname); 1965 } 1966 if (!found) 1967 { 1968 errorMessages.add(ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND.get(aliasString)); 1969 } 1970 } 1971 } 1972 } 1973 catch (final KeyStoreException ke) 1974 { 1975 // issue OPENDJ-18, related to JDK bug 1976 if (StaticUtils.stackTraceContainsCause(ke, ArithmeticException.class)) 1977 { 1978 errorMessages.add(INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG.get()); 1979 } 1980 else 1981 { 1982 // Could not access to the key store: because the password is no good, 1983 // because the provided file is not a valid key store, etc. 1984 switch (type) 1985 { 1986 case JKS: 1987 errorMessages.add(INFO_ERROR_ACCESSING_JKS_KEYSTORE.get()); 1988 break; 1989 case JCEKS: 1990 errorMessages.add(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE.get()); 1991 break; 1992 case PKCS12: 1993 errorMessages.add(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE.get()); 1994 break; 1995 case PKCS11: 1996 errorMessages.add(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE.get()); 1997 break; 1998 default: 1999 throw new IllegalArgumentException("Invalid type: " + type, ke); 2000 } 2001 } 2002 } 2003 } 2004 } 2005 2006 /** 2007 * Creates a SecurityOptions object that corresponds to the provided 2008 * parameters. If the parameters are not valid, it prompts the user to provide 2009 * them. 2010 * 2011 * @param type 2012 * the keystore type. 2013 * @param enableSSL 2014 * whether to enable SSL or not. 2015 * @param enableStartTLS 2016 * whether to enable StartTLS or not. 2017 * @param ldapsPort 2018 * the LDAPS port to use. 2019 * @return a SecurityOptions object that corresponds to the provided 2020 * parameters (or to what the user provided after being prompted). 2021 * @throws UserDataException 2022 * if the user did not manage to provide the keystore password after 2023 * a certain number of tries. 2024 * @throws ClientException 2025 */ 2026 private SecurityOptions createSecurityOptionsPrompting(SecurityOptions.CertificateType type, boolean enableSSL, 2027 boolean enableStartTLS, int ldapsPort) throws UserDataException, ClientException 2028 { 2029 SecurityOptions securityOptions; 2030 String path; 2031 Collection<String> certNicknames = argParser.certNicknameArg.getValues(); 2032 String pwd = argParser.getKeyStorePassword(); 2033 if (pwd != null && pwd.length() == 0) 2034 { 2035 pwd = null; 2036 } 2037 LocalizableMessage pathPrompt; 2038 String defaultPathValue; 2039 2040 switch (type) 2041 { 2042 case JKS: 2043 path = argParser.useJavaKeyStoreArg.getValue(); 2044 pathPrompt = INFO_INSTALLDS_PROMPT_JKS_PATH.get(); 2045 defaultPathValue = argParser.useJavaKeyStoreArg.getValue(); 2046 if (defaultPathValue == null) 2047 { 2048 defaultPathValue = lastResetKeyStorePath; 2049 } 2050 break; 2051 case JCEKS: 2052 path = argParser.useJCEKSArg.getValue(); 2053 pathPrompt = INFO_INSTALLDS_PROMPT_JCEKS_PATH.get(); 2054 defaultPathValue = argParser.useJCEKSArg.getValue(); 2055 if (defaultPathValue == null) 2056 { 2057 defaultPathValue = lastResetKeyStorePath; 2058 } 2059 break; 2060 case PKCS11: 2061 path = null; 2062 defaultPathValue = null; 2063 pathPrompt = null; 2064 break; 2065 case PKCS12: 2066 path = argParser.usePkcs12Arg.getValue(); 2067 defaultPathValue = argParser.usePkcs12Arg.getValue(); 2068 if (defaultPathValue == null) 2069 { 2070 defaultPathValue = lastResetKeyStorePath; 2071 } 2072 pathPrompt = INFO_INSTALLDS_PROMPT_PKCS12_PATH.get(); 2073 break; 2074 default: 2075 throw new IllegalStateException( 2076 "Called promptIfRequiredCertificate with invalid type: "+type); 2077 } 2078 final List<LocalizableMessage> errorMessages = new LinkedList<>(); 2079 final LinkedList<String> keystoreAliases = new LinkedList<>(); 2080 boolean firstTry = true; 2081 int nPasswordPrompts = 0; 2082 2083 while (!errorMessages.isEmpty() || firstTry) 2084 { 2085 boolean prompted = false; 2086 if (!errorMessages.isEmpty()) 2087 { 2088 println(); 2089 println(Utils.getMessageFromCollection(errorMessages, 2090 formatter.getLineBreak().toString())); 2091 } 2092 2093 if (type != SecurityOptions.CertificateType.PKCS11 2094 && (containsKeyStorePathErrorMessage(errorMessages) || path == null)) 2095 { 2096 println(); 2097 try 2098 { 2099 path = readInput(pathPrompt, defaultPathValue); 2100 } 2101 catch (final ClientException ce) 2102 { 2103 path = ""; 2104 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2105 } 2106 2107 prompted = true; 2108 if (pwd != null) 2109 { 2110 errorMessages.clear(); 2111 keystoreAliases.clear(); 2112 checkCertificateInKeystore(type, path, pwd, certNicknames, errorMessages, keystoreAliases); 2113 if (!errorMessages.isEmpty()) 2114 { 2115 // Reset password: this might be a new keystore 2116 pwd = null; 2117 } 2118 } 2119 } 2120 if (containsKeyStorePasswordErrorMessage(errorMessages) || pwd == null) 2121 { 2122 if (!prompted) 2123 { 2124 println(); 2125 } 2126 pwd = null; 2127 while (pwd == null) 2128 { 2129 if (nPasswordPrompts > LIMIT_KEYSTORE_PASSWORD_PROMPT) 2130 { 2131 throw new UserDataException(null, 2132 ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES.get(LIMIT_KEYSTORE_PASSWORD_PROMPT)); 2133 } 2134 pwd = String.valueOf(readPassword(INFO_INSTALLDS_PROMPT_KEYSTORE_PASSWORD.get())); 2135 nPasswordPrompts ++; 2136 } 2137 } 2138 if (containsCertNicknameErrorMessage(errorMessages)) 2139 { 2140 if (!prompted) 2141 { 2142 println(); 2143 } 2144 certNicknames = promptForCertificateNickname(keystoreAliases); 2145 } 2146 errorMessages.clear(); 2147 keystoreAliases.clear(); 2148 checkCertificateInKeystore(type, path, pwd, certNicknames, errorMessages, 2149 keystoreAliases); 2150 firstTry = false; 2151 } 2152 if (certNicknames.isEmpty() && !keystoreAliases.isEmpty()) 2153 { 2154 certNicknames = Arrays.asList(keystoreAliases.getFirst()); 2155 } 2156 switch (type) 2157 { 2158 case JKS: 2159 return SecurityOptions.createJKSCertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, 2160 certNicknames); 2161 case JCEKS: 2162 return SecurityOptions.createJCEKSCertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, 2163 certNicknames); 2164 case PKCS12: 2165 return SecurityOptions.createPKCS12CertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, 2166 certNicknames); 2167 case PKCS11: 2168 return SecurityOptions.createPKCS11CertificateOptions(pwd, enableSSL, enableStartTLS, ldapsPort, certNicknames); 2169 default: 2170 throw new IllegalStateException("Called createSecurityOptionsPrompting with invalid type: " + type); 2171 } 2172 } 2173 2174 /** 2175 * Tells if any of the error messages provided corresponds to a problem with 2176 * the key store path. 2177 * 2178 * @param msgs 2179 * the messages to analyze. 2180 * @return <CODE>true</CODE> if any of the error messages provided corresponds 2181 * to a problem with the key store path and <CODE>false</CODE> 2182 * otherwise. 2183 */ 2184 public static boolean containsKeyStorePathErrorMessage(Collection<LocalizableMessage> msgs) 2185 { 2186 for (final LocalizableMessage msg : msgs) 2187 { 2188 if (StaticUtils.hasDescriptor(msg, INFO_KEYSTORE_PATH_DOES_NOT_EXIST) || 2189 StaticUtils.hasDescriptor(msg, INFO_KEYSTORE_PATH_NOT_A_FILE) || 2190 StaticUtils.hasDescriptor(msg, INFO_JKS_KEYSTORE_DOES_NOT_EXIST) || 2191 StaticUtils.hasDescriptor(msg, INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) || 2192 StaticUtils.hasDescriptor(msg, INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) || 2193 StaticUtils.hasDescriptor(msg, INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) || 2194 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JKS_KEYSTORE) || 2195 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) || 2196 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) || 2197 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS11_KEYSTORE)) 2198 { 2199 return true; 2200 } 2201 } 2202 return false; 2203 } 2204 2205 /** 2206 * Tells if any of the error messages provided corresponds to a problem with 2207 * the key store password. 2208 * 2209 * @param msgs 2210 * the messages to analyze. 2211 * @return <CODE>true</CODE> if any of the error messages provided corresponds 2212 * to a problem with the key store password and <CODE>false</CODE> 2213 * otherwise. 2214 */ 2215 public static boolean containsKeyStorePasswordErrorMessage(Collection<LocalizableMessage> msgs) 2216 { 2217 for (final LocalizableMessage msg : msgs) 2218 { 2219 if (StaticUtils.hasDescriptor(msg, INFO_JKS_KEYSTORE_DOES_NOT_EXIST) || 2220 StaticUtils.hasDescriptor(msg, INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) || 2221 StaticUtils.hasDescriptor(msg, INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) || 2222 StaticUtils.hasDescriptor(msg, INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) || 2223 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JKS_KEYSTORE) || 2224 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) || 2225 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) || 2226 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS11_KEYSTORE) || 2227 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG)) 2228 { 2229 return true; 2230 } 2231 } 2232 return false; 2233 } 2234 2235 /** 2236 * Tells if any of the error messages provided corresponds to a problem with 2237 * the certificate nickname. 2238 * 2239 * @param msgs 2240 * the messages to analyze. 2241 * @return <CODE>true</CODE> if any of the error messages provided corresponds 2242 * to a problem with the certificate nickname and <CODE>false</CODE> 2243 * otherwise. 2244 */ 2245 public static boolean containsCertNicknameErrorMessage( 2246 Collection<LocalizableMessage> msgs) 2247 { 2248 boolean found = false; 2249 for (final LocalizableMessage msg : msgs) 2250 { 2251 if (StaticUtils.hasDescriptor(msg, ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND) || 2252 StaticUtils.hasDescriptor(msg, ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME)) 2253 { 2254 found = true; 2255 break; 2256 } 2257 } 2258 return found; 2259 } 2260 2261 /** 2262 * Interactively prompts (on standard output) the user to provide an integer 2263 * value. The answer provided must be parseable as an integer, and may be 2264 * required to be within a given set of bounds. It will keep prompting until 2265 * an acceptable value is given. 2266 * 2267 * @param prompt 2268 * The prompt to present to the user. 2269 * @param defaultValue 2270 * The default value to assume if the user presses ENTER without 2271 * typing anything, or <CODE>null</CODE> if there should not be a 2272 * default and the user must explicitly provide a value. 2273 * @param lowerBound 2274 * The lower bound that should be enforced, or <CODE>null</CODE> if 2275 * there is none. 2276 * @param upperBound 2277 * The upper bound that should be enforced, or <CODE>null</CODE> if 2278 * there is none. 2279 * @return The <CODE>int</CODE> value read from the user input. 2280 */ 2281 private int promptForInteger(LocalizableMessage prompt, Integer defaultValue, Integer lowerBound, Integer upperBound) 2282 { 2283 int returnValue = -1; 2284 while (returnValue == -1) 2285 { 2286 String s; 2287 try 2288 { 2289 s = readInput(prompt, String.valueOf(defaultValue)); 2290 } 2291 catch (final ClientException ce) 2292 { 2293 s = ""; 2294 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2295 } 2296 if ("".equals(s)) 2297 { 2298 if (defaultValue == null) 2299 { 2300 println(ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get()); 2301 println(); 2302 } 2303 else 2304 { 2305 returnValue = defaultValue; 2306 } 2307 } 2308 else 2309 { 2310 try 2311 { 2312 final int intValue = Integer.parseInt(s); 2313 if (lowerBound != null && intValue < lowerBound) 2314 { 2315 println(ERR_INSTALLDS_INTEGER_BELOW_LOWER_BOUND.get(lowerBound)); 2316 println(); 2317 } 2318 else if (upperBound != null && intValue > upperBound) 2319 { 2320 println(ERR_INSTALLDS_INTEGER_ABOVE_UPPER_BOUND.get(upperBound)); 2321 println(); 2322 } 2323 else 2324 { 2325 returnValue = intValue; 2326 } 2327 } 2328 catch (final NumberFormatException nfe) 2329 { 2330 println(ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get()); 2331 println(); 2332 } 2333 } 2334 } 2335 return returnValue; 2336 } 2337 2338 /** 2339 * Prompts the user to accept on the certificates that appears on the list and 2340 * returns the chosen certificate nickname. 2341 * 2342 * @param nicknames 2343 * the list of certificates the user must choose from. 2344 * @return the chosen certificate nickname. 2345 */ 2346 private Collection<String> promptForCertificateNickname(List<String> nicknames) 2347 { 2348 Collection<String> choosenNicknames = new ArrayList<>(); 2349 while (choosenNicknames.isEmpty()) 2350 { 2351 for (final String n : nicknames) 2352 { 2353 try 2354 { 2355 if (confirmAction(INFO_INSTALLDS_PROMPT_CERTNICKNAME.get(n), true)) 2356 { 2357 choosenNicknames.add(n); 2358 } 2359 } 2360 catch (final ClientException ce) 2361 { 2362 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2363 } 2364 } 2365 } 2366 return choosenNicknames; 2367 } 2368 2369 /** 2370 * It displays the information provided by the user. 2371 * 2372 * @param uData 2373 * the UserData that the user provided. 2374 */ 2375 private void printSummary(UserData uData) 2376 { 2377 println(); 2378 println(); 2379 println(INFO_INSTALLDS_SUMMARY.get()); 2380 final LocalizableMessage[] labels = 2381 { 2382 INFO_SERVER_PORT_LABEL.get(), 2383 INFO_ADMIN_CONNECTOR_PORT_LABEL.get(), 2384 INFO_INSTALLDS_SERVER_JMXPORT_LABEL.get(), 2385 INFO_SERVER_SECURITY_LABEL.get(), 2386 INFO_SERVER_DIRECTORY_MANAGER_DN_LABEL.get(), 2387 INFO_DIRECTORY_DATA_LABEL.get() 2388 }; 2389 2390 final int jmxPort = uData.getServerJMXPort(); 2391 2392 final LocalizableMessage[] values = 2393 { 2394 LocalizableMessage.raw(String.valueOf(uData.getServerPort())), 2395 LocalizableMessage.raw(String.valueOf(uData.getAdminConnectorPort())), 2396 LocalizableMessage.raw(jmxPort != -1 ? String.valueOf(jmxPort) : ""), 2397 LocalizableMessage.raw( 2398 Utils.getSecurityOptionsString(uData.getSecurityOptions(), false)), 2399 LocalizableMessage.raw(uData.getDirectoryManagerDn()), 2400 LocalizableMessage.raw(Utils.getDataDisplayString(uData)), 2401 }; 2402 int maxWidth = 0; 2403 for (final LocalizableMessage l : labels) 2404 { 2405 maxWidth = Math.max(maxWidth, l.length()); 2406 } 2407 2408 for (int i=0; i<labels.length; i++) 2409 { 2410 StringBuilder sb = new StringBuilder(); 2411 if (values[i] != null) 2412 { 2413 final LocalizableMessage l = labels[i]; 2414 sb.append(l).append(" "); 2415 2416 final String[] lines = values[i].toString().split(Constants.LINE_SEPARATOR); 2417 for (int j=0; j<lines.length; j++) 2418 { 2419 if (j != 0) 2420 { 2421 for (int k=0; k <= maxWidth; k++) 2422 { 2423 sb.append(" "); 2424 } 2425 } 2426 else 2427 { 2428 for (int k=0; k<maxWidth - l.length(); k++) 2429 { 2430 sb.append(" "); 2431 } 2432 } 2433 sb.append(lines[j]); 2434 println(LocalizableMessage.raw(sb)); 2435 sb = new StringBuilder(); 2436 } 2437 } 2438 } 2439 2440 println(); 2441 if (uData.getStartServer()) 2442 { 2443 println(INFO_INSTALLDS_START_SERVER.get()); 2444 } 2445 else 2446 { 2447 println(INFO_INSTALLDS_DO_NOT_START_SERVER.get()); 2448 } 2449 2450 if (isWindows()) 2451 { 2452 if (uData.getEnableWindowsService()) 2453 { 2454 println(INFO_INSTALLDS_ENABLE_WINDOWS_SERVICE.get()); 2455 } 2456 else 2457 { 2458 println(INFO_INSTALLDS_DO_NOT_ENABLE_WINDOWS_SERVICE.get()); 2459 } 2460 } 2461 } 2462 2463 private void printEquivalentCommandLine(UserData uData) 2464 { 2465 println(); 2466 2467 println(INFO_INSTALL_SETUP_EQUIVALENT_COMMAND_LINE.get()); 2468 println(); 2469 final List<String> cmd = Utils.getSetupEquivalentCommandLine(uData); 2470 println(LocalizableMessage.raw(Utils.getFormattedEquivalentCommandLine(cmd, formatter))); 2471 } 2472 2473 /** 2474 * This method asks the user to confirm to continue the setup. It basically 2475 * displays the information provided by the user and at the end proposes a 2476 * menu with the different options to choose from. 2477 * 2478 * @return the answer provided by the user: cancel setup, continue setup or 2479 * provide information again. 2480 */ 2481 private ConfirmCode askForConfirmation() 2482 { 2483 ConfirmCode returnValue; 2484 2485 println(); 2486 println(); 2487 2488 final LocalizableMessage[] msgs = new LocalizableMessage[] { 2489 INFO_INSTALLDS_CONFIRM_INSTALL.get(), 2490 INFO_INSTALLDS_PROVIDE_DATA_AGAIN.get(), 2491 INFO_INSTALLDS_PRINT_EQUIVALENT_COMMAND_LINE.get(), 2492 INFO_INSTALLDS_CANCEL.get() 2493 }; 2494 2495 final MenuBuilder<ConfirmCode> builder = new MenuBuilder<>(this); 2496 builder.setPrompt(INFO_INSTALLDS_CONFIRM_INSTALL_PROMPT.get()); 2497 2498 int i=0; 2499 for (final ConfirmCode code : ConfirmCode.values()) 2500 { 2501 builder.addNumberedOption(msgs[i], MenuResult.success(code)); 2502 i++; 2503 } 2504 2505 builder.setDefault(LocalizableMessage.raw( 2506 String.valueOf(ConfirmCode.CONTINUE.getReturnCode())), 2507 MenuResult.success(ConfirmCode.CONTINUE)); 2508 2509 final Menu<ConfirmCode> menu = builder.toMenu(); 2510 2511 try 2512 { 2513 final MenuResult<ConfirmCode> m = menu.run(); 2514 if (m.isSuccess()) 2515 { 2516 returnValue = m.getValue(); 2517 } 2518 else 2519 { 2520 // Should never happen. 2521 throw new RuntimeException(); 2522 } 2523 } 2524 catch (final ClientException ce) 2525 { 2526 returnValue = ConfirmCode.CANCEL; 2527 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2528 } 2529 return returnValue; 2530 } 2531 2532 private void resetArguments(UserData uData) 2533 { 2534 argParser = new InstallDSArgumentParser(InstallDS.class.getName()); 2535 try 2536 { 2537 argParser.initializeArguments(); 2538 lastResetDirectoryManagerDN = uData.getDirectoryManagerDn(); 2539 lastResetLdapPort = uData.getServerPort(); 2540 lastResetAdminConnectorPort = uData.getAdminConnectorPort(); 2541 2542 final int jmxPort = uData.getServerJMXPort(); 2543 if (jmxPort != -1) 2544 { 2545 lastResetJmxPort = jmxPort; 2546 } 2547 2548 final LinkedList<String> baseDNs = uData.getNewSuffixOptions().getBaseDns(); 2549 if (!baseDNs.isEmpty()) 2550 { 2551 lastResetBaseDN = baseDNs.getFirst(); 2552 } 2553 2554 final NewSuffixOptions suffixOptions = uData.getNewSuffixOptions(); 2555 lastResetPopulateOption = suffixOptions.getType(); 2556 2557 if (NewSuffixOptions.Type.IMPORT_AUTOMATICALLY_GENERATED_DATA == lastResetPopulateOption) 2558 { 2559 lastResetNumEntries = suffixOptions.getNumberEntries(); 2560 } 2561 else if (NewSuffixOptions.Type.IMPORT_FROM_LDIF_FILE == lastResetPopulateOption) 2562 { 2563 lastResetImportFile = suffixOptions.getLDIFPaths().getFirst(); 2564 lastResetRejectedFile = suffixOptions.getRejectedFile(); 2565 lastResetSkippedFile = suffixOptions.getSkippedFile(); 2566 } 2567 2568 final SecurityOptions sec = uData.getSecurityOptions(); 2569 if (sec.getEnableSSL()) 2570 { 2571 lastResetLdapsPort = sec.getSslPort(); 2572 } 2573 lastResetEnableSSL = sec.getEnableSSL(); 2574 lastResetEnableStartTLS = sec.getEnableStartTLS(); 2575 lastResetCertType = sec.getCertificateType(); 2576 if (SecurityOptions.CertificateType.JKS == lastResetCertType 2577 || SecurityOptions.CertificateType.JCEKS == lastResetCertType 2578 || SecurityOptions.CertificateType.PKCS12 == lastResetCertType) 2579 { 2580 lastResetKeyStorePath = sec.getKeystorePath(); 2581 } 2582 else 2583 { 2584 lastResetKeyStorePath = null; 2585 } 2586 2587 lastResetEnableWindowsService = uData.getEnableWindowsService(); 2588 lastResetStartServer = uData.getStartServer(); 2589 lastResetBackendType = uData.getBackendType(); 2590 } 2591 catch (final Throwable t) 2592 { 2593 logger.warn(LocalizableMessage.raw("Error resetting arguments: " + t, t)); 2594 } 2595 } 2596 2597 private String promptForHostNameIfRequired() 2598 { 2599 String hostName = null; 2600 if (argParser.hostNameArg.isPresent()) 2601 { 2602 hostName = argParser.hostNameArg.getValue(); 2603 } 2604 else 2605 { 2606 println(); 2607 while (hostName == null) 2608 { 2609 try 2610 { 2611 hostName = readInput(INFO_INSTALLDS_PROMPT_HOST_NAME.get(), argParser.hostNameArg.getDefaultValue()); 2612 } 2613 catch (final ClientException ce) 2614 { 2615 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2616 } 2617 } 2618 } 2619 return hostName; 2620 } 2621 2622 /** 2623 * Returns the timeout to be used to connect in milliseconds. The method must 2624 * be called after parsing the arguments. 2625 * 2626 * @return the timeout to be used to connect in milliseconds. Returns 2627 * {@code 0} if there is no timeout. 2628 */ 2629 private int getConnectTimeout() 2630 { 2631 return argParser.getConnectTimeout(); 2632 } 2633 2634}