From 2fd39676a887151aa4124a824fc0cd756b9fac3b Mon Sep 17 00:00:00 2001 From: Bryan Rosander Date: Thu, 11 Aug 2016 16:32:32 -0400 Subject: [PATCH] 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 --- .../src/main/resources/bin/tls-toolkit.bat | 2 +- .../resources/classpath/overlay.properties | 23 ++ .../nifi/toolkit/tls/TlsToolkitMain.java | 39 ++-- .../tls/configuration/InstanceDefinition.java | 109 +++++++++ .../tls/configuration/InstanceIdentifier.java | 174 +++++++++++++++ .../tls/configuration/StandaloneConfig.java | 54 +---- .../NifiPropertiesTlsClientConfigWriter.java | 76 +++++-- .../tls/standalone/TlsToolkitStandalone.java | 31 +-- .../TlsToolkitStandaloneCommandLine.java | 57 ++--- .../nifi/toolkit/tls/util/PasswordUtil.java | 22 ++ .../tls/util/PasswordsExhaustedException.java | 24 ++ .../nifi/toolkit/tls/ExitException.java | 31 +++ .../nifi/toolkit/tls/SystemExitCapturer.java | 67 ++++++ .../nifi/toolkit/tls/TlsToolkitMainTest.java | 114 ++++++++++ .../configuration/InstanceDefinitionTest.java | 132 +++++++++++ .../configuration/InstanceIdentifierTest.java | 162 ++++++++++++++ ...fiPropertiesTlsClientConfigWriterTest.java | 153 +++++++++++++ .../TlsToolkitStandaloneCommandLineTest.java | 210 ++++++++++-------- .../standalone/TlsToolkitStandaloneTest.java | 52 +---- .../toolkit/tls/util/PasswordUtilTest.java | 9 + .../src/test/resources/overlay.properties | 23 ++ 21 files changed, 1315 insertions(+), 249 deletions(-) create mode 100644 nifi-toolkit/nifi-toolkit-assembly/src/main/resources/classpath/overlay.properties create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinition.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordsExhaustedException.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/ExitException.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/SystemExitCapturer.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/TlsToolkitMainTest.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinitionTest.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriterTest.java create mode 100644 nifi-toolkit/nifi-toolkit-tls/src/test/resources/overlay.properties diff --git a/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/bin/tls-toolkit.bat b/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/bin/tls-toolkit.bat index b875e1ab47..025ec10878 100644 --- a/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/bin/tls-toolkit.bat +++ b/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/bin/tls-toolkit.bat @@ -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% %* diff --git a/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/classpath/overlay.properties b/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/classpath/overlay.properties new file mode 100644 index 0000000000..c4d02c27c3 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/classpath/overlay.properties @@ -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 \ No newline at end of file diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/TlsToolkitMain.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/TlsToolkitMain.java index 7d445a5ee6..4614558143 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/TlsToolkitMain.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/TlsToolkitMain.java @@ -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> mainMap; public TlsToolkitMain() { @@ -46,7 +47,7 @@ public class TlsToolkitMain { new TlsToolkitMain().doMain(args); } - private void printUsageAndExit(String message, ExitCode exitCode) { + private 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> 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) { diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinition.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinition.java new file mode 100644 index 0000000000..84b633faf9 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinition.java @@ -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 createDefinitions(Stream fullHostNameExpressions, Stream currentHostnameExpressions, Supplier keyStorePasswords, + Supplier keyPasswords, Supplier trustStorePasswords) { + if (fullHostNameExpressions == null) { + return InstanceIdentifier.createIdentifiers(currentHostnameExpressions).map(id -> createDefinition(id, id.getNumber(), keyStorePasswords, keyPasswords, trustStorePasswords)) + .collect(Collectors.toList()); + } else { + Map 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 keyStorePasswords, Supplier keyPasswords, + Supplier 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; + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java new file mode 100644 index 0000000000..9699236e8c --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java @@ -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 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 createOrderMap(Stream hostnameExpressions) { + List instanceIdentifiers = createIdentifiers(hostnameExpressions).sorted(HOST_IDENTIFIER_COMPARATOR).collect(Collectors.toList()); + Map 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 createIdentifiers(Stream 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 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 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 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 = ""; + } + } + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java index 342e1e5cc6..740a9a2621 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java @@ -25,14 +25,10 @@ import java.util.List; public class StandaloneConfig extends TlsConfig { private File baseDir; private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory; - private List hostnames; - private List keyStorePasswords; - private List keyPasswords; - private List trustStorePasswords; + private List instanceDefinitions; private List clientDns; private List clientPasswords; private boolean clientPasswordsGenerated; - private int httpsPort; private boolean overwrite; public List getClientDns() { @@ -67,38 +63,6 @@ public class StandaloneConfig extends TlsConfig { this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory; } - public List getHostnames() { - return hostnames; - } - - public void setHostnames(List hostnames) { - this.hostnames = hostnames; - } - - public List getKeyStorePasswords() { - return keyStorePasswords; - } - - public void setKeyStorePasswords(List keyStorePasswords) { - this.keyStorePasswords = keyStorePasswords; - } - - public List getKeyPasswords() { - return keyPasswords; - } - - public void setKeyPasswords(List keyPasswords) { - this.keyPasswords = keyPasswords; - } - - public List getTrustStorePasswords() { - return trustStorePasswords; - } - - public void setTrustStorePasswords(List trustStorePasswords) { - this.trustStorePasswords = trustStorePasswords; - } - public List 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 getInstanceDefinitions() { + return instanceDefinitions; + } + + public void setInstanceDefinitions(List instanceDefinitions) { + this.instanceDefinitions = instanceDefinitions; + } } diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriter.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriter.java index e1b03da246..c2ecca4eed 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriter.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriter.java @@ -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 { + 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 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 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 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 getHostnamePropertyStream() { + String hostnamePropertyString = overlayProperties.getProperty(HOSTNAME_PROPERTIES); + if (!StringUtils.isEmpty(hostnamePropertyString)) { + return Arrays.stream(hostnamePropertyString.split(",")).map(String::trim); + } + return Stream.of(); } } diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java index c91e7fa43e..8dc51866bc 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java @@ -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 hostnames = standaloneConfig.getHostnames(); if (logger.isInfoEnabled()) { logger.info("Running standalone certificate generation with output directory " + baseDir); } @@ -128,19 +128,22 @@ public class TlsToolkitStandalone { } } - List keyStorePasswords = standaloneConfig.getKeyStorePasswords(); - List keyPasswords = standaloneConfig.getKeyPasswords(); - List trustStorePasswords = standaloneConfig.getTrustStorePasswords(); NiFiPropertiesWriterFactory niFiPropertiesWriterFactory = standaloneConfig.getNiFiPropertiesWriterFactory(); - int httpsPort = standaloneConfig.getHttpsPort(); boolean overwrite = standaloneConfig.isOverwrite(); - if (hostnames.isEmpty() && logger.isInfoEnabled()) { + List 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); } } diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java index 602fce417c..4ba0746b6c 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java @@ -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 hostnames; - private int httpsPort; + private List instanceDefinitions; private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory; - private List keyStorePasswords; - private List keyPasswords; - private List trustStorePasswords; private List clientDns; private List 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 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 getKeyPasswords(CommandLine commandLine, List keyStorePasswords) throws CommandLineParseException { - if (differentPasswordForKeyAndKeystore() || commandLine.hasOption(KEY_PASSWORD_ARG)) { - return getPasswords(KEY_PASSWORD_ARG, commandLine, keyStorePasswords.size(), HOSTNAMES_ARG); + private Supplier parsePasswordSupplier(CommandLine commandLine, String option, Supplier 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); diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordUtil.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordUtil.java index c26fd7f127..2c7a6aa604 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordUtil.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordUtil.java @@ -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 passwordSupplier() { + return () -> generatePassword(); + } + + public static Supplier passwordSupplier(String password) { + return () -> password; + } + + public static Supplier 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); + } + }; + } } diff --git a/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordsExhaustedException.java b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordsExhaustedException.java new file mode 100644 index 0000000000..6d26a1987d --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordsExhaustedException.java @@ -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); + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/ExitException.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/ExitException.java new file mode 100644 index 0000000000..5134353807 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/ExitException.java @@ -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; + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/SystemExitCapturer.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/SystemExitCapturer.java new file mode 100644 index 0000000000..e4552a362e --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/SystemExitCapturer.java @@ -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); + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/TlsToolkitMainTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/TlsToolkitMainTest.java new file mode 100644 index 0000000000..c9a74dd996 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/TlsToolkitMainTest.java @@ -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 { + + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinitionTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinitionTest.java new file mode 100644 index 0000000000..9d4fcd0fbd --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinitionTest.java @@ -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 integerRange(int start, int endInclusive) { + return IntStream.range(start, endInclusive + 1).mapToObj(value -> value); + } + + private void testCreateDefinitions(List hostnameExpressions, List expectedHostnames, List expectedNumbers, boolean nullForKeyPasswords) { + List keyStorePasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testKeyStorePassword" + value).collect(Collectors.toList()); + List keyPasswords; + if (nullForKeyPasswords) { + keyPasswords = null; + } else { + keyPasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testKeyPassword" + value).collect(Collectors.toList()); + } + List trustStorePasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testTrustStorePassword" + value).collect(Collectors.toList()); + List 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 instanceDefinitions, List expectedHostnames, List expectedNumbers, List keyStorePasswords, + List keyPasswords, List 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 Supplier mockSupplier(T... values) { + Supplier 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; + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java new file mode 100644 index 0000000000..b75c02f9ef --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java @@ -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 orderMap = InstanceIdentifier.createOrderMap(Stream.of(abc123, abc0123 + "(2)", b)); + + AtomicInteger num = new AtomicInteger(1); + Consumer 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 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 hostnameExpressions, List expectedHostnames, List expectedNumbers) { + List 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()); + } + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriterTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriterTest.java new file mode 100644 index 0000000000..c96d906dbe --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriterTest.java @@ -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; + } +} diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java index 82c67d9d2e..a28431878b 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLineTest.java @@ -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 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 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 keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(); - List 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 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 keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(); - List 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 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 keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(); - assertEquals(1, keyStorePasswords.size()); - assertEquals(testPassword, keyStorePasswords.get(0)); - assertEquals(keyStorePasswords, tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords()); + List 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 keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords(); - assertNotEquals(tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(), keyPasswords); - assertEquals(1, keyPasswords.size()); - assertEquals(testPassword, keyPasswords.get(0)); + List 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 keyStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyStorePasswords(); - assertEquals(1, keyStorePasswords.size()); - assertEquals(testPassword, keyStorePasswords.get(0)); + List 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 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 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 keyPasswords = tlsToolkitStandaloneCommandLine.createConfig().getKeyPasswords(); - assertEquals(1, keyPasswords.size()); - assertEquals(testPassword, keyPasswords.get(0)); + List 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 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 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 trustStorePasswords = tlsToolkitStandaloneCommandLine.createConfig().getTrustStorePasswords(); - assertEquals(1, trustStorePasswords.size()); - assertEquals(testPassword, trustStorePasswords.get(0)); + List 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 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 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 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 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(); diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java index af81eaeff4..a8cf7a51e2 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneTest.java @@ -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); } } diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/PasswordUtilTest.java b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/PasswordUtilTest.java index e79815ae5c..f08c32cb5b 100644 --- a/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/PasswordUtilTest.java +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/util/PasswordUtilTest.java @@ -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 supplier = PasswordUtil.passwordSupplier("exhausted", new String[]{"a", "b"}); + supplier.get(); + supplier.get(); + supplier.get(); + } } diff --git a/nifi-toolkit/nifi-toolkit-tls/src/test/resources/overlay.properties b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/overlay.properties new file mode 100644 index 0000000000..c4d02c27c3 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-tls/src/test/resources/overlay.properties @@ -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 \ No newline at end of file