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 <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/2fd39676 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/2fd39676 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/2fd39676 Branch: refs/heads/master Commit: 2fd39676a887151aa4124a824fc0cd756b9fac3b Parents: b0122c6 Author: Bryan Rosander <[email protected]> Authored: Thu Aug 11 16:32:32 2016 -0400 Committer: Bryan Bende <[email protected]> Committed: Tue Aug 16 15:20:46 2016 -0400 ---------------------------------------------------------------------- .../src/main/resources/bin/tls-toolkit.bat | 2 +- .../main/resources/classpath/overlay.properties | 23 ++ .../apache/nifi/toolkit/tls/TlsToolkitMain.java | 33 +-- .../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 +++ .../apache/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 ++++++++++++++ ...NifiPropertiesTlsClientConfigWriterTest.java | 153 ++++++++++++++ .../TlsToolkitStandaloneCommandLineTest.java | 210 +++++++++++-------- .../standalone/TlsToolkitStandaloneTest.java | 52 +---- .../nifi/toolkit/tls/util/PasswordUtilTest.java | 9 + .../src/test/resources/overlay.properties | 23 ++ 21 files changed, 1312 insertions(+), 246 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/bin/tls-toolkit.bat ---------------------------------------------------------------------- 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 b875e1a..025ec10 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% %* http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-assembly/src/main/resources/classpath/overlay.properties ---------------------------------------------------------------------- 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 0000000..c4d02c2 --- /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 http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/TlsToolkitMain.java ---------------------------------------------------------------------- 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 7d445a5..4614558 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<String, Class<?>> mainMap; public TlsToolkitMain() { @@ -46,7 +47,7 @@ public class TlsToolkitMain { new TlsToolkitMain().doMain(args); } - private void printUsageAndExit(String message, ExitCode exitCode) { + private <T> T printUsageAndExit(String message, ExitCode exitCode) { System.out.println(message); System.out.println(); System.out.println("Usage: tls-toolkit service [-h] [args]"); @@ -55,38 +56,44 @@ 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() + ")"; } } - public void doMain(String[] args) { - if (args.length < 1) { - printUsageAndExit("Expected at least a service argument.", ExitCode.INVALID_ARGS); - } + protected Map<String, Class<?>> getMainMap() { + return mainMap; + } - String service = args[0].toLowerCase(); + protected Method getMain(String service) { Class<?> mainClass = mainMap.get(service); if (mainClass == null) { printUsageAndExit("Unknown service: " + service, ExitCode.INVALID_ARGS); } - Method main; try { - main = mainClass.getDeclaredMethod("main", String[].class); + return mainClass.getDeclaredMethod("main", String[].class); } catch (NoSuchMethodException e) { - printUsageAndExit("Service " + service + " is missing main method.", ExitCode.SERVICE_ERROR); - return; + return printUsageAndExit("Service " + service + " is missing main method.", ExitCode.SERVICE_ERROR); + } + } + + public void doMain(String[] args) { + if (args.length < 1) { + printUsageAndExit("Expected at least a service argument.", ExitCode.INVALID_ARGS); } + String service = args[0].toLowerCase(); + 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) { http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinition.java ---------------------------------------------------------------------- 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 0000000..84b633f --- /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<InstanceDefinition> createDefinitions(Stream<String> fullHostNameExpressions, Stream<String> currentHostnameExpressions, Supplier<String> keyStorePasswords, + Supplier<String> keyPasswords, Supplier<String> trustStorePasswords) { + if (fullHostNameExpressions == null) { + return InstanceIdentifier.createIdentifiers(currentHostnameExpressions).map(id -> createDefinition(id, id.getNumber(), keyStorePasswords, keyPasswords, trustStorePasswords)) + .collect(Collectors.toList()); + } else { + Map<InstanceIdentifier, Integer> orderMap = InstanceIdentifier.createOrderMap(fullHostNameExpressions); + return InstanceIdentifier.createIdentifiers(currentHostnameExpressions).map(id -> { + Integer number = orderMap.get(id); + if (number == null) { + throw new IllegalArgumentException("Unable to find " + id.getHostname() + " in specified " + TlsToolkitStandaloneCommandLine.GLOBAL_PORT_SEQUENCE_ARG + " expression(s)."); + } + return createDefinition(id, number, keyStorePasswords, keyPasswords, trustStorePasswords); + }).collect(Collectors.toList()); + } + } + + protected static InstanceDefinition createDefinition(InstanceIdentifier instanceIdentifier, int number, Supplier<String> keyStorePasswords, Supplier<String> keyPasswords, + Supplier<String> trustStorePasswords) { + String keyStorePassword = keyStorePasswords.get(); + String keyPassword; + if (keyPasswords == null) { + keyPassword = keyStorePassword; + } else { + keyPassword = keyPasswords.get(); + } + String trustStorePassword = trustStorePasswords.get(); + return new InstanceDefinition(instanceIdentifier, number, keyStorePassword, keyPassword, trustStorePassword); + } + + public String getHostname() { + return instanceIdentifier.getHostname(); + } + + public int getNumber() { + return number; + } + + public String getKeyStorePassword() { + return keyStorePassword; + } + + public String getKeyPassword() { + return keyPassword; + } + + public String getTrustStorePassword() { + return trustStorePassword; + } + + public InstanceIdentifier getInstanceIdentifier() { + return instanceIdentifier; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifier.java ---------------------------------------------------------------------- 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 0000000..9699236 --- /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<InstanceIdentifier> HOST_IDENTIFIER_COMPARATOR = (o1, o2) -> { + int i = o1.getHostname().compareTo(o2.getHostname()); + if (i == 0) { + return o1.getNumber() - o2.getNumber(); + } + return i; + }; + private static final Pattern RANGE_PATTERN = Pattern.compile("^[0-9]+(-[0-9]+)?$"); + private final String hostname; + private final int number; + + public InstanceIdentifier(String hostname, int number) { + this.hostname = hostname; + this.number = number; + } + + /** + * Creates a map that can be used to deterministically assign global instance numbers + * + * @param hostnameExpressions the hostname expressions to expand + * @return a map that can be used to deterministically assign global instance numbers + */ + public static Map<InstanceIdentifier, Integer> createOrderMap(Stream<String> hostnameExpressions) { + List<InstanceIdentifier> instanceIdentifiers = createIdentifiers(hostnameExpressions).sorted(HOST_IDENTIFIER_COMPARATOR).collect(Collectors.toList()); + Map<InstanceIdentifier, Integer> result = new HashMap<>(); + for (int i = 0; i < instanceIdentifiers.size(); i++) { + result.put(instanceIdentifiers.get(i), i + 1); + } + return result; + } + + /** + * Creates a stream of hostname identifiers from a stream of hostname expressions + * + * @param hostnameExpressions the hostname expressions + * @return the hostname identifiers + */ + public static Stream<InstanceIdentifier> createIdentifiers(Stream<String> hostnameExpressions) { + return hostnameExpressions.flatMap(hostnameExpression -> extractHostnames(hostnameExpression).flatMap(hostname -> { + ExtractedRange extractedRange = new ExtractedRange(hostname, '(', ')'); + if (extractedRange.range == null) { + return Stream.of(new InstanceIdentifier(hostname, 1)); + } + if (!StringUtils.isEmpty(extractedRange.afterClose)) { + throw new IllegalArgumentException("No characters expected after )"); + } + return extractedRange.range.map(numString -> new InstanceIdentifier(extractedRange.beforeOpen, Integer.parseInt(numString))); + })); + } + + protected static Stream<String> extractHostnames(String hostname) { + ExtractedRange extractedRange = new ExtractedRange(hostname, '[', ']'); + if (extractedRange.range == null) { + return Stream.of(hostname); + } + return extractedRange.range.map(s -> extractedRange.beforeOpen + s + extractedRange.afterClose).flatMap(InstanceIdentifier::extractHostnames); + } + + private static Stream<String> extractRange(String range) { + if (!RANGE_PATTERN.matcher(range).matches()) { + throw new IllegalArgumentException("Expected either one number or two separated by a single hyphen"); + } + String[] split = range.split("-"); + if (split.length == 1) { + String prefix = "1-"; + if (split[0].charAt(0) == '0') { + prefix = String.format("%0" + split[0].length() + "d-", 1); + } + return extractRange(prefix + split[0]); + } else { + int baseLength = split[0].length(); + int low = Integer.parseInt(split[0]); + String padding = split[0].substring(0, split[0].length() - Integer.toString(low).length()); + int high = Integer.parseInt(split[1]); + return IntStream.range(low, high + 1).mapToObj(i -> { + String s = Integer.toString(i); + int length = s.length(); + if (length >= baseLength) { + return s; + } else { + return padding.substring(0, baseLength - length) + s; + } + }); + } + } + + public String getHostname() { + return hostname; + } + + public int getNumber() { + return number; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InstanceIdentifier that = (InstanceIdentifier) o; + + if (number != that.number) return false; + return hostname != null ? hostname.equals(that.hostname) : that.hostname == null; + + } + + @Override + public int hashCode() { + int result = hostname != null ? hostname.hashCode() : 0; + result = 31 * result + number; + return result; + } + + private static class ExtractedRange { + private final String beforeOpen; + private final Stream<String> range; + private final String afterClose; + + public ExtractedRange(String string, char rangeOpen, char rangeClose) { + int openBracket = string.indexOf(rangeOpen); + if (openBracket >= 0) { + int closeBracket = string.indexOf(rangeClose, openBracket); + if (closeBracket < 0) { + throw new IllegalArgumentException("Unable to find matching " + rangeClose + " for " + rangeOpen + " in " + string); + } + beforeOpen = string.substring(0, openBracket); + if (closeBracket + 1 < string.length()) { + afterClose = string.substring(closeBracket + 1); + } else { + afterClose = ""; + } + range = extractRange(string.substring(openBracket + 1, closeBracket)); + } else { + beforeOpen = string; + range = null; + afterClose = ""; + } + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/configuration/StandaloneConfig.java ---------------------------------------------------------------------- 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 342e1e5..740a9a2 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<String> hostnames; - private List<String> keyStorePasswords; - private List<String> keyPasswords; - private List<String> trustStorePasswords; + private List<InstanceDefinition> instanceDefinitions; private List<String> clientDns; private List<String> clientPasswords; private boolean clientPasswordsGenerated; - private int httpsPort; private boolean overwrite; public List<String> getClientDns() { @@ -67,38 +63,6 @@ public class StandaloneConfig extends TlsConfig { this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory; } - public List<String> getHostnames() { - return hostnames; - } - - public void setHostnames(List<String> hostnames) { - this.hostnames = hostnames; - } - - public List<String> getKeyStorePasswords() { - return keyStorePasswords; - } - - public void setKeyStorePasswords(List<String> keyStorePasswords) { - this.keyStorePasswords = keyStorePasswords; - } - - public List<String> getKeyPasswords() { - return keyPasswords; - } - - public void setKeyPasswords(List<String> keyPasswords) { - this.keyPasswords = keyPasswords; - } - - public List<String> getTrustStorePasswords() { - return trustStorePasswords; - } - - public void setTrustStorePasswords(List<String> trustStorePasswords) { - this.trustStorePasswords = trustStorePasswords; - } - public List<String> getClientPasswords() { return clientPasswords; } @@ -107,14 +71,6 @@ public class StandaloneConfig extends TlsConfig { this.clientPasswords = clientPasswords; } - public int getHttpsPort() { - return httpsPort; - } - - public void setHttpsPort(int httpsPort) { - this.httpsPort = httpsPort; - } - public boolean isClientPasswordsGenerated() { return clientPasswordsGenerated; } @@ -122,4 +78,12 @@ public class StandaloneConfig extends TlsConfig { public void setClientPasswordsGenerated(boolean clientPasswordsGenerated) { this.clientPasswordsGenerated = clientPasswordsGenerated; } + + public List<InstanceDefinition> getInstanceDefinitions() { + return instanceDefinitions; + } + + public void setInstanceDefinitions(List<InstanceDefinition> instanceDefinitions) { + this.instanceDefinitions = instanceDefinitions; + } } http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriter.java ---------------------------------------------------------------------- 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 e1b03da..c2ecca4 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<TlsClientConfig> { + public static final String HOSTNAME_PROPERTIES = "hostname.properties"; + public static final String OVERLAY_PROPERTIES = "overlay.properties"; + public static final String CONF = "./conf/"; private final NiFiPropertiesWriterFactory niFiPropertiesWriterFactory; private final OutputStreamFactory outputStreamFactory; - private final File file; + private final File outputFile; private final String hostname; - private final int httpsPort; + private final int hostNum; + private final Properties overlayProperties; + private final Set<String> explicitProperties; - public NifiPropertiesTlsClientConfigWriter(NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, OutputStreamFactory outputStreamFactory, File file, String hostname, int httpsPort) { + public NifiPropertiesTlsClientConfigWriter(NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, OutputStreamFactory outputStreamFactory, File outputFile, String hostname, int hostNum) + throws IOException { this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory; this.outputStreamFactory = outputStreamFactory; - this.file = file; + this.outputFile = outputFile; this.hostname = hostname; - this.httpsPort = httpsPort; + this.hostNum = hostNum; + this.overlayProperties = new Properties(); + this.overlayProperties.load(getClass().getClassLoader().getResourceAsStream(OVERLAY_PROPERTIES)); + HashSet<String> explicitProperties = new HashSet<>(); + explicitProperties.add(HOSTNAME_PROPERTIES); + this.explicitProperties = Collections.unmodifiableSet(explicitProperties); } @Override public void write(TlsClientConfig tlsClientConfig) throws IOException { NiFiPropertiesWriter niFiPropertiesWriter = niFiPropertiesWriterFactory.create(); updateProperties(niFiPropertiesWriter, tlsClientConfig); - try (OutputStream stream = outputStreamFactory.create(file)) { + try (OutputStream stream = outputStreamFactory.create(outputFile)) { niFiPropertiesWriter.writeNiFiProperties(stream); } } - protected void updateProperties(NiFiPropertiesWriter niFiPropertiesWriter, TlsClientConfig tlsClientConfig) { - Path parentPath = Paths.get(file.getParentFile().getAbsolutePath()); - niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE, parentPath.relativize(Paths.get(tlsClientConfig.getKeyStore())).toString()); + protected void updateProperties(NiFiPropertiesWriter niFiPropertiesWriter, TlsClientConfig tlsClientConfig) throws IOException { + niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE, CONF + new File(tlsClientConfig.getKeyStore()).getName()); niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsClientConfig.getKeyStoreType()); niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsClientConfig.getKeyStorePassword()); niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEY_PASSWD, tlsClientConfig.getKeyPassword()); - niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE, parentPath.relativize(Paths.get(tlsClientConfig.getTrustStore())).toString()); + + niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE, CONF + new File(tlsClientConfig.getTrustStore()).getName()); niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsClientConfig.getTrustStoreType()); niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsClientConfig.getTrustStorePassword()); - if (!StringUtils.isEmpty(hostname)) { - niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTPS_HOST, hostname); - } - niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTPS_PORT, Integer.toString(httpsPort)); + niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTP_HOST, ""); niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTP_PORT, ""); niFiPropertiesWriter.setPropertyValue(NiFiProperties.SITE_TO_SITE_SECURE, "true"); + + getHostnamePropertyStream().forEach(s -> niFiPropertiesWriter.setPropertyValue(s, hostname)); + + getPropertyPortMap().entrySet().forEach(nameToPortEntry -> niFiPropertiesWriter.setPropertyValue(nameToPortEntry.getKey(), Integer.toString(nameToPortEntry.getValue()))); + } + + protected Properties getOverlayProperties() { + return overlayProperties; + } + + protected Map<String, Integer> getPropertyPortMap() { + return overlayProperties.stringPropertyNames().stream().filter(s -> !explicitProperties.contains(s)).collect(Collectors.toMap(Function.identity(), portProperty -> { + String portVal = overlayProperties.getProperty(portProperty); + int startingPort; + try { + startingPort = Integer.parseInt(portVal); + } catch (NumberFormatException e) { + throw new NumberFormatException("Expected numeric values in " + OVERLAY_PROPERTIES + " (" + portProperty + " was " + portVal + ")"); + } + return startingPort + hostNum - 1; + })); + } + + protected Stream<String> getHostnamePropertyStream() { + String hostnamePropertyString = overlayProperties.getProperty(HOSTNAME_PROPERTIES); + if (!StringUtils.isEmpty(hostnamePropertyString)) { + return Arrays.stream(hostnamePropertyString.split(",")).map(String::trim); + } + return Stream.of(); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandalone.java ---------------------------------------------------------------------- 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 c91e7fa..8dc5186 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<String> hostnames = standaloneConfig.getHostnames(); if (logger.isInfoEnabled()) { logger.info("Running standalone certificate generation with output directory " + baseDir); } @@ -128,19 +128,22 @@ public class TlsToolkitStandalone { } } - List<String> keyStorePasswords = standaloneConfig.getKeyStorePasswords(); - List<String> keyPasswords = standaloneConfig.getKeyPasswords(); - List<String> trustStorePasswords = standaloneConfig.getTrustStorePasswords(); NiFiPropertiesWriterFactory niFiPropertiesWriterFactory = standaloneConfig.getNiFiPropertiesWriterFactory(); - int httpsPort = standaloneConfig.getHttpsPort(); boolean overwrite = standaloneConfig.isOverwrite(); - if (hostnames.isEmpty() && logger.isInfoEnabled()) { + List<InstanceDefinition> instanceDefinitions = standaloneConfig.getInstanceDefinitions(); + if (!instanceDefinitions.isEmpty() && logger.isInfoEnabled()) { logger.info("No " + TlsToolkitStandaloneCommandLine.HOSTNAMES_ARG + " specified, not generating any host certificates or configuration."); } - for (int i = 0; i < hostnames.size(); i++) { - String hostname = hostnames.get(i); - File hostDir = new File(baseDir, hostname); + for (InstanceDefinition instanceDefinition : instanceDefinitions) { + String hostname = instanceDefinition.getHostname(); + File hostDir; + int hostIdentifierNumber = instanceDefinition.getInstanceIdentifier().getNumber(); + if (hostIdentifierNumber == 1) { + hostDir = new File(baseDir, hostname); + } else { + hostDir = new File(baseDir, hostname + "_" + hostIdentifierNumber); + } TlsClientConfig tlsClientConfig = new TlsClientConfig(standaloneConfig); File keystore = new File(hostDir, "keystore." + tlsClientConfig.getKeyStoreType().toLowerCase()); @@ -171,20 +174,20 @@ public class TlsToolkitStandalone { } tlsClientConfig.setKeyStore(keystore.getAbsolutePath()); - tlsClientConfig.setKeyStorePassword(keyStorePasswords.get(i)); - tlsClientConfig.setKeyPassword(keyPasswords.get(i)); + tlsClientConfig.setKeyStorePassword(instanceDefinition.getKeyStorePassword()); + tlsClientConfig.setKeyPassword(instanceDefinition.getKeyPassword()); tlsClientConfig.setTrustStore(truststore.getAbsolutePath()); - tlsClientConfig.setTrustStorePassword(trustStorePasswords.get(i)); + tlsClientConfig.setTrustStorePassword(instanceDefinition.getTrustStorePassword()); TlsClientManager tlsClientManager = new TlsClientManager(tlsClientConfig); KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize); tlsClientManager.addPrivateKeyToKeyStore(keyPair, NIFI_KEY, CertificateUtils.generateIssuedCertificate(TlsConfig.calcDefaultDn(hostname), keyPair.getPublic(), certificate, caKeyPair, signingAlgorithm, days), certificate); tlsClientManager.setCertificateEntry(NIFI_CERT, certificate); tlsClientManager.addClientConfigurationWriter(new NifiPropertiesTlsClientConfigWriter(niFiPropertiesWriterFactory, outputStreamFactory, new File(hostDir, "nifi.properties"), - hostname, httpsPort)); + hostname, instanceDefinition.getNumber())); tlsClientManager.write(outputStreamFactory); if (logger.isInfoEnabled()) { - logger.info("Successfully generated TLS configuration for " + hostname + ":" + httpsPort + " in " + hostDir); + logger.info("Successfully generated TLS configuration for " + hostname + " " + hostIdentifierNumber + " in " + hostDir); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/standalone/TlsToolkitStandaloneCommandLine.java ---------------------------------------------------------------------- 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 602fce4..4ba0746 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<String> hostnames; - private int httpsPort; + private List<InstanceDefinition> instanceDefinitions; private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory; - private List<String> keyStorePasswords; - private List<String> keyPasswords; - private List<String> trustStorePasswords; private List<String> clientDns; private List<String> clientPasswords; private boolean clientPasswordsGenerated; @@ -81,13 +78,14 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine { this.passwordUtil = passwordUtil; addOptionWithArg("o", OUTPUT_DIRECTORY_ARG, "The directory to output keystores, truststore, config files.", DEFAULT_OUTPUT_DIRECTORY); addOptionWithArg("n", HOSTNAMES_ARG, "Comma separated list of hostnames."); - addOptionWithArg("p", HTTPS_PORT_ARG, "Https port to use.", DEFAULT_HTTPS_PORT); addOptionWithArg("f", NIFI_PROPERTIES_FILE_ARG, "Base nifi.properties file to update. (Embedded file identical to the one in a default NiFi install will be used if not specified.)"); addOptionWithArg("S", KEY_STORE_PASSWORD_ARG, "Keystore password to use. Must either be one value or one for each host. (autogenerate if not specified)"); addOptionWithArg("K", KEY_PASSWORD_ARG, "Key password to use. Must either be one value or one for each host. (autogenerate if not specified)"); addOptionWithArg("P", TRUST_STORE_PASSWORD_ARG, "Keystore password to use. Must either be one value or one for each host. (autogenerate if not specified)"); addOptionWithArg("C", CLIENT_CERT_DN_ARG, "Generate client certificate suitable for use in browser with specified DN. (Can be specified multiple times.)"); addOptionWithArg("B", CLIENT_CERT_PASSWORD_ARG, "Password for client certificate. Must either be one value or one for each client DN. (autogenerate if not specified)"); + addOptionWithArg("G", GLOBAL_PORT_SEQUENCE_ARG, "Use sequential ports that are calculated for all hosts according to the provided hostname expressions. " + + "(Can be specified multiple times, MUST BE SAME FROM RUN TO RUN.)"); addOptionNoArg("O", OVERWRITE_ARG, "Overwrite existing host output."); } @@ -114,10 +112,20 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine { String outputDirectory = commandLine.getOptionValue(OUTPUT_DIRECTORY_ARG, DEFAULT_OUTPUT_DIRECTORY); baseDir = new File(outputDirectory); + Stream<String> globalOrderExpressions = null; + if (commandLine.hasOption(GLOBAL_PORT_SEQUENCE_ARG)) { + globalOrderExpressions = Arrays.stream(commandLine.getOptionValues(GLOBAL_PORT_SEQUENCE_ARG)).flatMap(s -> Arrays.stream(s.split(","))).map(String::trim); + } + if (commandLine.hasOption(HOSTNAMES_ARG)) { - hostnames = Collections.unmodifiableList(Arrays.stream(commandLine.getOptionValue(HOSTNAMES_ARG).split(",")).map(String::trim).collect(Collectors.toList())); + instanceDefinitions = Collections.unmodifiableList( + InstanceDefinition.createDefinitions(globalOrderExpressions, + Arrays.stream(commandLine.getOptionValues(HOSTNAMES_ARG)).flatMap(s -> Arrays.stream(s.split(",")).map(String::trim)), + parsePasswordSupplier(commandLine, KEY_STORE_PASSWORD_ARG, passwordUtil.passwordSupplier()), + parsePasswordSupplier(commandLine, KEY_PASSWORD_ARG, commandLine.hasOption(DIFFERENT_KEY_AND_KEYSTORE_PASSWORDS_ARG) ? passwordUtil.passwordSupplier() : null), + parsePasswordSupplier(commandLine, TRUST_STORE_PASSWORD_ARG, passwordUtil.passwordSupplier()))); } else { - hostnames = Collections.emptyList(); + instanceDefinitions = Collections.emptyList(); } String[] clientDnValues = commandLine.getOptionValues(CLIENT_CERT_DN_ARG); @@ -127,12 +135,6 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine { clientDns = Collections.emptyList(); } - httpsPort = getIntValue(commandLine, HTTPS_PORT_ARG, DEFAULT_HTTPS_PORT); - - int numHosts = hostnames.size(); - keyStorePasswords = Collections.unmodifiableList(getPasswords(KEY_STORE_PASSWORD_ARG, commandLine, numHosts, HOSTNAMES_ARG)); - keyPasswords = Collections.unmodifiableList(getKeyPasswords(commandLine, keyStorePasswords)); - trustStorePasswords = Collections.unmodifiableList(getPasswords(TRUST_STORE_PASSWORD_ARG, commandLine, numHosts, HOSTNAMES_ARG)); clientPasswords = Collections.unmodifiableList(getPasswords(CLIENT_CERT_PASSWORD_ARG, commandLine, clientDns.size(), CLIENT_CERT_DN_ARG)); clientPasswordsGenerated = commandLine.getOptionValues(CLIENT_CERT_PASSWORD_ARG) == null; overwrite = commandLine.hasOption(OVERWRITE_ARG); @@ -165,11 +167,18 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine { return printUsageAndThrow("Expected either 1 value or " + num + " (the number of " + numArg + ") values for " + arg, ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS); } - private List<String> getKeyPasswords(CommandLine commandLine, List<String> keyStorePasswords) throws CommandLineParseException { - if (differentPasswordForKeyAndKeystore() || commandLine.hasOption(KEY_PASSWORD_ARG)) { - return getPasswords(KEY_PASSWORD_ARG, commandLine, keyStorePasswords.size(), HOSTNAMES_ARG); + private Supplier<String> parsePasswordSupplier(CommandLine commandLine, String option, Supplier<String> defaultSupplier) { + if (commandLine.hasOption(option)) { + String[] values = commandLine.getOptionValues(option); + if (values.length == 1) { + return PasswordUtil.passwordSupplier(values[0]); + } else { + return PasswordUtil.passwordSupplier("Provided " + option + " exhausted, please don't specify " + option + + ", specify one value to be used for all NiFi instances, or specify one value for each NiFi instance.", values); + } + } else { + return defaultSupplier; } - return new ArrayList<>(keyStorePasswords); } public StandaloneConfig createConfig() { @@ -177,11 +186,7 @@ public class TlsToolkitStandaloneCommandLine extends BaseCommandLine { standaloneConfig.setBaseDir(baseDir); standaloneConfig.setNiFiPropertiesWriterFactory(niFiPropertiesWriterFactory); - standaloneConfig.setHostnames(hostnames); - standaloneConfig.setKeyStorePasswords(keyStorePasswords); - standaloneConfig.setKeyPasswords(keyPasswords); - standaloneConfig.setTrustStorePasswords(trustStorePasswords); - standaloneConfig.setHttpsPort(httpsPort); + standaloneConfig.setInstanceDefinitions(instanceDefinitions); standaloneConfig.setOverwrite(overwrite); standaloneConfig.setClientDns(clientDns); standaloneConfig.setClientPasswords(clientPasswords); http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordUtil.java ---------------------------------------------------------------------- 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 c26fd7f..2c7a6aa 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<String> passwordSupplier() { + return () -> generatePassword(); + } + + public static Supplier<String> passwordSupplier(String password) { + return () -> password; + } + + public static Supplier<String> passwordSupplier(String exhaustedMessage, String[] passwords) { + AtomicInteger index = new AtomicInteger(0); + return () -> { + int i = index.getAndIncrement(); + if (i < passwords.length) { + return passwords[i]; + } else { + throw new PasswordsExhaustedException(exhaustedMessage); + } + }; + } } http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/main/java/org/apache/nifi/toolkit/tls/util/PasswordsExhaustedException.java ---------------------------------------------------------------------- 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 0000000..6d26a19 --- /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); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/ExitException.java ---------------------------------------------------------------------- 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 0000000..5134353 --- /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; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/SystemExitCapturer.java ---------------------------------------------------------------------- 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 0000000..e4552a3 --- /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); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/TlsToolkitMainTest.java ---------------------------------------------------------------------- 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 0000000..c9a74dd --- /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 { + + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceDefinitionTest.java ---------------------------------------------------------------------- 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 0000000..9d4fcd0 --- /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<Integer> integerRange(int start, int endInclusive) { + return IntStream.range(start, endInclusive + 1).mapToObj(value -> value); + } + + private void testCreateDefinitions(List<String> hostnameExpressions, List<String> expectedHostnames, List<Integer> expectedNumbers, boolean nullForKeyPasswords) { + List<String> keyStorePasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testKeyStorePassword" + value).collect(Collectors.toList()); + List<String> keyPasswords; + if (nullForKeyPasswords) { + keyPasswords = null; + } else { + keyPasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testKeyPassword" + value).collect(Collectors.toList()); + } + List<String> trustStorePasswords = IntStream.range(0, expectedHostnames.size()).mapToObj(value -> "testTrustStorePassword" + value).collect(Collectors.toList()); + List<InstanceDefinition> instanceDefinitions = InstanceDefinition.createDefinitions(null, hostnameExpressions.stream(), + mockSupplier(keyStorePasswords.toArray(new String[keyStorePasswords.size()])), keyPasswords == null ? null : mockSupplier(keyPasswords.toArray(new String[keyPasswords.size()])), + mockSupplier(trustStorePasswords.toArray(new String[trustStorePasswords.size()]))); + testCreateDefinitionsOutput(instanceDefinitions, expectedHostnames, expectedNumbers, keyStorePasswords, keyPasswords, trustStorePasswords); + } + + private void testCreateDefinitionsOutput(List<InstanceDefinition> instanceDefinitions, List<String> expectedHostnames, List<Integer> expectedNumbers, List<String> keyStorePasswords, + List<String> keyPasswords, List<String> trustStorePasswords) { + assertEquals(instanceDefinitions.size(), expectedHostnames.size()); + for (int i = 0; i < instanceDefinitions.size(); i++) { + assertDefinitionEquals(instanceDefinitions.get(i), expectedHostnames.get(i), expectedNumbers.get(i), keyStorePasswords.get(i), + keyPasswords == null ? null : keyPasswords.get(i), trustStorePasswords.get(i)); + } + } + + private void testCreateDefinition(String hostname, int num, String keyStorePassword, String keyPassword, String trustStorePassword) { + InstanceDefinition definition = InstanceDefinition.createDefinition(new InstanceIdentifier(hostname, num), num, mockSupplier(keyStorePassword), + keyPassword == null ? null : mockSupplier(keyPassword), mockSupplier(trustStorePassword)); + assertDefinitionEquals(definition, hostname, num, keyStorePassword, keyPassword, trustStorePassword); + } + + private void assertDefinitionEquals(InstanceDefinition definition, String hostname, int num, String keyStorePassword, String keyPassword, String trustStorePassword) { + assertEquals(hostname, definition.getHostname()); + assertEquals(num, definition.getNumber()); + assertEquals(keyStorePassword, definition.getKeyStorePassword()); + assertEquals(keyPassword == null ? keyStorePassword : keyPassword, definition.getKeyPassword()); + assertEquals(trustStorePassword, definition.getTrustStorePassword()); + } + + private <T> Supplier<T> mockSupplier(T... values) { + Supplier<T> supplier = mock(Supplier.class); + if (values.length == 1) { + when(supplier.get()).thenReturn(values[0]); + } else if (values.length > 1) { + when(supplier.get()).thenReturn(values[0], Arrays.copyOfRange(values, 1, values.length)); + } + return supplier; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/configuration/InstanceIdentifierTest.java ---------------------------------------------------------------------- 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 0000000..b75c02f --- /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<InstanceIdentifier, Integer> orderMap = InstanceIdentifier.createOrderMap(Stream.of(abc123, abc0123 + "(2)", b)); + + AtomicInteger num = new AtomicInteger(1); + Consumer<InstanceIdentifier> action = id -> { + int i = num.getAndIncrement(); + assertEquals(i, orderMap.get(id).intValue()); + }; + + InstanceIdentifier.extractHostnames(abc0123).flatMap(s -> Stream.of(new InstanceIdentifier(s, 1), new InstanceIdentifier(s, 2))).forEach(action); + InstanceIdentifier.extractHostnames(abc123).map(s -> new InstanceIdentifier(s, 1)).forEach(action); + InstanceIdentifier.extractHostnames(b).map(s -> new InstanceIdentifier(s, 1)).forEach(action); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateIdentifiersCharactersAfterNumber() { + InstanceIdentifier.createIdentifiers(Stream.of("test(2)a")).count(); + } + + private Stream<Integer> integerRange(int start, int endInclusive) { + return IntStream.range(start, endInclusive + 1).mapToObj(value -> value); + } + + private void testExtractHostnames(String hostnameWithRange, String... expectedHostnames) { + assertEquals(Stream.of(expectedHostnames).collect(Collectors.toList()), InstanceIdentifier.extractHostnames(hostnameWithRange).collect(Collectors.toList())); + } + + private void testCreateIdentifiers(List<String> hostnameExpressions, List<String> expectedHostnames, List<Integer> expectedNumbers) { + List<InstanceIdentifier> instanceIdentifiers = InstanceIdentifier.createIdentifiers(hostnameExpressions.stream()).collect(Collectors.toList()); + assertEquals(instanceIdentifiers.size(), expectedHostnames.size()); + for (int i = 0; i < instanceIdentifiers.size(); i++) { + InstanceIdentifier identifier = instanceIdentifiers.get(i); + assertEquals(expectedHostnames.get(i), identifier.getHostname()); + assertEquals((int) expectedNumbers.get(i), identifier.getNumber()); + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/2fd39676/nifi-toolkit/nifi-toolkit-tls/src/test/java/org/apache/nifi/toolkit/tls/manager/writer/NifiPropertiesTlsClientConfigWriterTest.java ---------------------------------------------------------------------- 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 0000000..c96d906 --- /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; + } +}
