LDAPConnectionConsoleInteraction.java revision 88f16d892d54fd8c3e190cc1f6363638b11ae1a3
/*
* 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 legal-notices/CDDLv1_0.txt
* 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 legal-notices/CDDLv1_0.txt.
* 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
*
*
* Copyright 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* Supports interacting with a user through the command line to prompt for
* information necessary to create an LDAP connection.
*
* Actually the LDAPConnectionConsoleInteraction is used by UninstallCliHelper, StatusCli,
* LDAPManagementContextFactory and ReplicationCliMain.
*/
public class LDAPConnectionConsoleInteraction
{
private boolean useSSL;
private boolean useStartTLS;
private int portNumber;
private String providedBindDN;
private String providedAdminUID;
private String bindPassword;
private KeyManager keyManager;
private ApplicationTrustManager trustManager;
/** Boolean that tells if we ask for bind DN or admin UID in the same prompt. */
private boolean useAdminOrBindDn;
/**
* Boolean that tells if we must propose LDAP if it is available even if the
* user provided certificate parameters.
*/
private boolean displayLdapIfSecureParameters;
/** The SecureConnectionCliArgsList object. */
private SecureConnectionCliArgs secureArgsList;
/** Indicate if we need to display the heading. */
private boolean isHeadingDisplayed;
/** the Console application. */
private ConsoleApplication app;
/** Indicate if the trust store in in memory. */
private boolean trustStoreInMemory;
/** Indicate if the all certificates are accepted. */
private boolean trustAll;
/** Indicate that the trust manager was created with the parameters provided. */
private boolean trustManagerInitialized;
/** The trust store to use for the SSL or STARTTLS connection. */
private KeyStore truststore;
private String keystorePath;
private String keystorePassword;
private String certifNickname;
private String truststorePath;
private String truststorePassword;
/** The timeout to be used to connect. */
private int connectTimeout;
private LocalizableMessage heading =
/** A copy of the secureArgList for convenience. */
/** The command builder that we can return with the connection information. */
private CommandBuilder commandBuilder;
/**
* Enumeration description protocols for interactive CLI choices.
*/
private enum Protocols
{
private LocalizableMessage msg;
/**
* Private constructor.
*
* @param i
* the menu return value.
* @param msg
* the message message.
*/
{
choice = i;
}
/**
* Returns the choice number.
*
* @return the attribute name.
*/
{
return choice;
}
/**
* Return the menu message.
*
* @return the menu message.
*/
public LocalizableMessage getMenuMessage()
{
return msg;
}
}
/**
* Enumeration description protocols for interactive CLI choices.
*/
private enum TrustMethod
{
private LocalizableMessage msg;
/**
* Private constructor.
*
* @param i
* the menu return value.
* @param msg
* the message message.
*/
{
}
/**
* Returns the choice number.
*
* @return the attribute name.
*/
{
return choice;
}
/**
* Return the menu message.
*
* @return the menu message.
*/
public LocalizableMessage getMenuMessage()
{
return msg;
}
}
/**
* Enumeration description server certificate trust option.
*/
private enum TrustOption
{
.get());
private LocalizableMessage msg;
/**
* Private constructor.
*
* @param i
* the menu return value.
* @param msg
* the message message.
*/
{
}
/**
* Returns the choice number.
*
* @return the attribute name.
*/
{
return choice;
}
/**
* Return the menu message.
*
* @return the menu message.
*/
public LocalizableMessage 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
*/
{
this.secureArgsList = secureArgs;
try
{
}
catch (Throwable t)
{
// This is a bug: we should always be able to create the global arguments
// no need to localize this one.
throw new RuntimeException("Unexpected error: " + t, t);
}
}
/**
* 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
{
run(true);
}
/**
* Interact with the user though the console to get information necessary to
* establish an LDAP connection.
*
* @param canUseStartTLS
* whether we can propose to connect using Start TLS or not.
* @throws ArgumentException
* if there is a problem with the arguments
*/
public void run(boolean canUseStartTLS)
throws ArgumentException
{
// Reset everything
boolean secureConnection = true;
// Get the LDAP host.
{
{
throws ClientException
{
{
return tmpHostName;
}
else
{
try
{
return ninput;
}
catch (UnknownHostException e)
{
// Try again...
return null;
}
}
}
};
try
{
hostName =
}
catch (ClientException e)
{
throw cannotReadConnectionParameters(e);
}
}
// Connection type
boolean connectionTypeIsSet =
.isValueSetByProperty());
{
if (secureConnection)
{
}
else
{
}
{
{
continue;
}
{
continue;
}
int i =
.getChoice()));
if (p.equals(defaultProtocol))
{
}
}
try
{
{
{
useSSL = true;
}
{
useStartTLS = true;
}
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
catch (ClientException e)
{
throw new RuntimeException(e);
}
}
if (useSSL)
{
}
else if (useStartTLS)
{
}
// Get the LDAP port.
if (!useSSL)
{
}
else
{
{
}
else
{
}
}
final int tmpPortNumber = portNumber;
{
{
throws ClientException
{
{
return tmpPortNumber;
}
else
{
try
{
if (i < 1 || i > 65535)
{
throw new NumberFormatException();
}
return i;
}
catch (NumberFormatException e)
{
// Try again...
return null;
}
}
}
};
try
{
if (secureArgsList.alwaysSSL())
{
}
else
{
}
}
catch (ClientException e)
{
throw cannotReadConnectionParameters(e);
}
}
// Handle certificate
{
}
// Get the LDAP bind credentials.
{
}
else
{
}
{
}
else
{
}
if (keyManager == null)
{
{
{
throws ClientException
{
{
if (useAdmin)
{
return tmpAdminUID;
}
else
{
return tmpBindDN;
}
}
else
{
return ninput;
}
}
};
try
{
if (useAdminOrBindDn)
{
String v =
if (isDN(v))
{
bindDN = v;
providedBindDN = v;
}
else
{
adminUID = v;
providedAdminUID = v;
}
}
else if (useAdmin)
{
adminUID =
}
else
{
bindDN =
}
}
catch (ClientException e)
{
throw cannotReadConnectionParameters(e);
}
}
if (useAdminOrBindDn)
{
{
}
if (addAdmin)
{
}
else if (addBindDN)
{
}
}
else if (useAdmin)
{
}
else
{
}
}
else
{
}
boolean addedPasswordFileArgument = false;
{
}
if (keyManager == null)
{
{
// Read from file if it exists.
if (bindPassword == null)
{
if (useAdmin)
{
}
else
{
}
}
addedPasswordFileArgument = true;
}
{
// Read the password from the stdin.
if (!app.isInteractive())
{
}
try
{
if (providedAdminUID != null)
{
}
else if (providedBindDN != null)
{
}
{
}
else
{
}
}
catch (Exception e)
{
}
}
{
}
}
}
{
}
{
{
}
return null;
}
/**
* 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.
*/
throws ArgumentException
{
// Remove these arguments since this method might be called several times.
// If we have the trustALL flag, don't do anything
// just return null
{
return null;
}
// Check if some trust manager info are set
boolean weDontKnowTheTrustMethod =
boolean askForTrustStore = false;
// Try to use the local instance trust store, to avoid certificate
// validation when both the CLI and the server are in the same instance.
if (weDontKnowTheTrustMethod && addLocalTrustStore())
{
weDontKnowTheTrustMethod = false;
}
{
{
int i =
.getChoice()));
if (t.equals(defaultTrustMethod))
{
}
}
trustStoreInMemory = false;
try
{
{
{
trustAll = true;
// If we have the trustALL flag, don't do anything
// just return null
return null;
}
{
// We have to ask for trust store info
askForTrustStore = true;
}
{
// The certificate will be displayed to the user
askForTrustStore = false;
trustStoreInMemory = true;
// There is no direct equivalent for this option, so propose the
// trust all option as command-line argument.
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
catch (ClientException e)
{
throw new RuntimeException(e);
}
}
// If we do not trust all server certificates, we have to get info
// about trust store. First get the trust store path.
&& askForTrustStore)
{
{
throws ClientException
{
{
return null;
}
{
return ninput;
}
else
{
return null;
}
}
};
try
{
}
catch (ClientException e)
{
throw cannotReadConnectionParameters(e);
}
}
if (truststorePath != null)
{
}
// 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.
{
}
{
// Read from file if it exists.
}
{
// Read the password from the stdin.
if (!app.isInteractive())
{
}
else
{
try
{
}
catch (Exception e)
{
}
}
}
// We've got all the information to get the truststore manager
try
{
if (truststorePath != null)
{
if (truststorePassword != null)
{
}
else
{
}
}
else
{
}
&& truststorePath != null)
{
.putAll(
}
{
// Only add the trust store password if there is one AND if the user
// specified a trust store path.
}
return new ApplicationTrustManager(truststore);
}
catch (Exception 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.
*/
{
// Remove these arguments since this method might be called several times.
// 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 =
// 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.
{
return null;
}
// Get info about keystore. First get the keystore path.
{
{
throws ClientException
{
{
return ninput;
}
{
return ninput;
}
else
{
return null;
}
}
};
try
{
}
catch (ClientException e)
{
throw cannotReadConnectionParameters(e);
}
}
if (keystorePath != null)
{
}
else
{
// KeystorePath is null. Either it's unspecified or there's a pb
// We should throw an exception here, anyway since code below will
// anyway
.get("null keystorePath"));
}
// Then the keystore password.
{
// Read from file if it exists.
if (keystorePassword == null)
{
}
}
{
// Read the password from the stdin.
if (!app.isInteractive())
{
}
try
{
}
catch (Exception e)
{
}
}
// finally the certificate name, if needed.
try
{
}
catch (Exception e)
{
}
&& aliasesEnum.hasMoreElements())
{
try
{
.get());
int certificateNumber = 0;
for (; aliasesEnum.hasMoreElements();)
{
{
}
}
if (certificateNumber > 1)
{
{
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
else
{
}
}
catch (KeyStoreException e)
{
}
catch (ClientException e)
{
throw cannotReadConnectionParameters(e);
}
}
// We'we got all the information to get the keys manager
{
}
else if (keystorePassword != null)
{
}
if (certifNickname != null)
{
}
if (certifNickname != null)
{
}
return akm;
}
/**
* 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;
}
/**
* Sets the port number name that should be used for connections based on this
* interaction.
*
* @param portNumber
* port number for connections
*/
public void setPortNumber(int portNumber)
{
this.portNumber = portNumber;
}
/**
* Gets the bind DN name that should be used for connections based on this
* interaction.
*
* @return bind DN for connections
*/
{
if (useAdminOrBindDn)
{
if (providedBindDN != null)
{
dn = providedBindDN;
}
else if (providedAdminUID != null)
{
}
{
}
{
}
else
{
}
}
else if (secureArgsList.useAdminUID())
{
}
else
{
}
return dn;
}
/**
* Gets the administrator UID name that should be used for connections based
* on this interaction.
*
* @return administrator UID for connections
*/
public String getAdministratorUID()
{
return adminUID;
}
/**
* Gets the bind password that should be used for connections based on this
* interaction.
*
* @return bind password for connections
*/
public String getBindPassword()
{
return bindPassword;
}
/**
* Gets the trust manager that should be used for connections based on this
* interaction.
*
* @return trust manager for connections
*/
public ApplicationTrustManager getTrustManager()
{
return trustManager;
}
/**
* Gets the key store that should be used for connections based on this
* interaction.
*
* @return key store for connections
*/
public KeyStore getKeyStore()
{
return truststore;
}
/**
* Gets the key manager that should be used for connections based on this
* interaction.
*
* @return key manager for connections
*/
public KeyManager getKeyManager()
{
return keyManager;
}
/**
* Indicate if the trust store is in memory.
*
* @return true if the trust store is in memory.
*/
public boolean isTrustStoreInMemory()
{
return trustStoreInMemory;
}
/**
* Indicate if all certificates must be accepted.
*
* @return true all certificates must be accepted.
*/
public boolean isTrustAll()
{
return trustAll;
}
/**
* Returns the timeout to be used to connect with the server.
*
* @return the timeout to be used to connect with the server.
*/
public int getConnectTimeout()
{
return connectTimeout;
}
/**
* Indicate if the certificate chain can be trusted.
*
* @param chain
* The certificate chain to validate
* @return true if the server certificate is trusted.
*/
{
}
/**
* Indicate if the certificate chain can be trusted.
*
* @param chain
* The certificate chain to validate
* @param authType
* the authentication type.
* @param host
* the host we tried to connect and that presented the certificate.
* @return true if the server certificate is trusted.
*/
{
if (trustManager == null)
{
try
{
}
catch (ArgumentException ae)
{
// Should not occur
throw new RuntimeException(ae);
}
}
{
// Certificate DN
// certificate validity
// certificate Issuer
{
}
}
{
int i =
.getChoice()));
if (t.equals(defaultTrustMethod))
{
}
}
while (true)
{
try
{
{
{
return false;
}
{
{
}
continue;
}
// We should add it in the memory truststore
{
try
{
}
catch (KeyStoreException e1)
{
// What else should we do?
return false;
}
}
// Update the trust manager
if (trustManager == null)
{
}
{
// Update the trust manager with the new certificate
}
else
{
// Do a full reset of the contents of the keystore.
}
{
new ValidationCallback<String>()
{
throws ClientException
{
{
.get());
return null;
}
if (!f.isDirectory())
{
return ninput;
}
else
{
.get());
return null;
}
}
};
try
{
callback);
}
catch (ClientException e)
{
return true;
}
// Read the password from the stdin.
try
{
}
catch (Exception e)
{
return true;
}
try
{
try
{
}
catch (FileNotFoundException e)
{
}
{
}
{
}
try
{
}
finally
{
}
}
catch (Exception e)
{
return true;
}
}
return true;
}
else
{
// Should never happen.
throw new RuntimeException();
}
}
catch (ClientException cliE)
{
throw new RuntimeException(cliE);
}
}
}
/**
* Populates a set of LDAP options with state from this interaction.
*
* @param options
* existing set of options; may be null in which case this method
* will create a new set of <code>LDAPConnectionOptions</code> to be
* returned
* @return used during this interaction
* @throws SSLConnectionException
* if this interaction has specified the use of SSL and there is a
* problem initializing the SSL connection factory
*/
throws SSLConnectionException
{
{
options = new LDAPConnectionOptions();
}
if (this.useSSL)
{
}
else
{
}
return options;
}
/**
* Prompts the user to accept the certificate.
*
* @param t
* the throwable that was generated because the certificate was not
* trusted.
* @param usedTrustManager
* the trustManager used when trying to establish the connection.
* @param usedUrl
* the LDAP URL used to connect to the server.
* @param logger
* the Logger used to log messages.
* @return {@code true} if the user accepted the certificate and
* {@code false} otherwise.
*/
public boolean promptForCertificateConfirmation(Throwable t,
{
if (usedTrustManager != null)
{
}
else
{
}
{
+ cause));
}
{
String h;
int p;
try
{
}
{
p = -1;
}
{
}
else
{
}
{
return false;
}
{
}
if (h == null)
{
}
}
else
{
}
return false;
}
{
{
}
}
/**
* Sets the heading that is displayed in interactive mode.
*
* @param heading
* the heading that is displayed in interactive mode.
*/
{
}
/**
* Returns the command builder with the equivalent arguments on the
* non-interactive mode.
*
* @return the command builder with the equivalent arguments on the
* non-interactive mode.
*/
public CommandBuilder getCommandBuilder()
{
return commandBuilder;
}
/**
* Displays the heading if it was not displayed before.
*/
private void checkHeadingDisplayed()
{
if (!isHeadingDisplayed)
{
isHeadingDisplayed = true;
}
}
/**
* Tells whether during interaction we can ask for both the DN or the admin
* UID.
*
* @return {@code true} if during interaction we can ask for both the DN
* and the admin UID and {@code false} otherwise.
*/
public boolean isUseAdminOrBindDn()
{
return useAdminOrBindDn;
}
/**
* Tells whether we can ask during interaction for both the DN and the admin
* UID or not.
*
* @param useAdminOrBindDn
* whether we can ask for both the DN and the admin UID during
* interaction or not.
*/
public void setUseAdminOrBindDn(boolean useAdminOrBindDn)
{
this.useAdminOrBindDn = useAdminOrBindDn;
}
/**
* Tells whether we propose LDAP as protocol even if the user provided
* security parameters. This is required in command-lines that access multiple
* servers (like dsreplication).
*
* @param displayLdapIfSecureParameters
* whether propose LDAP as protocol even if the user provided
* security parameters or not.
*/
public void setDisplayLdapIfSecureParameters(
boolean displayLdapIfSecureParameters)
{
}
/**
* Resets the heading displayed flag, so that next time we call run the
* heading is displayed.
*/
public void resetHeadingDisplayed()
{
isHeadingDisplayed = false;
}
/**
* Resets the trust manager, so that next time we call the run() method the
* trust manager takes into account the local trust store.
*/
public void resetTrustManager()
{
trustManager = null;
}
/**
* Forces the initialization of the trust manager with the arguments provided
* by the user.
*
* @throws ArgumentException
* if there is an error with the arguments provided by the user.
*/
public void initializeTrustManagerIfRequired() throws ArgumentException
{
if (!trustManagerInitialized)
{
}
}
/**
* Initializes the global arguments in the parser with the provided values.
* This is useful when we want to call LDAPConnectionConsoleInteraction.run()
* with some default values.
*
* @param hostName
* the host name.
* @param port
* the port to connect to the server.
* @param adminUid
* the administrator UID.
* @param bindDn
* the bind DN to bind to the server.
* @param bindPwd
* the password to bind.
* @param pwdFile
* the Map containing the file and the password to bind.
*/
{
{
}
// resetConnectionArguments does not clear the values for the port
if (port != -1)
{
}
else
{
// This is done to be able to call IntegerArgument.getIntValue()
}
{
}
{
}
{
{
}
}
{
}
}
/**
* Resets the connection parameters for the LDAPConsoleInteraction object. The
* reset does not apply to the certificate parameters. This is called in order
* the LDAPConnectionConsoleInteraction object to ask for all this connection
* parameters next time we call LDAPConnectionConsoleInteraction.run().
*/
public void resetConnectionArguments()
{
// This is done to be able to call IntegerArgument.getIntValue()
}
private void initializeTrustManager() throws ArgumentException
{
// Get trust store info
// Check if we need client side authentication
trustManagerInitialized = true;
}
/**
* Returns the explicitly provided Admin UID from the user (interactively or
* through the argument).
*
* @return the explicitly provided Admin UID from the user (interactively or
* through the argument).
*/
public String getProvidedAdminUID()
{
return providedAdminUID;
}
/**
* Returns the explicitly provided bind DN from the user (interactively or
* through the argument).
*
* @return the explicitly provided bind DN from the user (interactively or
* through the argument).
*/
public String getProvidedBindDN()
{
return providedBindDN;
}
/**
* Add the TrustStore of the administration connector of the local instance.
*
* @return true if the local trust store has been added.
*/
private boolean addLocalTrustStore()
{
try
{
// If remote host, return
{
return false;
}
// check if we are in a local instance. Already checked the host,
// now check the port
{
return false;
}
if (truststoreFileAbsolute != null)
{
return true;
}
return false;
}
{
// do nothing
return false;
}
}
}