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.ArrayList; 048import java.util.Arrays; 049import java.util.Collection; 050import java.util.Collections; 051import java.util.LinkedList; 052import java.util.List; 053 054import javax.naming.ldap.LdapName; 055 056import org.forgerock.i18n.LocalizableMessage; 057import org.forgerock.i18n.LocalizableMessageDescriptor.Arg0; 058import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; 059import org.forgerock.i18n.slf4j.LocalizedLogger; 060import org.forgerock.opendj.config.ManagedObjectDefinition; 061import org.forgerock.opendj.server.config.client.BackendCfgClient; 062import org.forgerock.opendj.server.config.server.BackendCfg; 063import org.opends.messages.QuickSetupMessages; 064import org.opends.messages.ToolMessages; 065import org.opends.quicksetup.ApplicationException; 066import org.opends.quicksetup.Constants; 067import org.opends.quicksetup.CurrentInstallStatus; 068import org.opends.quicksetup.Installation; 069import org.opends.quicksetup.LicenseFile; 070import org.opends.quicksetup.QuickSetupLog; 071import org.opends.quicksetup.SecurityOptions; 072import org.opends.quicksetup.UserData; 073import org.opends.quicksetup.UserDataException; 074import org.opends.quicksetup.event.ProgressUpdateEvent; 075import org.opends.quicksetup.event.ProgressUpdateListener; 076import org.opends.quicksetup.installer.NewSuffixOptions; 077import org.opends.quicksetup.installer.offline.OfflineInstaller; 078import org.opends.quicksetup.util.IncompatibleVersionException; 079import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; 080import org.opends.quicksetup.util.Utils; 081import org.opends.server.types.InitializationException; 082import org.opends.server.types.NullOutputStream; 083import org.opends.server.util.CertificateManager; 084import org.opends.server.util.SetupUtils; 085import org.opends.server.util.StaticUtils; 086 087import com.forgerock.opendj.cli.ArgumentException; 088import com.forgerock.opendj.cli.ClientException; 089import com.forgerock.opendj.cli.ConsoleApplication; 090import com.forgerock.opendj.cli.IntegerArgument; 091import com.forgerock.opendj.cli.Menu; 092import com.forgerock.opendj.cli.MenuBuilder; 093import com.forgerock.opendj.cli.MenuResult; 094import com.forgerock.opendj.cli.StringArgument; 095 096/** 097 * This class provides a very simple mechanism for installing the OpenDS 098 * Directory Service. It performs the following tasks: 099 * <UL> 100 * <LI>Checks if the server is already installed and running</LI> 101 * <LI>Ask the user what base DN should be used for the data</LI> 102 * <LI>Ask the user whether to create the base entry, or to import LDIF</LI> 103 * <LI>Ask the user for the administration port and make sure it's available 104 * </LI> 105 * <LI>Ask the user for the LDAP port and make sure it's available</LI> 106 * <LI>Ask the user for the default root DN and password</LI> 107 * <LI>Ask the user to enable SSL or not and for the type of certificate that 108 * the server must use</LI> 109 * <LI>Ask the user if they want to start the server when done installing</LI> 110 * </UL> 111 */ 112public class InstallDS extends ConsoleApplication 113{ 114 115 private final PlainTextProgressMessageFormatter formatter = new PlainTextProgressMessageFormatter(); 116 117 /** Prefix for log files. */ 118 public static final String TMP_FILE_PREFIX = "opendj-setup-"; 119 120 /** Suffix for log files. */ 121 public static final String LOG_FILE_SUFFIX = ".log"; 122 123 /** 124 * The enumeration containing the different return codes that the command-line 125 * can have. 126 */ 127 private enum InstallReturnCode 128 { 129 SUCCESSFUL(0), 130 131 /** We did no have an error but the setup was not executed (displayed version or usage). */ 132 SUCCESSFUL_NOP(0), 133 134 /** Unexpected error (potential bug). */ 135 ERROR_UNEXPECTED(1), 136 137 /** Cannot parse arguments or data provided by user is not valid. */ 138 ERROR_USER_DATA(2), 139 140 /** Error server already installed. */ 141 ERROR_SERVER_ALREADY_INSTALLED(3), 142 143 /** Error initializing server. */ 144 ERROR_INITIALIZING_SERVER(4), 145 146 /** The user failed providing password (for the keystore for instance). */ 147 ERROR_PASSWORD_LIMIT(5), 148 149 /** The user cancelled the setup. */ 150 ERROR_USER_CANCELLED(6), 151 152 /** The user doesn't accept the license. */ 153 ERROR_LICENSE_NOT_ACCEPTED(7), 154 155 /** Incompatible java version. */ 156 JAVA_VERSION_INCOMPATIBLE(8); 157 158 private int returnCode; 159 private InstallReturnCode(int returnCode) 160 { 161 this.returnCode = returnCode; 162 } 163 164 /** 165 * Get the corresponding return code value. 166 * 167 * @return The corresponding return code value. 168 */ 169 public int getReturnCode() 170 { 171 return returnCode; 172 } 173 } 174 175 /** 176 * Enumeration describing the different answer that the user can provide when 177 * we ask to finalize the setup. Note that the code associated correspond to 178 * the order in the confirmation menu that is displayed at the end of the 179 * setup in interactive mode. 180 */ 181 private enum ConfirmCode 182 { 183 CONTINUE(1), 184 PROVIDE_INFORMATION_AGAIN(2), 185 PRINT_EQUIVALENT_COMMAND_LINE(3), 186 CANCEL(3); 187 188 private int returnCode; 189 private ConfirmCode(int returnCode) 190 { 191 this.returnCode = returnCode; 192 } 193 194 /** 195 * Get the corresponding return code value. 196 * 197 * @return The corresponding return code value. 198 */ 199 public int getReturnCode() 200 { 201 return returnCode; 202 } 203 } 204 205 /** 206 * The maximum number of times that we should ask the user to provide the 207 * password to access to a keystore. 208 */ 209 public static final int LIMIT_KEYSTORE_PASSWORD_PROMPT = 7; 210 211 private final BackendTypeHelper backendTypeHelper = new BackendTypeHelper(); 212 213 /** Different variables we use when the user decides to provide data again. */ 214 private NewSuffixOptions.Type lastResetPopulateOption; 215 private ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> lastResetBackendType; 216 217 private String lastResetImportFile; 218 private String lastResetRejectedFile; 219 private String lastResetSkippedFile; 220 221 private Integer lastResetNumEntries; 222 private Boolean lastResetEnableSSL; 223 private Boolean lastResetEnableStartTLS; 224 225 private SecurityOptions.CertificateType lastResetCertType; 226 private String lastResetKeyStorePath; 227 228 private Boolean lastResetEnableWindowsService; 229 private Boolean lastResetStartServer; 230 231 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 232 233 /** The argument parser. */ 234 private InstallDSArgumentParser argParser; 235 236 /** 237 * Constructor for the InstallDS object. 238 * 239 * @param out 240 * the print stream to use for standard output. 241 * @param err 242 * the print stream to use for standard error. 243 * @param in 244 * the input stream to use for standard input. 245 */ 246 public InstallDS(PrintStream out, PrintStream err, InputStream in) 247 { 248 super(out, err); 249 } 250 251 /** 252 * The main method for the InstallDS CLI tool. 253 * 254 * @param args 255 * the command-line arguments provided to this program. 256 */ 257 public static void main(String[] args) 258 { 259 final int retCode = mainCLI(args, System.out, System.err, System.in); 260 261 System.exit(retCode); 262 } 263 264 /** 265 * Parses the provided command-line arguments and uses that information to run 266 * the setup tool. 267 * 268 * @param args 269 * the command-line arguments provided to this program. 270 * @return The error code. 271 */ 272 public static int mainCLI(String[] args) 273 { 274 return mainCLI(args, System.out, System.err, System.in); 275 } 276 277 /** 278 * Parses the provided command-line arguments and uses that information to run 279 * the setup tool. 280 * 281 * @param args 282 * The command-line arguments provided to this program. 283 * @param outStream 284 * The output stream to use for standard output, or <CODE>null</CODE> 285 * if standard output is not needed. 286 * @param errStream 287 * The output stream to use for standard error, or <CODE>null</CODE> 288 * if standard error is not needed. 289 * @param inStream 290 * The input stream to use for standard input. 291 * @return The error code. 292 */ 293 public static int mainCLI(String[] args, OutputStream outStream, OutputStream errStream, InputStream inStream) 294 { 295 final PrintStream out = NullOutputStream.wrapOrNullStream(outStream); 296 297 System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); 298 299 final PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 300 301 try { 302 QuickSetupLog.initLogFileHandler( 303 QuickSetupLog.isInitialized() ? null : 304 File.createTempFile(TMP_FILE_PREFIX, LOG_FILE_SUFFIX)); 305 } catch (final Throwable t) { 306 System.err.println("Unable to initialize log"); 307 t.printStackTrace(); 308 } 309 310 final InstallDS install = new InstallDS(out, err, inStream); 311 312 int retCode = install.execute(args); 313 if (retCode == 0) 314 { 315 QuickSetupLog.closeAndDeleteLogFile(); 316 } 317 return retCode; 318 } 319 320 /** 321 * Parses the provided command-line arguments and uses that information to run 322 * the setup CLI. 323 * 324 * @param args 325 * the command-line arguments provided to this program. 326 * @return the return code (SUCCESSFUL, USER_DATA_ERROR or BUG). 327 */ 328 public int execute(String[] args) 329 { 330 argParser = new InstallDSArgumentParser(InstallDS.class.getName()); 331 try 332 { 333 argParser.initializeArguments(); 334 } 335 catch (final ArgumentException ae) 336 { 337 println(ToolMessages.ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 338 return InstallReturnCode.ERROR_UNEXPECTED.getReturnCode(); 339 } 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 initializeUserDataWithParser(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 (isInteractive() && 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 initializeUserDataWithParser(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, 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 promptMsg 1032 * the prompt message to be displayed. 1033 * @param includeLineBreak 1034 * whether to include a line break before the first prompt or not. 1035 * @return a list of valid DNs. 1036 * @throws UserDataException 1037 * if something went wrong checking the data. 1038 */ 1039 private LinkedList<String> promptIfRequiredForDNs(StringArgument arg, 1040 LocalizableMessage promptMsg, boolean includeLineBreak) throws UserDataException 1041 { 1042 final LinkedList<String> dns = new LinkedList<>(); 1043 1044 boolean usedProvided = false; 1045 boolean firstPrompt = true; 1046 int nTries = 0; 1047 while (dns.isEmpty()) 1048 { 1049 if (nTries >= CONFIRMATION_MAX_TRIES) 1050 { 1051 throw new UserDataException(null, ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES)); 1052 } 1053 boolean prompted = false; 1054 if (usedProvided || !arg.isPresent()) 1055 { 1056 if (firstPrompt && includeLineBreak) 1057 { 1058 println(); 1059 } 1060 try 1061 { 1062 final String dn = readInput(promptMsg, arg.getDefaultValue()); 1063 firstPrompt = false; 1064 dns.add(dn); 1065 prompted = true; 1066 } 1067 catch (final ClientException ce) 1068 { 1069 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1070 } 1071 } 1072 else 1073 { 1074 dns.addAll(arg.getValues()); 1075 usedProvided = true; 1076 } 1077 final List<String> toRemove = new LinkedList<>(); 1078 for (final String dn : dns) 1079 { 1080 try 1081 { 1082 new LdapName(dn); 1083 if (dn.trim().length() == 0) 1084 { 1085 toRemove.add(dn); 1086 println(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get()); 1087 } 1088 } 1089 catch (final Exception e) 1090 { 1091 toRemove.add(dn); 1092 final LocalizableMessage message = prompted ? ERR_INSTALLDS_INVALID_DN_RESPONSE.get() : 1093 ERR_INSTALLDS_CANNOT_PARSE_DN.get(dn, e.getMessage()); 1094 println(message); 1095 } 1096 } 1097 if (!toRemove.isEmpty()) 1098 { 1099 println(); 1100 } 1101 dns.removeAll(toRemove); 1102 nTries++; 1103 } 1104 return dns; 1105 } 1106 1107 /** 1108 * This method updates the contents of a UserData object with what the user 1109 * specified in the command-line for the administration connector, LDAP and 1110 * JMX port parameters. If the user did not provide explicitly some data or 1111 * if the provided data is not valid, it prompts the user to provide it. 1112 * Note: this method does not update nor check the LDAPS port. 1113 * 1114 * @param uData 1115 * the UserData object to be updated. 1116 */ 1117 private void promptIfRequiredForPortData(UserData uData) 1118 { 1119 uData.setHostName(promptForHostNameIfRequired()); 1120 1121 final List<Integer> usedPorts = new LinkedList<>(); 1122 // Determine the LDAP port number. 1123 final int ldapPort = promptIfRequiredForPortData(argParser.ldapPortArg, 1124 INFO_INSTALLDS_PROMPT_LDAPPORT.get(), usedPorts, true); 1125 uData.setServerPort(ldapPort); 1126 usedPorts.add(ldapPort); 1127 1128 // Determine the Admin Connector port number. 1129 final int adminConnectorPort = promptIfRequiredForPortData(argParser.adminConnectorPortArg, 1130 INFO_INSTALLDS_PROMPT_ADMINCONNECTORPORT.get(), usedPorts, true); 1131 uData.setAdminConnectorPort(adminConnectorPort); 1132 usedPorts.add(adminConnectorPort); 1133 1134 if (argParser.jmxPortArg.isPresent()) 1135 { 1136 final int jmxPort = promptIfRequiredForPortData(argParser.jmxPortArg, 1137 INFO_INSTALLDS_PROMPT_JMXPORT.get(), usedPorts, true); 1138 uData.setServerJMXPort(jmxPort); 1139 } 1140 else 1141 { 1142 uData.setServerJMXPort(-1); 1143 } 1144 } 1145 1146 /** 1147 * This method returns a valid port value. It checks that the provided 1148 * argument contains a valid port. If a valid port is not found it prompts the 1149 * user to provide a valid port. 1150 * 1151 * @param portArg 1152 * the Argument that the user provided to specify the port. 1153 * @param promptMsg 1154 * the prompt message to be displayed. 1155 * @param usedPorts 1156 * the list of ports the user provided before for other connection 1157 * handlers. 1158 * @param includeLineBreak 1159 * whether to include a line break before the first prompt or not. 1160 * @return a valid port number. 1161 */ 1162 private int promptIfRequiredForPortData(IntegerArgument portArg, LocalizableMessage promptMsg, 1163 Collection<Integer> usedPorts, boolean includeLineBreak) 1164 { 1165 int portNumber = -1; 1166 boolean usedProvided = false; 1167 boolean firstPrompt = true; 1168 while (portNumber == -1) 1169 { 1170 try 1171 { 1172 boolean prompted = false; 1173 if (usedProvided || !portArg.isPresent()) 1174 { 1175 int defaultValue = -1; 1176 if (portArg.getDefaultValue() != null) 1177 { 1178 defaultValue = Integer.parseInt(portArg.getDefaultValue()); 1179 } 1180 if (firstPrompt && includeLineBreak) 1181 { 1182 println(); 1183 } 1184 portNumber = -1; 1185 while (portNumber == -1) 1186 { 1187 try 1188 { 1189 portNumber = readPort(promptMsg, defaultValue); 1190 } 1191 catch (final ClientException ce) 1192 { 1193 portNumber = -1; 1194 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1195 } 1196 } 1197 prompted = true; 1198 firstPrompt = false; 1199 } 1200 else 1201 { 1202 portNumber = portArg.getIntValue(); 1203 usedProvided = true; 1204 } 1205 1206 if (!argParser.skipPortCheckArg.isPresent() && !SetupUtils.canUseAsPort(portNumber)) 1207 { 1208 final LocalizableMessage message = getCannotBindErrorMessage(portNumber); 1209 if (prompted || includeLineBreak) 1210 { 1211 println(); 1212 } 1213 println(message); 1214 if (!SetupUtils.isPrivilegedPort(portNumber)) 1215 { 1216 println(); 1217 } 1218 portNumber = -1; 1219 } 1220 if (portNumber != -1 && usedPorts.contains(portNumber)) 1221 { 1222 println(ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(portNumber)); 1223 println(); 1224 portNumber = -1; 1225 } 1226 } 1227 catch (final ArgumentException ae) 1228 { 1229 println(ae.getMessageObject()); 1230 } 1231 } 1232 return portNumber; 1233 } 1234 1235 /** 1236 * This method returns what the user specified in the command-line for the 1237 * base DN and data import parameters. If the user did not provide explicitly 1238 * some data or if the provided data is not valid, it prompts the user to 1239 * provide it. 1240 * 1241 * @param uData 1242 * The UserData object to be updated. 1243 * @return the NewSuffixOptions telling how to import data 1244 * @throws UserDataException 1245 * if something went wrong checking the data. 1246 */ 1247 private NewSuffixOptions promptIfRequiredForImportData(final UserData uData) throws UserDataException 1248 { 1249 boolean prompt = true; 1250 if (!argParser.baseDNArg.isPresent()) 1251 { 1252 println(); 1253 try 1254 { 1255 prompt = confirmAction(INFO_INSTALLDS_PROVIDE_BASE_DN_PROMPT.get(), true); 1256 } 1257 catch (final ClientException ce) 1258 { 1259 prompt = true; 1260 logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce)); 1261 } 1262 } 1263 1264 if (!prompt) 1265 { 1266 return NewSuffixOptions.createEmpty(new LinkedList<String>()); 1267 } 1268 1269 uData.setBackendType(getOrPromptForBackendType()); 1270 1271 // Add default value for base DN on first prompt 1272 if (argParser.baseDNArg.getDefaultValue() == null) 1273 { 1274 argParser.baseDNArg.setDefaultValue(Installation.DEFAULT_INTERACTIVE_BASE_DN); 1275 } 1276 // Check the validity of the base DNs 1277 final List<String> baseDNs = promptIfRequiredForDNs(argParser.baseDNArg, INFO_INSTALLDS_PROMPT_BASEDN.get(), true); 1278 return promptIfRequiredForDataOptions(baseDNs); 1279 1280 } 1281 1282 private ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> getOrPromptForBackendType() 1283 { 1284 if (argParser.backendTypeArg.isPresent()) 1285 { 1286 final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backend = 1287 backendTypeHelper.retrieveBackendTypeFromName(argParser.backendTypeArg.getValue().toLowerCase()); 1288 if ( backend != null) 1289 { 1290 return backend; 1291 } 1292 println(); 1293 println(ERR_INSTALLDS_NO_SUCH_BACKEND_TYPE.get( 1294 argParser.backendTypeArg.getValue(), backendTypeHelper.getPrintableBackendTypeNames())); 1295 } 1296 1297 return promptForBackendType(); 1298 } 1299 1300 private ManagedObjectDefinition<? extends BackendCfgClient,? extends BackendCfg> promptForBackendType() 1301 { 1302 println(); 1303 int backendTypeIndex = 1; 1304 final List<ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg>> backendTypes = 1305 backendTypeHelper.getBackendTypes(); 1306 if (backendTypes.size() == 1) { 1307 final ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg> backendType = backendTypes.get(0); 1308 println(INFO_INSTALLDS_BACKEND_TYPE_USED.get(backendType.getUserFriendlyName())); 1309 return backendType; 1310 } 1311 1312 try 1313 { 1314 final MenuResult<Integer> m = getBackendTypeMenu().run(); 1315 if (m.isSuccess()) 1316 { 1317 backendTypeIndex = m.getValue(); 1318 } 1319 } 1320 catch (final ClientException ce) 1321 { 1322 logger.warn(LocalizableMessage.raw("Error reading input: " + ce, ce)); 1323 } 1324 1325 return backendTypes.get(backendTypeIndex - 1); 1326 } 1327 1328 private Menu<Integer> getBackendTypeMenu() 1329 { 1330 final MenuBuilder<Integer> builder = new MenuBuilder<>(this); 1331 builder.setPrompt(INFO_INSTALLDS_PROMPT_BACKEND_TYPE.get()); 1332 int index = 1; 1333 for (final ManagedObjectDefinition<?, ?> backendType : backendTypeHelper.getBackendTypes()) 1334 { 1335 builder.addNumberedOption(backendType.getUserFriendlyName(), MenuResult.success(index++)); 1336 } 1337 1338 final int printableIndex = getPromptedBackendTypeIndex(); 1339 builder.setDefault(LocalizableMessage.raw(Integer.toString(printableIndex)), MenuResult.success(printableIndex)); 1340 return builder.toMenu(); 1341 } 1342 1343 private int getPromptedBackendTypeIndex() 1344 { 1345 if (lastResetBackendType != null) 1346 { 1347 return backendTypeHelper.getBackendTypes().indexOf(lastResetBackendType) + 1; 1348 } 1349 return 1; 1350 } 1351 1352 private NewSuffixOptions promptIfRequiredForDataOptions(List<String> baseDNs) 1353 { 1354 NewSuffixOptions dataOptions; 1355 if (argParser.importLDIFArg.isPresent()) 1356 { 1357 // Check that the files exist 1358 final List<String> nonExistingFiles = new LinkedList<>(); 1359 final List<String> importLDIFFiles = new LinkedList<>(); 1360 for (final String file : argParser.importLDIFArg.getValues()) 1361 { 1362 if (!Utils.fileExists(file)) 1363 { 1364 nonExistingFiles.add(file); 1365 } 1366 else 1367 { 1368 importLDIFFiles.add(file); 1369 } 1370 } 1371 if (!nonExistingFiles.isEmpty()) 1372 { 1373 println(); 1374 println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(joinAsString(", ", nonExistingFiles))); 1375 } 1376 1377 readImportLdifFile(importLDIFFiles, lastResetImportFile); 1378 String rejectedFile = readValidFilePath(argParser.rejectedImportFileArg, lastResetRejectedFile, 1379 ERR_INSTALLDS_CANNOT_WRITE_REJECTED, INFO_INSTALLDS_PROMPT_REJECTED_FILE); 1380 String skippedFile = readValidFilePath(argParser.skippedImportFileArg, lastResetSkippedFile, 1381 ERR_INSTALLDS_CANNOT_WRITE_SKIPPED, INFO_INSTALLDS_PROMPT_SKIPPED_FILE); 1382 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, 1383 importLDIFFiles, rejectedFile, skippedFile); 1384 } 1385 else if (argParser.addBaseEntryArg.isPresent()) 1386 { 1387 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); 1388 } 1389 else if (argParser.sampleDataArg.isPresent()) 1390 { 1391 int numUsers; 1392 try 1393 { 1394 numUsers = argParser.sampleDataArg.getIntValue(); 1395 } 1396 catch (final ArgumentException ae) 1397 { 1398 println(); 1399 println(ae.getMessageObject()); 1400 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get(); 1401 numUsers = promptForInteger(message, 2000, 0, Integer.MAX_VALUE); 1402 } 1403 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, numUsers); 1404 } 1405 else 1406 { 1407 final int POPULATE_TYPE_LEAVE_EMPTY = 1; 1408 final int POPULATE_TYPE_BASE_ONLY = 2; 1409 final int POPULATE_TYPE_IMPORT_FROM_LDIF = 3; 1410 final int POPULATE_TYPE_GENERATE_SAMPLE_DATA = 4; 1411 1412 final int[] indexes = {POPULATE_TYPE_LEAVE_EMPTY, POPULATE_TYPE_BASE_ONLY, 1413 POPULATE_TYPE_IMPORT_FROM_LDIF, POPULATE_TYPE_GENERATE_SAMPLE_DATA}; 1414 final LocalizableMessage[] msgs = new LocalizableMessage[] { 1415 INFO_INSTALLDS_POPULATE_OPTION_LEAVE_EMPTY.get(), 1416 INFO_INSTALLDS_POPULATE_OPTION_BASE_ONLY.get(), 1417 INFO_INSTALLDS_POPULATE_OPTION_IMPORT_LDIF.get(), 1418 INFO_INSTALLDS_POPULATE_OPTION_GENERATE_SAMPLE.get() 1419 }; 1420 1421 final MenuBuilder<Integer> builder = new MenuBuilder<>(this); 1422 builder.setPrompt(INFO_INSTALLDS_HEADER_POPULATE_TYPE.get()); 1423 1424 for (int i=0; i<indexes.length; i++) 1425 { 1426 builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); 1427 } 1428 1429 if (lastResetPopulateOption == null) 1430 { 1431 builder.setDefault(LocalizableMessage.raw( 1432 String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)), 1433 MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY)); 1434 } 1435 else 1436 { 1437 switch (lastResetPopulateOption) 1438 { 1439 case LEAVE_DATABASE_EMPTY: 1440 builder.setDefault(LocalizableMessage.raw( 1441 String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)), 1442 MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY)); 1443 break; 1444 case IMPORT_FROM_LDIF_FILE: 1445 builder.setDefault(LocalizableMessage.raw( 1446 String.valueOf(POPULATE_TYPE_IMPORT_FROM_LDIF)), 1447 MenuResult.success(POPULATE_TYPE_IMPORT_FROM_LDIF)); 1448 break; 1449 case IMPORT_AUTOMATICALLY_GENERATED_DATA: 1450 builder.setDefault(LocalizableMessage.raw( 1451 String.valueOf(POPULATE_TYPE_GENERATE_SAMPLE_DATA)), 1452 MenuResult.success(POPULATE_TYPE_GENERATE_SAMPLE_DATA)); 1453 break; 1454 default: 1455 builder.setDefault(LocalizableMessage.raw( 1456 String.valueOf(POPULATE_TYPE_BASE_ONLY)), 1457 MenuResult.success(POPULATE_TYPE_BASE_ONLY)); 1458 } 1459 } 1460 1461 final Menu<Integer> menu = builder.toMenu(); 1462 int populateType; 1463 try 1464 { 1465 final MenuResult<Integer> m = menu.run(); 1466 if (m.isSuccess()) 1467 { 1468 populateType = m.getValue(); 1469 } 1470 else 1471 { 1472 // Should never happen. 1473 throw new RuntimeException(); 1474 } 1475 } 1476 catch (final ClientException ce) 1477 { 1478 populateType = POPULATE_TYPE_BASE_ONLY; 1479 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1480 } 1481 1482 if (populateType == POPULATE_TYPE_IMPORT_FROM_LDIF) 1483 { 1484 final List<String> importLDIFFiles = new LinkedList<>(); 1485 readImportLdifFile(importLDIFFiles, null); 1486 String rejectedFile = readValidFilePath(argParser.rejectedImportFileArg, null, 1487 ERR_INSTALLDS_CANNOT_WRITE_REJECTED, INFO_INSTALLDS_PROMPT_REJECTED_FILE); 1488 String skippedFile = readValidFilePath(argParser.skippedImportFileArg, null, 1489 ERR_INSTALLDS_CANNOT_WRITE_SKIPPED, INFO_INSTALLDS_PROMPT_SKIPPED_FILE); 1490 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs, 1491 importLDIFFiles, rejectedFile, skippedFile); 1492 } 1493 else if (populateType == POPULATE_TYPE_GENERATE_SAMPLE_DATA) 1494 { 1495 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get(); 1496 int defaultValue = lastResetNumEntries != null ? lastResetNumEntries : 2000; 1497 final int numUsers = promptForInteger(message, defaultValue, 0, Integer.MAX_VALUE); 1498 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs, numUsers); 1499 } 1500 else if (populateType == POPULATE_TYPE_LEAVE_EMPTY) 1501 { 1502 dataOptions = NewSuffixOptions.createEmpty(baseDNs); 1503 } 1504 else if (populateType == POPULATE_TYPE_BASE_ONLY) 1505 { 1506 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs); 1507 } 1508 else 1509 { 1510 throw new IllegalStateException("Unexpected populateType: " + populateType); 1511 } 1512 } 1513 return dataOptions; 1514 } 1515 1516 private void readImportLdifFile(final List<String> importLDIFFiles, String defaultValue) 1517 { 1518 while (importLDIFFiles.isEmpty()) 1519 { 1520 println(); 1521 try 1522 { 1523 final String path = readInput(INFO_INSTALLDS_PROMPT_IMPORT_FILE.get(), defaultValue); 1524 if (Utils.fileExists(path)) 1525 { 1526 importLDIFFiles.add(path); 1527 } 1528 else 1529 { 1530 println(); 1531 println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path)); 1532 } 1533 } 1534 catch (final ClientException ce) 1535 { 1536 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1537 } 1538 } 1539 } 1540 1541 private String readValidFilePath(StringArgument arg, String defaultValue, Arg1<Object> errCannotWriteFile, 1542 Arg0 infoPromptFile) 1543 { 1544 String file = arg.getValue(); 1545 if (file != null) 1546 { 1547 while (!canWrite(file)) 1548 { 1549 println(); 1550 println(errCannotWriteFile.get(file)); 1551 println(); 1552 try 1553 { 1554 file = readInput(infoPromptFile.get(), defaultValue); 1555 } 1556 catch (final ClientException ce) 1557 { 1558 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1559 } 1560 } 1561 } 1562 return file; 1563 } 1564 1565 /** 1566 * This method returns what the user specified in the command-line for the 1567 * security parameters. If the user did not provide explicitly some data or if 1568 * the provided data is not valid, it prompts the user to provide it. 1569 * 1570 * @param uData 1571 * the current UserData object. 1572 * @return the {@link SecurityOptions} to be used when starting the server 1573 * @throws UserDataException 1574 * if the user did not manage to provide the keystore password after 1575 * a certain number of tries. 1576 * @throws ClientException 1577 * If an error occurs when reading inputs. 1578 */ 1579 private SecurityOptions promptIfRequiredForSecurityData(UserData uData) throws UserDataException, ClientException 1580 { 1581 // Check that the security data provided is valid. 1582 boolean enableSSL = false; 1583 boolean enableStartTLS = false; 1584 int ldapsPort = -1; 1585 1586 final List<Integer> usedPorts = new LinkedList<>(); 1587 usedPorts.add(uData.getServerPort()); 1588 if (uData.getServerJMXPort() != -1) 1589 { 1590 usedPorts.add(uData.getServerJMXPort()); 1591 } 1592 1593 // Ask to enable SSL 1594 if (!argParser.ldapsPortArg.isPresent()) 1595 { 1596 println(); 1597 try 1598 { 1599 final boolean defaultValue = lastResetEnableSSL != null ? lastResetEnableSSL : false; 1600 enableSSL = confirmAction(INFO_INSTALLDS_PROMPT_ENABLE_SSL.get(), defaultValue); 1601 if (enableSSL) 1602 { 1603 ldapsPort = promptIfRequiredForPortData(argParser.ldapsPortArg, 1604 INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, false); 1605 } 1606 } 1607 catch (final ClientException ce) 1608 { 1609 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1610 } 1611 } 1612 else 1613 { 1614 ldapsPort = promptIfRequiredForPortData(argParser.ldapsPortArg, 1615 INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, true); 1616 enableSSL = true; 1617 } 1618 1619 // Ask to enable Start TLS 1620 if (!argParser.enableStartTLSArg.isPresent()) 1621 { 1622 println(); 1623 try 1624 { 1625 final boolean defaultValue = lastResetEnableStartTLS != null ? 1626 lastResetEnableStartTLS : false; 1627 enableStartTLS = confirmAction(INFO_INSTALLDS_ENABLE_STARTTLS.get(), 1628 defaultValue); 1629 } 1630 catch (final ClientException ce) 1631 { 1632 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1633 } 1634 } 1635 else 1636 { 1637 enableStartTLS = true; 1638 } 1639 1640 SecurityOptions securityOptions; 1641 if (argParser.generateSelfSignedCertificateArg.isPresent()) 1642 { 1643 securityOptions = SecurityOptions.createSelfSignedCertificateOptions( 1644 enableSSL, enableStartTLS, ldapsPort); 1645 } 1646 else if (argParser.useJavaKeyStoreArg.isPresent()) 1647 { 1648 securityOptions = 1649 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS, 1650 enableSSL, enableStartTLS, ldapsPort); 1651 } 1652 else if (argParser.useJCEKSArg.isPresent()) 1653 { 1654 securityOptions = 1655 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JCEKS, 1656 enableSSL, enableStartTLS, ldapsPort); 1657 } 1658 else if (argParser.usePkcs12Arg.isPresent()) 1659 { 1660 securityOptions = 1661 createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS12, 1662 enableSSL, enableStartTLS, ldapsPort); 1663 } 1664 else if (argParser.usePkcs11Arg.isPresent()) 1665 { 1666 securityOptions = 1667 createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS11, 1668 enableSSL, enableStartTLS, ldapsPort); 1669 } 1670 else if (!enableSSL && !enableStartTLS) 1671 { 1672 // If the user did not want to enable SSL or start TLS do not ask 1673 // to create a certificate. 1674 securityOptions = SecurityOptions.createNoCertificateOptions(); 1675 } 1676 else 1677 { 1678 final int SELF_SIGNED = 1; 1679 final int JKS = 2; 1680 final int JCEKS = 3; 1681 final int PKCS12 = 4; 1682 final int PKCS11 = 5; 1683 final int[] indexes = {SELF_SIGNED, JKS, JCEKS, PKCS12, PKCS11}; 1684 final LocalizableMessage[] msgs = { 1685 INFO_INSTALLDS_CERT_OPTION_SELF_SIGNED.get(), 1686 INFO_INSTALLDS_CERT_OPTION_JKS.get(), 1687 INFO_INSTALLDS_CERT_OPTION_JCEKS.get(), 1688 INFO_INSTALLDS_CERT_OPTION_PKCS12.get(), 1689 INFO_INSTALLDS_CERT_OPTION_PKCS11.get() 1690 }; 1691 1692 1693 final MenuBuilder<Integer> builder = new MenuBuilder<>(this); 1694 builder.setPrompt(INFO_INSTALLDS_HEADER_CERT_TYPE.get()); 1695 1696 for (int i=0; i<indexes.length; i++) 1697 { 1698 builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i])); 1699 } 1700 1701 if (lastResetCertType == null) 1702 { 1703 builder.setDefault(LocalizableMessage.raw(String.valueOf(SELF_SIGNED)), 1704 MenuResult.success(SELF_SIGNED)); 1705 } 1706 else 1707 { 1708 switch (lastResetCertType) 1709 { 1710 case JKS: 1711 builder.setDefault(LocalizableMessage.raw(String.valueOf(JKS)), 1712 MenuResult.success(JKS)); 1713 break; 1714 case JCEKS: 1715 builder.setDefault(LocalizableMessage.raw(String.valueOf(JCEKS)), 1716 MenuResult.success(JCEKS)); 1717 break; 1718 case PKCS11: 1719 builder.setDefault(LocalizableMessage.raw(String.valueOf(PKCS11)), 1720 MenuResult.success(PKCS11)); 1721 break; 1722 case PKCS12: 1723 builder.setDefault(LocalizableMessage.raw(String.valueOf(PKCS12)), 1724 MenuResult.success(PKCS12)); 1725 break; 1726 default: 1727 builder.setDefault(LocalizableMessage.raw(String.valueOf(SELF_SIGNED)), 1728 MenuResult.success(SELF_SIGNED)); 1729 } 1730 } 1731 1732 final Menu<Integer> menu = builder.toMenu(); 1733 int certType; 1734 try 1735 { 1736 final MenuResult<Integer> m = menu.run(); 1737 if (m.isSuccess()) 1738 { 1739 certType = m.getValue(); 1740 } 1741 else 1742 { 1743 // Should never happen. 1744 throw new RuntimeException(); 1745 } 1746 } 1747 catch (final ClientException ce) 1748 { 1749 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1750 certType = SELF_SIGNED; 1751 } 1752 if (certType == SELF_SIGNED) 1753 { 1754 securityOptions = SecurityOptions.createSelfSignedCertificateOptions( 1755 enableSSL, enableStartTLS, ldapsPort); 1756 } 1757 else if (certType == JKS) 1758 { 1759 securityOptions = 1760 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS, 1761 enableSSL, enableStartTLS, ldapsPort); 1762 } 1763 else if (certType == JCEKS) 1764 { 1765 securityOptions = 1766 createSecurityOptionsPrompting( 1767 SecurityOptions.CertificateType.JCEKS, 1768 enableSSL, enableStartTLS, ldapsPort); 1769 } 1770 else if (certType == PKCS12) 1771 { 1772 securityOptions = 1773 createSecurityOptionsPrompting( 1774 SecurityOptions.CertificateType.PKCS12, enableSSL, 1775 enableStartTLS, ldapsPort); 1776 } 1777 else if (certType == PKCS11) 1778 { 1779 securityOptions = 1780 createSecurityOptionsPrompting( 1781 SecurityOptions.CertificateType.PKCS11, enableSSL, 1782 enableStartTLS, ldapsPort); 1783 } 1784 else 1785 { 1786 throw new IllegalStateException("Unexpected cert type: "+ certType); 1787 } 1788 } 1789 return securityOptions; 1790 } 1791 1792 /** 1793 * This method returns what the user specified in the command-line for the 1794 * Windows Service parameters. If the user did not provide explicitly the 1795 * data, it prompts the user to provide it. 1796 * 1797 * @return whether windows service should be enabled 1798 */ 1799 private boolean promptIfRequiredForWindowsService() 1800 { 1801 boolean enableService = false; 1802 // If we are in Windows ask if the server must run as a windows service. 1803 if (isWindows()) 1804 { 1805 if (argParser.enableWindowsServiceArg.isPresent()) 1806 { 1807 enableService = true; 1808 } 1809 else 1810 { 1811 println(); 1812 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_ENABLE_SERVICE.get(); 1813 try 1814 { 1815 final boolean defaultValue = (lastResetEnableWindowsService == null) ? 1816 false : lastResetEnableWindowsService; 1817 enableService = confirmAction(message, defaultValue); 1818 } 1819 catch (final ClientException ce) 1820 { 1821 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1822 } 1823 } 1824 } 1825 return enableService; 1826 } 1827 1828 /** 1829 * This method returns what the user specified in the command-line for the 1830 * Directory Manager parameters. If the user did not provide explicitly the 1831 * data, it prompts the user to provide it. 1832 * 1833 * @return whether server should be started 1834 */ 1835 private boolean promptIfRequiredForStartServer() 1836 { 1837 boolean startServer = false; 1838 if (!argParser.doNotStartArg.isPresent()) 1839 { 1840 println(); 1841 final LocalizableMessage message = INFO_INSTALLDS_PROMPT_START_SERVER.get(); 1842 try 1843 { 1844 final boolean defaultValue = (lastResetStartServer == null) ? 1845 true : lastResetStartServer; 1846 startServer = confirmAction(message, defaultValue); 1847 } 1848 catch (final ClientException ce) 1849 { 1850 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 1851 startServer = true; 1852 } 1853 } 1854 return startServer; 1855 } 1856 1857 /** 1858 * Checks that the provided parameters are valid to access an existing key 1859 * store. This method adds the encountered errors to the provided list of 1860 * LocalizableMessage. It also adds the alias (nicknames) found to the 1861 * provided list of String. 1862 * 1863 * @param type 1864 * the type of key store. 1865 * @param path 1866 * the path of the key store. 1867 * @param pwd 1868 * the password (PIN) to access the key store. 1869 * @param certNicknames 1870 * the certificate nicknames that we are looking for (or null if we 1871 * just one to get the one that is in the key store). 1872 * @param errorMessages 1873 * the list that will be updated with the errors encountered. 1874 * @param nicknameList 1875 * the list that will be updated with the nicknames found in the key 1876 * store. 1877 */ 1878 public static void checkCertificateInKeystore(SecurityOptions.CertificateType type, String path, String pwd, 1879 Collection<String> certNicknames, Collection<LocalizableMessage> errorMessages, Collection<String> nicknameList) 1880 { 1881 boolean errorWithPath = false; 1882 if (type != SecurityOptions.CertificateType.PKCS11) 1883 { 1884 final File f = new File(path); 1885 if (!f.exists()) 1886 { 1887 errorMessages.add(INFO_KEYSTORE_PATH_DOES_NOT_EXIST.get()); 1888 errorWithPath = true; 1889 } 1890 else if (!f.isFile()) 1891 { 1892 errorMessages.add(INFO_KEYSTORE_PATH_NOT_A_FILE.get()); 1893 errorWithPath = true; 1894 } 1895 } 1896 if (!errorWithPath) 1897 { 1898 try 1899 { 1900 CertificateManager certManager; 1901 switch (type) 1902 { 1903 case JKS: 1904 certManager = new CertificateManager( 1905 path, 1906 CertificateManager.KEY_STORE_TYPE_JKS, 1907 pwd); 1908 break; 1909 1910 case JCEKS: 1911 certManager = new CertificateManager( 1912 path, 1913 CertificateManager.KEY_STORE_TYPE_JCEKS, 1914 pwd); 1915 break; 1916 1917 case PKCS12: 1918 certManager = new CertificateManager( 1919 path, 1920 CertificateManager.KEY_STORE_TYPE_PKCS12, 1921 pwd); 1922 break; 1923 1924 case PKCS11: 1925 certManager = new CertificateManager( 1926 CertificateManager.KEY_STORE_PATH_PKCS11, 1927 CertificateManager.KEY_STORE_TYPE_PKCS11, 1928 pwd); 1929 break; 1930 1931 default: 1932 throw new IllegalArgumentException("Invalid type: "+type); 1933 } 1934 final String[] aliases = certManager.getCertificateAliases(); 1935 if (aliases == null || aliases.length == 0) 1936 { 1937 // Could not retrieve any certificate 1938 switch (type) 1939 { 1940 case JKS: 1941 errorMessages.add(INFO_JKS_KEYSTORE_DOES_NOT_EXIST.get()); 1942 break; 1943 case JCEKS: 1944 errorMessages.add(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST.get()); 1945 break; 1946 case PKCS12: 1947 errorMessages.add(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST.get()); 1948 break; 1949 case PKCS11: 1950 errorMessages.add(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST.get()); 1951 break; 1952 default: 1953 throw new IllegalArgumentException("Invalid type: "+type); 1954 } 1955 } 1956 else if (certManager.hasRealAliases()) 1957 { 1958 Collections.addAll(nicknameList, aliases); 1959 final String aliasString = joinAsString(", ", nicknameList); 1960 if (certNicknames.isEmpty() && aliases.length > 1) 1961 { 1962 errorMessages.add(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME.get(aliasString)); 1963 } 1964 for (String certNickname : certNicknames) 1965 { 1966 // Check if the certificate alias is in the list. 1967 boolean found = false; 1968 for (int i = 0; i < aliases.length && !found; i++) 1969 { 1970 found = aliases[i].equalsIgnoreCase(certNickname); 1971 } 1972 if (!found) 1973 { 1974 errorMessages.add(ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND.get(aliasString)); 1975 } 1976 } 1977 } 1978 } 1979 catch (final KeyStoreException ke) 1980 { 1981 // issue OPENDJ-18, related to JDK bug 1982 if (StaticUtils.stackTraceContainsCause(ke, ArithmeticException.class)) 1983 { 1984 errorMessages.add(INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG.get()); 1985 } 1986 else 1987 { 1988 // Could not access to the key store: because the password is no good, 1989 // because the provided file is not a valid key store, etc. 1990 switch (type) 1991 { 1992 case JKS: 1993 errorMessages.add(INFO_ERROR_ACCESSING_JKS_KEYSTORE.get()); 1994 break; 1995 case JCEKS: 1996 errorMessages.add(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE.get()); 1997 break; 1998 case PKCS12: 1999 errorMessages.add(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE.get()); 2000 break; 2001 case PKCS11: 2002 errorMessages.add(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE.get()); 2003 break; 2004 default: 2005 throw new IllegalArgumentException("Invalid type: " + type, ke); 2006 } 2007 } 2008 } 2009 } 2010 } 2011 2012 /** 2013 * Creates a SecurityOptions object that corresponds to the provided 2014 * parameters. If the parameters are not valid, it prompts the user to provide 2015 * them. 2016 * 2017 * @param type 2018 * the keystore type. 2019 * @param enableSSL 2020 * whether to enable SSL or not. 2021 * @param enableStartTLS 2022 * whether to enable StartTLS or not. 2023 * @param ldapsPort 2024 * the LDAPS port to use. 2025 * @return a SecurityOptions object that corresponds to the provided 2026 * parameters (or to what the user provided after being prompted). 2027 * @throws UserDataException 2028 * if the user did not manage to provide the keystore password after 2029 * a certain number of tries. 2030 * @throws ClientException 2031 */ 2032 private SecurityOptions createSecurityOptionsPrompting(SecurityOptions.CertificateType type, boolean enableSSL, 2033 boolean enableStartTLS, int ldapsPort) throws UserDataException, ClientException 2034 { 2035 SecurityOptions securityOptions; 2036 String path; 2037 Collection<String> certNicknames = argParser.certNicknameArg.getValues(); 2038 String pwd = argParser.getKeyStorePassword(); 2039 if (pwd != null && pwd.length() == 0) 2040 { 2041 pwd = null; 2042 } 2043 LocalizableMessage pathPrompt; 2044 String defaultPathValue; 2045 2046 switch (type) 2047 { 2048 case JKS: 2049 path = argParser.useJavaKeyStoreArg.getValue(); 2050 pathPrompt = INFO_INSTALLDS_PROMPT_JKS_PATH.get(); 2051 defaultPathValue = argParser.useJavaKeyStoreArg.getValue(); 2052 if (defaultPathValue == null) 2053 { 2054 defaultPathValue = lastResetKeyStorePath; 2055 } 2056 break; 2057 case JCEKS: 2058 path = argParser.useJCEKSArg.getValue(); 2059 pathPrompt = INFO_INSTALLDS_PROMPT_JCEKS_PATH.get(); 2060 defaultPathValue = argParser.useJCEKSArg.getValue(); 2061 if (defaultPathValue == null) 2062 { 2063 defaultPathValue = lastResetKeyStorePath; 2064 } 2065 break; 2066 case PKCS11: 2067 path = null; 2068 defaultPathValue = null; 2069 pathPrompt = null; 2070 break; 2071 case PKCS12: 2072 path = argParser.usePkcs12Arg.getValue(); 2073 defaultPathValue = argParser.usePkcs12Arg.getValue(); 2074 if (defaultPathValue == null) 2075 { 2076 defaultPathValue = lastResetKeyStorePath; 2077 } 2078 pathPrompt = INFO_INSTALLDS_PROMPT_PKCS12_PATH.get(); 2079 break; 2080 default: 2081 throw new IllegalStateException( 2082 "Called promptIfRequiredCertificate with invalid type: "+type); 2083 } 2084 final List<LocalizableMessage> errorMessages = new LinkedList<>(); 2085 final LinkedList<String> keystoreAliases = new LinkedList<>(); 2086 boolean firstTry = true; 2087 int nPasswordPrompts = 0; 2088 2089 while (!errorMessages.isEmpty() || firstTry) 2090 { 2091 boolean prompted = false; 2092 if (!errorMessages.isEmpty()) 2093 { 2094 println(); 2095 println(Utils.getMessageFromCollection(errorMessages, 2096 formatter.getLineBreak().toString())); 2097 } 2098 2099 if (type != SecurityOptions.CertificateType.PKCS11 2100 && (containsKeyStorePathErrorMessage(errorMessages) || path == null)) 2101 { 2102 println(); 2103 try 2104 { 2105 path = readInput(pathPrompt, defaultPathValue); 2106 } 2107 catch (final ClientException ce) 2108 { 2109 path = ""; 2110 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2111 } 2112 2113 prompted = true; 2114 if (pwd != null) 2115 { 2116 errorMessages.clear(); 2117 keystoreAliases.clear(); 2118 checkCertificateInKeystore(type, path, pwd, certNicknames, errorMessages, keystoreAliases); 2119 if (!errorMessages.isEmpty()) 2120 { 2121 // Reset password: this might be a new keystore 2122 pwd = null; 2123 } 2124 } 2125 } 2126 if (containsKeyStorePasswordErrorMessage(errorMessages) || pwd == null) 2127 { 2128 if (!prompted) 2129 { 2130 println(); 2131 } 2132 pwd = null; 2133 while (pwd == null) 2134 { 2135 if (nPasswordPrompts > LIMIT_KEYSTORE_PASSWORD_PROMPT) 2136 { 2137 throw new UserDataException(null, 2138 ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES.get(LIMIT_KEYSTORE_PASSWORD_PROMPT)); 2139 } 2140 pwd = String.valueOf(readPassword(INFO_INSTALLDS_PROMPT_KEYSTORE_PASSWORD.get())); 2141 nPasswordPrompts ++; 2142 } 2143 } 2144 if (containsCertNicknameErrorMessage(errorMessages)) 2145 { 2146 if (!prompted) 2147 { 2148 println(); 2149 } 2150 certNicknames = promptForCertificateNickname(keystoreAliases); 2151 } 2152 errorMessages.clear(); 2153 keystoreAliases.clear(); 2154 checkCertificateInKeystore(type, path, pwd, certNicknames, errorMessages, 2155 keystoreAliases); 2156 firstTry = false; 2157 } 2158 if (certNicknames.isEmpty() && !keystoreAliases.isEmpty()) 2159 { 2160 certNicknames = Arrays.asList(keystoreAliases.getFirst()); 2161 } 2162 switch (type) 2163 { 2164 case JKS: 2165 return SecurityOptions.createJKSCertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, 2166 certNicknames); 2167 case JCEKS: 2168 return SecurityOptions.createJCEKSCertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, 2169 certNicknames); 2170 case PKCS12: 2171 return SecurityOptions.createPKCS12CertificateOptions(path, pwd, enableSSL, enableStartTLS, ldapsPort, 2172 certNicknames); 2173 case PKCS11: 2174 return SecurityOptions.createPKCS11CertificateOptions(pwd, enableSSL, enableStartTLS, ldapsPort, certNicknames); 2175 default: 2176 throw new IllegalStateException("Called createSecurityOptionsPrompting with invalid type: " + type); 2177 } 2178 } 2179 2180 /** 2181 * Tells if any of the error messages provided corresponds to a problem with 2182 * the key store path. 2183 * 2184 * @param msgs 2185 * the messages to analyze. 2186 * @return <CODE>true</CODE> if any of the error messages provided corresponds 2187 * to a problem with the key store path and <CODE>false</CODE> 2188 * otherwise. 2189 */ 2190 public static boolean containsKeyStorePathErrorMessage(Collection<LocalizableMessage> msgs) 2191 { 2192 for (final LocalizableMessage msg : msgs) 2193 { 2194 if (StaticUtils.hasDescriptor(msg, INFO_KEYSTORE_PATH_DOES_NOT_EXIST) || 2195 StaticUtils.hasDescriptor(msg, INFO_KEYSTORE_PATH_NOT_A_FILE) || 2196 StaticUtils.hasDescriptor(msg, INFO_JKS_KEYSTORE_DOES_NOT_EXIST) || 2197 StaticUtils.hasDescriptor(msg, INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) || 2198 StaticUtils.hasDescriptor(msg, INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) || 2199 StaticUtils.hasDescriptor(msg, INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) || 2200 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JKS_KEYSTORE) || 2201 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) || 2202 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) || 2203 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS11_KEYSTORE)) 2204 { 2205 return true; 2206 } 2207 } 2208 return false; 2209 } 2210 2211 /** 2212 * Tells if any of the error messages provided corresponds to a problem with 2213 * the key store password. 2214 * 2215 * @param msgs 2216 * the messages to analyze. 2217 * @return <CODE>true</CODE> if any of the error messages provided corresponds 2218 * to a problem with the key store password and <CODE>false</CODE> 2219 * otherwise. 2220 */ 2221 public static boolean containsKeyStorePasswordErrorMessage(Collection<LocalizableMessage> msgs) 2222 { 2223 for (final LocalizableMessage msg : msgs) 2224 { 2225 if (StaticUtils.hasDescriptor(msg, INFO_JKS_KEYSTORE_DOES_NOT_EXIST) || 2226 StaticUtils.hasDescriptor(msg, INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST) || 2227 StaticUtils.hasDescriptor(msg, INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) || 2228 StaticUtils.hasDescriptor(msg, INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) || 2229 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JKS_KEYSTORE) || 2230 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_JCEKS_KEYSTORE) || 2231 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) || 2232 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_PKCS11_KEYSTORE) || 2233 StaticUtils.hasDescriptor(msg, INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG)) 2234 { 2235 return true; 2236 } 2237 } 2238 return false; 2239 } 2240 2241 /** 2242 * Tells if any of the error messages provided corresponds to a problem with 2243 * the certificate nickname. 2244 * 2245 * @param msgs 2246 * the messages to analyze. 2247 * @return <CODE>true</CODE> if any of the error messages provided corresponds 2248 * to a problem with the certificate nickname and <CODE>false</CODE> 2249 * otherwise. 2250 */ 2251 public static boolean containsCertNicknameErrorMessage( 2252 Collection<LocalizableMessage> msgs) 2253 { 2254 boolean found = false; 2255 for (final LocalizableMessage msg : msgs) 2256 { 2257 if (StaticUtils.hasDescriptor(msg, ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND) || 2258 StaticUtils.hasDescriptor(msg, ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME)) 2259 { 2260 found = true; 2261 break; 2262 } 2263 } 2264 return found; 2265 } 2266 2267 /** 2268 * Interactively prompts (on standard output) the user to provide an integer 2269 * value. The answer provided must be parseable as an integer, and may be 2270 * required to be within a given set of bounds. It will keep prompting until 2271 * an acceptable value is given. 2272 * 2273 * @param prompt 2274 * The prompt to present to the user. 2275 * @param defaultValue 2276 * The default value to assume if the user presses ENTER without 2277 * typing anything, or <CODE>null</CODE> if there should not be a 2278 * default and the user must explicitly provide a value. 2279 * @param lowerBound 2280 * The lower bound that should be enforced, or <CODE>null</CODE> if 2281 * there is none. 2282 * @param upperBound 2283 * The upper bound that should be enforced, or <CODE>null</CODE> if 2284 * there is none. 2285 * @return The <CODE>int</CODE> value read from the user input. 2286 */ 2287 private int promptForInteger(LocalizableMessage prompt, Integer defaultValue, Integer lowerBound, Integer upperBound) 2288 { 2289 int returnValue = -1; 2290 while (returnValue == -1) 2291 { 2292 String s; 2293 try 2294 { 2295 s = readInput(prompt, String.valueOf(defaultValue)); 2296 } 2297 catch (final ClientException ce) 2298 { 2299 s = ""; 2300 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2301 } 2302 if ("".equals(s)) 2303 { 2304 if (defaultValue == null) 2305 { 2306 println(ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get()); 2307 println(); 2308 } 2309 else 2310 { 2311 returnValue = defaultValue; 2312 } 2313 } 2314 else 2315 { 2316 try 2317 { 2318 final int intValue = Integer.parseInt(s); 2319 if (lowerBound != null && intValue < lowerBound) 2320 { 2321 println(ERR_INSTALLDS_INTEGER_BELOW_LOWER_BOUND.get(lowerBound)); 2322 println(); 2323 } 2324 else if (upperBound != null && intValue > upperBound) 2325 { 2326 println(ERR_INSTALLDS_INTEGER_ABOVE_UPPER_BOUND.get(upperBound)); 2327 println(); 2328 } 2329 else 2330 { 2331 returnValue = intValue; 2332 } 2333 } 2334 catch (final NumberFormatException nfe) 2335 { 2336 println(ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get()); 2337 println(); 2338 } 2339 } 2340 } 2341 return returnValue; 2342 } 2343 2344 /** 2345 * Prompts the user to accept on the certificates that appears on the list and 2346 * returns the chosen certificate nickname. 2347 * 2348 * @param nicknames 2349 * the list of certificates the user must choose from. 2350 * @return the chosen certificate nickname. 2351 */ 2352 private Collection<String> promptForCertificateNickname(List<String> nicknames) 2353 { 2354 Collection<String> choosenNicknames = new ArrayList<>(); 2355 while (choosenNicknames.isEmpty()) 2356 { 2357 for (final String n : nicknames) 2358 { 2359 try 2360 { 2361 if (confirmAction(INFO_INSTALLDS_PROMPT_CERTNICKNAME.get(n), true)) 2362 { 2363 choosenNicknames.add(n); 2364 } 2365 } 2366 catch (final ClientException ce) 2367 { 2368 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2369 } 2370 } 2371 } 2372 return choosenNicknames; 2373 } 2374 2375 /** 2376 * It displays the information provided by the user. 2377 * 2378 * @param uData 2379 * the UserData that the user provided. 2380 */ 2381 private void printSummary(UserData uData) 2382 { 2383 println(); 2384 println(); 2385 println(INFO_INSTALLDS_SUMMARY.get()); 2386 final LocalizableMessage[] labels = 2387 { 2388 INFO_SERVER_PORT_LABEL.get(), 2389 INFO_ADMIN_CONNECTOR_PORT_LABEL.get(), 2390 INFO_INSTALLDS_SERVER_JMXPORT_LABEL.get(), 2391 INFO_SERVER_SECURITY_LABEL.get(), 2392 INFO_SERVER_DIRECTORY_MANAGER_DN_LABEL.get(), 2393 INFO_DIRECTORY_DATA_LABEL.get() 2394 }; 2395 2396 final int jmxPort = uData.getServerJMXPort(); 2397 2398 final LocalizableMessage[] values = 2399 { 2400 LocalizableMessage.raw(String.valueOf(uData.getServerPort())), 2401 LocalizableMessage.raw(String.valueOf(uData.getAdminConnectorPort())), 2402 LocalizableMessage.raw(jmxPort != -1 ? String.valueOf(jmxPort) : ""), 2403 LocalizableMessage.raw( 2404 Utils.getSecurityOptionsString(uData.getSecurityOptions(), false)), 2405 LocalizableMessage.raw(uData.getDirectoryManagerDn()), 2406 LocalizableMessage.raw(Utils.getDataDisplayString(uData)), 2407 }; 2408 int maxWidth = 0; 2409 for (final LocalizableMessage l : labels) 2410 { 2411 maxWidth = Math.max(maxWidth, l.length()); 2412 } 2413 2414 for (int i=0; i<labels.length; i++) 2415 { 2416 StringBuilder sb = new StringBuilder(); 2417 if (values[i] != null) 2418 { 2419 final LocalizableMessage l = labels[i]; 2420 sb.append(l).append(" "); 2421 2422 final String[] lines = values[i].toString().split(Constants.LINE_SEPARATOR); 2423 for (int j=0; j<lines.length; j++) 2424 { 2425 if (j != 0) 2426 { 2427 for (int k=0; k <= maxWidth; k++) 2428 { 2429 sb.append(" "); 2430 } 2431 } 2432 else 2433 { 2434 for (int k=0; k<maxWidth - l.length(); k++) 2435 { 2436 sb.append(" "); 2437 } 2438 } 2439 sb.append(lines[j]); 2440 println(LocalizableMessage.raw(sb)); 2441 sb = new StringBuilder(); 2442 } 2443 } 2444 } 2445 2446 println(); 2447 if (uData.getStartServer()) 2448 { 2449 println(INFO_INSTALLDS_START_SERVER.get()); 2450 } 2451 else 2452 { 2453 println(INFO_INSTALLDS_DO_NOT_START_SERVER.get()); 2454 } 2455 2456 if (isWindows()) 2457 { 2458 if (uData.getEnableWindowsService()) 2459 { 2460 println(INFO_INSTALLDS_ENABLE_WINDOWS_SERVICE.get()); 2461 } 2462 else 2463 { 2464 println(INFO_INSTALLDS_DO_NOT_ENABLE_WINDOWS_SERVICE.get()); 2465 } 2466 } 2467 } 2468 2469 private void printEquivalentCommandLine(UserData uData) 2470 { 2471 println(); 2472 2473 println(INFO_INSTALL_SETUP_EQUIVALENT_COMMAND_LINE.get()); 2474 println(); 2475 final List<String> cmd = Utils.getSetupEquivalentCommandLine(uData); 2476 println(LocalizableMessage.raw(Utils.getFormattedEquivalentCommandLine(cmd, formatter))); 2477 } 2478 2479 /** 2480 * This method asks the user to confirm to continue the setup. It basically 2481 * displays the information provided by the user and at the end proposes a 2482 * menu with the different options to choose from. 2483 * 2484 * @return the answer provided by the user: cancel setup, continue setup or 2485 * provide information again. 2486 */ 2487 private ConfirmCode askForConfirmation() 2488 { 2489 ConfirmCode returnValue; 2490 2491 println(); 2492 println(); 2493 2494 final LocalizableMessage[] msgs = new LocalizableMessage[] { 2495 INFO_INSTALLDS_CONFIRM_INSTALL.get(), 2496 INFO_INSTALLDS_PROVIDE_DATA_AGAIN.get(), 2497 INFO_INSTALLDS_PRINT_EQUIVALENT_COMMAND_LINE.get(), 2498 INFO_INSTALLDS_CANCEL.get() 2499 }; 2500 2501 final MenuBuilder<ConfirmCode> builder = new MenuBuilder<>(this); 2502 builder.setPrompt(INFO_INSTALLDS_CONFIRM_INSTALL_PROMPT.get()); 2503 2504 int i=0; 2505 for (final ConfirmCode code : ConfirmCode.values()) 2506 { 2507 builder.addNumberedOption(msgs[i], MenuResult.success(code)); 2508 i++; 2509 } 2510 2511 builder.setDefault(LocalizableMessage.raw( 2512 String.valueOf(ConfirmCode.CONTINUE.getReturnCode())), 2513 MenuResult.success(ConfirmCode.CONTINUE)); 2514 2515 final Menu<ConfirmCode> menu = builder.toMenu(); 2516 2517 try 2518 { 2519 final MenuResult<ConfirmCode> m = menu.run(); 2520 if (m.isSuccess()) 2521 { 2522 returnValue = m.getValue(); 2523 } 2524 else 2525 { 2526 // Should never happen. 2527 throw new RuntimeException(); 2528 } 2529 } 2530 catch (final ClientException ce) 2531 { 2532 returnValue = ConfirmCode.CANCEL; 2533 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2534 } 2535 return returnValue; 2536 } 2537 2538 private void resetArguments(UserData uData) 2539 { 2540 argParser = new InstallDSArgumentParser(InstallDS.class.getName()); 2541 try 2542 { 2543 argParser.initializeArguments(); 2544 argParser.directoryManagerDNArg.setDefaultValue(uData.getDirectoryManagerDn()); 2545 argParser.ldapPortArg.setDefaultValue(String.valueOf(uData.getServerPort())); 2546 argParser.adminConnectorPortArg.setDefaultValue(String.valueOf(uData.getAdminConnectorPort())); 2547 2548 final int jmxPort = uData.getServerJMXPort(); 2549 if (jmxPort != -1) 2550 { 2551 argParser.jmxPortArg.setDefaultValue(String.valueOf(jmxPort)); 2552 } 2553 2554 final LinkedList<String> baseDNs = uData.getNewSuffixOptions().getBaseDns(); 2555 if (!baseDNs.isEmpty()) 2556 { 2557 argParser.baseDNArg.setDefaultValue(baseDNs.getFirst()); 2558 } 2559 2560 final NewSuffixOptions suffixOptions = uData.getNewSuffixOptions(); 2561 lastResetPopulateOption = suffixOptions.getType(); 2562 2563 if (NewSuffixOptions.Type.IMPORT_AUTOMATICALLY_GENERATED_DATA == lastResetPopulateOption) 2564 { 2565 lastResetNumEntries = suffixOptions.getNumberEntries(); 2566 } 2567 else if (NewSuffixOptions.Type.IMPORT_FROM_LDIF_FILE == lastResetPopulateOption) 2568 { 2569 lastResetImportFile = suffixOptions.getLDIFPaths().getFirst(); 2570 lastResetRejectedFile = suffixOptions.getRejectedFile(); 2571 lastResetSkippedFile = suffixOptions.getSkippedFile(); 2572 } 2573 2574 final SecurityOptions sec = uData.getSecurityOptions(); 2575 if (sec.getEnableSSL()) 2576 { 2577 argParser.ldapsPortArg.setDefaultValue(String.valueOf(sec.getSslPort())); 2578 } 2579 lastResetEnableSSL = sec.getEnableSSL(); 2580 lastResetEnableStartTLS = sec.getEnableStartTLS(); 2581 lastResetCertType = sec.getCertificateType(); 2582 if (SecurityOptions.CertificateType.JKS == lastResetCertType 2583 || SecurityOptions.CertificateType.JCEKS == lastResetCertType 2584 || SecurityOptions.CertificateType.PKCS12 == lastResetCertType) 2585 { 2586 lastResetKeyStorePath = sec.getKeystorePath(); 2587 } 2588 else 2589 { 2590 lastResetKeyStorePath = null; 2591 } 2592 2593 lastResetEnableWindowsService = uData.getEnableWindowsService(); 2594 lastResetStartServer = uData.getStartServer(); 2595 lastResetBackendType = uData.getBackendType(); 2596 } 2597 catch (final Throwable t) 2598 { 2599 logger.warn(LocalizableMessage.raw("Error resetting arguments: " + t, t)); 2600 } 2601 } 2602 2603 private String promptForHostNameIfRequired() 2604 { 2605 String hostName = null; 2606 if (argParser.hostNameArg.isPresent()) 2607 { 2608 hostName = argParser.hostNameArg.getValue(); 2609 } 2610 else 2611 { 2612 println(); 2613 while (hostName == null) 2614 { 2615 try 2616 { 2617 hostName = readInput(INFO_INSTALLDS_PROMPT_HOST_NAME.get(), argParser.hostNameArg.getDefaultValue()); 2618 } 2619 catch (final ClientException ce) 2620 { 2621 logger.warn(LocalizableMessage.raw("Error reading input: "+ce, ce)); 2622 } 2623 } 2624 } 2625 return hostName; 2626 } 2627 2628 /** 2629 * Returns the timeout to be used to connect in milliseconds. The method must 2630 * be called after parsing the arguments. 2631 * 2632 * @return the timeout to be used to connect in milliseconds. Returns 2633 * {@code 0} if there is no timeout. 2634 */ 2635 private int getConnectTimeout() 2636 { 2637 return argParser.getConnectTimeout(); 2638 } 2639 2640}