mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

kenneth_suter
07.38.2007 1735326f04db3fd21b50ef3cf3949ccc135c538f
separated CLI code from context creation for reuse
1 files added
2 files modified
1700 ■■■■■ changed files
opends/src/messages/messages/utility.properties 29 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java 776 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java 895 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/utility.properties
@@ -503,4 +503,31 @@
 it was rejected by a plugin
SEVERE_ERR_LDIF_REJECTED_BY_PLUGIN_225=Rejecting entry %s because it was \
 rejected by a plugin:  %s
INFO_LDAP_CONN_PROMPT_SECURITY_LDAP_226=LDAP
INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL_227=LDAP with SSL
INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TSL_228=LDAP with StartTSL
INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL_229=Do you want to automatically \
  trust the server certificate?
INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH_230=Truststore path:
INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD_231=Password for \
  truststore '%s':
INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_NEEDED_232=Do you want to perform \
  secure authentication (client side authentication)?
INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH_233=Keystore path:
INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD_234=Password for keystore \
  '%s':
INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_NAME_235=Certificate nickname:
INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS_236=>>>> Specify OpenDS LDAP \
  connection parameters
SEVERE_ERR_LDAP_CONN_BAD_HOST_NAME_237=The hostname "%s" could not be \
  resolved. Please check you have have the correct address
SEVERE_ERR_LDAP_CONN_BAD_PORT_NUMBER_238=Invalid port number "%s". Please \
  enter a valid port number between 1 and 65535
INFO_LDAP_CONN_PROMPT_HOST_NAME_239=Directory server hostname or IP address \
  [%s]:
INFO_LDAP_CONN_PROMPT_PORT_NUMBER_240=Directory server port number [%d]:
INFO_LDAP_CONN_PROMPT_BIND_DN_241=Administrator user bind DN [%s]:
INFO_LDAP_CONN_PROMPT_SECURITY_USE_SECURE_CTX_242=How do you want to connect?
INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE_243=%d
SEVERE_ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH_244=The provided path \
  is not valid
opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java
@@ -27,24 +27,8 @@
package org.opends.server.tools.dsconfig;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import java.io.File;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.util.LinkedHashSet;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import org.opends.admin.ads.util.ApplicationKeyManager;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.admin.ads.util.ConnectionUtils;
import static org.opends.messages.DSConfigMessages.*;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.client.AuthenticationException;
@@ -58,17 +42,17 @@
import org.opends.server.admin.client.ldap.LDAPManagementContext;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.ClientException;
import org.opends.server.util.SelectableCertificateKeyManager;
import org.opends.server.util.args.Argument;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.Menu;
import org.opends.server.util.cli.MenuBuilder;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.cli.ValidationCallback;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import java.util.LinkedHashSet;
/**
@@ -83,60 +67,6 @@
  // The management context.
  private ManagementContext context = null;
  // Indicate if we need to display the heading
  private boolean isHeadingDisplayed = false;
  // the Console application
  private ConsoleApplication app;
  /**
   * Enumeration description protocols for interactive CLI choices.
   */
  private enum Protocols
  {
    LDAP(1, INFO_DSCFG_PROMPT_SECURITY_LDAP.get()), SSL(2,
        INFO_DSCFG_PROMPT_SECURITY_USE_SSL.get()), START_TSL(3,
        INFO_DSCFG_PROMPT_SECURITY_USE_START_TSL.get());
    private Integer choice;
    private Message msg;
    /**
     * Private constructor.
     *
     * @param i
     *          the menu return value.
     * @param s
     *          the message message.
     */
    private Protocols(int i, Message msg)
    {
      choice = new Integer(i);
      this.msg = msg;
    }
    /**
     * Returns the choice number.
     *
     * @return the attribute name.
     */
    public Integer getChoice()
    {
      return choice;
    }
    /**
     * Return the menu message.
     *
     * @return the menu message.
     */
    public Message getMenuMessage()
    {
      return msg;
    }
  };
  /**
   * Creates a new LDAP management context factory.
   */
@@ -153,347 +83,23 @@
    // Lazily create the LDAP management context.
    if (context == null)
    {
      this.app = app;
      isHeadingDisplayed = false;
      boolean secureConnection =
        (
            secureArgsList.useSSLArg.isPresent()
            ||
            secureArgsList.useStartTLSArg.isPresent()
            ||
            secureArgsList.trustAllArg.isPresent()
            ||
            secureArgsList.trustStorePathArg.isPresent()
            ||
            secureArgsList.trustStorePasswordArg.isPresent()
            ||
            secureArgsList.trustStorePasswordFileArg.isPresent()
            ||
            secureArgsList.keyStorePathArg.isPresent()
            ||
            secureArgsList.keyStorePasswordArg.isPresent()
            ||
            secureArgsList.keyStorePasswordFileArg.isPresent()
        );
      // Get the LDAP host.
      String hostName = secureArgsList.hostNameArg.getValue();
      final String tmpHostName = hostName;
      if (app.isInteractive() && !secureArgsList.hostNameArg.isPresent())
      {
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        ValidationCallback<String> callback = new ValidationCallback<String>()
        {
          public String validate(ConsoleApplication app, String input)
              throws CLIException
          {
            String ninput = input.trim();
            if (ninput.length() == 0)
            {
              return tmpHostName;
            }
            else
            {
              try
              {
                InetAddress.getByName(ninput);
                return ninput;
              }
              catch (UnknownHostException e)
              {
                // Try again...
                app.println();
                app.println(ERR_DSCFG_BAD_HOST_NAME.get(ninput));
                app.println();
                return null;
              }
            }
          }
        };
        try
        {
          app.println();
          hostName = app.readValidatedInput(INFO_DSCFG_PROMPT_HOST_NAME
              .get(hostName), callback);
        }
        catch (CLIException e)
        {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
      boolean useSSL = secureArgsList.useSSL();
      boolean useStartTLS = secureArgsList.useStartTLS();
      KeyManager keyManager = null;
      TrustManager trustManager = null;
      boolean connectionTypeIsSet =
        (
          secureArgsList.useSSLArg.isPresent()
          ||
          secureArgsList.useStartTLSArg.isPresent()
        );
      if (app.isInteractive() && !connectionTypeIsSet)
      {
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
        builder.setPrompt(INFO_DSCFG_PROMPT_SECURITY_USE_SECURE_CTX.get());
        Protocols defaultProtocol ;
        if (secureConnection)
        {
          defaultProtocol = Protocols.SSL;
        }
        else
        {
          defaultProtocol = Protocols.LDAP;
        }
        for (Protocols p : Protocols.values())
        {
          if (secureConnection && p.equals(Protocols.LDAP))
          {
            continue ;
          }
          int i = builder.addNumberedOption(p.getMenuMessage(), MenuResult
              .success(p.getChoice()));
          if (p.equals(defaultProtocol))
          {
            builder.setDefault(
                INFO_DSCFG_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
                    .get(new Integer(i)), MenuResult.success(p.getChoice()));
          }
        }
        Menu<Integer> menu = builder.toMenu();
        try
        {
          MenuResult<Integer> result = menu.run();
          if (result.isSuccess())
          {
            if (result.getValue().equals(Protocols.SSL.getChoice()))
            {
              useSSL = true;
            }
            else if (result.getValue()
                .equals(Protocols.START_TSL.getChoice()))
            {
              useStartTLS = true;
            }
          }
          else
          {
            // Should never happen.
            throw new RuntimeException();
          }
        }
        catch (CLIException e)
        {
          throw new RuntimeException(e);
        }
      }
      if (useSSL || useStartTLS)
      {
        // Get truststore info
        trustManager = getTrustManager();
        // Check if we need client side authentication
        keyManager = getKeyManager();
      }
      // Get the LDAP port.
      int portNumber;
      if (!useSSL)
      {
        portNumber = secureArgsList.portArg.getIntValue();
      }
      else
      {
        if (secureArgsList.portArg.isPresent())
        {
          portNumber = secureArgsList.portArg.getIntValue();
        }
        else
        {
          portNumber = 636;
        }
      }
      final int tmpPortNumber = portNumber;
      if (app.isInteractive() && !secureArgsList.portArg.isPresent())
      {
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
        {
          public Integer validate(ConsoleApplication app, String input)
              throws CLIException
          {
            String ninput = input.trim();
            if (ninput.length() == 0)
            {
              return tmpPortNumber;
            }
            else
            {
              try
              {
                int i = Integer.parseInt(ninput);
                if (i < 1 || i > 65535)
                {
                  throw new NumberFormatException();
                }
                return i;
              }
              catch (NumberFormatException e)
              {
                // Try again...
                app.println();
                app.println(ERR_DSCFG_BAD_PORT_NUMBER.get(ninput));
                app.println();
                return null;
              }
            }
          }
        };
        try
        {
          app.println();
          portNumber = app.readValidatedInput(INFO_DSCFG_PROMPT_PORT_NUMBER
              .get(portNumber), callback);
        }
        catch (CLIException e)
        {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
      // Get the LDAP bind credentials.
      String bindDN = secureArgsList.bindDnArg.getValue();
      final String tmpBindDN = bindDN;
      if (keyManager == null)
      {
        if (app.isInteractive() && !secureArgsList.bindDnArg.isPresent())
        {
          if (!isHeadingDisplayed)
          {
            app.println();
            app.println();
            app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
            isHeadingDisplayed = true;
          }
          ValidationCallback<String> callback = new ValidationCallback<String>()
          {
            public String validate(ConsoleApplication app, String input)
                throws CLIException
            {
              String ninput = input.trim();
              if (ninput.length() == 0)
              {
                return tmpBindDN;
              }
              else
              {
                return ninput;
              }
            }
          };
          try
          {
            app.println();
            bindDN = app.readValidatedInput(INFO_DSCFG_PROMPT_BIND_DN
                .get(bindDN), callback);
          }
          catch (CLIException e)
          {
            throw ArgumentExceptionFactory
                .unableToReadConnectionParameters(e);
          }
        }
      }
      else
      {
        bindDN = null ;
      }
      String bindPassword = secureArgsList.bindPasswordArg.getValue();
      if (keyManager == null)
      {
        if (secureArgsList.bindPasswordFileArg.isPresent())
        {
          // Read from file if it exists.
          bindPassword = secureArgsList.bindPasswordFileArg.getValue();
          if (bindPassword == null)
          {
            throw ArgumentExceptionFactory.missingBindPassword(bindDN);
          }
        }
        else if (bindPassword == null || bindPassword.equals("-"))
        {
          // Read the password from the stdin.
          if (!app.isInteractive())
          {
            throw ArgumentExceptionFactory
                .unableToReadBindPasswordInteractively();
          }
          if (!isHeadingDisplayed)
          {
            app.println();
            app.println();
            app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
            isHeadingDisplayed = true;
          }
          try
          {
            app.println();
            Message prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
            bindPassword = app.readPassword(prompt);
          }
          catch (Exception e)
          {
            throw ArgumentExceptionFactory
                .unableToReadConnectionParameters(e);
          }
        }
      }
      // Interact with the user though the console to get
      // LDAP connection information
      LDAPConnectionConsoleInteraction ci =
              new LDAPConnectionConsoleInteraction(app, secureArgsList);
      ci.run();
      String hostName = ci.getHostName();
      Integer portNumber = ci.getPortNumber();
      String bindDN = ci.getBindDN();
      String bindPassword = ci.getBindPassword();
      TrustManager trustManager = ci.getTrustManager();
      KeyManager keyManager = ci.getKeyManager();
      // Do we have a secure connection ?
      LDAPConnection conn ;
      if (useSSL)
      if (ci.useSSL())
      {
        InitialLdapContext ctx = null;
        InitialLdapContext ctx;
        String ldapsUrl = "ldaps://" + hostName + ":" + portNumber;
        try
        {
@@ -510,9 +116,9 @@
              message) ;
        }
      }
      else if (useStartTLS)
      else if (ci.useStartTLS())
      {
        InitialLdapContext ctx = null;
        InitialLdapContext ctx;
        String ldapUrl = "ldap://" + hostName + ":" + portNumber;
        try
        {
@@ -600,342 +206,4 @@
    }
  }
  /**
   * Get the trust manager.
   *
   * @return The trust manager based on CLI args on interactive prompt.
   * @throws ArgumentException If an error occurs when getting args values.
   */
  private ApplicationTrustManager getTrustManager()
  throws ArgumentException
  {
    boolean trustAll = secureArgsList.trustAllArg.isPresent();
    if (app.isInteractive() && !secureArgsList.trustAllArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      try
      {
        app.println();
        trustAll = app.confirmAction(INFO_DSCFG_PROMPT_SECURITY_USE_TRUST_ALL
            .get(), false);
      }
      catch (CLIException e)
      {
        // Should never happen.
        throw new RuntimeException(e);
      }
    }
    // Trust everything, so no trust manager
    if (trustAll)
    {
      return null;
    }
    // If we not trust all server certificates, we have to get info
    // about truststore. First get the truststore path.
    String truststorePath = secureArgsList.trustStorePathArg.getValue();
    if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            app.println();
            app.println(ERR_DSCFG_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
          File f = new File(ninput);
          if (f.exists() && f.canRead() && !f.isDirectory())
          {
            return ninput;
          }
          else
          {
            app.println();
            app.println(ERR_DSCFG_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
        }
      };
      try
      {
        app.println();
        truststorePath = app.readValidatedInput(
            INFO_DSCFG_PROMPT_SECURITY_TRUSTSTORE_PATH.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // Then the truststore password.
    //  As the most common case is to have no password for truststore,
    // we don't ask it in the interactive mode.
    String truststorePassword = secureArgsList.trustStorePasswordArg
        .getValue();
    if (secureArgsList.trustStorePasswordFileArg.isPresent())
    {
      // Read from file if it exists.
      truststorePassword = secureArgsList.trustStorePasswordFileArg
          .getValue();
    }
    if ((truststorePassword !=  null) && (truststorePassword.equals("-")))
    {
      // Read the password from the stdin.
      if (!app.isInteractive())
      {
        truststorePassword = null;
      }
      else
      {
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        try
        {
          app.println();
          Message prompt = INFO_DSCFG_PROMPT_SECURITY_TRUSTSTORE_PASSWORD
              .get(truststorePath);
          truststorePassword = app.readPassword(prompt);
        }
        catch (Exception e)
        {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
    }
    // We'we got all the information to get the truststore manager
    try
    {
      FileInputStream fos = new FileInputStream(truststorePath);
      KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
      if (truststorePassword != null)
      {
        truststore.load(fos, truststorePassword.toCharArray());
      }
      else
      {
        truststore.load(fos, null);
      }
      fos.close();
      return new ApplicationTrustManager(truststore);
    }
    catch (Exception e)
    {
      throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
    }
  }
  /**
   * Get the key manager.
   *
   * @return The key manager based on CLI args on interactive prompt.
   * @throws ArgumentException If an error occurs when getting args values.
   */
  private KeyManager getKeyManager()
  throws ArgumentException
  {
    // Do we need client side authentication ?
    // If one of the client side authentication args is set, we assume
    // that we
    // need client side authentication.
    boolean weDontKnowIfWeNeedKeystore = !(secureArgsList.keyStorePathArg
        .isPresent()
        || secureArgsList.keyStorePasswordArg.isPresent()
        || secureArgsList.keyStorePasswordFileArg.isPresent()
        || secureArgsList.certNicknameArg
        .isPresent());
    // We don't have specific key manager parameter.
    // We assume that no client side authentication is required
    // Client side authentication is not the common use case. As a
    // consequence, interactive mode doesn't add an extra question
    // which will be in most cases useless.
    if (weDontKnowIfWeNeedKeystore)
    {
      return null;
    }
    // Get info about keystore. First get the keystore path.
    String keystorePath = secureArgsList.keyStorePathArg.getValue();
    if (app.isInteractive() && !secureArgsList.keyStorePathArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return ninput;
          }
          File f = new File(ninput);
          if (f.exists() && f.canRead() && !f.isDirectory())
          {
            return ninput;
          }
          else
          {
            app.println();
            app.println(ERR_DSCFG_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
        }
      };
      try
      {
        app.println();
        keystorePath = app.readValidatedInput(
            INFO_DSCFG_PROMPT_SECURITY_KEYSTORE_PATH.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // Then the keystore password.
    String keystorePassword = secureArgsList.keyStorePasswordArg.getValue();
    if (secureArgsList.keyStorePasswordFileArg.isPresent())
    {
      // Read from file if it exists.
      keystorePassword = secureArgsList.keyStorePasswordFileArg.getValue();
      if (keystorePassword == null)
      {
        throw ArgumentExceptionFactory.missingBindPassword(keystorePassword);
      }
    }
    else if (keystorePassword == null || keystorePassword.equals("-"))
    {
      // Read the password from the stdin.
      if (!app.isInteractive())
      {
        throw ArgumentExceptionFactory
            .unableToReadBindPasswordInteractively();
      }
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      try
      {
        app.println();
        Message prompt = INFO_DSCFG_PROMPT_SECURITY_KEYSTORE_PASSWORD
            .get(keystorePath);
        keystorePassword = app.readPassword(prompt);
      }
      catch (Exception e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // finally the certificate name, if needed.
    String certifNickname = secureArgsList.certNicknameArg.getValue();
    if (app.isInteractive() && !secureArgsList.certNicknameArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          return input.trim();
        }
      };
      try
      {
        app.println();
        certifNickname = app.readValidatedInput(
            INFO_DSCFG_PROMPT_SECURITY_CERTIFICATE_NAME.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // We'we got all the information to get the keystore manager
    try
    {
      FileInputStream fos = new FileInputStream(keystorePath);
      KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(fos, keystorePassword.toCharArray());
      fos.close();
      ApplicationKeyManager akm = new ApplicationKeyManager(keystore,
          keystorePassword.toCharArray());
      if (certifNickname.length() != 0)
      {
        return new SelectableCertificateKeyManager(akm, certifNickname);
      }
      else
      {
        return akm ;
      }
    }
    catch (Exception e)
    {
      throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
    }
  }
}
opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
New file
@@ -0,0 +1,895 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
import org.opends.messages.Message;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.messages.ToolMessages.INFO_LDAPAUTH_PASSWORD_PROMPT;
import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.SelectableCertificateKeyManager;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.admin.ads.util.ApplicationKeyManager;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
/**
 * Supports interacting with a user through the command line to
 * prompt for information necessary to create an LDAP connection.
 */
public class LDAPConnectionConsoleInteraction {
  private boolean useSSL;
  private boolean useStartTLS;
  private String hostName;
  private int portNumber;
  private String bindDN;
  private String bindPassword;
  private KeyManager keyManager;
  private TrustManager trustManager;
  // The SecureConnectionCliArgsList object.
  private SecureConnectionCliArgs secureArgsList = null;
  // Indicate if we need to display the heading
  private boolean isHeadingDisplayed = false;
  // the Console application
  private ConsoleApplication app;
  /**
   * Enumeration description protocols for interactive CLI choices.
   */
  private enum Protocols
  {
    LDAP(1, INFO_LDAP_CONN_PROMPT_SECURITY_LDAP.get()), SSL(2,
        INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL.get()), START_TSL(3,
        INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TSL.get());
    private Integer choice;
    private Message msg;
    /**
     * Private constructor.
     *
     * @param i
     *          the menu return value.
     * @param msg
     *          the message message.
     */
    private Protocols(int i, Message msg)
    {
      choice = i;
      this.msg = msg;
    }
    /**
     * Returns the choice number.
     *
     * @return the attribute name.
     */
    public Integer getChoice()
    {
      return choice;
    }
    /**
     * Return the menu message.
     *
     * @return the menu message.
     */
    public Message getMenuMessage()
    {
      return msg;
    }
  }
  /**
   * Constructs a parameterized instance.
   *
   * @param app console application
   * @param secureArgs existing set of arguments that have already
   *        been parsed and contain some potential command line specified
   *        LDAP arguments
   */
  public LDAPConnectionConsoleInteraction(ConsoleApplication app,
                                          SecureConnectionCliArgs secureArgs) {
    this.app = app;
    this.secureArgsList = secureArgs;
  }
  /**
   * Interact with the user though the console to get information
   * necessary to establish an LDAP connection.
   *
   * @throws ArgumentException if there is a problem with the arguments
   */
  public void run()
          throws ArgumentException
  {
    boolean secureConnection =
      (
          secureArgsList.useSSLArg.isPresent()
          ||
          secureArgsList.useStartTLSArg.isPresent()
          ||
          secureArgsList.trustAllArg.isPresent()
          ||
          secureArgsList.trustStorePathArg.isPresent()
          ||
          secureArgsList.trustStorePasswordArg.isPresent()
          ||
          secureArgsList.trustStorePasswordFileArg.isPresent()
          ||
          secureArgsList.keyStorePathArg.isPresent()
          ||
          secureArgsList.keyStorePasswordArg.isPresent()
          ||
          secureArgsList.keyStorePasswordFileArg.isPresent()
      );
    // Get the LDAP host.
    hostName = secureArgsList.hostNameArg.getValue();
    final String tmpHostName = hostName;
    if (app.isInteractive() && !secureArgsList.hostNameArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return tmpHostName;
          }
          else
          {
            try
            {
              InetAddress.getByName(ninput);
              return ninput;
            }
            catch (UnknownHostException e)
            {
              // Try again...
              app.println();
              app.println(ERR_LDAP_CONN_BAD_HOST_NAME.get(ninput));
              app.println();
              return null;
            }
          }
        }
      };
      try
      {
        app.println();
        hostName = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_HOST_NAME
            .get(hostName), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    useSSL = secureArgsList.useSSL();
    useStartTLS = secureArgsList.useStartTLS();
    boolean connectionTypeIsSet =
      (
        secureArgsList.useSSLArg.isPresent()
        ||
        secureArgsList.useStartTLSArg.isPresent()
      );
    if (app.isInteractive() && !connectionTypeIsSet)
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
      builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_USE_SECURE_CTX.get());
      Protocols defaultProtocol ;
      if (secureConnection)
      {
        defaultProtocol = Protocols.SSL;
      }
      else
      {
        defaultProtocol = Protocols.LDAP;
      }
      for (Protocols p : Protocols.values())
      {
        if (secureConnection && p.equals(Protocols.LDAP))
        {
          continue ;
        }
        int i = builder.addNumberedOption(p.getMenuMessage(), MenuResult
            .success(p.getChoice()));
        if (p.equals(defaultProtocol))
        {
          builder.setDefault(
              INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
                  .get(i), MenuResult.success(p.getChoice()));
        }
      }
      Menu<Integer> menu = builder.toMenu();
      try
      {
        MenuResult<Integer> result = menu.run();
        if (result.isSuccess())
        {
          if (result.getValue().equals(Protocols.SSL.getChoice()))
          {
            useSSL = true;
          }
          else if (result.getValue()
              .equals(Protocols.START_TSL.getChoice()))
          {
            useStartTLS = true;
          }
        }
        else
        {
          // Should never happen.
          throw new RuntimeException();
        }
      }
      catch (CLIException e)
      {
        throw new RuntimeException(e);
      }
    }
    if (useSSL || useStartTLS)
    {
      // Get truststore info
      trustManager = getTrustManagerInternal();
      // Check if we need client side authentication
      keyManager = getKeyManagerInternal();
    }
    // Get the LDAP port.
    if (!useSSL)
    {
      portNumber = secureArgsList.portArg.getIntValue();
    }
    else
    {
      if (secureArgsList.portArg.isPresent())
      {
        portNumber = secureArgsList.portArg.getIntValue();
      }
      else
      {
        portNumber = 636;
      }
    }
    final int tmpPortNumber = portNumber;
    if (app.isInteractive() && !secureArgsList.portArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
      {
        public Integer validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return tmpPortNumber;
          }
          else
          {
            try
            {
              int i = Integer.parseInt(ninput);
              if (i < 1 || i > 65535)
              {
                throw new NumberFormatException();
              }
              return i;
            }
            catch (NumberFormatException e)
            {
              // Try again...
              app.println();
              app.println(ERR_LDAP_CONN_BAD_PORT_NUMBER.get(ninput));
              app.println();
              return null;
            }
          }
        }
      };
      try
      {
        app.println();
        portNumber = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_PORT_NUMBER
            .get(portNumber), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // Get the LDAP bind credentials.
    bindDN = secureArgsList.bindDnArg.getValue();
    final String tmpBindDN = bindDN;
    if (keyManager == null)
    {
      if (app.isInteractive() && !secureArgsList.bindDnArg.isPresent())
      {
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        ValidationCallback<String> callback = new ValidationCallback<String>()
        {
          public String validate(ConsoleApplication app, String input)
              throws CLIException
          {
            String ninput = input.trim();
            if (ninput.length() == 0)
            {
              return tmpBindDN;
            }
            else
            {
              return ninput;
            }
          }
        };
        try
        {
          app.println();
          bindDN = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_BIND_DN
              .get(bindDN), callback);
        }
        catch (CLIException e)
        {
          throw ArgumentExceptionFactory
              .unableToReadConnectionParameters(e);
        }
      }
    }
    else
    {
      bindDN = null ;
    }
    bindPassword = secureArgsList.bindPasswordArg.getValue();
    if (keyManager == null)
    {
      if (secureArgsList.bindPasswordFileArg.isPresent())
      {
        // Read from file if it exists.
        bindPassword = secureArgsList.bindPasswordFileArg.getValue();
        if (bindPassword == null)
        {
          throw ArgumentExceptionFactory.missingBindPassword(bindDN);
        }
      }
      else if (bindPassword == null || bindPassword.equals("-"))
      {
        // Read the password from the stdin.
        if (!app.isInteractive())
        {
          throw ArgumentExceptionFactory
              .unableToReadBindPasswordInteractively();
        }
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        try
        {
          app.println();
          Message prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
          bindPassword = app.readPassword(prompt);
        }
        catch (Exception e)
        {
          throw ArgumentExceptionFactory
              .unableToReadConnectionParameters(e);
        }
      }
    }
  }
  /**
   * Get the trust manager.
   *
   * @return The trust manager based on CLI args on interactive prompt.
   * @throws ArgumentException If an error occurs when getting args values.
   */
  private ApplicationTrustManager getTrustManagerInternal()
  throws ArgumentException
  {
    boolean trustAll = secureArgsList.trustAllArg.isPresent();
    if (app.isInteractive() && !secureArgsList.trustAllArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      try
      {
        app.println();
        trustAll = app.confirmAction(
                INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get(), false);
      }
      catch (CLIException e)
      {
        // Should never happen.
        throw new RuntimeException(e);
      }
    }
    // Trust everything, so no trust manager
    if (trustAll)
    {
      return null;
    }
    // If we not trust all server certificates, we have to get info
    // about truststore. First get the truststore path.
    String truststorePath = secureArgsList.trustStorePathArg.getValue();
    if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            app.println();
            app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
          File f = new File(ninput);
          if (f.exists() && f.canRead() && !f.isDirectory())
          {
            return ninput;
          }
          else
          {
            app.println();
            app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
        }
      };
      try
      {
        app.println();
        truststorePath = app.readValidatedInput(
            INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // Then the truststore password.
    //  As the most common case is to have no password for truststore,
    // we don't ask it in the interactive mode.
    String truststorePassword = secureArgsList.trustStorePasswordArg
        .getValue();
    if (secureArgsList.trustStorePasswordFileArg.isPresent())
    {
      // Read from file if it exists.
      truststorePassword = secureArgsList.trustStorePasswordFileArg
          .getValue();
    }
    if ((truststorePassword !=  null) && (truststorePassword.equals("-")))
    {
      // Read the password from the stdin.
      if (!app.isInteractive())
      {
        truststorePassword = null;
      }
      else
      {
        if (!isHeadingDisplayed)
        {
          app.println();
          app.println();
          app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        try
        {
          app.println();
          Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD
              .get(truststorePath);
          truststorePassword = app.readPassword(prompt);
        }
        catch (Exception e)
        {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
    }
    // We'we got all the information to get the truststore manager
    try
    {
      FileInputStream fos = new FileInputStream(truststorePath);
      KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
      if (truststorePassword != null)
      {
        truststore.load(fos, truststorePassword.toCharArray());
      }
      else
      {
        truststore.load(fos, null);
      }
      fos.close();
      return new ApplicationTrustManager(truststore);
    }
    catch (Exception e)
    {
      throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
    }
  }
  /**
   * Get the key manager.
   *
   * @return The key manager based on CLI args on interactive prompt.
   * @throws ArgumentException If an error occurs when getting args values.
   */
  private KeyManager getKeyManagerInternal()
  throws ArgumentException
  {
    // Do we need client side authentication ?
    // If one of the client side authentication args is set, we assume
    // that we
    // need client side authentication.
    boolean weDontKnowIfWeNeedKeystore = !(secureArgsList.keyStorePathArg
        .isPresent()
        || secureArgsList.keyStorePasswordArg.isPresent()
        || secureArgsList.keyStorePasswordFileArg.isPresent()
        || secureArgsList.certNicknameArg
        .isPresent());
    // We don't have specific key manager parameter.
    // We assume that no client side authentication is required
    // Client side authentication is not the common use case. As a
    // consequence, interactive mode doesn't add an extra question
    // which will be in most cases useless.
    if (weDontKnowIfWeNeedKeystore)
    {
      return null;
    }
    // Get info about keystore. First get the keystore path.
    String keystorePath = secureArgsList.keyStorePathArg.getValue();
    if (app.isInteractive() && !secureArgsList.keyStorePathArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          String ninput = input.trim();
          if (ninput.length() == 0)
          {
            return ninput;
          }
          File f = new File(ninput);
          if (f.exists() && f.canRead() && !f.isDirectory())
          {
            return ninput;
          }
          else
          {
            app.println();
            app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
                .get());
            app.println();
            return null;
          }
        }
      };
      try
      {
        app.println();
        keystorePath = app.readValidatedInput(
            INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // Then the keystore password.
    String keystorePassword = secureArgsList.keyStorePasswordArg.getValue();
    if (secureArgsList.keyStorePasswordFileArg.isPresent())
    {
      // Read from file if it exists.
      keystorePassword = secureArgsList.keyStorePasswordFileArg.getValue();
      if (keystorePassword == null)
      {
        throw ArgumentExceptionFactory.missingBindPassword(keystorePassword);
      }
    }
    else if (keystorePassword == null || keystorePassword.equals("-"))
    {
      // Read the password from the stdin.
      if (!app.isInteractive())
      {
        throw ArgumentExceptionFactory
            .unableToReadBindPasswordInteractively();
      }
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      try
      {
        app.println();
        Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
            .get(keystorePath);
        keystorePassword = app.readPassword(prompt);
      }
      catch (Exception e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // finally the certificate name, if needed.
    String certifNickname = secureArgsList.certNicknameArg.getValue();
    if (app.isInteractive() && !secureArgsList.certNicknameArg.isPresent())
    {
      if (!isHeadingDisplayed)
      {
        app.println();
        app.println();
        app.println(INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get());
        isHeadingDisplayed = true;
      }
      ValidationCallback<String> callback = new ValidationCallback<String>()
      {
        public String validate(ConsoleApplication app, String input)
            throws CLIException
        {
          return input.trim();
        }
      };
      try
      {
        app.println();
        certifNickname = app.readValidatedInput(
            INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_NAME.get(), callback);
      }
      catch (CLIException e)
      {
        throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
      }
    }
    // We'we got all the information to get the keystore manager
    try
    {
      FileInputStream fos = new FileInputStream(keystorePath);
      KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(fos, keystorePassword.toCharArray());
      fos.close();
      ApplicationKeyManager akm = new ApplicationKeyManager(keystore,
          keystorePassword.toCharArray());
      if (certifNickname.length() != 0)
      {
        return new SelectableCertificateKeyManager(akm, certifNickname);
      }
      else
      {
        return akm ;
      }
    }
    catch (Exception e)
    {
      throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
    }
  }
  /**
   * Indicates whether or not a connection should use SSL based on
   * this interaction.
   *
   * @return boolean where true means use SSL
   */
  public boolean useSSL() {
    return useSSL;
  }
  /**
   * Indicates whether or not a connection should use StartTLS based on
   * this interaction.
   *
   * @return boolean where true means use StartTLS
   */
  public boolean useStartTLS() {
    return useStartTLS;
  }
  /**
   * Gets the host name that should be used for connections based on
   * this interaction.
   *
   * @return host name for connections
   */
  public String getHostName() {
    return hostName;
  }
  /**
   * Gets the port number name that should be used for connections based on
   * this interaction.
   *
   * @return port number for connections
   */
  public int getPortNumber() {
    return portNumber;
  }
  /**
   * Gets the bind DN name that should be used for connections based on
   * this interaction.
   *
   * @return bind DN for connections
   */
  public String getBindDN() {
    return this.bindDN;
  }
  /**
   * Gets the bind password that should be used for connections based on
   * this interaction.
   *
   * @return bind password for connections
   */
  public String getBindPassword() {
    return this.bindPassword;
  }
  /**
   * Gets the trust manager that should be used for connections based on
   * this interaction.
   *
   * @return trust manager for connections
   */
  public TrustManager getTrustManager() {
    return this.trustManager;
  }
  /**
   * Gets the key manager that should be used for connections based on
   * this interaction.
   *
   * @return key manager for connections
   */
  public KeyManager getKeyManager() {
    return this.keyManager;
  }
}