NIFI-2569 - Multiple instances on same host, range enhancements, nifi.properties incrementing ports NIFI-2569 - Updating main class in windows bat file

This closes #861.

Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
Bryan Rosander 2016-08-11 16:32:32 -04:00 committed by Bryan Bende
parent b0122c6a73
commit 2fd39676a8
No known key found for this signature in database
GPG Key ID: A0DDA9ED50711C39
21 changed files with 1315 additions and 249 deletions

View File

@ -33,7 +33,7 @@ goto startConfig
:startConfig
set LIB_DIR=%~dp0..\classpath;%~dp0..\lib
SET JAVA_PARAMS=-cp %LIB_DIR%\* -Xms12m -Xmx24m %JAVA_ARGS% org.apache.nifi.toolkit.tls.service.server.TlsCertificateAuthorityService
SET JAVA_PARAMS=-cp %LIB_DIR%\* -Xms12m -Xmx24m %JAVA_ARGS% org.apache.nifi.toolkit.tls.TlsToolkitMain
cmd.exe /C "%JAVA_EXE%" %JAVA_PARAMS% %*

View File

@ -0,0 +1,23 @@
#
# 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.
#
# Comma separated list of properties to put the hostname into
hostname.properties=nifi.remote.input.host,nifi.web.https.host,nifi.cluster.node.address
nifi.web.https.port=9443
nifi.remote.input.socket.port=10443
nifi.cluster.node.protocol.port=11443

View File

@ -33,6 +33,7 @@ import java.util.Map;
*/
public class TlsToolkitMain {
public static final String DESCRIPTION = "DESCRIPTION";
public static final String UNABLE_TO_GET_DESCRIPTION = "Unable to get description. (";
private final Map<String, Class<?>> mainMap;
public TlsToolkitMain() {
@ -46,7 +47,7 @@ public class TlsToolkitMain {
new TlsToolkitMain().doMain(args);
}
private void printUsageAndExit(String message, ExitCode exitCode) {
private <T> T printUsageAndExit(String message, ExitCode exitCode) {
System.out.println(message);
System.out.println();
System.out.println("Usage: tls-toolkit service [-h] [args]");
@ -55,14 +56,32 @@ public class TlsToolkitMain {
mainMap.forEach((s, aClass) -> System.out.println(" " + s + ": " + getDescription(aClass)));
System.out.println();
System.exit(exitCode.ordinal());
return null;
}
private String getDescription(Class<?> clazz) {
protected String getDescription(Class<?> clazz) {
try {
Field declaredField = clazz.getDeclaredField(DESCRIPTION);
return String.valueOf(declaredField.get(null));
} catch (Exception e) {
return "Unable to get description. (" + e.getMessage() + ")";
return UNABLE_TO_GET_DESCRIPTION + e.getMessage() + ")";
}
}
protected Map<String, Class<?>> getMainMap() {
return mainMap;
}
protected Method getMain(String service) {
Class<?> mainClass = mainMap.get(service);
if (mainClass == null) {
printUsageAndExit("Unknown service: " + service, ExitCode.INVALID_ARGS);
}
try {
return mainClass.getDeclaredMethod("main", String[].class);
} catch (NoSuchMethodException e) {
return printUsageAndExit("Service " + service + " is missing main method.", ExitCode.SERVICE_ERROR);
}
}
@ -72,21 +91,9 @@ public class TlsToolkitMain {
}
String service = args[0].toLowerCase();
Class<?> mainClass = mainMap.get(service);
if (mainClass == null) {
printUsageAndExit("Unknown service: " + service, ExitCode.INVALID_ARGS);
}
Method main;
try {
main = mainClass.getDeclaredMethod("main", String[].class);
} catch (NoSuchMethodException e) {
printUsageAndExit("Service " + service + " is missing main method.", ExitCode.SERVICE_ERROR);
return;
}
try {
main.invoke(null, (Object) args);
getMain(service).invoke(null, (Object) args);
} catch (IllegalAccessException e) {
printUsageAndExit("Service " + service + " has invalid main method.", ExitCode.SERVICE_ERROR);
} catch (InvocationTargetException e) {

View File

@ -0,0 +1,109 @@
/*
* 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.nifi.toolkit.tls.configuration;
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandaloneCommandLine;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Contains an instance identifier, a number that either corresponds to the instance identifier or to the global order and relevant passwords
*/
public class InstanceDefinition {
private final InstanceIdentifier instanceIdentifier;
private final int number;
private final String keyStorePassword;
private final String keyPassword;
private final String trustStorePassword;
public InstanceDefinition(InstanceIdentifier instanceIdentifier, int number, String keyStorePassword, String keyPassword, String trustStorePassword) {
this.number = number;
this.instanceIdentifier = instanceIdentifier;
this.keyStorePassword = keyStorePassword;
this.keyPassword = keyPassword;
this.trustStorePassword = trustStorePassword;
}
/**
* Creates a list of instance definitions
*
* @param fullHostNameExpressions the host expressions defining the global order (null if not relevant)
* @param currentHostnameExpressions the host expressions to create instance definitions for
* @param keyStorePasswords a supplier for keyStorePasswords
* @param keyPasswords a supplier for keyPasswords
* @param trustStorePasswords a supplier for trustStorePasswords
* @return a list of instance definitions
*/
public static List<InstanceDefinition> createDefinitions(Stream<String> fullHostNameExpressions, Stream<String> currentHostnameExpressions, Supplier<String> keyStorePasswords,
Supplier<String> keyPasswords, Supplier<String> trustStorePasswords) {
if (fullHostNameExpressions == null) {
return InstanceIdentifier.createIdentifiers(currentHostnameExpressions).map(id -> createDefinition(id, id.getNumber(), keyStorePasswords, keyPasswords, trustStorePasswords))
.collect(Collectors.toList());
} else {
Map<InstanceIdentifier, Integer> orderMap = InstanceIdentifier.createOrderMap(fullHostNameExpressions);
return InstanceIdentifier.createIdentifiers(currentHostnameExpressions).map(id -> {
Integer number = orderMap.get(id);
if (number == null) {
throw new IllegalArgumentException("Unable to find " + id.getHostname() + " in specified " + TlsToolkitStandaloneCommandLine.GLOBAL_PORT_SEQUENCE_ARG + " expression(s).");
}
return createDefinition(id, number, keyStorePasswords, keyPasswords, trustStorePasswords);
}).collect(Collectors.toList());
}
}
protected static InstanceDefinition createDefinition(InstanceIdentifier instanceIdentifier, int number, Supplier<String> keyStorePasswords, Supplier<String> keyPasswords,
Supplier<String> trustStorePasswords) {
String keyStorePassword = keyStorePasswords.get();
String keyPassword;
if (keyPasswords == null) {
keyPassword = keyStorePassword;
} else {
keyPassword = keyPasswords.get();
}
String trustStorePassword = trustStorePasswords.get();
return new InstanceDefinition(instanceIdentifier, number, keyStorePassword, keyPassword, trustStorePassword);
}
public String getHostname() {
return instanceIdentifier.getHostname();
}
public int getNumber() {
return number;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public String getKeyPassword() {
return keyPassword;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
public InstanceIdentifier getInstanceIdentifier() {
return instanceIdentifier;
}
}

View File

@ -0,0 +1,174 @@
/*
* 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.nifi.toolkit.tls.configuration;
import org.apache.nifi.util.StringUtils;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* Each instance is uniquely identified by its hostname and instance number
*/
public class InstanceIdentifier {
public static final Comparator<InstanceIdentifier> HOST_IDENTIFIER_COMPARATOR = (o1, o2) -> {
int i = o1.getHostname().compareTo(o2.getHostname());
if (i == 0) {
return o1.getNumber() - o2.getNumber();
}
return i;
};
private static final Pattern RANGE_PATTERN = Pattern.compile("^[0-9]+(-[0-9]+)?$");
private final String hostname;
private final int number;
public InstanceIdentifier(String hostname, int number) {
this.hostname = hostname;
this.number = number;
}
/**
* Creates a map that can be used to deterministically assign global instance numbers
*
* @param hostnameExpressions the hostname expressions to expand
* @return a map that can be used to deterministically assign global instance numbers
*/
public static Map<InstanceIdentifier, Integer> createOrderMap(Stream<String> hostnameExpressions) {
List<InstanceIdentifier> instanceIdentifiers = createIdentifiers(hostnameExpressions).sorted(HOST_IDENTIFIER_COMPARATOR).collect(Collectors.toList());
Map<InstanceIdentifier, Integer> result = new HashMap<>();
for (int i = 0; i < instanceIdentifiers.size(); i++) {
result.put(instanceIdentifiers.get(i), i + 1);
}
return result;
}
/**
* Creates a stream of hostname identifiers from a stream of hostname expressions
*
* @param hostnameExpressions the hostname expressions
* @return the hostname identifiers
*/
public static Stream<InstanceIdentifier> createIdentifiers(Stream<String> hostnameExpressions) {
return hostnameExpressions.flatMap(hostnameExpression -> extractHostnames(hostnameExpression).flatMap(hostname -> {
ExtractedRange extractedRange = new ExtractedRange(hostname, '(', ')');
if (extractedRange.range == null) {
return Stream.of(new InstanceIdentifier(hostname, 1));
}
if (!StringUtils.isEmpty(extractedRange.afterClose)) {
throw new IllegalArgumentException("No characters expected after )");
}
return extractedRange.range.map(numString -> new InstanceIdentifier(extractedRange.beforeOpen, Integer.parseInt(numString)));
}));
}
protected static Stream<String> extractHostnames(String hostname) {
ExtractedRange extractedRange = new ExtractedRange(hostname, '[', ']');
if (extractedRange.range == null) {
return Stream.of(hostname);
}
return extractedRange.range.map(s -> extractedRange.beforeOpen + s + extractedRange.afterClose).flatMap(InstanceIdentifier::extractHostnames);
}
private static Stream<String> extractRange(String range) {
if (!RANGE_PATTERN.matcher(range).matches()) {
throw new IllegalArgumentException("Expected either one number or two separated by a single hyphen");
}
String[] split = range.split("-");
if (split.length == 1) {
String prefix = "1-";
if (split[0].charAt(0) == '0') {
prefix = String.format("%0" + split[0].length() + "d-", 1);
}
return extractRange(prefix + split[0]);
} else {
int baseLength = split[0].length();
int low = Integer.parseInt(split[0]);
String padding = split[0].substring(0, split[0].length() - Integer.toString(low).length());
int high = Integer.parseInt(split[1]);
return IntStream.range(low, high + 1).mapToObj(i -> {
String s = Integer.toString(i);
int length = s.length();
if (length >= baseLength) {
return s;
} else {
return padding.substring(0, baseLength - length) + s;
}
});
}
}
public String getHostname() {
return hostname;
}
public int getNumber() {
return number;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstanceIdentifier that = (InstanceIdentifier) o;
if (number != that.number) return false;
return hostname != null ? hostname.equals(that.hostname) : that.hostname == null;
}
@Override
public int hashCode() {
int result = hostname != null ? hostname.hashCode() : 0;
result = 31 * result + number;
return result;
}
private static class ExtractedRange {
private final String beforeOpen;
private final Stream<String> range;
private final String afterClose;
public ExtractedRange(String string, char rangeOpen, char rangeClose) {
int openBracket = string.indexOf(rangeOpen);
if (openBracket >= 0) {
int closeBracket = string.indexOf(rangeClose, openBracket);
if (closeBracket < 0) {
throw new IllegalArgumentException("Unable to find matching " + rangeClose + " for " + rangeOpen + " in " + string);
}
beforeOpen = string.substring(0, openBracket);
if (closeBracket + 1 < string.length()) {
afterClose = string.substring(closeBracket + 1);
} else {
afterClose = "";
}
range = extractRange(string.substring(openBracket + 1, closeBracket));
} else {
beforeOpen = string;
range = null;
afterClose = "";
}
}
}
}

View File

@ -25,14 +25,10 @@ import java.util.List;
public class StandaloneConfig extends TlsConfig {
private File baseDir;
private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
private List<String> hostnames;
private List<String> keyStorePasswords;
private List<String> keyPasswords;
private List<String> trustStorePasswords;
private List<InstanceDefinition> instanceDefinitions;
private List<String> clientDns;
private List<String> clientPasswords;
private boolean clientPasswordsGenerated;
private int httpsPort;
private boolean overwrite;
public List<String> getClientDns() {
@ -67,38 +63,6 @@ public class StandaloneConfig extends TlsConfig {
this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory;
}
public List<String> getHostnames() {
return hostnames;
}
public void setHostnames(List<String> hostnames) {
this.hostnames = hostnames;
}
public List<String> getKeyStorePasswords() {
return keyStorePasswords;
}
public void setKeyStorePasswords(List<String> keyStorePasswords) {
this.keyStorePasswords = keyStorePasswords;
}
public List<String> getKeyPasswords() {
return keyPasswords;
}
public void setKeyPasswords(List<String> keyPasswords) {
this.keyPasswords = keyPasswords;
}
public List<String> getTrustStorePasswords() {
return trustStorePasswords;
}
public void setTrustStorePasswords(List<String> trustStorePasswords) {
this.trustStorePasswords = trustStorePasswords;
}
public List<String> getClientPasswords() {
return clientPasswords;
}
@ -107,14 +71,6 @@ public class StandaloneConfig extends TlsConfig {
this.clientPasswords = clientPasswords;
}
public int getHttpsPort() {
return httpsPort;
}
public void setHttpsPort(int httpsPort) {
this.httpsPort = httpsPort;
}
public boolean isClientPasswordsGenerated() {
return clientPasswordsGenerated;
}
@ -122,4 +78,12 @@ public class StandaloneConfig extends TlsConfig {
public void setClientPasswordsGenerated(boolean clientPasswordsGenerated) {
this.clientPasswordsGenerated = clientPasswordsGenerated;
}
public List<InstanceDefinition> getInstanceDefinitions() {
return instanceDefinitions;
}
public void setInstanceDefinitions(List<InstanceDefinition> instanceDefinitions) {
this.instanceDefinitions = instanceDefinitions;
}
}

View File

@ -27,48 +27,92 @@ import org.apache.nifi.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class NifiPropertiesTlsClientConfigWriter implements ConfigurationWriter<TlsClientConfig> {
public static final String HOSTNAME_PROPERTIES = "hostname.properties";
public static final String OVERLAY_PROPERTIES = "overlay.properties";
public static final String CONF = "./conf/";
private final NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
private final OutputStreamFactory outputStreamFactory;
private final File file;
private final File outputFile;
private final String hostname;
private final int httpsPort;
private final int hostNum;
private final Properties overlayProperties;
private final Set<String> explicitProperties;
public NifiPropertiesTlsClientConfigWriter(NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, OutputStreamFactory outputStreamFactory, File file, String hostname, int httpsPort) {
public NifiPropertiesTlsClientConfigWriter(NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, OutputStreamFactory outputStreamFactory, File outputFile, String hostname, int hostNum)
throws IOException {
this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory;
this.outputStreamFactory = outputStreamFactory;
this.file = file;
this.outputFile = outputFile;
this.hostname = hostname;
this.httpsPort = httpsPort;
this.hostNum = hostNum;
this.overlayProperties = new Properties();
this.overlayProperties.load(getClass().getClassLoader().getResourceAsStream(OVERLAY_PROPERTIES));
HashSet<String> explicitProperties = new HashSet<>();
explicitProperties.add(HOSTNAME_PROPERTIES);
this.explicitProperties = Collections.unmodifiableSet(explicitProperties);
}
@Override
public void write(TlsClientConfig tlsClientConfig) throws IOException {
NiFiPropertiesWriter niFiPropertiesWriter = niFiPropertiesWriterFactory.create();
updateProperties(niFiPropertiesWriter, tlsClientConfig);
try (OutputStream stream = outputStreamFactory.create(file)) {
try (OutputStream stream = outputStreamFactory.create(outputFile)) {
niFiPropertiesWriter.writeNiFiProperties(stream);
}
}
protected void updateProperties(NiFiPropertiesWriter niFiPropertiesWriter, TlsClientConfig tlsClientConfig) {
Path parentPath = Paths.get(file.getParentFile().getAbsolutePath());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE, parentPath.relativize(Paths.get(tlsClientConfig.getKeyStore())).toString());
protected void updateProperties(NiFiPropertiesWriter niFiPropertiesWriter, TlsClientConfig tlsClientConfig) throws IOException {
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE, CONF + new File(tlsClientConfig.getKeyStore()).getName());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsClientConfig.getKeyStoreType());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsClientConfig.getKeyStorePassword());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEY_PASSWD, tlsClientConfig.getKeyPassword());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE, parentPath.relativize(Paths.get(tlsClientConfig.getTrustStore())).toString());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE, CONF + new File(tlsClientConfig.getTrustStore()).getName());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsClientConfig.getTrustStoreType());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsClientConfig.getTrustStorePassword());
if (!StringUtils.isEmpty(hostname)) {
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTPS_HOST, hostname);
}
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTPS_PORT, Integer.toString(httpsPort));
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTP_HOST, "");
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTP_PORT, "");
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SITE_TO_SITE_SECURE, "true");
getHostnamePropertyStream().forEach(s -> niFiPropertiesWriter.setPropertyValue(s, hostname));
getPropertyPortMap().entrySet().forEach(nameToPortEntry -> niFiPropertiesWriter.setPropertyValue(nameToPortEntry.getKey(), Integer.toString(nameToPortEntry.getValue())));
}
protected Properties getOverlayProperties() {
return overlayProperties;
}
protected Map<String, Integer> getPropertyPortMap() {
return overlayProperties.stringPropertyNames().stream().filter(s -> !explicitProperties.contains(s)).collect(Collectors.toMap(Function.identity(), portProperty -> {
String portVal = overlayProperties.getProperty(portProperty);
int startingPort;
try {
startingPort = Integer.parseInt(portVal);
} catch (NumberFormatException e) {
throw new NumberFormatException("Expected numeric values in " + OVERLAY_PROPERTIES + " (" + portProperty + " was " + portVal + ")");
}
return startingPort + hostNum - 1;
}));
}
protected Stream<String> getHostnamePropertyStream() {
String hostnamePropertyString = overlayProperties.getProperty(HOSTNAME_PROPERTIES);
if (!StringUtils.isEmpty(hostnamePropertyString)) {
return Arrays.stream(hostnamePropertyString.split(",")).map(String::trim);
}
return Stream.of();
}
}

View File

@ -18,6 +18,7 @@
package org.apache.nifi.toolkit.tls.standalone;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
@ -84,7 +85,6 @@ public class TlsToolkitStandalone {
X509Certificate certificate;
KeyPair caKeyPair;
List<String> hostnames = standaloneConfig.getHostnames();
if (logger.isInfoEnabled()) {
logger.info("Running standalone certificate generation with output directory " + baseDir);
}
@ -128,19 +128,22 @@ public class TlsToolkitStandalone {
}
}
List<String> keyStorePasswords = standaloneConfig.getKeyStorePasswords();
List<String> keyPasswords = standaloneConfig.getKeyPasswords();
List<String> trustStorePasswords = standaloneConfig.getTrustStorePasswords();
NiFiPropertiesWriterFactory niFiPropertiesWriterFactory = standaloneConfig.getNiFiPropertiesWriterFactory();
int httpsPort = standaloneConfig.getHttpsPort();
boolean overwrite = standaloneConfig.isOverwrite();
if (hostnames.isEmpty() && logger.isInfoEnabled()) {
List<InstanceDefinition> instanceDefinitions = standaloneConfig.getInstanceDefinitions();
if (!instanceDefinitions.isEmpty() && logger.isInfoEnabled()) {
logger.info("No " + TlsToolkitStandaloneCommandLine.HOSTNAMES_ARG + " specified, not generating any host certificates or configuration.");
}
for (int i = 0; i < hostnames.size(); i++) {
String hostname = hostnames.get(i);
File hostDir = new File(baseDir, hostname);
for (InstanceDefinition instanceDefinition : instanceDefinitions) {
String hostname = instanceDefinition.getHostname();
File hostDir;
int hostIdentifierNumber = instanceDefinition.getInstanceIdentifier().getNumber();
if (hostIdentifierNumber == 1) {
hostDir = new File(baseDir, hostname);
} else {
hostDir = new File(baseDir, hostname + "_" + hostIdentifierNumber);
}
TlsClientConfig tlsClientConfig = new TlsClientConfig(standaloneConfig);
File keystore = new File(hostDir, "keystore." + tlsClientConfig.getKeyStoreType().toLowerCase());
@ -171,20 +174,20 @@ public class TlsToolkitStandalone {
}
tlsClientConfig.setKeyStore(keystore.getAbsolutePath());
tlsClientConfig.setKeyStorePassword(keyStorePasswords.get(i));
tlsClientConfig.setKeyPassword(keyPasswords.get(i));
tlsClientConfig.setKeyStorePassword(instanceDefinition.getKeyStorePassword());
tlsClientConfig.setKeyPassword(instanceDefinition.getKeyPassword());
tlsClientConfig.setTrustStore(truststore.getAbsolutePath());
tlsClientConfig.setTrustStorePassword(trustStorePasswords.get(i));
tlsClientConfig.setTrustStorePassword(instanceDefinition.getTrustStorePassword());
TlsClientManager tlsClientManager = new TlsClientManager(tlsClientConfig);
KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
tlsClientManager.addPrivateKeyToKeyStore(keyPair, NIFI_KEY, CertificateUtils.generateIssuedCertificate(TlsConfig.calcDefaultDn(hostname),
keyPair.getPublic(), certificate, caKeyPair, signingAlgorithm, days), certificate);
tlsClientManager.setCertificateEntry(NIFI_CERT, certificate);
tlsClientManager.addClientConfigurationWriter(new NifiPropertiesTlsClientConfigWriter(niFiPropertiesWriterFactory, outputStreamFactory, new File(hostDir, "nifi.properties"),
hostname, httpsPort));
hostname, instanceDefinition.getNumber()));
tlsClientManager.write(outputStreamFactory);
if (logger.isInfoEnabled()) {
logger.info("Successfully generated TLS configuration for " + hostname + ":" + httpsPort + " in " + hostDir);
logger.info("Successfully generated TLS configuration for " + hostname + " " + hostIdentifierNumber + " in " + hostDir);
}
}

View File

@ -21,6 +21,7 @@ import org.apache.commons.cli.CommandLine;
import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
import org.apache.nifi.toolkit.tls.util.PasswordUtil;
@ -33,12 +34,13 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
public static final String OUTPUT_DIRECTORY_ARG = "outputDirectory";
@ -47,13 +49,12 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
public static final String TRUST_STORE_PASSWORD_ARG = "trustStorePassword";
public static final String KEY_PASSWORD_ARG = "keyPassword";
public static final String HOSTNAMES_ARG = "hostnames";
public static final String HTTPS_PORT_ARG = "httpsPort";
public static final String OVERWRITE_ARG = "isOverwrite";
public static final String CLIENT_CERT_DN_ARG = "clientCertDn";
public static final String CLIENT_CERT_PASSWORD_ARG = "clientCertPassword";
public static final String GLOBAL_PORT_SEQUENCE_ARG = "globalPortSequence";
public static final String DEFAULT_OUTPUT_DIRECTORY = "../" + Paths.get(".").toAbsolutePath().normalize().getFileName().toString();
public static final int DEFAULT_HTTPS_PORT = 9091;
public static final String DESCRIPTION = "Creates certificates and config files for nifi cluster.";
@ -61,12 +62,8 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
private final PasswordUtil passwordUtil;
private File baseDir;
private List<String> hostnames;
private int httpsPort;
private List<InstanceDefinition> instanceDefinitions;
private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
private List<String> keyStorePasswords;
private List<String> keyPasswords;
private List<String> trustStorePasswords;
private List<String> clientDns;
private List<String> clientPasswords;
private boolean clientPasswordsGenerated;
@ -81,13 +78,14 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
this.passwordUtil = passwordUtil;
addOptionWithArg("o", OUTPUT_DIRECTORY_ARG, "The directory to output keystores, truststore, config files.", DEFAULT_OUTPUT_DIRECTORY);
addOptionWithArg("n", HOSTNAMES_ARG, "Comma separated list of hostnames.");
addOptionWithArg("p", HTTPS_PORT_ARG, "Https port to use.", DEFAULT_HTTPS_PORT);
addOptionWithArg("f", NIFI_PROPERTIES_FILE_ARG, "Base nifi.properties file to update. (Embedded file identical to the one in a default NiFi install will be used if not specified.)");
addOptionWithArg("S", KEY_STORE_PASSWORD_ARG, "Keystore password to use. Must either be one value or one for each host. (autogenerate if not specified)");
addOptionWithArg("K", KEY_PASSWORD_ARG, "Key password to use. Must either be one value or one for each host. (autogenerate if not specified)");
addOptionWithArg("P", TRUST_STORE_PASSWORD_ARG, "Keystore password to use. Must either be one value or one for each host. (autogenerate if not specified)");
addOptionWithArg("C", CLIENT_CERT_DN_ARG, "Generate client certificate suitable for use in browser with specified DN. (Can be specified multiple times.)");
addOptionWithArg("B", CLIENT_CERT_PASSWORD_ARG, "Password for client certificate. Must either be one value or one for each client DN. (autogenerate if not specified)");
addOptionWithArg("G", GLOBAL_PORT_SEQUENCE_ARG, "Use sequential ports that are calculated for all hosts according to the provided hostname expressions. " +
"(Can be specified multiple times, MUST BE SAME FROM RUN TO RUN.)");
addOptionNoArg("O", OVERWRITE_ARG, "Overwrite existing host output.");
}
@ -114,10 +112,20 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
String outputDirectory = commandLine.getOptionValue(OUTPUT_DIRECTORY_ARG, DEFAULT_OUTPUT_DIRECTORY);
baseDir = new File(outputDirectory);
Stream<String> globalOrderExpressions = null;
if (commandLine.hasOption(GLOBAL_PORT_SEQUENCE_ARG)) {
globalOrderExpressions = Arrays.stream(commandLine.getOptionValues(GLOBAL_PORT_SEQUENCE_ARG)).flatMap(s -> Arrays.stream(s.split(","))).map(String::trim);
}
if (commandLine.hasOption(HOSTNAMES_ARG)) {
hostnames = Collections.unmodifiableList(Arrays.stream(commandLine.getOptionValue(HOSTNAMES_ARG).split(",")).map(String::trim).collect(Collectors.toList()));
instanceDefinitions = Collections.unmodifiableList(
InstanceDefinition.createDefinitions(globalOrderExpressions,
Arrays.stream(commandLine.getOptionValues(HOSTNAMES_ARG)).flatMap(s -> Arrays.stream(s.split(",")).map(String::trim)),
parsePasswordSupplier(commandLine, KEY_STORE_PASSWORD_ARG, passwordUtil.passwordSupplier()),
parsePasswordSupplier(commandLine, KEY_PASSWORD_ARG, commandLine.hasOption(DIFFERENT_KEY_AND_KEYSTORE_PASSWORDS_ARG) ? passwordUtil.passwordSupplier() : null),
parsePasswordSupplier(commandLine, TRUST_STORE_PASSWORD_ARG, passwordUtil.passwordSupplier())));
} else {
hostnames = Collections.emptyList();
instanceDefinitions = Collections.emptyList();
}
String[] clientDnValues = commandLine.getOptionValues(CLIENT_CERT_DN_ARG);
@ -127,12 +135,6 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
clientDns = Collections.emptyList();
}
httpsPort = getIntValue(commandLine, HTTPS_PORT_ARG, DEFAULT_HTTPS_PORT);
int numHosts = hostnames.size();
keyStorePasswords = Collections.unmodifiableList(getPasswords(KEY_STORE_PASSWORD_ARG, commandLine, numHosts, HOSTNAMES_ARG));
keyPasswords = Collections.unmodifiableList(getKeyPasswords(commandLine, keyStorePasswords));
trustStorePasswords = Collections.unmodifiableList(getPasswords(TRUST_STORE_PASSWORD_ARG, commandLine, numHosts, HOSTNAMES_ARG));
clientPasswords = Collections.unmodifiableList(getPasswords(CLIENT_CERT_PASSWORD_ARG, commandLine, clientDns.size(), CLIENT_CERT_DN_ARG));
clientPasswordsGenerated = commandLine.getOptionValues(CLIENT_CERT_PASSWORD_ARG) == null;
overwrite = commandLine.hasOption(OVERWRITE_ARG);
@ -165,11 +167,18 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
return printUsageAndThrow("Expected either 1 value or " + num + " (the number of " + numArg + ") values for " + arg, ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS);
}
private List<String> getKeyPasswords(CommandLine commandLine, List<String> keyStorePasswords) throws CommandLineParseException {
if (differentPasswordForKeyAndKeystore() || commandLine.hasOption(KEY_PASSWORD_ARG)) {
return getPasswords(KEY_PASSWORD_ARG, commandLine, keyStorePasswords.size(), HOSTNAMES_ARG);
private Supplier<String> parsePasswordSupplier(CommandLine commandLine, String option, Supplier<String> defaultSupplier) {
if (commandLine.hasOption(option)) {
String[] values = commandLine.getOptionValues(option);
if (values.length == 1) {
return PasswordUtil.passwordSupplier(values[0]);
} else {
return PasswordUtil.passwordSupplier("Provided " + option + " exhausted, please don't specify " + option
+ ", specify one value to be used for all NiFi instances, or specify one value for each NiFi instance.", values);
}
} else {
return defaultSupplier;
}
return new ArrayList<>(keyStorePasswords);
}
public StandaloneConfig createConfig() {
@ -177,11 +186,7 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
standaloneConfig.setBaseDir(baseDir);
standaloneConfig.setNiFiPropertiesWriterFactory(niFiPropertiesWriterFactory);
standaloneConfig.setHostnames(hostnames);
standaloneConfig.setKeyStorePasswords(keyStorePasswords);
standaloneConfig.setKeyPasswords(keyPasswords);
standaloneConfig.setTrustStorePasswords(trustStorePasswords);
standaloneConfig.setHttpsPort(httpsPort);
standaloneConfig.setInstanceDefinitions(instanceDefinitions);
standaloneConfig.setOverwrite(overwrite);
standaloneConfig.setClientDns(clientDns);
standaloneConfig.setClientPasswords(clientPasswords);

View File

@ -20,6 +20,8 @@ package org.apache.nifi.toolkit.tls.util;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
public class PasswordUtil {
private final SecureRandom secureRandom;
@ -40,4 +42,24 @@ public class PasswordUtil {
}
return string;
}
public Supplier<String> passwordSupplier() {
return () -> generatePassword();
}
public static Supplier<String> passwordSupplier(String password) {
return () -> password;
}
public static Supplier<String> passwordSupplier(String exhaustedMessage, String[] passwords) {
AtomicInteger index = new AtomicInteger(0);
return () -> {
int i = index.getAndIncrement();
if (i < passwords.length) {
return passwords[i];
} else {
throw new PasswordsExhaustedException(exhaustedMessage);
}
};
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.nifi.toolkit.tls.util;
public class PasswordsExhaustedException extends RuntimeException {
public PasswordsExhaustedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.nifi.toolkit.tls;
public class ExitException extends SecurityException {
// [see http://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit#answer-309427]
private final int exitCode;
public ExitException(int exitCode) {
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.nifi.toolkit.tls;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import java.io.Closeable;
import java.io.IOException;
import java.security.Permission;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class SystemExitCapturer implements Closeable {
private final SecurityManager originalSecurityManager;
public SystemExitCapturer() {
originalSecurityManager = System.getSecurityManager();
// [see http://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit#answer-309427]
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm) {
// Noop
}
@Override
public void checkPermission(Permission perm, Object context) {
// Noop
}
@Override
public void checkExit(int status) {
super.checkExit(status);
throw new ExitException(status);
}
});
}
public void runAndAssertExitCode(Runnable runnable, ExitCode exitCode) {
try {
runnable.run();
fail("Expecting exit code " + exitCode);
} catch (ExitException e) {
assertEquals("Expecting exit code: " + exitCode + ", got " + ExitCode.values()[e.getExitCode()], exitCode.ordinal(), e.getExitCode());
}
}
@Override
public void close() throws IOException {
System.setSecurityManager(originalSecurityManager);
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.nifi.toolkit.tls;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.util.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class TlsToolkitMainTest {
private TlsToolkitMain tlsToolkitMain;
private SystemExitCapturer systemExitCapturer;
@Before
public void setup() {
systemExitCapturer = new SystemExitCapturer();
tlsToolkitMain = new TlsToolkitMain();
}
@After
public void tearDown() throws IOException {
systemExitCapturer.close();
}
@Test
public void testAllMainClassesHaveDescription() {
tlsToolkitMain.getMainMap().values().forEach(mainClass -> {
String description = tlsToolkitMain.getDescription(mainClass);
assertFalse(StringUtils.isEmpty(description));
assertFalse(description.contains(TlsToolkitMain.UNABLE_TO_GET_DESCRIPTION));
});
}
@Test
public void testGetDescriptionClassWithNoDescription() {
assertTrue(tlsToolkitMain.getDescription(TlsToolkitMainTest.class).startsWith(TlsToolkitMain.UNABLE_TO_GET_DESCRIPTION));
}
@Test
public void testAllMainClassesHaveMain() {
tlsToolkitMain.getMainMap().keySet().stream().map(String::toLowerCase).forEach(service -> {
assertNotNull(tlsToolkitMain.getMain(service));
});
}
@Test
public void testWrongServiceName() {
systemExitCapturer.runAndAssertExitCode(() -> tlsToolkitMain.doMain(new String[] {"fakeService"}), ExitCode.INVALID_ARGS);
}
@Test
public void testNoArguments() {
systemExitCapturer.runAndAssertExitCode(() -> tlsToolkitMain.doMain(new String[0]), ExitCode.INVALID_ARGS);
}
@Test
public void testInaccessibleMain() {
String privateMain = "privatemain";
tlsToolkitMain.getMainMap().put(privateMain, PrivateMain.class);
systemExitCapturer.runAndAssertExitCode(() -> tlsToolkitMain.doMain(new String[]{privateMain}), ExitCode.SERVICE_ERROR);
}
@Test
public void testInvocationTargetException() {
String throwingMain = "throwingmain";
tlsToolkitMain.getMainMap().put(throwingMain, ThrowingMain.class);
systemExitCapturer.runAndAssertExitCode(() -> tlsToolkitMain.doMain(new String[]{throwingMain}), ExitCode.SERVICE_ERROR);
}
@Test
public void testNoMain() {
String noMain = "nomain";
tlsToolkitMain.getMainMap().put(noMain, NoMain.class);
systemExitCapturer.runAndAssertExitCode(() -> tlsToolkitMain.doMain(new String[]{noMain}), ExitCode.SERVICE_ERROR);
}
private static class PrivateMain {
private static void main(String[] args) {
}
}
private static class ThrowingMain {
public static void main(String[] args) {
throw new IllegalArgumentException();
}
}
private static class NoMain {
}
}

View File

@ -0,0 +1,132 @@
/*
* 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.nifi.toolkit.tls.configuration;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class InstanceDefinitionTest {
@Test
public void testCreateDefinitionKeyPassword() {
testCreateDefinition("testHostname", 4, "keyStorePassword", "keyPassword", "trustStorePassword");
}
@Test
public void testCreateDefinitionNoKeyPassword() {
testCreateDefinition("testHostname", 5, "keyStorePassword", null, "trustStorePassword");
}
@Test
public void testCreateDefinitionsSingleHostSingleName() {
testCreateDefinitions(Arrays.asList("hostname"), Arrays.asList("hostname"), Arrays.asList(1), false);
}
@Test
public void testCreateDefinitionsSingleHostnameOneNumberInParens() {
testCreateDefinitions(Arrays.asList("hostname(20)"),
IntStream.range(1, 21).mapToObj(operand -> "hostname").collect(Collectors.toList()),
integerRange(1, 20).collect(Collectors.toList()), false);
}
@Test
public void testCreateDefinitionsSingleHostnameTwoNumbersInParens() {
testCreateDefinitions(Arrays.asList("hostname(5-20)"),
IntStream.range(5, 21).mapToObj(operand -> "hostname").collect(Collectors.toList()),
integerRange(5, 20).collect(Collectors.toList()), false);
}
@Test
public void testCreateDefinitionsMultipleHostnamesWithMultipleNumbers() {
testCreateDefinitions(Arrays.asList("host[10]name[02-5](20)"),
integerRange(1, 10).flatMap(v -> integerRange(2, 5).flatMap(v2 -> integerRange(1, 20).map(v3 -> "host" + v + "name" + String.format("%02d", v2)))).collect(Collectors.toList()),
integerRange(1, 10).flatMap(val -> integerRange(2, 5).flatMap(val2 -> integerRange(1, 20))).collect(Collectors.toList()), false);
}
@Test
public void testCreateDefinitionsStream() {
testCreateDefinitions(Arrays.asList("host", "name"), Arrays.asList("host", "name"), Arrays.asList(1, 1), true);
}
@Test
public void testCreateDefinitionsStreamNonNullKeyPasswords() {
testCreateDefinitions(Arrays.asList("host", "name"), Arrays.asList("host", "name"), Arrays.asList(1, 1), false);
}
private Stream<Integer> integerRange(int start, int endInclusive) {
return IntStream.range(start, endInclusive + 1).mapToObj(value -> value);
}
private void testCreateDefinitions(List<String> hostnameExpressions, List<String> expectedHostnames, List<Integer> expectedNumbers, boolean nullForKeyPasswords) {
List<String> keyStorePasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testKeyStorePassword" + value).collect(Collectors.toList());
List<String> keyPasswords;
if (nullForKeyPasswords) {
keyPasswords = null;
} else {
keyPasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testKeyPassword" + value).collect(Collectors.toList());
}
List<String> trustStorePasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testTrustStorePassword" + value).collect(Collectors.toList());
List<InstanceDefinition> instanceDefinitions = InstanceDefinition.createDefinitions(null, hostnameExpressions.stream(),
mockSupplier(keyStorePasswords.toArray(new String[keyStorePasswords.size()])), keyPasswords == null ? null : mockSupplier(keyPasswords.toArray(new String[keyPasswords.size()])),
mockSupplier(trustStorePasswords.toArray(new String[trustStorePasswords.size()])));
testCreateDefinitionsOutput(instanceDefinitions, expectedHostnames, expectedNumbers, keyStorePasswords, keyPasswords, trustStorePasswords);
}
private void testCreateDefinitionsOutput(List<InstanceDefinition> instanceDefinitions, List<String> expectedHostnames, List<Integer> expectedNumbers, List<String> keyStorePasswords,
List<String> keyPasswords, List<String> trustStorePasswords) {
assertEquals(instanceDefinitions.size(), expectedHostnames.size());
for (int i = 0; i < instanceDefinitions.size(); i++) {
assertDefinitionEquals(instanceDefinitions.get(i), expectedHostnames.get(i), expectedNumbers.get(i), keyStorePasswords.get(i),
keyPasswords == null ? null : keyPasswords.get(i), trustStorePasswords.get(i));
}
}
private void testCreateDefinition(String hostname, int num, String keyStorePassword, String keyPassword, String trustStorePassword) {
InstanceDefinition definition = InstanceDefinition.createDefinition(new InstanceIdentifier(hostname, num), num, mockSupplier(keyStorePassword),
keyPassword == null ? null : mockSupplier(keyPassword), mockSupplier(trustStorePassword));
assertDefinitionEquals(definition, hostname, num, keyStorePassword, keyPassword, trustStorePassword);
}
private void assertDefinitionEquals(InstanceDefinition definition, String hostname, int num, String keyStorePassword, String keyPassword, String trustStorePassword) {
assertEquals(hostname, definition.getHostname());
assertEquals(num, definition.getNumber());
assertEquals(keyStorePassword, definition.getKeyStorePassword());
assertEquals(keyPassword == null ? keyStorePassword : keyPassword, definition.getKeyPassword());
assertEquals(trustStorePassword, definition.getTrustStorePassword());
}
private <T> Supplier<T> mockSupplier(T... values) {
Supplier<T> supplier = mock(Supplier.class);
if (values.length == 1) {
when(supplier.get()).thenReturn(values[0]);
} else if (values.length > 1) {
when(supplier.get()).thenReturn(values[0], Arrays.copyOfRange(values, 1, values.length));
}
return supplier;
}
}

View File

@ -0,0 +1,162 @@
/*
* 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.nifi.toolkit.tls.configuration;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
public class InstanceIdentifierTest {
@Test
public void testExtractHostnamesSingle() {
testExtractHostnames("test[1-3]", "test1", "test2", "test3");
}
@Test
public void testExtractHostnamesPadding() {
testExtractHostnames("test[0001-3]", "test0001", "test0002", "test0003");
}
@Test
public void testExtractHostnamesLowGreaterThanHigh() {
testExtractHostnames("test[3-1]");
}
@Test
public void testExtractHostnamesLowEqualToHigh() {
testExtractHostnames("test[3-3]", "test3");
}
@Test
public void testExtractHostnamesSingleNumber() {
testExtractHostnames("test[2]", "test1", "test2");
}
@Test
public void testExtractHostnamesSingleNumberPadding() {
testExtractHostnames("test[002]", "test001", "test002");
}
@Test(expected = IllegalArgumentException.class)
public void testExtractHostnamesNoNumber() {
testExtractHostnames("test[]", "test");
}
@Test
public void testExtractHostnamesMultiple() {
testExtractHostnames("test[1-3]name[1-3]", "test1name1", "test1name2", "test1name3", "test2name1", "test2name2", "test2name3", "test3name1", "test3name2", "test3name3");
}
@Test(expected = IllegalArgumentException.class)
public void testExtractHostnamesUnmatched() {
testExtractHostnames("test[");
}
@Test(expected = IllegalArgumentException.class)
public void testExtractHostnamesSpace() {
testExtractHostnames("test[ 1-2]");
}
@Test(expected = IllegalArgumentException.class)
public void testExtractHostnamesMultipleHyphens() {
testExtractHostnames("test[1-2-3]");
}
@Test
public void testCreateDefinitionsSingleHostSingleName() {
testCreateIdentifiers(Arrays.asList("hostname"), Arrays.asList("hostname"), Arrays.asList(1));
}
@Test
public void testCreateDefinitionsSingleHostnameOneNumberInParens() {
testCreateIdentifiers(Arrays.asList("hostname(20)"),
IntStream.range(1, 21).mapToObj(operand -> "hostname").collect(Collectors.toList()),
integerRange(1, 20).collect(Collectors.toList()));
}
@Test
public void testCreateDefinitionsSingleHostnameTwoNumbersInParens() {
testCreateIdentifiers(Arrays.asList("hostname(5-20)"),
IntStream.range(5, 21).mapToObj(operand -> "hostname").collect(Collectors.toList()),
integerRange(5, 20).collect(Collectors.toList()));
}
@Test
public void testCreateDefinitionsMultipleHostnamesWithMultipleNumbers() {
testCreateIdentifiers(Arrays.asList("host[10]name[02-5](20)"),
integerRange(1, 10).flatMap(v -> integerRange(2, 5).flatMap(v2 -> integerRange(1, 20).map(v3 -> "host" + v + "name" + String.format("%02d", v2)))).collect(Collectors.toList()),
integerRange(1, 10).flatMap(val -> integerRange(2, 5).flatMap(val2 -> integerRange(1, 20))).collect(Collectors.toList()));
}
@Test
public void testCreateDefinitionsStream() {
testCreateIdentifiers(Arrays.asList("host", "name"), Arrays.asList("host", "name"), Arrays.asList(1, 1));
}
@Test
public void testCreateOrderMap() {
String abc123 = "abc[1-3]";
String abc0123 = "abc[01-3]";
String b = "b";
Map<InstanceIdentifier, Integer> orderMap = InstanceIdentifier.createOrderMap(Stream.of(abc123, abc0123 + "(2)", b));
AtomicInteger num = new AtomicInteger(1);
Consumer<InstanceIdentifier> action = id -> {
int i = num.getAndIncrement();
assertEquals(i, orderMap.get(id).intValue());
};
InstanceIdentifier.extractHostnames(abc0123).flatMap(s -> Stream.of(new InstanceIdentifier(s, 1), new InstanceIdentifier(s, 2))).forEach(action);
InstanceIdentifier.extractHostnames(abc123).map(s -> new InstanceIdentifier(s, 1)).forEach(action);
InstanceIdentifier.extractHostnames(b).map(s -> new InstanceIdentifier(s, 1)).forEach(action);
}
@Test(expected = IllegalArgumentException.class)
public void testCreateIdentifiersCharactersAfterNumber() {
InstanceIdentifier.createIdentifiers(Stream.of("test(2)a")).count();
}
private Stream<Integer> integerRange(int start, int endInclusive) {
return IntStream.range(start, endInclusive + 1).mapToObj(value -> value);
}
private void testExtractHostnames(String hostnameWithRange, String... expectedHostnames) {
assertEquals(Stream.of(expectedHostnames).collect(Collectors.toList()), InstanceIdentifier.extractHostnames(hostnameWithRange).collect(Collectors.toList()));
}
private void testCreateIdentifiers(List<String> hostnameExpressions, List<String> expectedHostnames, List<Integer> expectedNumbers) {
List<InstanceIdentifier> instanceIdentifiers = InstanceIdentifier.createIdentifiers(hostnameExpressions.stream()).collect(Collectors.toList());
assertEquals(instanceIdentifiers.size(), expectedHostnames.size());
for (int i = 0; i < instanceIdentifiers.size(); i++) {
InstanceIdentifier identifier = instanceIdentifiers.get(i);
assertEquals(expectedHostnames.get(i), identifier.getHostname());
assertEquals((int) expectedNumbers.get(i), identifier.getNumber());
}
}
}

View File

@ -0,0 +1,153 @@
/*
* 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.nifi.toolkit.tls.manager.writer;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriter;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.util.NiFiProperties;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class NifiPropertiesTlsClientConfigWriterTest {
@Mock
NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
@Mock
OutputStreamFactory outputStreamFactory;
private NiFiPropertiesWriter niFiPropertiesWriter;
private int hostNum;
private String testHostname;
private File outputFile;
private NifiPropertiesTlsClientConfigWriter nifiPropertiesTlsClientConfigWriter;
private TlsClientConfig tlsClientConfig;
private ByteArrayOutputStream outputStream;
private String keyStore;
private String keyStorePassword;
private String trustStore;
private String trustStorePassword;
private Properties overlayProperties;
private String keyPassword;
private String keyStoreType;
private String trustStoreType;
@Before
public void setup() throws IOException {
testHostname = "testHostname";
hostNum = 22;
keyStore = "testKeyStore.jks";
keyStoreType = TlsConfig.DEFAULT_KEY_STORE_TYPE;
keyStorePassword = "badKeyStorePassword";
keyPassword = "badKeyPassword";
trustStore = "testTrustStore.jks";
trustStoreType = TlsConfig.DEFAULT_KEY_STORE_TYPE;
trustStorePassword = "badTrustStorePassword";
outputFile = File.createTempFile("temp", "nifi");
outputStream = new ByteArrayOutputStream();
when(outputStreamFactory.create(outputFile)).thenReturn(outputStream);
tlsClientConfig = new TlsClientConfig();
tlsClientConfig.setKeyStore(keyStore);
tlsClientConfig.setKeyStoreType(keyStoreType);
tlsClientConfig.setKeyStorePassword(keyStorePassword);
tlsClientConfig.setKeyPassword(keyPassword);
tlsClientConfig.setTrustStore(trustStore);
tlsClientConfig.setTrustStoreType(trustStoreType);
tlsClientConfig.setTrustStorePassword(trustStorePassword);
niFiPropertiesWriter = new NiFiPropertiesWriter(new ArrayList<>());
when(niFiPropertiesWriterFactory.create()).thenReturn(niFiPropertiesWriter);
nifiPropertiesTlsClientConfigWriter = new NifiPropertiesTlsClientConfigWriter(niFiPropertiesWriterFactory, outputStreamFactory, outputFile, testHostname, hostNum);
overlayProperties = nifiPropertiesTlsClientConfigWriter.getOverlayProperties();
}
@Test
public void testDefaults() throws IOException {
nifiPropertiesTlsClientConfigWriter.write(tlsClientConfig);
testHostnamesAndPorts();
assertNotEquals(0, nifiPropertiesTlsClientConfigWriter.getPropertyPortMap().size());
}
@Test(expected = NumberFormatException.class)
public void testBadPortNum() throws IOException {
nifiPropertiesTlsClientConfigWriter.getOverlayProperties().setProperty(nifiPropertiesTlsClientConfigWriter.getPropertyPortMap().keySet().iterator().next(), "notAnInt");
nifiPropertiesTlsClientConfigWriter.write(tlsClientConfig);
}
@Test
public void testNoHostnameProperties() throws IOException {
nifiPropertiesTlsClientConfigWriter.getOverlayProperties().setProperty(NifiPropertiesTlsClientConfigWriter.HOSTNAME_PROPERTIES, "");
nifiPropertiesTlsClientConfigWriter.write(tlsClientConfig);
testHostnamesAndPorts();
Properties nifiProperties = getNifiProperties();
nifiProperties.stringPropertyNames().forEach(s -> assertNotEquals(testHostname, nifiProperties.getProperty(s)));
}
private void testHostnamesAndPorts() {
Properties nifiProperties = getNifiProperties();
assertEquals(NifiPropertiesTlsClientConfigWriter.CONF + keyStore, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE));
assertEquals(keyStoreType, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE));
assertEquals(keyStorePassword, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD));
assertEquals(keyPassword, nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
assertEquals(NifiPropertiesTlsClientConfigWriter.CONF + trustStore, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE));
assertEquals(trustStoreType, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE));
assertEquals(trustStorePassword, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
assertEquals("", nifiProperties.getProperty(NiFiProperties.WEB_HTTP_HOST));
assertEquals("", nifiProperties.getProperty(NiFiProperties.WEB_HTTP_PORT));
assertEquals(Boolean.toString(true), nifiProperties.getProperty(NiFiProperties.SITE_TO_SITE_SECURE));
nifiPropertiesTlsClientConfigWriter.getHostnamePropertyStream().forEach(s -> assertEquals(testHostname, nifiProperties.getProperty(s)));
nifiPropertiesTlsClientConfigWriter.getPropertyPortMap().entrySet().forEach(propertyToPortEntry -> {
assertEquals(Integer.toString(propertyToPortEntry.getValue()), nifiProperties.getProperty(propertyToPortEntry.getKey()));
assertEquals(Integer.parseInt(overlayProperties.getProperty(propertyToPortEntry.getKey())) + hostNum - 1, propertyToPortEntry.getValue().intValue());
});
}
private Properties getNifiProperties() {
Properties properties = new Properties();
try {
properties.load(new ByteArrayInputStream(outputStream.toByteArray()));
} catch (IOException e) {
throw new RuntimeException(e);
}
return properties;
}
}

View File

@ -19,6 +19,8 @@ package org.apache.nifi.toolkit.tls.standalone;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.InstanceDefinition;
import org.apache.nifi.toolkit.tls.configuration.InstanceIdentifier;
import org.apache.nifi.toolkit.tls.configuration.StandaloneConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriter;
@ -35,11 +37,15 @@ import java.io.IOException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
@ -145,17 +151,10 @@ public class TlsToolkitStandaloneCommandLineTest {
tlsToolkitStandaloneCommandLine.parse("-n", nifi1 + " , " + nifi2);
List<String> hostnames = tlsToolkitStandaloneCommandLine.createConfig().getHostnames();
assertEquals(2, hostnames.size());
assertEquals(nifi1, hostnames.get(0));
assertEquals(nifi2, hostnames.get(1));
}
@Test
public void testHttpsPort() throws CommandLineParseException {
int testPort = 8998;
tlsToolkitStandaloneCommandLine.parse("-p", Integer.toString(testPort));
assertEquals(testPort, tlsToolkitStandaloneCommandLine.createConfig().getHttpsPort());
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(nifi1, instanceDefinitions.get(0).getHostname());
assertEquals(nifi2, instanceDefinitions.get(1).getHostname());
}
@Test
@ -183,52 +182,46 @@ public class TlsToolkitStandaloneCommandLineTest {
@Test
public void testNotSameKeyAndKeystorePassword() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse("-g", "-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
assertEquals(1, tlsToolkitStandaloneCommandLine.createConfig().getHostnames().size());
assertEquals(1, keyStorePasswords.size());
assertEquals(1, keyPasswords.size());
assertNotEquals(keyStorePasswords.get(0), keyPasswords.get(0));
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertNotEquals(instanceDefinitions.get(0).getKeyStorePassword(), instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testSameKeyAndKeystorePassword() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse("-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
assertEquals(1, tlsToolkitStandaloneCommandLine.createConfig().getHostnames().size());
assertEquals(1, keyStorePasswords.size());
assertEquals(1, keyPasswords.size());
assertEquals(keyStorePasswords.get(0), keyPasswords.get(0));
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(instanceDefinitions.get(0).getKeyStorePassword(), instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testSameKeyAndKeystorePasswordWithKeystorePasswordSpecified() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-S", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
assertEquals(1, keyStorePasswords.size());
assertEquals(testPassword, keyStorePasswords.get(0));
assertEquals(keyStorePasswords, tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords());
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyStorePassword());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testSameKeyAndKeystorePasswordWithKeyPasswordSpecified() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-K", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
assertNotEquals(tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(), keyPasswords);
assertEquals(1, keyPasswords.size());
assertEquals(testPassword, keyPasswords.get(0));
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertNotEquals(instanceDefinitions.get(0).getKeyStorePassword(), instanceDefinitions.get(0).getKeyPassword());
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testKeyStorePasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-S", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
assertEquals(1, keyStorePasswords.size());
assertEquals(testPassword, keyStorePasswords.get(0));
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyStorePassword());
}
@Test
@ -236,31 +229,19 @@ public class TlsToolkitStandaloneCommandLineTest {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-S", testPassword1, "-S", testPassword2);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords();
assertEquals(2, keyStorePasswords.size());
assertEquals(testPassword1, keyStorePasswords.get(0));
assertEquals(testPassword2, keyStorePasswords.get(1));
}
@Test
public void testMultipleKeystorePasswordArgSingleHost() {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
try {
tlsToolkitStandaloneCommandLine.parse("-S", testPassword1, "-S", testPassword2);
fail("Expected error with mismatch keystore password number");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS.ordinal(), e.getExitCode());
}
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(testPassword1, instanceDefinitions.get(0).getKeyStorePassword());
assertEquals(testPassword2, instanceDefinitions.get(1).getKeyStorePassword());
}
@Test
public void testKeyPasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-K", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
assertEquals(1, keyPasswords.size());
assertEquals(testPassword, keyPasswords.get(0));
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyPassword());
}
@Test
@ -268,31 +249,19 @@ public class TlsToolkitStandaloneCommandLineTest {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-K", testPassword1, "-K", testPassword2);
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords();
assertEquals(2, keyPasswords.size());
assertEquals(testPassword1, keyPasswords.get(0));
assertEquals(testPassword2, keyPasswords.get(1));
}
@Test
public void testMultipleKeyPasswordArgSingleHost() {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
try {
tlsToolkitStandaloneCommandLine.parse("-K", testPassword1, "-K", testPassword2);
fail("Expected error with mismatch keystore password number");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS.ordinal(), e.getExitCode());
}
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(testPassword1, instanceDefinitions.get(0).getKeyPassword());
assertEquals(testPassword2, instanceDefinitions.get(1).getKeyPassword());
}
@Test
public void testTruststorePasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-P", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getTrustStorePasswords();
assertEquals(1, trustStorePasswords.size());
assertEquals(testPassword, trustStorePasswords.get(0));
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getTrustStorePassword());
}
@Test
@ -300,22 +269,10 @@ public class TlsToolkitStandaloneCommandLineTest {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-P", testPassword1, "-P", testPassword2);
List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getTrustStorePasswords();
assertEquals(2, trustStorePasswords.size());
assertEquals(testPassword1, trustStorePasswords.get(0));
assertEquals(testPassword2, trustStorePasswords.get(1));
}
@Test
public void testMultipleTruststorePasswordArgSingleHost() {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
try {
tlsToolkitStandaloneCommandLine.parse("-P", testPassword1, "-P", testPassword2);
fail("Expected error with mismatch keystore password number");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS.ordinal(), e.getExitCode());
}
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(testPassword1, instanceDefinitions.get(0).getTrustStorePassword());
assertEquals(testPassword2, instanceDefinitions.get(1).getTrustStorePassword());
}
@Test
@ -364,6 +321,83 @@ public class TlsToolkitStandaloneCommandLineTest {
assertEquals(testPass2, clientPasswords.get(1));
}
@Test
public void testNoGlobalOrder() throws CommandLineParseException {
String hostname1 = "other0[4-6]";
String hostname2 = "nifi3(2)";
tlsToolkitStandaloneCommandLine.parse("-n", hostname1, "-n", hostname2);
Map<InstanceIdentifier, InstanceDefinition> definitionMap = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions().stream()
.collect(Collectors.toMap(InstanceDefinition::getInstanceIdentifier, Function.identity()));
assertEquals(5, definitionMap.size());
InstanceDefinition nifi3_1 = definitionMap.get(new InstanceIdentifier("nifi3", 1));
assertNotNull(nifi3_1);
assertEquals(1, nifi3_1.getInstanceIdentifier().getNumber());
assertEquals(1, nifi3_1.getNumber());
InstanceDefinition nifi3_2 = definitionMap.get(new InstanceIdentifier("nifi3", 2));
assertNotNull(nifi3_2);
assertEquals(2, nifi3_2.getInstanceIdentifier().getNumber());
assertEquals(2, nifi3_2.getNumber());
InstanceDefinition other04 = definitionMap.get(new InstanceIdentifier("other04", 1));
assertNotNull(other04);
assertEquals(1, other04.getInstanceIdentifier().getNumber());
assertEquals(1, other04.getNumber());
InstanceDefinition other05 = definitionMap.get(new InstanceIdentifier("other05", 1));
assertNotNull(other05);
assertEquals(1, other05.getInstanceIdentifier().getNumber());
assertEquals(1, other05.getNumber());
InstanceDefinition other06 = definitionMap.get(new InstanceIdentifier("other06", 1));
assertNotNull(other06);
assertEquals(1, other06.getInstanceIdentifier().getNumber());
assertEquals(1, other06.getNumber());
}
@Test
public void testGlobalOrder() throws CommandLineParseException {
String hostname1 = "other0[4-6]";
String hostname2 = "nifi3(2)";
String globalOrder1 = "nifi[1-5](2),other[01-4]";
String globalOrder2 = "other[05-10]";
tlsToolkitStandaloneCommandLine.parse("-n", hostname1, "-n", hostname2, "-G", globalOrder1, "-G", globalOrder2);
Map<InstanceIdentifier, InstanceDefinition> definitionMap = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions().stream()
.collect(Collectors.toMap(InstanceDefinition::getInstanceIdentifier, Function.identity()));
assertEquals(5, definitionMap.size());
InstanceDefinition nifi3_1 = definitionMap.get(new InstanceIdentifier("nifi3", 1));
assertNotNull(nifi3_1);
assertEquals(1, nifi3_1.getInstanceIdentifier().getNumber());
assertEquals(5, nifi3_1.getNumber());
InstanceDefinition nifi3_2 = definitionMap.get(new InstanceIdentifier("nifi3", 2));
assertNotNull(nifi3_2);
assertEquals(2, nifi3_2.getInstanceIdentifier().getNumber());
assertEquals(6, nifi3_2.getNumber());
InstanceDefinition other04 = definitionMap.get(new InstanceIdentifier("other04", 1));
assertNotNull(other04);
assertEquals(1, other04.getInstanceIdentifier().getNumber());
assertEquals(14, other04.getNumber());
InstanceDefinition other05 = definitionMap.get(new InstanceIdentifier("other05", 1));
assertNotNull(other05);
assertEquals(1, other05.getInstanceIdentifier().getNumber());
assertEquals(15, other05.getNumber());
InstanceDefinition other06 = definitionMap.get(new InstanceIdentifier("other06", 1));
assertNotNull(other06);
assertEquals(1, other06.getInstanceIdentifier().getNumber());
assertEquals(16, other06.getNumber());
}
@Test(expected = IllegalArgumentException.class)
public void testBadGlobalOrder() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse("-n", "notInGlobalOrder", "-G", "nifi[1-3]");
}
private Properties getProperties() throws IOException {
NiFiPropertiesWriter niFiPropertiesWriter = tlsToolkitStandaloneCommandLine.createConfig().getNiFiPropertiesWriterFactory().create();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

View File

@ -20,6 +20,7 @@ package org.apache.nifi.toolkit.tls.standalone;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.SystemExitCapturer;
import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
@ -40,7 +41,6 @@ import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
@ -54,13 +54,12 @@ import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class TlsToolkitStandaloneTest {
public static final String NIFI_FAKE_PROPERTY = "nifi.fake.property";
public static final String FAKE_VALUE = "fake value";
public static final String TEST_NIFI_PROPERTIES = "src/test/resources/localhost/nifi.properties";
private SecurityManager originalSecurityManager;
private SystemExitCapturer systemExitCapturer;
private File tempDir;
@ -74,31 +73,12 @@ public class TlsToolkitStandaloneTest {
if (!tempDir.mkdirs()) {
throw new IOException("Couldn't make directory " + tempDir);
}
originalSecurityManager = System.getSecurityManager();
// [see http://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit#answer-309427]
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm) {
// Noop
}
@Override
public void checkPermission(Permission perm, Object context) {
// Noop
}
@Override
public void checkExit(int status) {
super.checkExit(status);
throw new ExitException(status);
}
});
systemExitCapturer = new SystemExitCapturer();
}
@After
public void teardown() throws IOException {
System.setSecurityManager(originalSecurityManager);
systemExitCapturer.close();
FileUtils.deleteDirectory(tempDir);
}
@ -248,13 +228,16 @@ public class TlsToolkitStandaloneTest {
trustStore.load(inputStream, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray());
}
String trustStoreFilename = BaseCommandLine.KEYSTORE + trustStoreType;
assertEquals("./conf/" + trustStoreFilename, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE));
Certificate certificate = trustStore.getCertificate(TlsToolkitStandalone.NIFI_CERT);
assertEquals(rootCert, certificate);
String keyStoreType = nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
String keyStoreFilename = BaseCommandLine.KEYSTORE + keyStoreType;
File keyStoreFile = new File(hostDir, keyStoreFilename);
assertEquals(keyStoreFilename, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE));
assertEquals("./conf/" + keyStoreFilename, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE));
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
try (InputStream inputStream = new FileInputStream(keyStoreFile)) {
@ -303,23 +286,6 @@ public class TlsToolkitStandaloneTest {
}
private void runAndAssertExitCode(ExitCode exitCode, String... args) {
try {
TlsToolkitStandaloneCommandLine.main(args);
fail("Expecting exit code: " + exitCode);
} catch (ExitException e) {
assertEquals(exitCode, ExitCode.values()[e.getExitCode()]);
}
}
private static class ExitException extends SecurityException {
private final int exitCode;
public ExitException(int exitCode) {
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
systemExitCapturer.runAndAssertExitCode(() -> TlsToolkitStandaloneCommandLine.main(args), exitCode);
}
}

View File

@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import java.util.function.Supplier;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
@ -48,4 +49,12 @@ public class PasswordUtilTest {
String actual = passwordUtil.generatePassword();
assertEquals(expected, actual);
}
@Test(expected = PasswordsExhaustedException.class)
public void testPasswordExhausted() {
Supplier<String> supplier = PasswordUtil.passwordSupplier("exhausted", new String[]{"a", "b"});
supplier.get();
supplier.get();
supplier.get();
}
}

View File

@ -0,0 +1,23 @@
#
# 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.
#
# Comma separated list of properties to put the hostname into
hostname.properties=nifi.remote.input.host,nifi.web.https.host,nifi.cluster.node.address
nifi.web.https.port=9443
nifi.remote.input.socket.port=10443
nifi.cluster.node.protocol.port=11443