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