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