NIFI-12073 removed toolkit tests which rely on system exit calls and captures not allowed any longer by defunct security manager support in Java 21

Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com>

This closes #7743.
This commit is contained in:
Joseph Witt 2023-09-15 14:40:26 -07:00 committed by Pierre Villard
parent 019e16c728
commit ad753318e7
No known key found for this signature in database
GPG Key ID: F92A93B30C07C6D5
6 changed files with 0 additions and 1569 deletions

View File

@ -1,31 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.toolkit.tls;
public class ExitException extends SecurityException {
// [see http://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit#answer-309427]
private final int exitCode;
public ExitException(int exitCode) {
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
}

View File

@ -1,63 +0,0 @@
/*
* 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.security.Permission;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
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) {
final ExitException e = assertThrows(ExitException.class, runnable::run);
assertEquals(exitCode.ordinal(), e.getExitCode(), "Expecting exit code: " + exitCode + ", got " + ExitCode.values()[e.getExitCode()]);
}
@Override
public void close() {
System.setSecurityManager(originalSecurityManager);
}
}

View File

@ -1,129 +0,0 @@
/*
* 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.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TlsToolkitMainTest {
private TlsToolkitMain tlsToolkitMain;
private SystemExitCapturer systemExitCapturer;
@BeforeEach
public void setup() {
systemExitCapturer = new SystemExitCapturer();
tlsToolkitMain = new TlsToolkitMain();
}
@AfterEach
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);
}
@Test
public void testRemovesServiceArg() {
String storingMain = "storingmain";
tlsToolkitMain.getMainMap().put(storingMain, StoringMain.class);
tlsToolkitMain.doMain(new String[]{storingMain, "-h"});
assertArrayEquals(new String[]{"-h"}, StoringMain.args);
}
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 {
}
private static class StoringMain {
private static String[] args;
public static void main(String[] args) {
StoringMain.args = args;
}
}
}

View File

@ -1,512 +0,0 @@
/*
* 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.standalone;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStoreException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Collectors;
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;
import org.apache.nifi.toolkit.tls.util.PasswordUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public class TlsToolkitStandaloneCommandLineTest {
private TlsToolkitStandaloneCommandLine tlsToolkitStandaloneCommandLine;
final String CHANGEIT = "changeit";
final String keyPass = CHANGEIT;
final String keystorePass = CHANGEIT;
final String wrongPass = "wrongpass";
private File outputFolder = null;
final String keystoreFile = Objects.requireNonNull(getClass().getClassLoader().getResource("keystore.jks")).getFile();
@BeforeEach
public void setup(@TempDir Path tempDir) throws IOException {
final SecureRandom secureRandom = new SecureRandom();
tlsToolkitStandaloneCommandLine = new TlsToolkitStandaloneCommandLine(new PasswordUtil(secureRandom));
final Path outputDir = Files.createTempDirectory(tempDir, "splitKeystoreOutputDir");
outputFolder = outputDir.toFile();
}
@Test
public void testHelp() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("-h"));
assertEquals(ExitCode.HELP, e.getExitCode());
}
@Test
public void testUnknownArg() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("--unknownArg"));
assertEquals(ExitCode.ERROR_PARSING_COMMAND_LINE, e.getExitCode());
}
@Test
public void testKeyAlgorithm() throws CommandLineParseException {
String testKeyAlgorithm = "testKeyAlgorithm";
tlsToolkitStandaloneCommandLine.parse("-a", testKeyAlgorithm);
assertEquals(testKeyAlgorithm, tlsToolkitStandaloneCommandLine.createConfig().getKeyPairAlgorithm());
}
@Test
public void testKeySizeArgNotInteger() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("-k", "badVal"));
assertEquals(ExitCode.ERROR_PARSING_INT_ARG, e.getExitCode());
}
@Test
public void testKeySize() throws CommandLineParseException {
int testKeySize = 4096;
tlsToolkitStandaloneCommandLine.parse("-k", Integer.toString(testKeySize));
assertEquals(testKeySize, tlsToolkitStandaloneCommandLine.createConfig().getKeySize());
}
@Test
public void testSigningAlgorithm() throws CommandLineParseException {
String testSigningAlgorithm = "testSigningAlgorithm";
tlsToolkitStandaloneCommandLine.parse("-s", testSigningAlgorithm);
assertEquals(testSigningAlgorithm, tlsToolkitStandaloneCommandLine.createConfig().getSigningAlgorithm());
}
@Test
public void testSAN() throws CommandLineParseException {
String dnsSAN = "nifi.apache.org";
tlsToolkitStandaloneCommandLine.parse("--subjectAlternativeNames", dnsSAN);
assertEquals(dnsSAN, tlsToolkitStandaloneCommandLine.createConfig().getDomainAlternativeNames().get(0));
}
@Test
public void testDaysNotInteger() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("-d", "badVal"));
assertEquals(ExitCode.ERROR_PARSING_INT_ARG, e.getExitCode());
}
@Test
public void testDays() throws CommandLineParseException {
int testDays = 29;
tlsToolkitStandaloneCommandLine.parse("-d", Integer.toString(testDays));
assertEquals(testDays, tlsToolkitStandaloneCommandLine.createConfig().getDays());
}
@Test
public void testKeyStoreType() throws CommandLineParseException {
String testKeyStoreType = "testKeyStoreType";
tlsToolkitStandaloneCommandLine.parse("-T", testKeyStoreType);
assertEquals(testKeyStoreType, tlsToolkitStandaloneCommandLine.getKeyStoreType());
}
@Test
public void testOutputDirectory() throws CommandLineParseException {
String testPath = File.separator + "fake" + File.separator + "path" + File.separator + "doesnt" + File.separator + "exist";
tlsToolkitStandaloneCommandLine.parse("-o", testPath);
assertEquals(testPath, tlsToolkitStandaloneCommandLine.createConfig().getBaseDir().getPath());
}
@Test
public void testHostnames() throws CommandLineParseException {
String nifi1 = "nifi1";
String nifi2 = "nifi2";
tlsToolkitStandaloneCommandLine.parse("-n", nifi1 + " , " + nifi2);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(nifi1, instanceDefinitions.get(0).getHostname());
assertEquals(nifi2, instanceDefinitions.get(1).getHostname());
}
@Test
public void testNifiPropertiesFile() throws CommandLineParseException, IOException {
tlsToolkitStandaloneCommandLine.parse("-f", TlsToolkitStandaloneTest.TEST_NIFI_PROPERTIES);
assertEquals(TlsToolkitStandaloneTest.FAKE_VALUE, getProperties().get(TlsToolkitStandaloneTest.NIFI_FAKE_PROPERTY));
}
@Test
public void testNifiPropertiesFileDefault() throws CommandLineParseException, IOException {
tlsToolkitStandaloneCommandLine.parse();
assertNull(getProperties().get(TlsToolkitStandaloneTest.NIFI_FAKE_PROPERTY));
}
@Test
public void testBadNifiPropertiesFile() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("-f", "/this/file/should/not/exist.txt"));
assertEquals(ExitCode.ERROR_READING_NIFI_PROPERTIES, e.getExitCode());
}
@Test
public void testNotSameKeyAndKeystorePassword() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse("-g", "-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertNotEquals(instanceDefinitions.get(0).getKeyStorePassword(), instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testSameKeyAndKeystorePassword() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse("-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(instanceDefinitions.get(0).getKeyStorePassword(), instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testSameKeyAndKeystorePasswordWithKeystorePasswordSpecified() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-S", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyStorePassword());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testSameKeyAndKeystorePasswordWithKeyPasswordSpecified() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-K", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertNotEquals(instanceDefinitions.get(0).getKeyStorePassword(), instanceDefinitions.get(0).getKeyPassword());
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testKeyStorePasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-S", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyStorePassword());
}
@Test
public void testMultipleKeystorePasswordArgs() throws CommandLineParseException {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-S", testPassword1, "-S", testPassword2);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(testPassword1, instanceDefinitions.get(0).getKeyStorePassword());
assertEquals(testPassword2, instanceDefinitions.get(1).getKeyStorePassword());
}
@Test
public void testKeyPasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-K", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getKeyPassword());
}
@Test
public void testMultipleKeyPasswordArgs() throws CommandLineParseException {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-K", testPassword1, "-K", testPassword2);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(testPassword1, instanceDefinitions.get(0).getKeyPassword());
assertEquals(testPassword2, instanceDefinitions.get(1).getKeyPassword());
}
@Test
public void testTruststorePasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-P", testPassword, "-n", TlsConfig.DEFAULT_HOSTNAME);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(1, instanceDefinitions.size());
assertEquals(testPassword, instanceDefinitions.get(0).getTrustStorePassword());
}
@Test
public void testMultipleTruststorePasswordArgs() throws CommandLineParseException {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-P", testPassword1, "-P", testPassword2);
List<InstanceDefinition> instanceDefinitions = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions();
assertEquals(2, instanceDefinitions.size());
assertEquals(testPassword1, instanceDefinitions.get(0).getTrustStorePassword());
assertEquals(testPassword2, instanceDefinitions.get(1).getTrustStorePassword());
}
@Test
public void testNifiDnPrefix() throws CommandLineParseException {
String testPrefix = "O=apache, CN=";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi", "--nifiDnPrefix", testPrefix);
StandaloneConfig config = tlsToolkitStandaloneCommandLine.createConfig();
assertEquals(testPrefix, config.getDnPrefix());
}
@Test
public void testNifiDnSuffix() throws CommandLineParseException {
String testSuffix = ", O=apache, OU=nifi";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi", "--nifiDnSuffix", testSuffix);
StandaloneConfig config = tlsToolkitStandaloneCommandLine.createConfig();
assertEquals(testSuffix, config.getDnSuffix());
}
@Test
public void testClientDnDefault() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse();
assertEquals(Collections.emptyList(), tlsToolkitStandaloneCommandLine.createConfig().getClientDns());
}
@Test
public void testClientDnSingle() throws CommandLineParseException {
String testCn = "OU=NIFI,CN=testuser";
tlsToolkitStandaloneCommandLine.parse("-C", testCn);
List<String> clientDns = tlsToolkitStandaloneCommandLine.createConfig().getClientDns();
assertEquals(1, clientDns.size());
assertEquals(testCn, clientDns.get(0));
}
@Test
public void testClientDnMulti() throws CommandLineParseException {
String testCn = "OU=NIFI,CN=testuser";
String testCn2 = "OU=NIFI,CN=testuser2";
tlsToolkitStandaloneCommandLine.parse("-C", testCn, "-C", testCn2);
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createConfig();
List<String> clientDns = standaloneConfig.getClientDns();
assertEquals(2, clientDns.size());
assertEquals(testCn, clientDns.get(0));
assertEquals(testCn2, clientDns.get(1));
assertEquals(2, standaloneConfig.getClientPasswords().size());
}
@Test
public void testClientPasswordMulti() throws CommandLineParseException {
String testCn = "OU=NIFI,CN=testuser";
String testCn2 = "OU=NIFI,CN=testuser2";
String testPass1 = "testPass1";
String testPass2 = "testPass2";
tlsToolkitStandaloneCommandLine.parse("-C", testCn, "-C", testCn2, "-B", testPass1, "-B", testPass2);
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createConfig();
List<String> clientDns = standaloneConfig.getClientDns();
assertEquals(2, clientDns.size());
assertEquals(testCn, clientDns.get(0));
assertEquals(testCn2, clientDns.get(1));
List<String> clientPasswords = standaloneConfig.getClientPasswords();
assertEquals(2, clientPasswords.size());
assertEquals(testPass1, clientPasswords.get(0));
assertEquals(testPass2, clientPasswords.get(1));
}
@Test
public void testNoGlobalOrder() throws CommandLineParseException {
String hostname1 = "other0[4-6]";
String hostname2 = "nifi3(2)";
tlsToolkitStandaloneCommandLine.parse("-n", hostname1, "-n", hostname2);
Map<InstanceIdentifier, InstanceDefinition> definitionMap = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions().stream()
.collect(Collectors.toMap(InstanceDefinition::getInstanceIdentifier, Function.identity()));
assertEquals(5, definitionMap.size());
InstanceDefinition nifi3_1 = definitionMap.get(new InstanceIdentifier("nifi3", 1));
assertNotNull(nifi3_1);
assertEquals(1, nifi3_1.getInstanceIdentifier().getNumber());
assertEquals(1, nifi3_1.getNumber());
InstanceDefinition nifi3_2 = definitionMap.get(new InstanceIdentifier("nifi3", 2));
assertNotNull(nifi3_2);
assertEquals(2, nifi3_2.getInstanceIdentifier().getNumber());
assertEquals(2, nifi3_2.getNumber());
InstanceDefinition other04 = definitionMap.get(new InstanceIdentifier("other04", 1));
assertNotNull(other04);
assertEquals(1, other04.getInstanceIdentifier().getNumber());
assertEquals(1, other04.getNumber());
InstanceDefinition other05 = definitionMap.get(new InstanceIdentifier("other05", 1));
assertNotNull(other05);
assertEquals(1, other05.getInstanceIdentifier().getNumber());
assertEquals(1, other05.getNumber());
InstanceDefinition other06 = definitionMap.get(new InstanceIdentifier("other06", 1));
assertNotNull(other06);
assertEquals(1, other06.getInstanceIdentifier().getNumber());
assertEquals(1, other06.getNumber());
}
@Test
public void testGlobalOrder() throws CommandLineParseException {
String hostname1 = "other0[4-6]";
String hostname2 = "nifi3(2)";
String globalOrder1 = "nifi[1-5](2),other[01-4]";
String globalOrder2 = "other[05-10]";
tlsToolkitStandaloneCommandLine.parse("-n", hostname1, "-n", hostname2, "-G", globalOrder1, "-G", globalOrder2);
Map<InstanceIdentifier, InstanceDefinition> definitionMap = tlsToolkitStandaloneCommandLine.createConfig().getInstanceDefinitions().stream()
.collect(Collectors.toMap(InstanceDefinition::getInstanceIdentifier, Function.identity()));
assertEquals(5, definitionMap.size());
InstanceDefinition nifi3_1 = definitionMap.get(new InstanceIdentifier("nifi3", 1));
assertNotNull(nifi3_1);
assertEquals(1, nifi3_1.getInstanceIdentifier().getNumber());
assertEquals(5, nifi3_1.getNumber());
InstanceDefinition nifi3_2 = definitionMap.get(new InstanceIdentifier("nifi3", 2));
assertNotNull(nifi3_2);
assertEquals(2, nifi3_2.getInstanceIdentifier().getNumber());
assertEquals(6, nifi3_2.getNumber());
InstanceDefinition other04 = definitionMap.get(new InstanceIdentifier("other04", 1));
assertNotNull(other04);
assertEquals(1, other04.getInstanceIdentifier().getNumber());
assertEquals(14, other04.getNumber());
InstanceDefinition other05 = definitionMap.get(new InstanceIdentifier("other05", 1));
assertNotNull(other05);
assertEquals(1, other05.getInstanceIdentifier().getNumber());
assertEquals(15, other05.getNumber());
InstanceDefinition other06 = definitionMap.get(new InstanceIdentifier("other06", 1));
assertNotNull(other06);
assertEquals(1, other06.getInstanceIdentifier().getNumber());
assertEquals(16, other06.getNumber());
}
@Test
public void testBadGlobalOrder() {
assertThrows(IllegalArgumentException.class, () -> tlsToolkitStandaloneCommandLine.parse("-n", "notInGlobalOrder", "-G", "nifi[1-3]"));
}
@Test
public void testDefaultOutputPathRoot() {
Path root = Paths.get(".").toAbsolutePath().getRoot().resolve(".");
String calculateDefaultOutputDirectory = TlsToolkitStandaloneCommandLine.calculateDefaultOutputDirectory(root);
assertEquals(root.toAbsolutePath().getRoot().toString(), calculateDefaultOutputDirectory);
}
@Test
public void testDefaultOutputPath() {
Path path = Paths.get(".");
String calculateDefaultOutputDirectory = TlsToolkitStandaloneCommandLine.calculateDefaultOutputDirectory(path);
assertEquals("../" + path.toAbsolutePath().normalize().getFileName().toString(), calculateDefaultOutputDirectory);
}
private Properties getProperties() throws IOException {
NiFiPropertiesWriter niFiPropertiesWriter = tlsToolkitStandaloneCommandLine.createConfig().getNiFiPropertiesWriterFactory().create();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
niFiPropertiesWriter.writeNiFiProperties(byteArrayOutputStream);
Properties properties = new Properties();
properties.load(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
return properties;
}
@Test
public void testSplitKeystore() throws Exception {
tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", keystorePass, "-K", keyPass, "-o", outputFolder.getPath());
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
assertTrue(standaloneConfig.isSplitKeystore());
assertEquals(keyPass, standaloneConfig.getKeyPassword());
assertEquals(keystorePass, standaloneConfig.getKeyStorePassword());
TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
toolkit.splitKeystore(standaloneConfig);
assertEquals(3, Objects.requireNonNull(outputFolder.listFiles()).length);
// Validity checking of the output is done in TlsHelperTest
for (File file : Objects.requireNonNull(outputFolder.listFiles())) {
assertTrue(file.length() > 0);
}
}
@Test
public void testSplitKeystoreMissingPasswords() {
assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-o", outputFolder.getPath()));
}
@Test
public void testSplitKeystoreWithSameKeystoreAndKeyPassword() throws Exception {
tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", keystorePass, "-o", outputFolder.getPath());
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
toolkit.splitKeystore(standaloneConfig);
}
@Test
public void testSplitKeystoreWrongKeyPass() throws Exception {
tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", keystorePass, "-K", wrongPass, "-o", outputFolder.getPath());
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
assertThrows(UnrecoverableKeyException.class, () -> toolkit.splitKeystore(standaloneConfig));
}
@Test
public void testSplitKeystoreWrongKeystorePass() throws Exception {
tlsToolkitStandaloneCommandLine.parse("-splitKeystore", keystoreFile, "-S", wrongPass, "-K", keyPass, "-o", outputFolder.getPath());
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
assertThrows(IOException.class, () -> toolkit.splitKeystore(standaloneConfig));
}
@Test
public void testSplitKeystoreNoKeystore() {
assertThrows(CommandLineParseException.class, () -> tlsToolkitStandaloneCommandLine.parse("-splitKeystore", "-S", keystorePass, "-K", keyPass, "-o", outputFolder.getPath()));
}
@Test
public void testSplitKeystoreEmptyKeystore() throws Exception {
tlsToolkitStandaloneCommandLine.parse(
"-splitKeystore", new File("src/test/resources/empty-keystore.jks").getPath(), "-S", keystorePass, "-K", keyPass, "-o", outputFolder.getPath());
StandaloneConfig standaloneConfig = tlsToolkitStandaloneCommandLine.createSplitKeystoreConfig();
assertTrue(standaloneConfig.isSplitKeystore());
assertEquals(keyPass, standaloneConfig.getKeyPassword());
assertEquals(keystorePass, standaloneConfig.getKeyStorePassword());
TlsToolkitStandalone toolkit = new TlsToolkitStandalone();
assertThrows(KeyStoreException.class, () -> toolkit.splitKeystore(standaloneConfig));
}
}

View File

@ -1,726 +0,0 @@
/*
* 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.standalone;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.toolkit.tls.SystemExitCapturer;
import org.apache.nifi.toolkit.tls.commandLine.BaseTlsToolkitCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
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.service.TlsCertificateAuthorityTest;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.apache.nifi.toolkit.tls.util.TlsHelperTest;
import org.apache.nifi.util.NiFiProperties;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
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 SystemExitCapturer systemExitCapturer;
@TempDir
private File tempDir;
@BeforeEach
public void setup() throws IOException {
systemExitCapturer = new SystemExitCapturer();
}
@AfterEach
public void teardown() {
systemExitCapturer.close();
}
@Test
public void testBadParse() {
runAndAssertExitCode(ExitCode.ERROR_PARSING_COMMAND_LINE, "--unknownArgument");
}
@Test
public void testHelp() {
runAndAssertExitCode(ExitCode.HELP, "-h");
runAndAssertExitCode(ExitCode.HELP, "--help");
}
@Test
public void testDirOutput() throws Exception {
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", TlsConfig.DEFAULT_HOSTNAME);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertNull(nifiProperties.get("nifi.fake.property"));
assertEquals(nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD), nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
}
@Test
public void testDifferentArg() throws Exception {
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-g", "-n", TlsConfig.DEFAULT_HOSTNAME);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertNull(nifiProperties.get("nifi.fake.property"));
assertNotEquals(nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD), nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
}
@Test
public void testFileArg() throws Exception {
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-f", TEST_NIFI_PROPERTIES, "-n", TlsConfig.DEFAULT_HOSTNAME);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(FAKE_VALUE, nifiProperties.get(NIFI_FAKE_PROPERTY));
}
@Test
public void testHostnamesArgumentOverwrite() throws Exception {
String nifi1 = "nifi1";
String nifi2 = "nifi2";
String nifi3 = "nifi3";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nifi1 + "," + nifi2);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nifi3);
checkHostDirAndReturnNifiProperties(nifi1, x509Certificate);
checkHostDirAndReturnNifiProperties(nifi2, x509Certificate);
checkHostDirAndReturnNifiProperties(nifi3, x509Certificate);
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-O", "-n", nifi3);
checkHostDirAndReturnNifiProperties(nifi3, x509Certificate);
}
@Test
public void testHostnamesArgumentNoOverwrite() throws Exception {
String nifi = "nifi";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nifi);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
checkHostDirAndReturnNifiProperties(nifi, x509Certificate);
runAndAssertExitCode(ExitCode.ERROR_GENERATING_CONFIG, "-o", tempDir.getAbsolutePath(), "-n", nifi);
}
@Test
public void testKeyPasswordArg() throws Exception {
String testKey = "testKey";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-K", testKey, "-n", TlsConfig.DEFAULT_HOSTNAME);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(testKey, nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
}
@Test
public void testKeyStorePasswordArg() throws Exception {
String testKeyStore = "testKeyStore";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-S", testKeyStore, "-n", TlsConfig.DEFAULT_HOSTNAME);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(testKeyStore, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD));
}
@Test
public void testTrustStorePasswordArg() throws Exception {
String testTrustStore = "testTrustStore";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-P", testTrustStore, "-n", TlsConfig.DEFAULT_HOSTNAME);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(testTrustStore, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
}
@Test
public void testDnArgs() throws Exception {
String nifiDnPrefix = "O=apache, CN=";
String nifiDnSuffix = ", OU=nifi";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", TlsConfig.DEFAULT_HOSTNAME,
"--" + TlsToolkitStandaloneCommandLine.NIFI_DN_PREFIX_ARG, nifiDnPrefix, "--" + TlsToolkitStandaloneCommandLine.NIFI_DN_SUFFIX_ARG, nifiDnSuffix);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, nifiDnPrefix, nifiDnSuffix, x509Certificate);
}
@Test
public void testKeyStoreTypeArg() throws Exception {
final String certificateAuthorityHostname = "certificate-authority";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", TlsConfig.DEFAULT_HOSTNAME, "-T", KeystoreType.PKCS12.toString().toLowerCase(),
"-K", "change", "-S", "change", "-P", "change", "-c", certificateAuthorityHostname);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
}
@Test
public void testClientDnsArg() throws Exception {
String clientDn = "OU=NIFI,CN=testuser";
String clientDn2 = "OU=NIFI,CN=testuser2";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-C", clientDn, "-C", clientDn2, "-B", "pass1", "-P", "pass2");
X509Certificate x509Certificate = checkLoadCertPrivateKey();
checkClientCert(clientDn, x509Certificate);
checkClientCert(clientDn2, x509Certificate);
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-O", "-C", clientDn2, "-B", "pass3");
checkClientCert(clientDn2, x509Certificate);
}
@Test
public void testClientDnsArgNoOverwrite() throws Exception {
String clientDn = "OU=NIFI,CN=testuser";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-C", clientDn, "-B", "passwor");
X509Certificate x509Certificate = checkLoadCertPrivateKey();
checkClientCert(clientDn, x509Certificate);
runAndAssertExitCode(ExitCode.ERROR_GENERATING_CONFIG, "-o", tempDir.getAbsolutePath(), "-C", clientDn);
}
@Test
public void testStaticHostnameNoSan() throws Exception {
String hostname = "static.nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", hostname);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Certificate[] certificateChain = loadCertificateChain(hostname, x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have one san that matches
assertEquals(1, clientSaNames.size());
List<?> firstSan = clientSaNames.toArray(new List<?>[0])[0];
assertEquals(GeneralName.dNSName, firstSan.get(0));
assertEquals(hostname, firstSan.get(1));
}
@Test
public void testStaticHostnameStaticSan() throws Exception {
String hostname = "static.nifi.apache.org";
String san = "alternative.nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", hostname, "--subjectAlternativeName", san);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Certificate[] certificateChain = loadCertificateChain(hostname, x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have two sans, and one san that matches
assertEquals(2, clientSaNames.size());
List<?> explicitSan = clientSaNames.toArray(new List<?>[0])[1];
assertEquals(GeneralName.dNSName, explicitSan.get(0));
assertEquals(san, explicitSan.get(1));
}
@Test
public void testDynamicHostnameStaticSan() throws Exception {
String nodeNames = "node[1-2].nifi.apache.org";
String san = "alternative.nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", san);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Stream<InstanceIdentifier> hostIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
for (InstanceIdentifier hostInstance : (Iterable<InstanceIdentifier>) hostIds::iterator) {
Certificate[] certificateChain = loadCertificateChain(hostInstance.getHostname(), x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have two sans, and one san that matches
assertEquals(2, clientSaNames.size());
List<?> explicitSan = clientSaNames.toArray(new List<?>[0])[1];
assertEquals(GeneralName.dNSName, explicitSan.get(0));
assertEquals(san, explicitSan.get(1));
}
}
@Test
public void testDynamicHostnameDynamicSansSameRange() throws Exception {
String nodeNames = "node[1-2].nifi.apache.org";
String saNames = "alternative[1-2].nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", saNames);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Stream<InstanceIdentifier> hostIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
Stream<InstanceIdentifier> sansIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
String[] nodeHosts = hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
String[] sanHosts = sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
assertEquals(nodeHosts.length, sanHosts.length);
for (int i = 0; i< nodeHosts.length; i++) {
String host = nodeHosts[i];
String san = sanHosts[i];
Certificate[] certificateChain = loadCertificateChain(host, x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have two sans, and both must match
assertEquals(2, clientSaNames.size());
List<?> hostSan = clientSaNames.toArray(new List<?>[0])[0];
assertEquals(GeneralName.dNSName, hostSan.get(0));
assertEquals(host, hostSan.get(1));
List<?> altSan = clientSaNames.toArray(new List<?>[0])[1];
assertEquals(GeneralName.dNSName, altSan.get(0));
assertEquals(san, altSan.get(1));
}
}
@Test
public void testDynamicHostnameDynamicSansSameRangeDiffValues() throws Exception {
String nodeNames = "node[1-2].nifi.apache.org";
String saNames = "alternative[3-4].nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", saNames);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Stream<InstanceIdentifier> hostIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
Stream<InstanceIdentifier> sansIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
String[] nodeHosts = hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
String[] sanHosts = sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
assertEquals(nodeHosts.length, sanHosts.length);
for (int i = 0; i< nodeHosts.length; i++) {
String host = nodeHosts[i];
String san = sanHosts[i];
Certificate[] certificateChain = loadCertificateChain(host, x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have two sans, and both must match
assertEquals(2, clientSaNames.size());
List<?> hostSan = clientSaNames.toArray(new List<?>[0])[0];
assertEquals(GeneralName.dNSName, hostSan.get(0));
assertEquals(host, hostSan.get(1));
List<?> altSan = clientSaNames.toArray(new List<?>[0])[1];
assertEquals(GeneralName.dNSName, altSan.get(0));
assertEquals(san, altSan.get(1));
}
}
@Test
public void testDynamicHostnameDynamicSansDiffRange() throws Exception {
String nodeNames = "node[1-2].nifi.apache.org";
String saNames = "alternative[5-7].nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", saNames);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Stream<InstanceIdentifier> hostIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
Stream<InstanceIdentifier> sansIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
String[] nodeHosts = hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
String[] sanHosts = sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
assertEquals(2, nodeHosts.length);
assertEquals(3, sanHosts.length);
for (int i = 0; i< nodeHosts.length; i++) {
String host = nodeHosts[i];
Certificate[] certificateChain = loadCertificateChain(host, x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have sans + cn
assertEquals(1 + sanHosts.length, clientSaNames.size());
for (int j = 0; j < sanHosts.length; j++) {
String sanHost = clientSaNames.stream().collect(Collectors.toList()).get(j+1).get(1).toString();
assertEquals(sanHosts[j], sanHost);
}
}
}
@Test
public void testDynamicHostnameDynamicSansSameRangeReverseOrder() throws Exception {
String nodeNames = "node[1-2].nifi.apache.org";
String saNames = "alternative[2-1].nifi.apache.org";
runAndAssertExitCode(ExitCode.SUCCESS, "-o", tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", saNames);
X509Certificate x509Certificate = checkLoadCertPrivateKey();
Stream<InstanceIdentifier> hostIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{nodeNames}));
Stream<InstanceIdentifier> sansIds = InstanceIdentifier.createIdentifiers(Arrays.stream(new String[]{saNames}));
String[] nodeHosts = hostIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
String[] sanHosts = sansIds.map(InstanceIdentifier::getHostname).toArray(String[]::new);
assertTrue(nodeHosts.length > 0);
assertEquals(nodeHosts.length, sanHosts.length);
for (int i = 0; i< nodeHosts.length; i++) {
String host = nodeHosts[i];
String san = sanHosts[i];
Certificate[] certificateChain = loadCertificateChain(host, x509Certificate);
X509Certificate clientCert = (X509Certificate) certificateChain[0];
Collection<List<?>> clientSaNames = clientCert.getSubjectAlternativeNames();
// Must have sans + cn
assertEquals(2, clientSaNames.size());
List<?> hostSan = clientSaNames.toArray(new List<?>[0])[0];
assertEquals(GeneralName.dNSName, hostSan.get(0));
assertEquals(host, hostSan.get(1));
List<?> altSan = clientSaNames.toArray(new List<?>[0])[1];
assertEquals(GeneralName.dNSName, altSan.get(0));
assertEquals(san, altSan.get(1));
}
}
@Test
public void testDynamicHostnameDynamicSansNonNumeric() {
String nodeNames = "node[1-2].nifi.apache.org";
String saNames = "alternative[A-B].nifi.apache.org";
runAndAssertExitCode(ExitCode.ERROR_PARSING_INT_ARG, "-o", tempDir.getAbsolutePath(), "-n", nodeNames, "--subjectAlternativeName", saNames);
}
@Test
void testShouldVerifyCertificateSignatureWhenSelfSigned() throws Exception {
// Create a temp directory for this test and populate it with the nifi-cert.pem and nifi-key.key files
populateTempDirWithCAFiles();
// Make a standalone config which doesn't trigger any keystore generation and just has a self-signed cert and key
StandaloneConfig standaloneConfig = new StandaloneConfig();
standaloneConfig.setBaseDir(tempDir);
standaloneConfig.setInstanceDefinitions(new ArrayList<>());
standaloneConfig.setClientDns(new ArrayList<>());
standaloneConfig.initDefaults();
TlsToolkitStandalone standalone = new TlsToolkitStandalone();
// The test will fail with an exception if the certificate is not signed by a known certificate
assertDoesNotThrow(() -> standalone.createNifiKeystoresAndTrustStores(standaloneConfig));
}
/**
* The certificate under examination is self-signed, but there is another signing cert which will be iterated over first, fail, and then the self-signed signature will be validated.
*/
@Test
void testShouldVerifyCertificateSignatureWithMultipleSigningCerts() throws Exception {
// Populate temp directory for this test with the nifi-cert.pem and nifi-key.key files
populateTempDirWithCAFiles();
// Create a different cert and persist it to the base dir
X509Certificate otherCert = generateX509Certificate();
File otherCertFile = writeCertificateToPEMFile(otherCert, tempDir.getPath() + "/other.pem");
// Make a standalone config which doesn't trigger any keystore generation and just has a self-signed cert and key
StandaloneConfig standaloneConfig = new StandaloneConfig();
standaloneConfig.setBaseDir(tempDir);
standaloneConfig.setInstanceDefinitions(new ArrayList<>());
standaloneConfig.setClientDns(new ArrayList<>());
standaloneConfig.initDefaults();
// Inject the additional CA cert path
standaloneConfig.setAdditionalCACertificate(otherCertFile.getPath());
TlsToolkitStandalone standalone = new TlsToolkitStandalone();
assertDoesNotThrow(() -> standalone.createNifiKeystoresAndTrustStores(standaloneConfig));
}
/**
* The certificate under examination is signed with the external signing cert.
*/
@Test
void testShouldVerifyCertificateSignatureWithAdditionalSigningCert() throws Exception {
// Create a root CA, create an intermediate CA, use the root to sign the intermediate and then provide the root
KeyPair rootKeyPair = generateKeyPair();
X509Certificate rootCert = generateX509Certificate("CN=Root CA", rootKeyPair);
File rootCertFile = writeCertificateToPEMFile(rootCert, tempDir.getPath() + "/root.pem");
KeyPair intermediateKeyPair = generateKeyPair();
X509Certificate intermediateCert = new StandardCertificateBuilder(rootKeyPair, rootCert.getIssuerX500Principal(), Duration.ofDays(1))
.setSubject(new X500Principal("CN=Intermediate CA"))
.setSubjectPublicKey(intermediateKeyPair.getPublic())
.build();
//intermediateCertFile
writeCertificateToPEMFile(intermediateCert, tempDir.getPath() + "/nifi-cert.pem");
// Write the private key of the intermediate cert to nifi-key.key
//intermediateKeyFile
writePrivateKeyToFile(intermediateKeyPair, tempDir.getPath() + "/nifi-key.key");
// Make a standalone config which doesn't trigger any keystore generation and just has a signed cert and key
StandaloneConfig standaloneConfig = new StandaloneConfig();
standaloneConfig.setBaseDir(tempDir);
standaloneConfig.setInstanceDefinitions(new ArrayList<>());
standaloneConfig.setClientDns(new ArrayList<>());
standaloneConfig.initDefaults();
// Inject the additional CA cert path
standaloneConfig.setAdditionalCACertificate(rootCertFile.getPath());
TlsToolkitStandalone standalone = new TlsToolkitStandalone();
assertDoesNotThrow(() -> standalone.createNifiKeystoresAndTrustStores(standaloneConfig));
}
@Test
void testShouldNotVerifyCertificateSignatureWithWrongSigningCert() throws Exception {
// Create a root CA, create an intermediate CA, use the root to sign the intermediate and then do not provide the root
KeyPair rootKeyPair = generateKeyPair();
X509Certificate rootCert = generateX509Certificate("CN=Root CA", rootKeyPair);
KeyPair intermediateKeyPair = generateKeyPair();
X509Certificate intermediateCert = new StandardCertificateBuilder(rootKeyPair, rootCert.getIssuerX500Principal(), Duration.ofDays(1))
.setSubject(new X500Principal("CN=Intermediate CA"))
.setSubjectPublicKey(intermediateKeyPair.getPublic())
.build();
//intermediateCertFile
writeCertificateToPEMFile(intermediateCert, tempDir.getPath() + "/nifi-cert.pem");
// Write the private key of the intermediate cert to nifi-key.key
//intermediateKeyFile
writePrivateKeyToFile(intermediateKeyPair, tempDir.getPath() + "/nifi-key.key");
// Make a standalone config which doesn't trigger any keystore generation and just has a signed cert and key
StandaloneConfig standaloneConfig = new StandaloneConfig();
standaloneConfig.setBaseDir(tempDir);
standaloneConfig.setInstanceDefinitions(new ArrayList<>());
standaloneConfig.setClientDns(new ArrayList<>());
standaloneConfig.initDefaults();
TlsToolkitStandalone standalone = new TlsToolkitStandalone();
assertThrows(SignatureException.class, () -> standalone.createNifiKeystoresAndTrustStores(standaloneConfig));
}
private X509Certificate checkLoadCertPrivateKey() throws IOException, CertificateException {
KeyPair keyPair = TlsHelperTest.loadKeyPair(new File(tempDir, TlsToolkitStandalone.NIFI_KEY + ".key"));
assertEquals(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, keyPair.getPrivate().getAlgorithm());
assertEquals(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, keyPair.getPublic().getAlgorithm());
X509Certificate x509Certificate = TlsHelperTest.loadCertificate(new File(tempDir, TlsToolkitStandalone.NIFI_CERT + ".pem"));
assertEquals(keyPair.getPublic(), x509Certificate.getPublicKey());
return x509Certificate;
}
private Properties checkHostDirAndReturnNifiProperties(String hostname, X509Certificate rootCert) throws Exception {
return checkHostDirAndReturnNifiProperties(hostname, TlsConfig.DEFAULT_DN_PREFIX, TlsConfig.DEFAULT_DN_SUFFIX, rootCert);
}
private Properties checkHostDirAndReturnNifiProperties(String hostname, String dnPrefix, String dnSuffix, X509Certificate rootCert) throws Exception {
File hostDir = new File(tempDir, hostname);
Properties nifiProperties = new Properties();
try (InputStream inputStream = new FileInputStream(new File(hostDir, TlsToolkitStandalone.NIFI_PROPERTIES))) {
nifiProperties.load(inputStream);
}
String trustStoreType = nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE);
assertEquals(KeystoreType.JKS.toString().toLowerCase(), trustStoreType.toLowerCase());
KeyStore trustStore = KeyStoreUtils.getKeyStore(trustStoreType);
try (InputStream inputStream = new FileInputStream(new File(hostDir, "truststore." + trustStoreType))) {
trustStore.load(inputStream, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray());
}
String trustStoreFilename = BaseTlsToolkitCommandLine.TRUSTSTORE + trustStoreType;
assertEquals("./conf/" + trustStoreFilename, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE));
Certificate certificate = trustStore.getCertificate(TlsToolkitStandalone.NIFI_CERT);
assertEquals(rootCert, certificate);
String keyStoreType = nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
String keyStoreFilename = BaseTlsToolkitCommandLine.KEYSTORE + keyStoreType;
File keyStoreFile = new File(hostDir, keyStoreFilename);
assertEquals("./conf/" + keyStoreFilename, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE));
KeyStore keyStore = KeyStoreUtils.getKeyStore(keyStoreType);
char[] keyStorePassword = nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray();
try (InputStream inputStream = new FileInputStream(keyStoreFile)) {
keyStore.load(inputStream, keyStorePassword);
}
char[] keyPassword = nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD).toCharArray();
if(ArrayUtils.isEmpty(keyPassword)) {
keyPassword = keyStorePassword;
}
KeyStore.Entry entry = keyStore.getEntry(TlsToolkitStandalone.NIFI_KEY, new KeyStore.PasswordProtection(keyPassword));
assertEquals(KeyStore.PrivateKeyEntry.class, entry.getClass());
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
Certificate[] certificateChain = privateKeyEntry.getCertificateChain();
assertEquals(2, certificateChain.length);
assertEquals(rootCert, certificateChain[1]);
certificateChain[1].verify(rootCert.getPublicKey());
certificateChain[0].verify(rootCert.getPublicKey());
TlsConfig tlsConfig = new TlsConfig();
tlsConfig.setDnPrefix(dnPrefix);
tlsConfig.setDnSuffix(dnSuffix);
TlsCertificateAuthorityTest.assertPrivateAndPublicKeyMatch(privateKeyEntry.getPrivateKey(), certificateChain[0].getPublicKey());
return nifiProperties;
}
private void checkClientCert(String clientDn, X509Certificate rootCert) throws Exception {
String clientDnFile = TlsHelper.escapeFilename(clientDn);
String password;
try (FileReader fileReader = new FileReader(new File(tempDir, clientDnFile + ".password"))) {
List<String> lines = IOUtils.readLines(fileReader);
assertEquals(1, lines.size());
password = lines.get(0);
}
KeyStore keyStore = KeyStoreUtils.getKeyStore(KeystoreType.PKCS12.toString());
try (FileInputStream fileInputStream = new FileInputStream(new File(tempDir, clientDnFile + ".p12"))) {
keyStore.load(fileInputStream, password.toCharArray());
}
final char[] keyPassword = password.toCharArray();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(TlsToolkitStandalone.NIFI_KEY, keyPassword);
Certificate[] certificateChain = keyStore.getCertificateChain(TlsToolkitStandalone.NIFI_KEY);
assertEquals(2, certificateChain.length);
assertEquals(rootCert, certificateChain[1]);
certificateChain[1].verify(rootCert.getPublicKey());
certificateChain[0].verify(rootCert.getPublicKey());
PublicKey publicKey = certificateChain[0].getPublicKey();
TlsCertificateAuthorityTest.assertPrivateAndPublicKeyMatch(privateKey, publicKey);
}
private Certificate[] loadCertificateChain(String hostname, X509Certificate rootCert) throws Exception {
File hostDir = new File(tempDir, hostname);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(hostname, rootCert);
String keyStoreType = nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
String keyStoreFilename = BaseTlsToolkitCommandLine.KEYSTORE + keyStoreType;
File keyStoreFile = new File(hostDir, keyStoreFilename);
KeyStore keyStore = KeyStoreUtils.getKeyStore(keyStoreType);
try (FileInputStream fileInputStream = new FileInputStream(keyStoreFile)) {
keyStore.load(fileInputStream, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray());
}
return keyStore.getCertificateChain(TlsToolkitStandalone.NIFI_KEY);
}
private void runAndAssertExitCode(ExitCode exitCode, String... args) {
systemExitCapturer.runAndAssertExitCode(() -> TlsToolkitStandaloneCommandLine.main(args), exitCode);
}
private void populateTempDirWithCAFiles() throws Exception {
final String testSrcDir = "src/test/resources/";
File certificateFile = new File(testSrcDir, "rootCert.crt");
File keyFile = new File(testSrcDir, "rootCert.key");
File destinationCertFile = new File(tempDir, "nifi-cert.pem");
Files.copy(certificateFile.toPath(), destinationCertFile.toPath());
File destinationKeyFile = new File(tempDir, "nifi-key.key");
Files.copy(keyFile.toPath(), destinationKeyFile.toPath());
}
/**
* Returns an {@link X509Certificate} with the provided DN and default algorithms. The validity period is only 1 day.
* DN (defaults to {@code CN=Test Certificate})
* @return the X509Certificate
*/
private static X509Certificate generateX509Certificate() throws Exception {
return generateX509Certificate("CN=Test Certificate", generateKeyPair());
}
/**
* Returns an {@link X509Certificate} with the provided DN and default algorithms. The validity period is only 1 day.
*
* @param dn the DN (defaults to {@code CN=Test Certificate})
* @return the X509Certificate
*/
private static X509Certificate generateX509Certificate(String dn, KeyPair keyPair) {
return new StandardCertificateBuilder(keyPair, new X500Principal(dn), Duration.ofDays(1)).build();
}
private static KeyPair generateKeyPair() throws Exception {
final String defaultKeyPairAlgorithm = "RSA";
KeyPairGenerator instance = KeyPairGenerator.getInstance(defaultKeyPairAlgorithm);
instance.initialize(2048);
return instance.generateKeyPair();
}
/**
* Writes the provided {@link X509Certificate} to the specified file in PEM format.
*
* @param certificate the certificate
* @param destination the path to write the certificate in PEM format
* @return the file
*/
private static File writeCertificateToPEMFile(X509Certificate certificate, String destination) throws Exception {
return writePem(certificate, destination);
}
private static File writePem(Object object, String destination) throws Exception{
File destinationFile = new File(destination);
try(PemWriter pemWriter = new PemWriter(new FileWriter(destinationFile))) {
pemWriter.writeObject(new JcaMiscPEMGenerator(object));
}
return destinationFile;
}
private static void writePrivateKeyToFile(KeyPair intermediateKeyPair, String destination) throws Exception {
writePem(intermediateKeyPair, destination);
}
}

View File

@ -1,108 +0,0 @@
/*
* 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.status;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.GetStatusConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import javax.net.ssl.SSLContext;
import java.net.URI;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TlsToolkitGetStatusCommandLineTest {
private final String TRUSTSTORE_PATH = "src/test/resources/localhost/truststore.jks";
private final String TRUSTSTORE_PASSWORD = "passwordpassword";
private final String JKS_TYPE = "JKS";
private TlsToolkitGetStatusCommandLine commandLine;
@BeforeEach
public void setup() {
commandLine = new TlsToolkitGetStatusCommandLine();
}
@Test
public void testHelp() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> commandLine.parse("-h"));
assertEquals(ExitCode.HELP, e.getExitCode());
}
@Test
public void testSuccess() throws CommandLineParseException {
final String urlStr = "https://localhost:8443/test";
commandLine.parse(
"-u", urlStr,
"-ts", TRUSTSTORE_PATH,
"-tst", JKS_TYPE,
"-tsp", TRUSTSTORE_PASSWORD);
final GetStatusConfig config = commandLine.createConfig();
assertNotNull(config);
final URI url = config.getUrl();
assertNotNull(url);
assertEquals(urlStr, url.toString());
final SSLContext sslContext = config.getSslContext();
assertNotNull(sslContext);
}
@Test
public void testMissingUrl() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> commandLine.parse(
"-ts", TRUSTSTORE_PATH,
"-tst", JKS_TYPE,
"-tsp", TRUSTSTORE_PASSWORD)
);
assertEquals(ExitCode.INVALID_ARGS, e.getExitCode());
}
@Test
public void testTruststoreDoesNotExist() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> commandLine.parse(
"-u", "https://localhost:8443/test",
"-ts", "does/not/exist/truststore.jks",
"-tst", JKS_TYPE,
"-tsp", TRUSTSTORE_PASSWORD)
);
assertEquals(ExitCode.INVALID_ARGS, e.getExitCode());
}
@Test
public void testInvalidTruststoreType() {
final CommandLineParseException e = assertThrows(CommandLineParseException.class, () -> commandLine.parse(
"-u", "https://localhost:8443/test",
"-ts", TRUSTSTORE_PATH,
"-tst", "INVALID",
"-tsp", TRUSTSTORE_PASSWORD)
);
assertEquals(ExitCode.INVALID_ARGS, e.getExitCode());
}
}