Repository: geode Updated Branches: refs/heads/develop cea8312b0 -> b61078a1d
GEODE-3328: simply extract connect command from ShellCommand Project: http://git-wip-us.apache.org/repos/asf/geode/repo Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/b61078a1 Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/b61078a1 Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/b61078a1 Branch: refs/heads/develop Commit: b61078a1d9aaff81d4f00eb552cc70a8a7331d09 Parents: cea8312 Author: Jinmei Liao <jil...@pivotal.io> Authored: Fri Jul 28 13:37:29 2017 -0700 Committer: Jinmei Liao <jil...@pivotal.io> Committed: Thu Aug 3 09:03:28 2017 -0700 ---------------------------------------------------------------------- .../geode/internal/util/PasswordUtil.java | 33 +- .../internal/cli/commands/ConnectCommand.java | 572 +++++++++++++++++++ .../internal/cli/commands/ShellCommands.java | 539 ----------------- .../cli/commands/StartLocatorCommand.java | 4 +- .../internal/cli/util/GfshConsoleReader.java | 19 - .../internal/util/PasswordUtilJUnitTest.java | 9 +- 6 files changed, 601 insertions(+), 575 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/geode/blob/b61078a1/geode-core/src/main/java/org/apache/geode/internal/util/PasswordUtil.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/util/PasswordUtil.java b/geode-core/src/main/java/org/apache/geode/internal/util/PasswordUtil.java index ac0b845..b5769b3 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/util/PasswordUtil.java +++ b/geode-core/src/main/java/org/apache/geode/internal/util/PasswordUtil.java @@ -33,20 +33,27 @@ public class PasswordUtil { */ @Deprecated public static String decrypt(String password) { - if (password.startsWith("encrypted(") && password.endsWith(")")) { - byte[] decrypted; - try { - String toDecrypt = password.substring(10, password.length() - 1); - SecretKeySpec key = new SecretKeySpec(init, "Blowfish"); - Cipher cipher = Cipher.getInstance("Blowfish"); - cipher.init(Cipher.DECRYPT_MODE, key); - decrypted = cipher.doFinal(hexStringToByteArray(toDecrypt)); - return new String(decrypted); - } catch (Exception e) { - e.printStackTrace(); - } + if (!isEncryptedPassword(password)) + return password; + + String toDecrypt = password.substring(10, password.length() - 1); + SecretKeySpec key = new SecretKeySpec(init, "Blowfish"); + try { + Cipher cipher = Cipher.getInstance("Blowfish"); + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] decrypted = cipher.doFinal(hexStringToByteArray(toDecrypt)); + + return new String(decrypted); + + } catch (Exception e) { + throw new RuntimeException(e); } - return password; + } + + private static boolean isEncryptedPassword(String password) { + if (password == null) + return false; + return password.startsWith("encrypted(") && password.endsWith(")"); } private static byte[] hexStringToByteArray(String s) { http://git-wip-us.apache.org/repos/asf/geode/blob/b61078a1/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConnectCommand.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConnectCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConnectCommand.java new file mode 100644 index 0000000..be556a4 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConnectCommand.java @@ -0,0 +1,572 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.geode.management.internal.cli.commands; + +import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS; +import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; +import static org.apache.geode.distributed.ConfigurationProperties.SSL_CIPHERS; +import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE; +import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD; +import static org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS; +import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE; +import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD; +import static org.apache.geode.management.internal.cli.shell.Gfsh.SSL_ENABLED_CIPHERS; +import static org.apache.geode.management.internal.cli.shell.Gfsh.SSL_ENABLED_PROTOCOLS; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.security.KeyStore; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.commons.lang.StringUtils; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; + +import org.apache.geode.internal.DSFIDFactory; +import org.apache.geode.internal.lang.Initializer; +import org.apache.geode.internal.util.IOUtils; +import org.apache.geode.internal.util.PasswordUtil; +import org.apache.geode.management.cli.CliMetaData; +import org.apache.geode.management.cli.ConverterHint; +import org.apache.geode.management.cli.Result; +import org.apache.geode.management.internal.JmxManagerLocatorRequest; +import org.apache.geode.management.internal.JmxManagerLocatorResponse; +import org.apache.geode.management.internal.SSLUtil; +import org.apache.geode.management.internal.cli.CliUtil; +import org.apache.geode.management.internal.cli.LogWrapper; +import org.apache.geode.management.internal.cli.converters.ConnectionEndpointConverter; +import org.apache.geode.management.internal.cli.domain.ConnectToLocatorResult; +import org.apache.geode.management.internal.cli.i18n.CliStrings; +import org.apache.geode.management.internal.cli.result.InfoResultData; +import org.apache.geode.management.internal.cli.result.ResultBuilder; +import org.apache.geode.management.internal.cli.shell.Gfsh; +import org.apache.geode.management.internal.cli.shell.JmxOperationInvoker; +import org.apache.geode.management.internal.cli.util.ConnectionEndpoint; +import org.apache.geode.management.internal.web.domain.LinkIndex; +import org.apache.geode.management.internal.web.http.support.SimpleHttpRequester; +import org.apache.geode.management.internal.web.shell.HttpOperationInvoker; +import org.apache.geode.management.internal.web.shell.RestHttpOperationInvoker; +import org.apache.geode.security.AuthenticationFailedException; + +public class ConnectCommand implements GfshCommand { + // millis that connect --locator will wait for a response from the locator. + public final static int CONNECT_LOCATOR_TIMEOUT_MS = 60000; // see bug 45971 + + @CliCommand(value = {CliStrings.CONNECT}, help = CliStrings.CONNECT__HELP) + @CliMetaData(shellOnly = true, relatedTopic = {CliStrings.TOPIC_GFSH, CliStrings.TOPIC_GEODE_JMX, + CliStrings.TOPIC_GEODE_MANAGER}) + public Result connect( + @CliOption(key = {CliStrings.CONNECT__LOCATOR}, + unspecifiedDefaultValue = ConnectionEndpointConverter.DEFAULT_LOCATOR_ENDPOINTS, + optionContext = ConnectionEndpoint.LOCATOR_OPTION_CONTEXT, + help = CliStrings.CONNECT__LOCATOR__HELP) ConnectionEndpoint locatorTcpHostPort, + @CliOption(key = {CliStrings.CONNECT__JMX_MANAGER}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + optionContext = ConnectionEndpoint.JMXMANAGER_OPTION_CONTEXT, + help = CliStrings.CONNECT__JMX_MANAGER__HELP) ConnectionEndpoint memberRmiHostPort, + @CliOption(key = {CliStrings.CONNECT__USE_HTTP}, mandatory = false, + specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", + help = CliStrings.CONNECT__USE_HTTP__HELP) boolean useHttp, + @CliOption(key = {CliStrings.CONNECT__URL}, mandatory = false, + unspecifiedDefaultValue = CliStrings.CONNECT__DEFAULT_BASE_URL, + help = CliStrings.CONNECT__URL__HELP) String url, + @CliOption(key = {CliStrings.CONNECT__USERNAME}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__USERNAME__HELP) String userName, + @CliOption(key = {CliStrings.CONNECT__PASSWORD}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__PASSWORD__HELP) String password, + @CliOption(key = {CliStrings.CONNECT__KEY_STORE}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__KEY_STORE__HELP) String keystore, + @CliOption(key = {CliStrings.CONNECT__KEY_STORE_PASSWORD}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__KEY_STORE_PASSWORD__HELP) String keystorePassword, + @CliOption(key = {CliStrings.CONNECT__TRUST_STORE}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__TRUST_STORE__HELP) String truststore, + @CliOption(key = {CliStrings.CONNECT__TRUST_STORE_PASSWORD}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__TRUST_STORE_PASSWORD__HELP) String truststorePassword, + @CliOption(key = {CliStrings.CONNECT__SSL_CIPHERS}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__SSL_CIPHERS__HELP) String sslCiphers, + @CliOption(key = {CliStrings.CONNECT__SSL_PROTOCOLS}, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__SSL_PROTOCOLS__HELP) String sslProtocols, + @CliOption(key = CliStrings.CONNECT__SECURITY_PROPERTIES, + optionContext = ConverterHint.FILE_PATH, + unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, + help = CliStrings.CONNECT__SECURITY_PROPERTIES__HELP) final String gfSecurityPropertiesPath, + @CliOption(key = {CliStrings.CONNECT__USE_SSL}, specifiedDefaultValue = "true", + unspecifiedDefaultValue = "false", + help = CliStrings.CONNECT__USE_SSL__HELP) final boolean useSsl) { + Result result; + String passwordToUse = PasswordUtil.decrypt(password); + String keystoreToUse = keystore; + String keystorePasswordToUse = keystorePassword; + String truststoreToUse = truststore; + String truststorePasswordToUse = truststorePassword; + String sslCiphersToUse = sslCiphers; + String sslProtocolsToUse = sslProtocols; + + Gfsh gfsh = getGfsh(); + if (gfsh != null && gfsh.isConnectedAndReady()) { + return ResultBuilder + .createInfoResult("Already connected to: " + getGfsh().getOperationInvoker().toString()); + } + + Map<String, String> sslConfigProps = null; + try { + if (userName != null && userName.length() > 0) { + if (passwordToUse == null || passwordToUse.length() == 0) { + passwordToUse = gfsh.readPassword(CliStrings.CONNECT__PASSWORD + ": "); + } + if (passwordToUse == null || passwordToUse.length() == 0) { + return ResultBuilder + .createConnectionErrorResult(CliStrings.CONNECT__MSG__JMX_PASSWORD_MUST_BE_SPECIFIED); + } + } + + sslConfigProps = this.readSSLConfiguration(useSsl, keystoreToUse, keystorePasswordToUse, + truststoreToUse, truststorePasswordToUse, sslCiphersToUse, sslProtocolsToUse, + gfSecurityPropertiesPath); + } catch (IOException e) { + return handleExcpetion(e, null); + } + + if (useHttp) { + result = httpConnect(sslConfigProps, useSsl, url, userName, passwordToUse); + } else { + result = jmxConnect(sslConfigProps, memberRmiHostPort, locatorTcpHostPort, useSsl, userName, + passwordToUse, gfSecurityPropertiesPath, false); + } + + return result; + } + + + private Result httpConnect(Map<String, String> sslConfigProps, boolean useSsl, String url, + String userName, String passwordToUse) { + Gfsh gfsh = getGfsh(); + try { + Map<String, String> securityProperties = new HashMap<String, String>(); + + // at this point, if userName is not empty, password should not be empty either + if (userName != null && userName.length() > 0) { + securityProperties.put("security-username", userName); + securityProperties.put("security-password", passwordToUse); + } + + if (useSsl) { + configureHttpsURLConnection(sslConfigProps); + if (url.startsWith("http:")) { + url = url.replace("http:", "https:"); + } + } + + Iterator<String> it = sslConfigProps.keySet().iterator(); + while (it.hasNext()) { + String secKey = it.next(); + securityProperties.put(secKey, sslConfigProps.get(secKey)); + } + + // This is so that SSL termination results in https URLs being returned + String query = (url.startsWith("https")) ? "?scheme=https" : ""; + + LogWrapper.getInstance().warning(String.format( + "Sending HTTP request for Link Index at (%1$s)...", url.concat("/index").concat(query))); + + LinkIndex linkIndex = + new SimpleHttpRequester(gfsh, CONNECT_LOCATOR_TIMEOUT_MS, securityProperties) + .exchange(url.concat("/index").concat(query), LinkIndex.class); + + LogWrapper.getInstance() + .warning(String.format("Received Link Index (%1$s)", linkIndex.toString())); + + HttpOperationInvoker operationInvoker = + new RestHttpOperationInvoker(linkIndex, gfsh, url, securityProperties); + + Initializer.init(operationInvoker); + gfsh.setOperationInvoker(operationInvoker); + + LogWrapper.getInstance() + .info(CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, operationInvoker.toString())); + return ResultBuilder.createInfoResult( + CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, operationInvoker.toString())); + + } catch (Exception e) { + // all other exceptions, just logs it and returns a connection error + if (!(e instanceof SecurityException) && !(e instanceof AuthenticationFailedException)) { + return handleExcpetion(e, null); + } + + // if it's security exception, and we already sent in username and password, still retuns the + // connection error + if (userName != null) { + return handleExcpetion(e, null); + } + + // otherwise, prompt for username and password and retry the conenction + try { + userName = gfsh.readText(CliStrings.CONNECT__USERNAME + ": "); + passwordToUse = gfsh.readPassword(CliStrings.CONNECT__PASSWORD + ": "); + return httpConnect(sslConfigProps, useSsl, url, userName, passwordToUse); + } catch (IOException ioe) { + return handleExcpetion(ioe, null); + } + } finally { + Gfsh.redirectInternalJavaLoggers(); + } + } + + private Result jmxConnect(Map<String, String> sslConfigProps, + ConnectionEndpoint memberRmiHostPort, ConnectionEndpoint locatorTcpHostPort, boolean useSsl, + String userName, String passwordToUse, String gfSecurityPropertiesPath, boolean retry) { + ConnectionEndpoint hostPortToConnect = null; + Gfsh gfsh = getGfsh(); + + try { + + // trying to find the hostPortToConnect, if rmi host port exists, use that, otherwise, use + // locator to find the rmi host port + if (memberRmiHostPort != null) { + hostPortToConnect = memberRmiHostPort; + } else { + // Props required to configure a SocketCreator with SSL. + // Used for gfsh->locator connection & not needed for gfsh->manager connection + if (useSsl || !sslConfigProps.isEmpty()) { + sslConfigProps.put(MCAST_PORT, String.valueOf(0)); + sslConfigProps.put(LOCATORS, ""); + + String sslInfoLogMsg = "Connecting to Locator via SSL."; + if (useSsl) { + sslInfoLogMsg = CliStrings.CONNECT__USE_SSL + " is set to true. " + sslInfoLogMsg; + } + gfsh.logToFile(sslInfoLogMsg, null); + } + + Gfsh.println(CliStrings.format(CliStrings.CONNECT__MSG__CONNECTING_TO_LOCATOR_AT_0, + new Object[] {locatorTcpHostPort.toString(false)})); + ConnectToLocatorResult connectToLocatorResult = + connectToLocator(locatorTcpHostPort.getHost(), locatorTcpHostPort.getPort(), + CONNECT_LOCATOR_TIMEOUT_MS, sslConfigProps); + hostPortToConnect = connectToLocatorResult.getMemberEndpoint(); + + // when locator is configured to use SSL (ssl-enabled=true) but manager is not + // (jmx-manager-ssl=false) + if ((useSsl || !sslConfigProps.isEmpty()) + && !connectToLocatorResult.isJmxManagerSslEnabled()) { + gfsh.logInfo( + CliStrings.CONNECT__USE_SSL + + " is set to true. But JMX Manager doesn't support SSL, connecting without SSL.", + null); + sslConfigProps.clear(); + } + } + + if (!sslConfigProps.isEmpty()) { + gfsh.logToFile("Connecting to manager via SSL.", null); + } + + // print out the connecting endpoint + if (!retry) { + Gfsh.println(CliStrings.format(CliStrings.CONNECT__MSG__CONNECTING_TO_MANAGER_AT_0, + new Object[] {hostPortToConnect.toString(false)})); + } + + InfoResultData infoResultData = ResultBuilder.createInfoResultData(); + JmxOperationInvoker operationInvoker = + new JmxOperationInvoker(hostPortToConnect.getHost(), hostPortToConnect.getPort(), + userName, passwordToUse, sslConfigProps, gfSecurityPropertiesPath); + + gfsh.setOperationInvoker(operationInvoker); + infoResultData.addLine( + CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, hostPortToConnect.toString(false))); + LogWrapper.getInstance().info( + CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, hostPortToConnect.toString(false))); + return ResultBuilder.buildResult(infoResultData); + } catch (Exception e) { + // all other exceptions, just logs it and returns a connection error + if (!(e instanceof SecurityException) && !(e instanceof AuthenticationFailedException)) { + return handleExcpetion(e, hostPortToConnect); + } + + // if it's security exception, and we already sent in username and password, still returns the + // connection error + if (userName != null) { + return handleExcpetion(e, hostPortToConnect); + } + + // otherwise, prompt for username and password and retry the conenction + try { + userName = gfsh.readText(CliStrings.CONNECT__USERNAME + ": "); + passwordToUse = gfsh.readPassword(CliStrings.CONNECT__PASSWORD + ": "); + // GEODE-2250 If no value for both username and password, at this point we need to error to + // avoid a stack overflow. + if (userName == null && passwordToUse == null) + return handleExcpetion(e, hostPortToConnect); + return jmxConnect(sslConfigProps, hostPortToConnect, null, useSsl, userName, passwordToUse, + gfSecurityPropertiesPath, true); + } catch (IOException ioe) { + return handleExcpetion(ioe, hostPortToConnect); + } + } finally { + Gfsh.redirectInternalJavaLoggers(); + } + } + + /** + * Common code to read SSL information. Used by JMX, Locator & HTTP mode connect + */ + private Map<String, String> readSSLConfiguration(boolean useSsl, String keystoreToUse, + String keystorePasswordToUse, String truststoreToUse, String truststorePasswordToUse, + String sslCiphersToUse, String sslProtocolsToUse, String gfSecurityPropertiesPath) + throws IOException { + + Gfsh gfshInstance = getGfsh(); + final Map<String, String> sslConfigProps = new LinkedHashMap<String, String>(); + + // JMX SSL Config 1: + // First from gfsecurity properties file if it's specified OR + // if the default gfsecurity.properties exists useSsl==true + if (useSsl || gfSecurityPropertiesPath != null) { + // reference to hold resolved gfSecurityPropertiesPath + String gfSecurityPropertiesPathToUse = CliUtil.resolvePathname(gfSecurityPropertiesPath); + URL gfSecurityPropertiesUrl = null; + + // Case 1: User has specified gfSecurity properties file + if (StringUtils.isNotBlank(gfSecurityPropertiesPathToUse)) { + // User specified gfSecurity properties doesn't exist + if (!IOUtils.isExistingPathname(gfSecurityPropertiesPathToUse)) { + gfshInstance + .printAsSevere(CliStrings.format(CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE, + "Security ", gfSecurityPropertiesPathToUse)); + } else { + gfSecurityPropertiesUrl = new File(gfSecurityPropertiesPathToUse).toURI().toURL(); + } + } else if (useSsl && gfSecurityPropertiesPath == null) { + // Case 2: User has specified to useSsl but hasn't specified + // gfSecurity properties file. Use default "gfsecurity.properties" + // in current dir, user's home or classpath + gfSecurityPropertiesUrl = ShellCommands.getFileUrl("gfsecurity.properties"); + } + // if 'gfSecurityPropertiesPath' OR gfsecurity.properties has resolvable path + if (gfSecurityPropertiesUrl != null) { + gfshInstance.logToFile("Using security properties file : " + + CliUtil.decodeWithDefaultCharSet(gfSecurityPropertiesUrl.getPath()), null); + Map<String, String> gfsecurityProps = + ShellCommands.loadPropertiesFromURL(gfSecurityPropertiesUrl); + // command line options (if any) would override props in gfsecurity.properties + sslConfigProps.putAll(gfsecurityProps); + } + } + + int numTimesPrompted = 0; + /* + * Using do-while here for a case when --use-ssl=true is specified but no SSL options were + * specified & there was no gfsecurity properties specified or readable in default gfsh + * directory. + * + * NOTE: 2nd round of prompting is done only when sslConfigProps map is empty & useSsl is true - + * so we won't over-write any previous values. + */ + do { + // JMX SSL Config 2: Now read the options + if (numTimesPrompted > 0) { + Gfsh.println("Please specify these SSL Configuration properties: "); + } + + if (numTimesPrompted > 0) { + // NOTE: sslConfigProps map was empty + keystoreToUse = gfshInstance.readText(CliStrings.CONNECT__KEY_STORE + ": "); + } + if (keystoreToUse != null && keystoreToUse.length() > 0) { + if (keystorePasswordToUse == null || keystorePasswordToUse.length() == 0) { + // Check whether specified in gfsecurity props earlier + keystorePasswordToUse = sslConfigProps.get(SSL_KEYSTORE_PASSWORD); + if (keystorePasswordToUse == null || keystorePasswordToUse.length() == 0) { + // not even in properties file, prompt user for it + keystorePasswordToUse = + gfshInstance.readPassword(CliStrings.CONNECT__KEY_STORE_PASSWORD + ": "); + sslConfigProps.put(SSL_KEYSTORE_PASSWORD, keystorePasswordToUse); + } + } else {// For cases where password is already part of command option + sslConfigProps.put(SSL_KEYSTORE_PASSWORD, keystorePasswordToUse); + } + sslConfigProps.put(SSL_KEYSTORE, keystoreToUse); + } + + if (numTimesPrompted > 0) { + truststoreToUse = gfshInstance.readText(CliStrings.CONNECT__TRUST_STORE + ": "); + } + if (truststoreToUse != null && truststoreToUse.length() > 0) { + if (truststorePasswordToUse == null || truststorePasswordToUse.length() == 0) { + // Check whether specified in gfsecurity props earlier? + truststorePasswordToUse = sslConfigProps.get(SSL_TRUSTSTORE_PASSWORD); + if (truststorePasswordToUse == null || truststorePasswordToUse.length() == 0) { + // not even in properties file, prompt user for it + truststorePasswordToUse = + gfshInstance.readPassword(CliStrings.CONNECT__TRUST_STORE_PASSWORD + ": "); + sslConfigProps.put(SSL_TRUSTSTORE_PASSWORD, truststorePasswordToUse); + } + } else {// For cases where password is already part of command option + sslConfigProps.put(SSL_TRUSTSTORE_PASSWORD, truststorePasswordToUse); + } + sslConfigProps.put(SSL_TRUSTSTORE, truststoreToUse); + } + + if (numTimesPrompted > 0) { + sslCiphersToUse = gfshInstance.readText(CliStrings.CONNECT__SSL_CIPHERS + ": "); + } + if (sslCiphersToUse != null && sslCiphersToUse.length() > 0) { + // sslConfigProps.put(DistributionConfig.CLUSTER_SSL_CIPHERS_NAME, sslCiphersToUse); + sslConfigProps.put(SSL_ENABLED_CIPHERS, sslCiphersToUse); + } + + if (numTimesPrompted > 0) { + sslProtocolsToUse = gfshInstance.readText(CliStrings.CONNECT__SSL_PROTOCOLS + ": "); + } + if (sslProtocolsToUse != null && sslProtocolsToUse.length() > 0) { + // sslConfigProps.put(DistributionConfig.CLUSTER_SSL_PROTOCOLS_NAME, sslProtocolsToUse); + sslConfigProps.put(SSL_ENABLED_PROTOCOLS, sslProtocolsToUse); + } + + // SSL is required to be used but no SSL config found + } while (useSsl && sslConfigProps.isEmpty() && (0 == numTimesPrompted++) + && !gfshInstance.isQuietMode()); + return sslConfigProps; + } + + + public static ConnectToLocatorResult connectToLocator(String host, int port, int timeout, + Map<String, String> props) throws IOException { + // register DSFID types first; invoked explicitly so that all message type + // initializations do not happen in first deserialization on a possibly + // "precious" thread + DSFIDFactory.registerTypes(); + + JmxManagerLocatorResponse locatorResponse = + JmxManagerLocatorRequest.send(host, port, timeout, props); + + if (StringUtils.isBlank(locatorResponse.getHost()) || locatorResponse.getPort() == 0) { + Throwable locatorResponseException = locatorResponse.getException(); + String exceptionMessage = CliStrings.CONNECT__MSG__LOCATOR_COULD_NOT_FIND_MANAGER; + + if (locatorResponseException != null) { + String locatorResponseExceptionMessage = locatorResponseException.getMessage(); + locatorResponseExceptionMessage = (StringUtils.isNotBlank(locatorResponseExceptionMessage) + ? locatorResponseExceptionMessage : locatorResponseException.toString()); + exceptionMessage = "Exception caused JMX Manager startup to fail because: '" + .concat(locatorResponseExceptionMessage).concat("'"); + } + + throw new IllegalStateException(exceptionMessage, locatorResponseException); + } + + ConnectionEndpoint memberEndpoint = + new ConnectionEndpoint(locatorResponse.getHost(), locatorResponse.getPort()); + + String resultMessage = CliStrings.format(CliStrings.CONNECT__MSG__CONNECTING_TO_MANAGER_AT_0, + memberEndpoint.toString(false)); + + return new ConnectToLocatorResult(memberEndpoint, resultMessage, + locatorResponse.isJmxManagerSslEnabled()); + } + + private void configureHttpsURLConnection(Map<String, String> sslConfigProps) throws Exception { + String keystoreToUse = sslConfigProps.get(SSL_KEYSTORE); + String keystorePasswordToUse = sslConfigProps.get(SSL_KEYSTORE_PASSWORD); + String truststoreToUse = sslConfigProps.get(SSL_TRUSTSTORE); + String truststorePasswordToUse = sslConfigProps.get(SSL_TRUSTSTORE_PASSWORD); + // Ciphers are not passed to HttpsURLConnection. Could not find a clean way + // to pass this attribute to socket layer (see #51645) + String sslCiphersToUse = sslConfigProps.get(SSL_CIPHERS); + String sslProtocolsToUse = sslConfigProps.get(SSL_PROTOCOLS); + + // Commenting the code to set cipher suites in GFSH rest connect (see #51645) + /* + * if(sslCiphersToUse != null){ System.setProperty("https.cipherSuites", sslCiphersToUse); } + */ + FileInputStream keyStoreStream = null; + FileInputStream trustStoreStream = null; + try { + + KeyManagerFactory keyManagerFactory = null; + if (StringUtils.isNotBlank(keystoreToUse)) { + KeyStore clientKeys = KeyStore.getInstance("JKS"); + keyStoreStream = new FileInputStream(keystoreToUse); + clientKeys.load(keyStoreStream, keystorePasswordToUse.toCharArray()); + + keyManagerFactory = + KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(clientKeys, keystorePasswordToUse.toCharArray()); + } + + // load server public key + TrustManagerFactory trustManagerFactory = null; + if (StringUtils.isNotBlank(truststoreToUse)) { + KeyStore serverPub = KeyStore.getInstance("JKS"); + trustStoreStream = new FileInputStream(truststoreToUse); + serverPub.load(trustStoreStream, truststorePasswordToUse.toCharArray()); + trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(serverPub); + } + + SSLContext ssl = + SSLContext.getInstance(SSLUtil.getSSLAlgo(SSLUtil.readArray(sslProtocolsToUse))); + + ssl.init(keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null, + trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null, + new java.security.SecureRandom()); + + HttpsURLConnection.setDefaultSSLSocketFactory(ssl.getSocketFactory()); + } finally { + if (keyStoreStream != null) { + keyStoreStream.close(); + } + if (trustStoreStream != null) { + trustStoreStream.close(); + } + + } + + + } + + + private Result handleExcpetion(Exception e, ConnectionEndpoint hostPortToConnect) { + String errorMessage = e.getMessage(); + if (hostPortToConnect != null) { + errorMessage = CliStrings.format(CliStrings.CONNECT__MSG__ERROR, + hostPortToConnect.toString(false), e.getMessage()); + } + LogWrapper.getInstance().severe(errorMessage, e); + return ResultBuilder.createConnectionErrorResult(errorMessage); + } + +} http://git-wip-us.apache.org/repos/asf/geode/blob/b61078a1/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ShellCommands.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ShellCommands.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ShellCommands.java index d5e1b27..1aea253 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ShellCommands.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ShellCommands.java @@ -15,23 +15,9 @@ package org.apache.geode.management.internal.cli.commands; -import static org.apache.geode.distributed.ConfigurationProperties.CLUSTER_SSL_CIPHERS; -import static org.apache.geode.distributed.ConfigurationProperties.CLUSTER_SSL_PROTOCOLS; -import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS; -import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; -import static org.apache.geode.distributed.ConfigurationProperties.SSL_CIPHERS; -import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE; -import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD; -import static org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS; -import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE; -import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD; -import static org.apache.geode.management.internal.cli.shell.Gfsh.SSL_ENABLED_CIPHERS; -import static org.apache.geode.management.internal.cli.shell.Gfsh.SSL_ENABLED_PROTOCOLS; - import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; @@ -39,9 +25,7 @@ import java.io.InputStreamReader; import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; -import java.security.KeyStore; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -50,49 +34,28 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; - -import org.apache.commons.lang.StringUtils; import org.springframework.shell.core.ExitShellRequest; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.ClassPathLoader; -import org.apache.geode.internal.DSFIDFactory; -import org.apache.geode.internal.lang.Initializer; import org.apache.geode.internal.lang.SystemUtils; import org.apache.geode.internal.util.IOUtils; -import org.apache.geode.internal.util.PasswordUtil; import org.apache.geode.management.cli.CliMetaData; import org.apache.geode.management.cli.ConverterHint; import org.apache.geode.management.cli.Result; -import org.apache.geode.management.internal.JmxManagerLocatorRequest; -import org.apache.geode.management.internal.JmxManagerLocatorResponse; -import org.apache.geode.management.internal.SSLUtil; import org.apache.geode.management.internal.cli.CliUtil; import org.apache.geode.management.internal.cli.GfshParser; import org.apache.geode.management.internal.cli.LogWrapper; -import org.apache.geode.management.internal.cli.converters.ConnectionEndpointConverter; -import org.apache.geode.management.internal.cli.domain.ConnectToLocatorResult; import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.geode.management.internal.cli.result.ErrorResultData; import org.apache.geode.management.internal.cli.result.InfoResultData; import org.apache.geode.management.internal.cli.result.ResultBuilder; import org.apache.geode.management.internal.cli.result.TabularResultData; import org.apache.geode.management.internal.cli.shell.Gfsh; -import org.apache.geode.management.internal.cli.shell.JmxOperationInvoker; import org.apache.geode.management.internal.cli.shell.OperationInvoker; import org.apache.geode.management.internal.cli.shell.jline.GfshHistory; -import org.apache.geode.management.internal.cli.util.ConnectionEndpoint; -import org.apache.geode.management.internal.web.domain.LinkIndex; -import org.apache.geode.management.internal.web.http.support.SimpleHttpRequester; -import org.apache.geode.management.internal.web.shell.HttpOperationInvoker; -import org.apache.geode.management.internal.web.shell.RestHttpOperationInvoker; -import org.apache.geode.security.AuthenticationFailedException; /** * @@ -100,13 +63,6 @@ import org.apache.geode.security.AuthenticationFailedException; */ public class ShellCommands implements GfshCommand { - // millis that connect --locator will wait for a response from the locator. - private final static int CONNECT_LOCATOR_TIMEOUT_MS = 60000; // see bug 45971 - - public static int getConnectLocatorTimeoutInMS() { - return ShellCommands.CONNECT_LOCATOR_TIMEOUT_MS; - } - /* package-private */ static Map<String, String> loadPropertiesFromURL(URL gfSecurityPropertiesUrl) { Map<String, String> propsMap = Collections.emptyMap(); @@ -173,41 +129,6 @@ public class ShellCommands implements GfshCommand { return ClassPathLoader.getLatest().getResource(ShellCommands.class, fileName); } - public static ConnectToLocatorResult connectToLocator(String host, int port, int timeout, - Map<String, String> props) throws IOException { - // register DSFID types first; invoked explicitly so that all message type - // initializations do not happen in first deserialization on a possibly - // "precious" thread - DSFIDFactory.registerTypes(); - - JmxManagerLocatorResponse locatorResponse = - JmxManagerLocatorRequest.send(host, port, timeout, props); - - if (StringUtils.isBlank(locatorResponse.getHost()) || locatorResponse.getPort() == 0) { - Throwable locatorResponseException = locatorResponse.getException(); - String exceptionMessage = CliStrings.CONNECT__MSG__LOCATOR_COULD_NOT_FIND_MANAGER; - - if (locatorResponseException != null) { - String locatorResponseExceptionMessage = locatorResponseException.getMessage(); - locatorResponseExceptionMessage = (StringUtils.isNotBlank(locatorResponseExceptionMessage) - ? locatorResponseExceptionMessage : locatorResponseException.toString()); - exceptionMessage = "Exception caused JMX Manager startup to fail because: '" - .concat(locatorResponseExceptionMessage).concat("'"); - } - - throw new IllegalStateException(exceptionMessage, locatorResponseException); - } - - ConnectionEndpoint memberEndpoint = - new ConnectionEndpoint(locatorResponse.getHost(), locatorResponse.getPort()); - - String resultMessage = CliStrings.format(CliStrings.CONNECT__MSG__CONNECTING_TO_MANAGER_AT_0, - memberEndpoint.toString(false)); - - return new ConnectToLocatorResult(memberEndpoint, resultMessage, - locatorResponse.isJmxManagerSslEnabled()); - } - private static InfoResultData executeCommand(Gfsh gfsh, String userCommand, boolean useConsole) throws IOException { InfoResultData infoResultData = ResultBuilder.createInfoResultData(); @@ -265,467 +186,7 @@ public class ShellCommands implements GfshCommand { return exitShellRequest; } - @CliCommand(value = {CliStrings.CONNECT}, help = CliStrings.CONNECT__HELP) - @CliMetaData(shellOnly = true, relatedTopic = {CliStrings.TOPIC_GFSH, CliStrings.TOPIC_GEODE_JMX, - CliStrings.TOPIC_GEODE_MANAGER}) - public Result connect( - @CliOption(key = {CliStrings.CONNECT__LOCATOR}, - unspecifiedDefaultValue = ConnectionEndpointConverter.DEFAULT_LOCATOR_ENDPOINTS, - optionContext = ConnectionEndpoint.LOCATOR_OPTION_CONTEXT, - help = CliStrings.CONNECT__LOCATOR__HELP) ConnectionEndpoint locatorTcpHostPort, - @CliOption(key = {CliStrings.CONNECT__JMX_MANAGER}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - optionContext = ConnectionEndpoint.JMXMANAGER_OPTION_CONTEXT, - help = CliStrings.CONNECT__JMX_MANAGER__HELP) ConnectionEndpoint memberRmiHostPort, - @CliOption(key = {CliStrings.CONNECT__USE_HTTP}, mandatory = false, - specifiedDefaultValue = "true", unspecifiedDefaultValue = "false", - help = CliStrings.CONNECT__USE_HTTP__HELP) boolean useHttp, - @CliOption(key = {CliStrings.CONNECT__URL}, mandatory = false, - unspecifiedDefaultValue = CliStrings.CONNECT__DEFAULT_BASE_URL, - help = CliStrings.CONNECT__URL__HELP) String url, - @CliOption(key = {CliStrings.CONNECT__USERNAME}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__USERNAME__HELP) String userName, - @CliOption(key = {CliStrings.CONNECT__PASSWORD}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__PASSWORD__HELP) String password, - @CliOption(key = {CliStrings.CONNECT__KEY_STORE}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__KEY_STORE__HELP) String keystore, - @CliOption(key = {CliStrings.CONNECT__KEY_STORE_PASSWORD}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__KEY_STORE_PASSWORD__HELP) String keystorePassword, - @CliOption(key = {CliStrings.CONNECT__TRUST_STORE}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__TRUST_STORE__HELP) String truststore, - @CliOption(key = {CliStrings.CONNECT__TRUST_STORE_PASSWORD}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__TRUST_STORE_PASSWORD__HELP) String truststorePassword, - @CliOption(key = {CliStrings.CONNECT__SSL_CIPHERS}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__SSL_CIPHERS__HELP) String sslCiphers, - @CliOption(key = {CliStrings.CONNECT__SSL_PROTOCOLS}, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__SSL_PROTOCOLS__HELP) String sslProtocols, - @CliOption(key = CliStrings.CONNECT__SECURITY_PROPERTIES, - optionContext = ConverterHint.FILE_PATH, - unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, - help = CliStrings.CONNECT__SECURITY_PROPERTIES__HELP) final String gfSecurityPropertiesPath, - @CliOption(key = {CliStrings.CONNECT__USE_SSL}, specifiedDefaultValue = "true", - unspecifiedDefaultValue = "false", - help = CliStrings.CONNECT__USE_SSL__HELP) final boolean useSsl) { - Result result; - String passwordToUse = decrypt(password); - String keystoreToUse = keystore; - String keystorePasswordToUse = keystorePassword; - String truststoreToUse = truststore; - String truststorePasswordToUse = truststorePassword; - String sslCiphersToUse = sslCiphers; - String sslProtocolsToUse = sslProtocols; - - Gfsh gfsh = getGfsh(); - if (gfsh != null && gfsh.isConnectedAndReady()) { - return ResultBuilder - .createInfoResult("Already connected to: " + getGfsh().getOperationInvoker().toString()); - } - - Map<String, String> sslConfigProps = null; - try { - if (userName != null && userName.length() > 0) { - if (passwordToUse == null || passwordToUse.length() == 0) { - passwordToUse = gfsh.readPassword(CliStrings.CONNECT__PASSWORD + ": "); - } - if (passwordToUse == null || passwordToUse.length() == 0) { - return ResultBuilder - .createConnectionErrorResult(CliStrings.CONNECT__MSG__JMX_PASSWORD_MUST_BE_SPECIFIED); - } - } - - sslConfigProps = this.readSSLConfiguration(useSsl, keystoreToUse, keystorePasswordToUse, - truststoreToUse, truststorePasswordToUse, sslCiphersToUse, sslProtocolsToUse, - gfSecurityPropertiesPath); - } catch (IOException e) { - return handleExcpetion(e, null); - } - if (useHttp) { - result = httpConnect(sslConfigProps, useSsl, url, userName, passwordToUse); - } else { - result = jmxConnect(sslConfigProps, memberRmiHostPort, locatorTcpHostPort, useSsl, userName, - passwordToUse, gfSecurityPropertiesPath, false); - } - - return result; - } - - private Result httpConnect(Map<String, String> sslConfigProps, boolean useSsl, String url, - String userName, String passwordToUse) { - Gfsh gfsh = getGfsh(); - try { - Map<String, String> securityProperties = new HashMap<String, String>(); - - // at this point, if userName is not empty, password should not be empty either - if (userName != null && userName.length() > 0) { - securityProperties.put("security-username", userName); - securityProperties.put("security-password", passwordToUse); - } - - if (useSsl) { - configureHttpsURLConnection(sslConfigProps); - if (url.startsWith("http:")) { - url = url.replace("http:", "https:"); - } - } - - Iterator<String> it = sslConfigProps.keySet().iterator(); - while (it.hasNext()) { - String secKey = it.next(); - securityProperties.put(secKey, sslConfigProps.get(secKey)); - } - - // This is so that SSL termination results in https URLs being returned - String query = (url.startsWith("https")) ? "?scheme=https" : ""; - - LogWrapper.getInstance().warning(String.format( - "Sending HTTP request for Link Index at (%1$s)...", url.concat("/index").concat(query))); - - LinkIndex linkIndex = - new SimpleHttpRequester(gfsh, CONNECT_LOCATOR_TIMEOUT_MS, securityProperties) - .exchange(url.concat("/index").concat(query), LinkIndex.class); - - LogWrapper.getInstance() - .warning(String.format("Received Link Index (%1$s)", linkIndex.toString())); - - HttpOperationInvoker operationInvoker = - new RestHttpOperationInvoker(linkIndex, gfsh, url, securityProperties); - - Initializer.init(operationInvoker); - gfsh.setOperationInvoker(operationInvoker); - - LogWrapper.getInstance() - .info(CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, operationInvoker.toString())); - return ResultBuilder.createInfoResult( - CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, operationInvoker.toString())); - - } catch (Exception e) { - // all other exceptions, just logs it and returns a connection error - if (!(e instanceof SecurityException) && !(e instanceof AuthenticationFailedException)) { - return handleExcpetion(e, null); - } - - // if it's security exception, and we already sent in username and password, still retuns the - // connection error - if (userName != null) { - return handleExcpetion(e, null); - } - - // otherwise, prompt for username and password and retry the conenction - try { - userName = gfsh.readText(CliStrings.CONNECT__USERNAME + ": "); - passwordToUse = gfsh.readPassword(CliStrings.CONNECT__PASSWORD + ": "); - return httpConnect(sslConfigProps, useSsl, url, userName, passwordToUse); - } catch (IOException ioe) { - return handleExcpetion(ioe, null); - } - } finally { - Gfsh.redirectInternalJavaLoggers(); - } - } - - private Result jmxConnect(Map<String, String> sslConfigProps, - ConnectionEndpoint memberRmiHostPort, ConnectionEndpoint locatorTcpHostPort, boolean useSsl, - String userName, String passwordToUse, String gfSecurityPropertiesPath, boolean retry) { - ConnectionEndpoint hostPortToConnect = null; - Gfsh gfsh = getGfsh(); - - try { - - // trying to find the hostPortToConnect, if rmi host port exists, use that, otherwise, use - // locator to find the rmi host port - if (memberRmiHostPort != null) { - hostPortToConnect = memberRmiHostPort; - } else { - // Props required to configure a SocketCreator with SSL. - // Used for gfsh->locator connection & not needed for gfsh->manager connection - if (useSsl || !sslConfigProps.isEmpty()) { - sslConfigProps.put(MCAST_PORT, String.valueOf(0)); - sslConfigProps.put(LOCATORS, ""); - - String sslInfoLogMsg = "Connecting to Locator via SSL."; - if (useSsl) { - sslInfoLogMsg = CliStrings.CONNECT__USE_SSL + " is set to true. " + sslInfoLogMsg; - } - gfsh.logToFile(sslInfoLogMsg, null); - } - - Gfsh.println(CliStrings.format(CliStrings.CONNECT__MSG__CONNECTING_TO_LOCATOR_AT_0, - new Object[] {locatorTcpHostPort.toString(false)})); - ConnectToLocatorResult connectToLocatorResult = - connectToLocator(locatorTcpHostPort.getHost(), locatorTcpHostPort.getPort(), - CONNECT_LOCATOR_TIMEOUT_MS, sslConfigProps); - hostPortToConnect = connectToLocatorResult.getMemberEndpoint(); - - // when locator is configured to use SSL (ssl-enabled=true) but manager is not - // (jmx-manager-ssl=false) - if ((useSsl || !sslConfigProps.isEmpty()) - && !connectToLocatorResult.isJmxManagerSslEnabled()) { - gfsh.logInfo( - CliStrings.CONNECT__USE_SSL - + " is set to true. But JMX Manager doesn't support SSL, connecting without SSL.", - null); - sslConfigProps.clear(); - } - } - - if (!sslConfigProps.isEmpty()) { - gfsh.logToFile("Connecting to manager via SSL.", null); - } - - // print out the connecting endpoint - if (!retry) { - Gfsh.println(CliStrings.format(CliStrings.CONNECT__MSG__CONNECTING_TO_MANAGER_AT_0, - new Object[] {hostPortToConnect.toString(false)})); - } - - InfoResultData infoResultData = ResultBuilder.createInfoResultData(); - JmxOperationInvoker operationInvoker = - new JmxOperationInvoker(hostPortToConnect.getHost(), hostPortToConnect.getPort(), - userName, passwordToUse, sslConfigProps, gfSecurityPropertiesPath); - - gfsh.setOperationInvoker(operationInvoker); - infoResultData.addLine( - CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, hostPortToConnect.toString(false))); - LogWrapper.getInstance().info( - CliStrings.format(CliStrings.CONNECT__MSG__SUCCESS, hostPortToConnect.toString(false))); - return ResultBuilder.buildResult(infoResultData); - } catch (Exception e) { - // all other exceptions, just logs it and returns a connection error - if (!(e instanceof SecurityException) && !(e instanceof AuthenticationFailedException)) { - return handleExcpetion(e, hostPortToConnect); - } - - // if it's security exception, and we already sent in username and password, still returns the - // connection error - if (userName != null) { - return handleExcpetion(e, hostPortToConnect); - } - - // otherwise, prompt for username and password and retry the conenction - try { - userName = gfsh.readText(CliStrings.CONNECT__USERNAME + ": "); - passwordToUse = gfsh.readPassword(CliStrings.CONNECT__PASSWORD + ": "); - // GEODE-2250 If no value for both username and password, at this point we need to error to - // avoid a stack overflow. - if (userName == null && passwordToUse == null) - return handleExcpetion(e, hostPortToConnect); - return jmxConnect(sslConfigProps, hostPortToConnect, null, useSsl, userName, passwordToUse, - gfSecurityPropertiesPath, true); - } catch (IOException ioe) { - return handleExcpetion(ioe, hostPortToConnect); - } - } finally { - Gfsh.redirectInternalJavaLoggers(); - } - } - - private Result handleExcpetion(Exception e, ConnectionEndpoint hostPortToConnect) { - String errorMessage = e.getMessage(); - if (hostPortToConnect != null) { - errorMessage = CliStrings.format(CliStrings.CONNECT__MSG__ERROR, - hostPortToConnect.toString(false), e.getMessage()); - } - LogWrapper.getInstance().severe(errorMessage, e); - return ResultBuilder.createConnectionErrorResult(errorMessage); - } - - private String decrypt(String password) { - if (password != null) { - return PasswordUtil.decrypt(password); - } - return null; - } - - private void configureHttpsURLConnection(Map<String, String> sslConfigProps) throws Exception { - String keystoreToUse = sslConfigProps.get(SSL_KEYSTORE); - String keystorePasswordToUse = sslConfigProps.get(SSL_KEYSTORE_PASSWORD); - String truststoreToUse = sslConfigProps.get(SSL_TRUSTSTORE); - String truststorePasswordToUse = sslConfigProps.get(SSL_TRUSTSTORE_PASSWORD); - // Ciphers are not passed to HttpsURLConnection. Could not find a clean way - // to pass this attribute to socket layer (see #51645) - String sslCiphersToUse = sslConfigProps.get(SSL_CIPHERS); - String sslProtocolsToUse = sslConfigProps.get(SSL_PROTOCOLS); - - // Commenting the code to set cipher suites in GFSH rest connect (see #51645) - /* - * if(sslCiphersToUse != null){ System.setProperty("https.cipherSuites", sslCiphersToUse); } - */ - FileInputStream keyStoreStream = null; - FileInputStream trustStoreStream = null; - try { - - KeyManagerFactory keyManagerFactory = null; - if (StringUtils.isNotBlank(keystoreToUse)) { - KeyStore clientKeys = KeyStore.getInstance("JKS"); - keyStoreStream = new FileInputStream(keystoreToUse); - clientKeys.load(keyStoreStream, keystorePasswordToUse.toCharArray()); - - keyManagerFactory = - KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(clientKeys, keystorePasswordToUse.toCharArray()); - } - - // load server public key - TrustManagerFactory trustManagerFactory = null; - if (StringUtils.isNotBlank(truststoreToUse)) { - KeyStore serverPub = KeyStore.getInstance("JKS"); - trustStoreStream = new FileInputStream(truststoreToUse); - serverPub.load(trustStoreStream, truststorePasswordToUse.toCharArray()); - trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(serverPub); - } - - SSLContext ssl = - SSLContext.getInstance(SSLUtil.getSSLAlgo(SSLUtil.readArray(sslProtocolsToUse))); - - ssl.init(keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null, - trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null, - new java.security.SecureRandom()); - - HttpsURLConnection.setDefaultSSLSocketFactory(ssl.getSocketFactory()); - } finally { - if (keyStoreStream != null) { - keyStoreStream.close(); - } - if (trustStoreStream != null) { - trustStoreStream.close(); - } - - } - - - } - - /** - * Common code to read SSL information. Used by JMX, Locator & HTTP mode connect - */ - private Map<String, String> readSSLConfiguration(boolean useSsl, String keystoreToUse, - String keystorePasswordToUse, String truststoreToUse, String truststorePasswordToUse, - String sslCiphersToUse, String sslProtocolsToUse, String gfSecurityPropertiesPath) - throws IOException { - - Gfsh gfshInstance = getGfsh(); - final Map<String, String> sslConfigProps = new LinkedHashMap<String, String>(); - - // JMX SSL Config 1: - // First from gfsecurity properties file if it's specified OR - // if the default gfsecurity.properties exists useSsl==true - if (useSsl || gfSecurityPropertiesPath != null) { - // reference to hold resolved gfSecurityPropertiesPath - String gfSecurityPropertiesPathToUse = CliUtil.resolvePathname(gfSecurityPropertiesPath); - URL gfSecurityPropertiesUrl = null; - - // Case 1: User has specified gfSecurity properties file - if (StringUtils.isNotBlank(gfSecurityPropertiesPathToUse)) { - // User specified gfSecurity properties doesn't exist - if (!IOUtils.isExistingPathname(gfSecurityPropertiesPathToUse)) { - gfshInstance - .printAsSevere(CliStrings.format(CliStrings.GEODE_0_PROPERTIES_1_NOT_FOUND_MESSAGE, - "Security ", gfSecurityPropertiesPathToUse)); - } else { - gfSecurityPropertiesUrl = new File(gfSecurityPropertiesPathToUse).toURI().toURL(); - } - } else if (useSsl && gfSecurityPropertiesPath == null) { - // Case 2: User has specified to useSsl but hasn't specified - // gfSecurity properties file. Use default "gfsecurity.properties" - // in current dir, user's home or classpath - gfSecurityPropertiesUrl = getFileUrl("gfsecurity.properties"); - } - // if 'gfSecurityPropertiesPath' OR gfsecurity.properties has resolvable path - if (gfSecurityPropertiesUrl != null) { - gfshInstance.logToFile("Using security properties file : " - + CliUtil.decodeWithDefaultCharSet(gfSecurityPropertiesUrl.getPath()), null); - Map<String, String> gfsecurityProps = loadPropertiesFromURL(gfSecurityPropertiesUrl); - // command line options (if any) would override props in gfsecurity.properties - sslConfigProps.putAll(gfsecurityProps); - } - } - - int numTimesPrompted = 0; - /* - * Using do-while here for a case when --use-ssl=true is specified but no SSL options were - * specified & there was no gfsecurity properties specified or readable in default gfsh - * directory. - * - * NOTE: 2nd round of prompting is done only when sslConfigProps map is empty & useSsl is true - - * so we won't over-write any previous values. - */ - do { - // JMX SSL Config 2: Now read the options - if (numTimesPrompted > 0) { - Gfsh.println("Please specify these SSL Configuration properties: "); - } - - if (numTimesPrompted > 0) { - // NOTE: sslConfigProps map was empty - keystoreToUse = gfshInstance.readText(CliStrings.CONNECT__KEY_STORE + ": "); - } - if (keystoreToUse != null && keystoreToUse.length() > 0) { - if (keystorePasswordToUse == null || keystorePasswordToUse.length() == 0) { - // Check whether specified in gfsecurity props earlier - keystorePasswordToUse = sslConfigProps.get(SSL_KEYSTORE_PASSWORD); - if (keystorePasswordToUse == null || keystorePasswordToUse.length() == 0) { - // not even in properties file, prompt user for it - keystorePasswordToUse = - gfshInstance.readPassword(CliStrings.CONNECT__KEY_STORE_PASSWORD + ": "); - sslConfigProps.put(SSL_KEYSTORE_PASSWORD, keystorePasswordToUse); - } - } else {// For cases where password is already part of command option - sslConfigProps.put(SSL_KEYSTORE_PASSWORD, keystorePasswordToUse); - } - sslConfigProps.put(SSL_KEYSTORE, keystoreToUse); - } - - if (numTimesPrompted > 0) { - truststoreToUse = gfshInstance.readText(CliStrings.CONNECT__TRUST_STORE + ": "); - } - if (truststoreToUse != null && truststoreToUse.length() > 0) { - if (truststorePasswordToUse == null || truststorePasswordToUse.length() == 0) { - // Check whether specified in gfsecurity props earlier? - truststorePasswordToUse = sslConfigProps.get(SSL_TRUSTSTORE_PASSWORD); - if (truststorePasswordToUse == null || truststorePasswordToUse.length() == 0) { - // not even in properties file, prompt user for it - truststorePasswordToUse = - gfshInstance.readPassword(CliStrings.CONNECT__TRUST_STORE_PASSWORD + ": "); - sslConfigProps.put(SSL_TRUSTSTORE_PASSWORD, truststorePasswordToUse); - } - } else {// For cases where password is already part of command option - sslConfigProps.put(SSL_TRUSTSTORE_PASSWORD, truststorePasswordToUse); - } - sslConfigProps.put(SSL_TRUSTSTORE, truststoreToUse); - } - - if (numTimesPrompted > 0) { - sslCiphersToUse = gfshInstance.readText(CliStrings.CONNECT__SSL_CIPHERS + ": "); - } - if (sslCiphersToUse != null && sslCiphersToUse.length() > 0) { - // sslConfigProps.put(DistributionConfig.CLUSTER_SSL_CIPHERS_NAME, sslCiphersToUse); - sslConfigProps.put(SSL_ENABLED_CIPHERS, sslCiphersToUse); - } - - if (numTimesPrompted > 0) { - sslProtocolsToUse = gfshInstance.readText(CliStrings.CONNECT__SSL_PROTOCOLS + ": "); - } - if (sslProtocolsToUse != null && sslProtocolsToUse.length() > 0) { - // sslConfigProps.put(DistributionConfig.CLUSTER_SSL_PROTOCOLS_NAME, sslProtocolsToUse); - sslConfigProps.put(SSL_ENABLED_PROTOCOLS, sslProtocolsToUse); - } - - // SSL is required to be used but no SSL config found - } while (useSsl && sslConfigProps.isEmpty() && (0 == numTimesPrompted++) - && !gfshInstance.isQuietMode()); - return sslConfigProps; - } @CliCommand(value = {CliStrings.DISCONNECT}, help = CliStrings.DISCONNECT__HELP) @CliMetaData(shellOnly = true, relatedTopic = {CliStrings.TOPIC_GFSH, CliStrings.TOPIC_GEODE_JMX, http://git-wip-us.apache.org/repos/asf/geode/blob/b61078a1/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommand.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommand.java index 10dc0db..5b289a5 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommand.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/StartLocatorCommand.java @@ -367,8 +367,8 @@ public class StartLocatorCommand implements GfshCommand { for (int attempts = 0; (attempts < 10 && !connectSuccess); attempts++) { try { ConnectToLocatorResult connectToLocatorResult = - ShellCommands.connectToLocator(locatorHostname, locatorPort, - ShellCommands.getConnectLocatorTimeoutInMS() / 4, locatorConfigurationProperties); + ConnectCommand.connectToLocator(locatorHostname, locatorPort, + ConnectCommand.CONNECT_LOCATOR_TIMEOUT_MS / 4, locatorConfigurationProperties); ConnectionEndpoint memberEndpoint = connectToLocatorResult.getMemberEndpoint(); http://git-wip-us.apache.org/repos/asf/geode/blob/b61078a1/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/GfshConsoleReader.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/GfshConsoleReader.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/GfshConsoleReader.java index 80a20e6..9251d7e 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/GfshConsoleReader.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/GfshConsoleReader.java @@ -66,22 +66,3 @@ public class GfshConsoleReader extends GfeConsoleReader { return password; } } - -// package org.apache.geode.management.internal.cli.util; -// -// import org.apache.geode.internal.GfeConsoleReaderFactory.GfeConsoleReader; -// -// public class GfshConsoleReader extends GfeConsoleReader { -// -// public boolean isSupported() { -// return false; -// } -// -// public String readLine(String textToPrompt) { -// throw new UnsupportedOperationException("readLine() not supported for GfshConsoleReader"); -// } -// -// public char[] readPassword(String textToPrompt) { -// throw new UnsupportedOperationException("readPassword() not supported for GfshConsoleReader"); -// } -// } http://git-wip-us.apache.org/repos/asf/geode/blob/b61078a1/geode-core/src/test/java/org/apache/geode/internal/util/PasswordUtilJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/internal/util/PasswordUtilJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/util/PasswordUtilJUnitTest.java index 8051c56..2430ef2 100644 --- a/geode-core/src/test/java/org/apache/geode/internal/util/PasswordUtilJUnitTest.java +++ b/geode-core/src/test/java/org/apache/geode/internal/util/PasswordUtilJUnitTest.java @@ -15,7 +15,7 @@ package org.apache.geode.internal.util; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -36,6 +36,11 @@ public class PasswordUtilJUnitTest { String password = "password"; String encrypted = "encrypted(C3CDC3485F7FF64381841CD344CBDF8A)"; String decrypted = PasswordUtil.decrypt(encrypted); - assertEquals(password, decrypted); + assertThat(decrypted).isEqualTo(password); + } + + @Test + public void testNullPassword() throws Exception { + assertThat(PasswordUtil.decrypt(null)).isNull(); } }