You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2021/06/11 20:48:39 UTC
[nifi] branch main updated: NIFI-8651: Refactor Sensitive
Properties Providers for extension
This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 1ccc4fb NIFI-8651: Refactor Sensitive Properties Providers for extension
1ccc4fb is described below
commit 1ccc4fbb0ff6a4041ddff180ab07a1675308e4a3
Author: Joe Gresock <jg...@gmail.com>
AuthorDate: Sun Jun 6 21:53:39 2021 -0400
NIFI-8651: Refactor Sensitive Properties Providers for extension
This closes #5131
Signed-off-by: David Handermann <ex...@apache.org>
---
.../nifi/bootstrap/util/SecureNiFiConfigUtil.java | 4 +-
nifi-commons/nifi-properties/pom.xml | 8 +
.../nifi/util/NiFiBootstrapPropertiesLoader.java | 19 +-
.../org/apache/nifi/util/NiFiBootstrapUtils.java | 82 ++++
.../java/org/apache/nifi/util/NiFiProperties.java | 48 +-
.../pom.xml | 2 +-
.../AbstractBootstrapPropertiesLoader.java | 162 +++++++
.../nifi/properties/ApplicationProperties.java | 14 +-
.../nifi/properties/BootstrapProperties.java | 86 ++++
.../nifi/properties/ProtectedProperties.java | 59 +++
.../apache/nifi/properties/ReadableProperties.java | 50 ++
.../properties/StandardReadableProperties.java | 57 +--
.../org/apache/nifi/security/kms/CryptoUtils.java | 112 +----
.../security/util/StandardTlsConfiguration.java | 32 +-
.../nifi-sensitive-property-provider}/pom.xml | 78 ++--
.../properties/AESSensitivePropertyProvider.java | 117 +++--
.../AbstractSensitivePropertyProvider.java | 63 +++
.../properties/ApplicationPropertiesProtector.java | 340 ++++++++++++++
...ltipleSensitivePropertyProtectionException.java | 0
.../nifi/properties/PropertyProtectionScheme.java | 79 ++++
.../SensitivePropertyProtectionException.java | 0
.../properties/SensitivePropertyProtector.java | 146 ++++++
.../nifi/properties/SensitivePropertyProvider.java | 7 +
.../SensitivePropertyProviderFactory.java | 15 +-
.../SensitivePropertyProviderFactoryAware.java | 46 ++
.../StandardSensitivePropertyProviderFactory.java | 121 +++++
.../AESSensitivePropertyProviderTest.groovy | 0
...andardSensitivePropertyProviderFactoryTest.java | 140 ++++++
nifi-commons/pom.xml | 2 +
.../nifi/authorization/AuthorizerFactoryBean.java | 89 ++--
.../authorization/AuthorizerFactoryBeanTest.groovy | 29 +-
...ncryptedRepositoryRecordSerdeFactoryTest.groovy | 7 +-
.../PopularVoteFlowElectionFactoryBeanTest.groovy | 4 +-
.../okhttp/OkHttpReplicationClientTest.groovy | 20 +-
.../nifi/cluster/ZooKeeperClientConfigTest.java | 38 +-
.../state/server/TestZooKeeperStateServer.java | 10 +-
.../election/ITSecureClientZooKeeperFactory.java | 16 +-
.../nifi-framework/nifi-properties-loader/pom.xml | 4 +
.../AESSensitivePropertyProviderFactory.java | 53 ---
.../nifi/properties/NiFiPropertiesLoader.java | 92 +---
.../nifi/properties/ProtectedNiFiProperties.java | 466 ++++---------------
.../AESSensitivePropertyProviderFactoryTest.groovy | 105 -----
...Test.groovy => NiFiPropertiesGroovyTest.groovy} | 88 ++--
.../NiFiPropertiesLoaderGroovyTest.groovy | 51 +--
.../ProtectedNiFiPropertiesGroovyTest.groovy | 80 ++--
.../groovy/org/apache/nifi/NiFiGroovyTest.groovy | 25 +-
.../remote/StandardPublicPortGroovyTest.groovy | 4 +-
.../nifi/remote/TestHttpRemoteSiteListener.java | 7 +-
.../nifi/remote/TestPeerDescriptionModifier.java | 23 +-
.../http/TestHttpFlowFileServerProtocol.java | 7 +-
.../nifi/web/server/JettyServerGroovyTest.groovy | 45 +-
.../nifi/web/server/HostHeaderHandlerTest.groovy | 10 +-
.../nifi/web/api/ApplicationResourceTest.groovy | 16 +-
.../nifi/integration/util/NiFiTestServer.java | 3 +-
.../spring/LoginIdentityProviderFactoryBean.java | 80 ++--
.../web/security/oidc/OidcServiceGroovyTest.groovy | 24 +-
.../StandardOidcIdentityProviderGroovyTest.groovy | 29 +-
.../LoginIdentityProviderFactoryBeanTest.groovy | 48 +-
.../jwt/JwtAuthenticationProviderTest.java | 3 +-
.../nifi/web/security/jwt/JwtServiceTest.java | 4 +-
.../saml/impl/TestStandardSAMLService.java | 11 +
nifi-nar-bundles/nifi-framework-bundle/pom.xml | 5 +
.../EncryptedWriteAheadProvenanceRepository.java | 16 +-
.../authentication/IdentityProviderFactory.java | 4 +-
.../security/authorization/AuthorizerFactory.java | 4 +-
.../SensitivePropertyProviderConfiguration.java | 36 +-
.../provider/TestStandardProviderFactory.java | 21 +-
.../provider/hook/TestScriptEventHookProvider.java | 6 +-
.../database/TestDatabaseAccessPolicyProvider.java | 15 +-
.../database/TestDatabaseUserGroupProvider.java | 7 +-
.../nifi-registry-properties/pom.xml | 6 +
.../properties/AESSensitivePropertyProvider.java | 265 -----------
.../AESSensitivePropertyProviderFactory.java | 54 ---
...ltipleSensitivePropertyProtectionException.java | 129 ------
.../properties/NiFiRegistryProperties.java | 41 +-
.../properties/NiFiRegistryPropertiesLoader.java | 63 +--
.../ProtectedNiFiRegistryProperties.java | 505 ++++-----------------
.../SensitivePropertyProtectionException.java | 89 ----
.../properties/SensitivePropertyProvider.java | 52 ---
.../NiFiRegistryBootstrapPropertiesLoader.java | 20 +-
.../util/NiFiRegistryBootstrapUtils.java | 82 ++++
.../crypto/BootstrapFileCryptoKeyProvider.java | 3 +-
.../registry/security/crypto/CryptoKeyLoader.java | 87 ----
.../AESSensitivePropertyProviderFactoryTest.groovy | 81 ----
.../AESSensitivePropertyProviderTest.groovy | 471 -------------------
.../NiFiRegistryPropertiesGroovyTest.groovy | 13 +-
.../NiFiRegistryPropertiesLoaderGroovyTest.groovy | 40 +-
...otectedNiFiRegistryPropertiesGroovyTest.groovy} | 111 +++--
.../NiFiRegistryBootstrapUtilsGroovyTest.groovy} | 17 +-
.../org/apache/nifi/registry/NiFiRegistry.java | 18 +-
.../nifi/registry/web/api/IntegrationTestBase.java | 11 +-
.../apache/nifi/registry/web/api/SecureLdapIT.java | 8 +-
.../FlowPersistenceProviderMigrator.java | 12 +-
.../admin/nodemanager/NodeManagerTool.groovy | 3 +-
.../toolkit/admin/notify/NotificationTool.groovy | 3 +-
.../admin/client/NiFiClientFactorySpec.groovy | 3 +-
nifi-toolkit/nifi-toolkit-encrypt-config/pom.xml | 7 +-
.../nifi/properties/ConfigEncryptionTool.groovy | 177 +++++---
.../nifi/toolkit/encryptconfig/DecryptMode.groovy | 33 +-
.../encryptconfig/NiFiRegistryDecryptMode.groovy | 12 +-
.../toolkit/encryptconfig/NiFiRegistryMode.groovy | 70 ++-
.../NiFiRegistryAuthorizersXmlEncryptor.groovy | 2 +-
.../properties/ConfigEncryptionToolTest.groovy | 176 ++-----
.../encryptconfig/EncryptConfigMainTest.groovy | 7 +-
.../nifi/toolkit/encryptconfig/TestUtil.groovy | 7 +-
.../src/test/resources/bootstrap.conf | 6 +-
.../test/resources/login-identity-providers.xml | 2 +-
pom.xml | 2 +-
108 files changed, 2766 insertions(+), 3416 deletions(-)
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/SecureNiFiConfigUtil.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/SecureNiFiConfigUtil.java
index 10d825f..bb1abee 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/SecureNiFiConfigUtil.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/SecureNiFiConfigUtil.java
@@ -96,7 +96,7 @@ public class SecureNiFiConfigUtil {
* @throws IOException can be thrown when writing keystores to disk
* @throws RuntimeException indicates a security exception while generating keystores
*/
- public static void configureSecureNiFiProperties(String nifiPropertiesFilename, Logger cmdLogger) throws IOException, RuntimeException {
+ public static void configureSecureNiFiProperties(final String nifiPropertiesFilename, final Logger cmdLogger) throws IOException, RuntimeException {
final File propertiesFile = new File(nifiPropertiesFilename);
final Properties nifiProperties = loadProperties(propertiesFile);
@@ -111,7 +111,7 @@ public class SecureNiFiConfigUtil {
boolean truststoreExists = fileExists(truststorePath);
if (!keystoreExists && !truststoreExists) {
- TlsConfiguration tlsConfiguration = null;
+ TlsConfiguration tlsConfiguration;
cmdLogger.info("Generating Self-Signed Certificate: Expires on {}", LocalDate.now().plus(CERT_DURATION_DAYS, ChronoUnit.DAYS));
try {
String[] subjectAlternativeNames = getSubjectAlternativeNames(nifiProperties, cmdLogger);
diff --git a/nifi-commons/nifi-properties/pom.xml b/nifi-commons/nifi-properties/pom.xml
index fbcfd9d..5a81dad 100644
--- a/nifi-commons/nifi-properties/pom.xml
+++ b/nifi-commons/nifi-properties/pom.xml
@@ -15,6 +15,14 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-property-utils</artifactId>
+ <version>1.14.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons</artifactId>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProviderFactory.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiBootstrapPropertiesLoader.java
similarity index 60%
rename from nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProviderFactory.java
rename to nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiBootstrapPropertiesLoader.java
index c9d4313..da0e889 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProviderFactory.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiBootstrapPropertiesLoader.java
@@ -14,10 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.nifi.registry.properties;
+package org.apache.nifi.util;
-public interface SensitivePropertyProviderFactory {
+import org.apache.nifi.properties.AbstractBootstrapPropertiesLoader;
- SensitivePropertyProvider getProvider();
+public class NiFiBootstrapPropertiesLoader extends AbstractBootstrapPropertiesLoader {
+ @Override
+ protected String getApplicationPrefix() {
+ return "nifi";
+ }
+ @Override
+ protected String getApplicationPropertiesFilename() {
+ return "nifi.properties";
+ }
+
+ @Override
+ protected String getApplicationPropertiesFilePathSystemProperty() {
+ return NiFiProperties.PROPERTIES_FILE_PATH;
+ }
}
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiBootstrapUtils.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiBootstrapUtils.java
new file mode 100644
index 0000000..1360578
--- /dev/null
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiBootstrapUtils.java
@@ -0,0 +1,82 @@
+/*
+ * 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.util;
+
+import org.apache.nifi.properties.AbstractBootstrapPropertiesLoader;
+import org.apache.nifi.properties.BootstrapProperties;
+
+import java.io.IOException;
+
+/**
+ * Encapsulates utility methods for dealing with bootstrap.conf or nifi.properties.
+ */
+public class NiFiBootstrapUtils {
+ private static final AbstractBootstrapPropertiesLoader BOOTSTRAP_PROPERTIES_LOADER = new NiFiBootstrapPropertiesLoader();
+
+ /**
+ * Returns the key (if any) used to encrypt sensitive properties, extracted from
+ * {@code $NIFI_HOME/conf/bootstrap.conf}.
+ *
+ * @return the key in hexadecimal format
+ * @throws IOException if the file is not readable
+ */
+ public static String extractKeyFromBootstrapFile() throws IOException {
+ return BOOTSTRAP_PROPERTIES_LOADER.extractKeyFromBootstrapFile();
+ }
+
+ /**
+ * Loads the default bootstrap.conf file into a BootstrapProperties object.
+ * @return The default bootstrap.conf as a BootstrapProperties object
+ * @throws IOException If the file is not readable
+ */
+ public static BootstrapProperties loadBootstrapProperties() throws IOException {
+ return loadBootstrapProperties(null);
+ }
+
+ /**
+ * Loads the bootstrap.conf file into a BootstrapProperties object.
+ * @param bootstrapPath the path to the bootstrap file
+ * @return The bootstrap.conf as a BootstrapProperties object
+ * @throws IOException If the file is not readable
+ */
+ public static BootstrapProperties loadBootstrapProperties(final String bootstrapPath) throws IOException {
+ return BOOTSTRAP_PROPERTIES_LOADER.loadBootstrapProperties(bootstrapPath);
+ }
+
+ /**
+ * Returns the key (if any) used to encrypt sensitive properties, extracted from
+ * {@code $NIFI_HOME/conf/bootstrap.conf}.
+ *
+ * @param bootstrapPath the path to the bootstrap file (if null, returns the sensitive key
+ * found in $NIFI_HOME/conf/bootstrap.conf)
+ * @return the key in hexadecimal format
+ * @throws IOException if the file is not readable
+ */
+ public static String extractKeyFromBootstrapFile(final String bootstrapPath) throws IOException {
+ return BOOTSTRAP_PROPERTIES_LOADER.extractKeyFromBootstrapFile(bootstrapPath);
+ }
+
+ /**
+ * Returns the default file path to {@code $NIFI_HOME/conf/nifi.properties}. If the system
+ * property nifi.properties.file.path is not set, it will be set to the relative conf/nifi.properties
+ *
+ * @return the path to the nifi.properties file
+ */
+ public static String getDefaultApplicationPropertiesFilePath() {
+ return BOOTSTRAP_PROPERTIES_LOADER.getDefaultApplicationPropertiesFilePath();
+ }
+}
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index 371cc58..fdf473e 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.util;
+import org.apache.nifi.properties.ApplicationProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +47,7 @@ import java.util.stream.Stream;
* this class or passing it along. Its use should be refactored and minimized
* over time.
*/
-public abstract class NiFiProperties {
+public class NiFiProperties extends ApplicationProperties {
private static final Logger logger = LoggerFactory.getLogger(NiFiProperties.class);
// core properties
@@ -392,20 +393,17 @@ public abstract class NiFiProperties {
public static final int DEFAULT_COMPONENT_STATUS_REPOSITORY_PERSIST_COMPONENT_DAYS = 3;
public static final String DEFAULT_COMPONENT_STATUS_REPOSITORY_PERSIST_LOCATION = "./status_repository";
- /**
- * Retrieves the property value for the given property key.
- *
- * @param key the key of property value to lookup
- * @return value of property at given key or null if not found
- */
- public abstract String getProperty(String key);
+ public NiFiProperties() {
+ this(Collections.EMPTY_MAP);
+ }
- /**
- * Retrieves all known property keys.
- *
- * @return all known property keys
- */
- public abstract Set<String> getPropertyKeys();
+ public NiFiProperties(final Map<String, String> props) {
+ super(props);
+ }
+
+ public NiFiProperties(final Properties props) {
+ super(props);
+ }
// getters for core properties //
public File getFlowConfigurationFile() {
@@ -1591,14 +1589,10 @@ public abstract class NiFiProperties {
}
public boolean isTlsConfigurationPresent() {
- return StringUtils.isNotBlank(getProperty(NiFiProperties.SECURITY_KEYSTORE))
- && getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD) != null
- && StringUtils.isNotBlank(getProperty(NiFiProperties.SECURITY_TRUSTSTORE))
- && getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD) != null;
- }
-
- public int size() {
- return getPropertyKeys().size();
+ return StringUtils.isNotBlank(getProperty(SECURITY_KEYSTORE))
+ && getProperty(SECURITY_KEYSTORE_PASSWD) != null
+ && StringUtils.isNotBlank(getProperty(SECURITY_TRUSTSTORE))
+ && getProperty(SECURITY_TRUSTSTORE_PASSWD) != null;
}
public String getFlowFileRepoEncryptionKeyId() {
@@ -2026,6 +2020,11 @@ public abstract class NiFiProperties {
public Set<String> getPropertyKeys() {
return properties.stringPropertyNames();
}
+
+ @Override
+ public int size() {
+ return getPropertyKeys().size();
+ }
};
}
@@ -2077,4 +2076,9 @@ public abstract class NiFiProperties {
}
// Other properties to validate...
}
+
+ @Override
+ public String toString() {
+ return "NiFiProperties instance with " + size() + " properties";
+ }
}
diff --git a/nifi-commons/nifi-properties/pom.xml b/nifi-commons/nifi-property-utils/pom.xml
similarity index 96%
copy from nifi-commons/nifi-properties/pom.xml
copy to nifi-commons/nifi-property-utils/pom.xml
index fbcfd9d..6ef57b2 100644
--- a/nifi-commons/nifi-properties/pom.xml
+++ b/nifi-commons/nifi-property-utils/pom.xml
@@ -20,5 +20,5 @@
<artifactId>nifi-commons</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
- <artifactId>nifi-properties</artifactId>
+ <artifactId>nifi-property-utils</artifactId>
</project>
diff --git a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/AbstractBootstrapPropertiesLoader.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/AbstractBootstrapPropertiesLoader.java
new file mode 100644
index 0000000..cbeea84
--- /dev/null
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/AbstractBootstrapPropertiesLoader.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.properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+/**
+ * An abstract base class for an application-specific BootstrapProperties loader.
+ */
+public abstract class AbstractBootstrapPropertiesLoader {
+ private static final Logger logger = LoggerFactory.getLogger(AbstractBootstrapPropertiesLoader.class);
+
+ private static final String RELATIVE_APPLICATION_PROPERTIES_PATTERN = "conf/%s";
+ private static final String BOOTSTRAP_CONF = "bootstrap.conf";
+
+ /**
+ * Return the property prefix used in the bootstrap.conf file for this application.
+ * @return the property prefix
+ */
+ protected abstract String getApplicationPrefix();
+
+ /**
+ * Return the name of the main application properties file (e.g., nifi.properties). This will be
+ * used to determine the default location of the application properties file.
+ * @return The name of the application properties file
+ */
+ protected abstract String getApplicationPropertiesFilename();
+
+ /**
+ * Return the system property name that should specify the file path of the main
+ * application properties file.
+ * @return The system property name that should provide the file path of the main application
+ * properties file
+ */
+ protected abstract String getApplicationPropertiesFilePathSystemProperty();
+
+ /**
+ * Returns the key (if any) used to encrypt sensitive properties, extracted from
+ * {@code $APPLICATION_HOME/conf/bootstrap.conf}.
+ *
+ * @return the key in hexadecimal format
+ * @throws IOException if the file is not readable
+ */
+ public String extractKeyFromBootstrapFile() throws IOException {
+ return extractKeyFromBootstrapFile(null);
+ }
+
+ /**
+ * Loads the bootstrap.conf file into a BootstrapProperties object.
+ * @param bootstrapPath the path to the bootstrap file
+ * @return The bootstrap.conf as a BootstrapProperties object
+ * @throws IOException If the file is not readable
+ */
+ public BootstrapProperties loadBootstrapProperties(final String bootstrapPath) throws IOException {
+ final Properties properties = new Properties();
+ final Path bootstrapFilePath = getBootstrapFile(bootstrapPath).toPath();
+ try (final InputStream bootstrapInput = Files.newInputStream(bootstrapFilePath)) {
+ properties.load(bootstrapInput);
+ return new BootstrapProperties(getApplicationPrefix(), properties, bootstrapFilePath);
+ } catch (final IOException e) {
+ logger.error("Cannot read from bootstrap.conf file at {}", bootstrapFilePath);
+ throw new IOException("Cannot read from bootstrap.conf", e);
+ }
+ }
+
+ /**
+ * Returns the key (if any) used to encrypt sensitive properties, extracted from
+ * {@code $APPLICATION_HOME/conf/bootstrap.conf}.
+ *
+ * @param bootstrapPath the path to the bootstrap file (if null, returns the sensitive key
+ * found in $APPLICATION_HOME/conf/bootstrap.conf)
+ * @return the key in hexadecimal format
+ * @throws IOException if the file is not readable
+ */
+ public String extractKeyFromBootstrapFile(final String bootstrapPath) throws IOException {
+ final BootstrapProperties bootstrapProperties = loadBootstrapProperties(bootstrapPath);
+
+ return bootstrapProperties.getBootstrapSensitiveKey().orElseGet(() -> {
+ logger.warn("No encryption key present in the bootstrap.conf file at {}", bootstrapProperties.getConfigFilePath());
+ return "";
+ });
+ }
+
+ /**
+ * Returns the file for bootstrap.conf.
+ *
+ * @param bootstrapPath the path to the bootstrap file (defaults to $APPLICATION_HOME/conf/bootstrap.conf
+ * if null)
+ * @return the {@code $APPLICATION_HOME/conf/bootstrap.conf} file
+ * @throws IOException if the directory containing the file is not readable
+ */
+ private File getBootstrapFile(final String bootstrapPath) throws IOException {
+ final File expectedBootstrapFile;
+ if (bootstrapPath == null) {
+ // Guess at location of bootstrap.conf file from nifi.properties file
+ final String defaultApplicationPropertiesFilePath = getDefaultApplicationPropertiesFilePath();
+ final File propertiesFile = new File(defaultApplicationPropertiesFilePath);
+ final File confDir = new File(propertiesFile.getParent());
+ if (confDir.exists() && confDir.canRead()) {
+ expectedBootstrapFile = new File(confDir, BOOTSTRAP_CONF);
+ } else {
+ logger.error("Cannot read from bootstrap.conf file at {} -- conf/ directory is missing or permissions are incorrect", confDir.getAbsolutePath());
+ throw new IOException("Cannot read from bootstrap.conf");
+ }
+ } else {
+ expectedBootstrapFile = new File(bootstrapPath);
+ }
+
+ if (expectedBootstrapFile.exists() && expectedBootstrapFile.canRead()) {
+ return expectedBootstrapFile;
+ } else {
+ logger.error("Cannot read from bootstrap.conf file at {} -- file is missing or permissions are incorrect", expectedBootstrapFile.getAbsolutePath());
+ throw new IOException("Cannot read from bootstrap.conf");
+ }
+ }
+
+ /**
+ * Returns the default file path to {@code $APPLICATION_HOME/conf/$APPLICATION.properties}. If the system
+ * property provided by {@code AbstractBootstrapPropertiesLoader#getApplicationPropertiesFilePathSystemProperty()}
+ * is not set, it will be set to the relative path provided by
+ * {@code AbstractBootstrapPropertiesLoader#getRelativeApplicationPropertiesFilePath()}.
+ *
+ * @return the path to the application properties file
+ */
+ public String getDefaultApplicationPropertiesFilePath() {
+ final String systemPropertyName = getApplicationPropertiesFilePathSystemProperty();
+ final String defaultRelativePath = String.format(RELATIVE_APPLICATION_PROPERTIES_PATTERN, getApplicationPropertiesFilename());
+
+ String systemPath = System.getProperty(systemPropertyName);
+
+ if (systemPath == null || systemPath.trim().isEmpty()) {
+ logger.warn("The system property {} is not set, so it is being set to '{}'", systemPropertyName, defaultRelativePath);
+ System.setProperty(systemPropertyName, defaultRelativePath);
+ systemPath = defaultRelativePath;
+ }
+
+ logger.info("Determined default application properties path to be '{}'", systemPath);
+ return systemPath;
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ApplicationProperties.java
similarity index 65%
copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
copy to nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ApplicationProperties.java
index c800b3a..16bbee1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ApplicationProperties.java
@@ -16,8 +16,18 @@
*/
package org.apache.nifi.properties;
-public interface SensitivePropertyProviderFactory {
+import java.util.Map;
+import java.util.Properties;
- SensitivePropertyProvider getProvider();
+/**
+ * A tagging class that represents the main configuration properties for an application (e.g. NiFi or NiFi Registry).
+ */
+public class ApplicationProperties extends StandardReadableProperties {
+ public ApplicationProperties(Properties properties) {
+ super(properties);
+ }
+ public ApplicationProperties(Map<String, String> properties) {
+ super(properties);
+ }
}
diff --git a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java
new file mode 100644
index 0000000..4713e27
--- /dev/null
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/BootstrapProperties.java
@@ -0,0 +1,86 @@
+/*
+ * 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.properties;
+
+import java.nio.file.Path;
+import java.util.Enumeration;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
+
+/**
+ * Properties representing bootstrap.conf.
+ */
+public class BootstrapProperties extends StandardReadableProperties {
+ private static final String PROPERTY_KEY_FORMAT = "%s.%s";
+ private static final String BOOTSTRAP_SENSITIVE_KEY = "bootstrap.sensitive.key";
+
+ private final String propertyPrefix;
+ private final Path configFilePath;
+
+ public BootstrapProperties(final String propertyPrefix, final Properties properties, final Path configFilePath) {
+ super(new Properties());
+
+ Objects.requireNonNull(properties, "Properties are required");
+ this.propertyPrefix = Objects.requireNonNull(propertyPrefix, "Property prefix is required");
+ this.configFilePath = configFilePath;
+
+ this.filterProperties(properties);
+
+ }
+
+ /**
+ * Returns the path to the bootstrap config file.
+ * @return The path to the file
+ */
+ public Path getConfigFilePath() {
+ return configFilePath;
+ }
+
+ /**
+ * Includes only the properties starting with the propertyPrefix.
+ * @param properties Unfiltered properties
+ */
+ private void filterProperties(final Properties properties) {
+ getRawProperties().clear();
+ final Properties filteredProperties = new Properties();
+ for(final Enumeration<Object> e = properties.keys() ; e.hasMoreElements(); ) {
+ final String key = e.nextElement().toString();
+ if (key.startsWith(propertyPrefix)) {
+ filteredProperties.put(key, properties.getProperty(key));
+ }
+ }
+ getRawProperties().putAll(filteredProperties);
+ }
+
+ private String getPropertyKey(final String subKey) {
+ return String.format(PROPERTY_KEY_FORMAT, propertyPrefix, subKey);
+ }
+
+ /**
+ * Returns the bootstrap sensitive key.
+ * @return The bootstrap sensitive key
+ */
+ public Optional<String> getBootstrapSensitiveKey() {
+ return Optional.ofNullable(getProperty(getPropertyKey(BOOTSTRAP_SENSITIVE_KEY)));
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Bootstrap properties [%s] with prefix [%s]", configFilePath, propertyPrefix);
+ }
+}
diff --git a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedProperties.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedProperties.java
new file mode 100644
index 0000000..f04b1a8
--- /dev/null
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ProtectedProperties.java
@@ -0,0 +1,59 @@
+/*
+ * 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.properties;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Represents a protected set of ApplicationProperties, with methods regarding which sensitive properties
+ * are protected.
+ * @param <T> The ApplicationProperties type
+ */
+public interface ProtectedProperties<T extends ApplicationProperties> {
+
+ /**
+ * Additional sensitive properties keys
+ * @return Additional sensitive properties keys
+ */
+ String getAdditionalSensitivePropertiesKeys();
+
+ /**
+ * Returns the name of the property that specifies the additional sensitive properties keys
+ * @return Name of additional sensitive properties keys
+ */
+ String getAdditionalSensitivePropertiesKeysName();
+
+ /**
+ * Additional sensitive properties keys
+ * @return Additional sensitive properties keys
+ */
+ List<String> getDefaultSensitiveProperties();
+
+ /**
+ * Returns the application properties.
+ * @return The application properties
+ */
+ T getApplicationProperties();
+
+ /**
+ * Create a new ApplicationProperties object of the generic type.
+ * @param rawProperties Plain old properties
+ * @return The ApplicationProperties
+ */
+ T createApplicationProperties(Properties rawProperties);
+}
diff --git a/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ReadableProperties.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ReadableProperties.java
new file mode 100644
index 0000000..1dd3285
--- /dev/null
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/ReadableProperties.java
@@ -0,0 +1,50 @@
+/*
+ * 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.properties;
+
+import java.util.Set;
+
+/**
+ * A base interface for providing a readable set of properties.
+ */
+public interface ReadableProperties {
+
+ /**
+ * Retrieves the property value for the given property key.
+ *
+ * @param key the key of property value to lookup
+ * @return value of property at given key or null if not found
+ */
+ String getProperty(String key);
+
+ /**
+ * Retrieves the property value for the given property key.
+ *
+ * @param key the key of property value to lookup
+ * @param defaultValue The default value to use if the property does not exist
+ * @return value of property at given key or null if not found
+ */
+ String getProperty(String key, String defaultValue);
+
+ /**
+ * Retrieves all known property keys.
+ *
+ * @return all known property keys
+ */
+ Set<String> getPropertyKeys();
+
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/StandardNiFiProperties.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/StandardReadableProperties.java
similarity index 55%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/StandardNiFiProperties.java
rename to nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/StandardReadableProperties.java
index b7561ed..d23b677 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/StandardNiFiProperties.java
+++ b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/properties/StandardReadableProperties.java
@@ -18,42 +18,39 @@ package org.apache.nifi.properties;
import java.util.Enumeration;
import java.util.HashSet;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import org.apache.nifi.util.NiFiProperties;
-public class StandardNiFiProperties extends NiFiProperties {
+/**
+ * A Properties-backed implementation of ReadableProperties.
+ */
+public class StandardReadableProperties implements ReadableProperties {
- private Properties rawProperties = new Properties();
+ private final Properties rawProperties = new Properties();
- public StandardNiFiProperties() {
- this(null);
+ public StandardReadableProperties(final Properties properties) {
+ rawProperties.putAll(properties);
}
- public StandardNiFiProperties(Properties props) {
- this.rawProperties = props == null ? new Properties() : props;
+ public StandardReadableProperties(final Map<String, String> properties) {
+ rawProperties.putAll(properties);
}
- /**
- * Retrieves the property value for the given property key.
- *
- * @param key the key of property value to lookup
- * @return value of property at given key or null if not found
- */
@Override
- public String getProperty(String key) {
+ public String getProperty(final String key) {
return rawProperties.getProperty(key);
}
- /**
- * Retrieves all known property keys.
- *
- * @return all known property keys
- */
+ @Override
+ public String getProperty(final String key, String defaultValue) {
+ return rawProperties.getProperty(key, defaultValue);
+ }
+
@Override
public Set<String> getPropertyKeys() {
Set<String> propertyNames = new HashSet<>();
- Enumeration e = getRawProperties().propertyNames();
+ Enumeration e = rawProperties.propertyNames();
for (; e.hasMoreElements(); ){
propertyNames.add((String) e.nextElement());
}
@@ -61,21 +58,15 @@ public class StandardNiFiProperties extends NiFiProperties {
return propertyNames;
}
- Properties getRawProperties() {
- if (this.rawProperties == null) {
- this.rawProperties = new Properties();
- }
-
- return this.rawProperties;
+ protected Properties getRawProperties() {
+ return rawProperties;
}
- @Override
+ /**
+ * Returns the size of the properties.
+ * @return The size of the properties (number of keys)
+ */
public int size() {
- return getRawProperties().size();
- }
-
- @Override
- public String toString() {
- return "StandardNiFiProperties instance with " + size() + " properties";
+ return rawProperties.size();
}
}
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
index 6aa7fdb..c0b60c8 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
@@ -16,6 +16,21 @@
*/
package org.apache.nifi.security.kms;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.security.repository.config.RepositoryEncryptionConfiguration;
+import org.apache.nifi.security.util.EncryptionMethod;
+import org.apache.nifi.security.util.crypto.AESKeyedCipherProvider;
+import org.apache.nifi.util.NiFiBootstrapUtils;
+import org.bouncycastle.util.encoders.DecoderException;
+import org.bouncycastle.util.encoders.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
@@ -24,8 +39,6 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -34,22 +47,7 @@ import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.regex.Pattern;
-import java.util.stream.Stream;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.security.repository.config.RepositoryEncryptionConfiguration;
-import org.apache.nifi.security.util.EncryptionMethod;
-import org.apache.nifi.security.util.crypto.AESKeyedCipherProvider;
-import org.apache.nifi.util.NiFiProperties;
-import org.bouncycastle.util.encoders.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class CryptoUtils {
private static final Logger logger = LoggerFactory.getLogger(CryptoUtils.class);
@@ -60,9 +58,6 @@ public class CryptoUtils {
public static final String LEGACY_SKP_FQCN = "org.apache.nifi.provenance.StaticKeyProvider";
public static final String LEGACY_FBKP_FQCN = "org.apache.nifi.provenance.FileBasedKeyProvider";
- private static final String RELATIVE_NIFI_PROPS_PATH = "conf/nifi.properties";
- private static final String BOOTSTRAP_KEY_PREFIX = "nifi.bootstrap.sensitive.key=";
-
// TODO: Enforce even length
private static final Pattern HEX_PATTERN = Pattern.compile("(?i)^[0-9a-f]+$");
@@ -309,88 +304,15 @@ public class CryptoUtils {
public static SecretKey getRootKey() throws KeyManagementException {
try {
// Get the root encryption key from bootstrap.conf
- String rootKeyHex = extractKeyFromBootstrapFile();
+ String rootKeyHex = NiFiBootstrapUtils.extractKeyFromBootstrapFile();
return new SecretKeySpec(Hex.decode(rootKeyHex), "AES");
- } catch (IOException e) {
+ } catch (IOException | DecoderException e) {
logger.error("Encountered an error: ", e);
throw new KeyManagementException(e);
}
}
/**
- * Returns the key (if any) used to encrypt sensitive properties, extracted from {@code $NIFI_HOME/conf/bootstrap.conf}.
- *
- * @return the key in hexadecimal format
- * @throws IOException if the file is not readable
- */
- public static String extractKeyFromBootstrapFile() throws IOException {
- return extractKeyFromBootstrapFile("");
- }
-
- /**
- * Returns the key (if any) used to encrypt sensitive properties, extracted from {@code $NIFI_HOME/conf/bootstrap.conf}.
- *
- * @param bootstrapPath the path to the bootstrap file
- * @return the key in hexadecimal format
- * @throws IOException if the file is not readable
- */
- public static String extractKeyFromBootstrapFile(String bootstrapPath) throws IOException {
- File expectedBootstrapFile;
- if (StringUtils.isBlank(bootstrapPath)) {
- // Guess at location of bootstrap.conf file from nifi.properties file
- String defaultNiFiPropertiesPath = getDefaultFilePath();
- File propertiesFile = new File(defaultNiFiPropertiesPath);
- File confDir = new File(propertiesFile.getParent());
- if (confDir.exists() && confDir.canRead()) {
- expectedBootstrapFile = new File(confDir, "bootstrap.conf");
- } else {
- logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- conf/ directory is missing or permissions are incorrect", confDir.getAbsolutePath());
- throw new IOException("Cannot read from bootstrap.conf");
- }
- } else {
- expectedBootstrapFile = new File(bootstrapPath);
- }
-
- if (expectedBootstrapFile.exists() && expectedBootstrapFile.canRead()) {
- try (Stream<String> stream = Files.lines(Paths.get(expectedBootstrapFile.getAbsolutePath()))) {
- Optional<String> keyLine = stream.filter(l -> l.startsWith(BOOTSTRAP_KEY_PREFIX)).findFirst();
- if (keyLine.isPresent()) {
- return keyLine.get().split("=", 2)[1];
- } else {
- logger.warn("No encryption key present in the bootstrap.conf file at {}", expectedBootstrapFile.getAbsolutePath());
- return "";
- }
- } catch (IOException e) {
- logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key", expectedBootstrapFile.getAbsolutePath());
- throw new IOException("Cannot read from bootstrap.conf", e);
- }
- } else {
- logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- file is missing or permissions are incorrect", expectedBootstrapFile.getAbsolutePath());
- throw new IOException("Cannot read from bootstrap.conf");
- }
- }
-
- /**
- * Returns the default file path to {@code $NIFI_HOME/conf/nifi.properties}. If the system
- * property {@code nifi.properties.file.path} is not set, it will be set to the relative
- * path {@code conf/nifi.properties}.
- *
- * @return the path to the nifi.properties file
- */
- public static String getDefaultFilePath() {
- String systemPath = System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH);
-
- if (systemPath == null || systemPath.trim().isEmpty()) {
- logger.warn("The system variable {} is not set, so it is being set to '{}'", NiFiProperties.PROPERTIES_FILE_PATH, RELATIVE_NIFI_PROPS_PATH);
- System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, RELATIVE_NIFI_PROPS_PATH);
- systemPath = RELATIVE_NIFI_PROPS_PATH;
- }
-
- logger.info("Determined default nifi.properties path to be '{}'", systemPath);
- return systemPath;
- }
-
- /**
* Returns true if the two parameters are equal. This method is null-safe and evaluates the
* equality in constant-time rather than "short-circuiting" on the first inequality. This
* prevents timing attacks (side channel attacks) when comparing passwords or hash values.
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java
index 4b731d4..c627e5c 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java
@@ -174,42 +174,24 @@ public class StandardTlsConfiguration implements TlsConfiguration {
// Static factory method from NiFiProperties
/**
- * Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated from the relevant {@link NiFiProperties} properties.
+ * Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated from the relevant NiFi properties.
*
* @param niFiProperties the NiFi properties
* @return a populated TlsConfiguration container object
*/
- public static TlsConfiguration fromNiFiProperties(NiFiProperties niFiProperties) {
- Objects.requireNonNull("The NiFi properties cannot be null");
-
- String keystorePath = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE);
- String keystorePassword = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD);
- String keyPassword = niFiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD);
- String keystoreType = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
- String truststorePath = niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE);
- String truststorePassword = niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD);
- String truststoreType = niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE);
- String protocol = TLS_PROTOCOL_VERSION;
-
- final StandardTlsConfiguration tlsConfiguration = new StandardTlsConfiguration(keystorePath, keystorePassword, keyPassword,
- keystoreType, truststorePath, truststorePassword,
- truststoreType, protocol);
- if (logger.isDebugEnabled()) {
- logger.debug("Instantiating TlsConfiguration from NiFi properties: {}, {}, {}, {}, {}, {}, {}, {}",
- keystorePath, tlsConfiguration.getKeystorePasswordForLogging(), tlsConfiguration.getKeyPasswordForLogging(), keystoreType,
- truststorePath, tlsConfiguration.getTruststorePasswordForLogging(), truststoreType, protocol);
- }
-
- return tlsConfiguration;
+ public static TlsConfiguration fromNiFiProperties(final NiFiProperties niFiProperties) {
+ final Properties properties = new Properties();
+ niFiProperties.getPropertyKeys().forEach(key -> properties.setProperty(key, niFiProperties.getProperty(key)));
+ return fromNiFiProperties(properties);
}
/**
- * Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated from the relevant {@link NiFiProperties} properties.
+ * Returns a {@link org.apache.nifi.security.util.TlsConfiguration} instantiated from the relevant NiFi properties.
*
* @param niFiProperties the NiFi properties, as a simple java.util.Properties object
* @return a populated TlsConfiguration container object
*/
- public static TlsConfiguration fromNiFiProperties(Properties niFiProperties) {
+ public static TlsConfiguration fromNiFiProperties(final Properties niFiProperties) {
Objects.requireNonNull("The NiFi properties cannot be null");
String keystorePath = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE);
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml b/nifi-commons/nifi-sensitive-property-provider/pom.xml
similarity index 61%
copy from nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml
copy to nifi-commons/nifi-sensitive-property-provider/pom.xml
index 6458aec..bdfa9ba 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml
+++ b/nifi-commons/nifi-sensitive-property-provider/pom.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -16,61 +16,55 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
- <groupId>org.apache.nifi.registry</groupId>
- <artifactId>nifi-registry-core</artifactId>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-commons</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
- <artifactId>nifi-registry-properties</artifactId>
- <packaging>jar</packaging>
+ <artifactId>nifi-sensitive-property-provider</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-properties</artifactId>
+ <version>1.14.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.12.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-security-utils</artifactId>
+ <version>1.14.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
<build>
+ <!-- Required to run Groovy tests without any Java tests -->
<plugins>
<plugin>
- <groupId>org.codehaus.gmavenplus</groupId>
- <artifactId>gmavenplus-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
+ <id>add-test-source</id>
+ <phase>generate-test-sources</phase>
<goals>
- <goal>addTestSources</goal>
- <goal>testCompile</goal>
+ <goal>add-test-source</goal>
</goals>
+ <configuration>
+ <sources>
+ <source>src/test/groovy</source>
+ </sources>
+ </configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- <version>${org.bouncycastle.version}</version>
- </dependency>
- <dependency>
- <groupId>org.codehaus.groovy</groupId>
- <artifactId>groovy-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib-nodep</artifactId>
- <version>2.2.2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>${org.slf4j.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- </dependency>
- </dependencies>
</project>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProvider.java
similarity index 78%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProvider.java
rename to nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProvider.java
index 062e352..3999a3a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProvider.java
@@ -16,6 +16,21 @@
*/
package org.apache.nifi.properties;
+import org.apache.commons.lang3.StringUtils;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.DecoderException;
+import org.bouncycastle.util.encoders.EncoderException;
+import org.bouncycastle.util.encoders.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -25,48 +40,51 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import org.apache.commons.lang3.StringUtils;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.encoders.DecoderException;
-import org.bouncycastle.util.encoders.EncoderException;
-import org.bouncycastle.util.encoders.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
+public class AESSensitivePropertyProvider extends AbstractSensitivePropertyProvider {
private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProvider.class);
private static final String IMPLEMENTATION_NAME = "AES Sensitive Property Provider";
- private static final String IMPLEMENTATION_KEY = "aes/gcm/";
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final String PROVIDER = "BC";
private static final String DELIMITER = "||"; // "|" is not a valid Base64 character, so ensured not to be present in cipher text
private static final int IV_LENGTH = 12;
private static final int MIN_CIPHER_TEXT_LENGTH = IV_LENGTH * 4 / 3 + DELIMITER.length() + 1;
- private Cipher cipher;
+ private final Cipher cipher;
private final SecretKey key;
+ private final int keySize;
+
+ AESSensitivePropertyProvider(final byte[] keyHex) {
+ this(keyHex == null ? "" : Hex.toHexString(keyHex));
+ }
- public AESSensitivePropertyProvider(String keyHex) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
- byte[] key = validateKey(keyHex);
+ AESSensitivePropertyProvider(final String keyHex) {
+ super(null);
+
+ byte[] keyBytes = validateKey(keyHex);
try {
- cipher = Cipher.getInstance(ALGORITHM, PROVIDER);
+ this.cipher = Cipher.getInstance(ALGORITHM, PROVIDER);
// Only store the key if the cipher was initialized successfully
- this.key = new SecretKeySpec(key, "AES");
+ this.key = new SecretKeySpec(keyBytes, "AES");
+ this.keySize = getKeySize(Hex.toHexString(this.key.getEncoded()));
} catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
logger.error("Encountered an error initializing the {}: {}", IMPLEMENTATION_NAME, e.getMessage());
throw new SensitivePropertyProtectionException("Error initializing the protection cipher", e);
}
}
+ @Override
+ protected PropertyProtectionScheme getProtectionScheme() {
+ return PropertyProtectionScheme.AES_GCM;
+ }
+
+ @Override
+ protected boolean isSupported(final BootstrapProperties bootstrapProperties) {
+ return true; // AES protection is always supported
+ }
+
private byte[] validateKey(String keyHex) {
if (keyHex == null || StringUtils.isBlank(keyHex)) {
throw new SensitivePropertyProtectionException("The key cannot be empty");
@@ -84,10 +102,6 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
return key;
}
- public AESSensitivePropertyProvider(byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
- this(key == null ? "" : Hex.toHexString(key));
- }
-
private static String formatHexKey(String input) {
if (input == null || StringUtils.isBlank(input)) {
return "";
@@ -122,32 +136,18 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
}
/**
- * Returns the name of the underlying implementation.
- *
- * @return the name of this sensitive property provider
- */
- @Override
- public String getName() {
- return IMPLEMENTATION_NAME;
- }
-
- /**
* Returns the key used to identify the provider implementation in {@code nifi.properties}.
*
* @return the key to persist in the sibling property
*/
@Override
public String getIdentifierKey() {
- return IMPLEMENTATION_KEY + getKeySize(Hex.toHexString(key.getEncoded()));
+ return getProtectionScheme().getIdentifier(String.valueOf(keySize));
}
- private int getKeySize(String key) {
- if (StringUtils.isBlank(key)) {
- return 0;
- } else {
- // A key in hexadecimal format has one char per nibble (4 bits)
- return formatHexKey(key).length() * 4;
- }
+ private static int getKeySize(final String key) {
+ // A key in hexadecimal format has one char per nibble (4 bits)
+ return StringUtils.isBlank(key) ? 0 : formatHexKey(key).length() * 4;
}
/**
@@ -158,8 +158,8 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
* @throws SensitivePropertyProtectionException if there is an exception encrypting the value
*/
@Override
- public String protect(String unprotectedValue) throws SensitivePropertyProtectionException {
- if (unprotectedValue == null || unprotectedValue.trim().length() == 0) {
+ public String protect(final String unprotectedValue) throws SensitivePropertyProtectionException {
+ if (StringUtils.isBlank(unprotectedValue)) {
throw new IllegalArgumentException("Cannot encrypt an empty value");
}
@@ -185,7 +185,7 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
}
}
- private String base64Encode(byte[] input) {
+ private String base64Encode(final byte[] input) {
return Base64.toBase64String(input).replaceAll("=", "");
}
@@ -195,7 +195,7 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
* @return the IV
*/
private byte[] generateIV() {
- byte[] iv = new byte[IV_LENGTH];
+ final byte[] iv = new byte[IV_LENGTH];
new SecureRandom().nextBytes(iv);
return iv;
}
@@ -208,7 +208,7 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
* @throws SensitivePropertyProtectionException if there is an error decrypting the cipher text
*/
@Override
- public String unprotect(String protectedValue) throws SensitivePropertyProtectionException {
+ public String unprotect(final String protectedValue) throws SensitivePropertyProtectionException {
if (protectedValue == null || protectedValue.trim().length() < MIN_CIPHER_TEXT_LENGTH) {
throw new IllegalArgumentException("Cannot decrypt a cipher text shorter than " + MIN_CIPHER_TEXT_LENGTH + " chars");
}
@@ -216,28 +216,27 @@ public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
if (!protectedValue.contains(DELIMITER)) {
throw new IllegalArgumentException("The cipher text does not contain the delimiter " + DELIMITER + " -- it should be of the form Base64(IV) || Base64(cipherText)");
}
+ final String trimmedProtectedValue = protectedValue.trim();
- protectedValue = protectedValue.trim();
-
- final String IV_B64 = protectedValue.substring(0, protectedValue.indexOf(DELIMITER));
- byte[] iv = Base64.decode(IV_B64);
+ final String armoredIV = trimmedProtectedValue.substring(0, trimmedProtectedValue.indexOf(DELIMITER));
+ final byte[] iv = Base64.decode(armoredIV);
if (iv.length < IV_LENGTH) {
- throw new IllegalArgumentException("The IV (" + iv.length + " bytes) must be at least " + IV_LENGTH + " bytes");
+ throw new IllegalArgumentException(String.format("The IV (%s bytes) must be at least %s bytes", iv.length, IV_LENGTH));
}
- String CIPHERTEXT_B64 = protectedValue.substring(protectedValue.indexOf(DELIMITER) + 2);
+ String armoredCipherText = trimmedProtectedValue.substring(trimmedProtectedValue.indexOf(DELIMITER) + 2);
// Restore the = padding if necessary to reconstitute the GCM MAC check
- if (CIPHERTEXT_B64.length() % 4 != 0) {
- final int paddedLength = CIPHERTEXT_B64.length() + 4 - (CIPHERTEXT_B64.length() % 4);
- CIPHERTEXT_B64 = StringUtils.rightPad(CIPHERTEXT_B64, paddedLength, '=');
+ if (armoredCipherText.length() % 4 != 0) {
+ final int paddedLength = armoredCipherText.length() + 4 - (armoredCipherText.length() % 4);
+ armoredCipherText = StringUtils.rightPad(armoredCipherText, paddedLength, '=');
}
try {
- byte[] cipherBytes = Base64.decode(CIPHERTEXT_B64);
+ final byte[] cipherBytes = Base64.decode(armoredCipherText);
cipher.init(Cipher.DECRYPT_MODE, this.key, new IvParameterSpec(iv));
- byte[] plainBytes = cipher.doFinal(cipherBytes);
+ final byte[] plainBytes = cipher.doFinal(cipherBytes);
logger.debug(getName() + " decrypted a sensitive value successfully");
return new String(plainBytes, StandardCharsets.UTF_8);
} catch (BadPaddingException | IllegalBlockSizeException | DecoderException | InvalidAlgorithmParameterException | InvalidKeyException e) {
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractSensitivePropertyProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractSensitivePropertyProvider.java
new file mode 100644
index 0000000..b52fb73
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/AbstractSensitivePropertyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * 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.properties;
+
+public abstract class AbstractSensitivePropertyProvider implements SensitivePropertyProvider {
+ private final BootstrapProperties bootstrapProperties;
+
+ public AbstractSensitivePropertyProvider(final BootstrapProperties bootstrapProperties) {
+ this.bootstrapProperties = bootstrapProperties;
+ }
+
+ protected BootstrapProperties getBootstrapProperties() {
+ return bootstrapProperties;
+ }
+
+ /**
+ * Return the appropriate PropertyProtectionScheme for this provider.
+ * @return The PropertyProtectionScheme
+ */
+ protected abstract PropertyProtectionScheme getProtectionScheme();
+
+ /**
+ * Return true if this SensitivePropertyProvider is supported, given the provided
+ * Bootstrap properties.
+ * @param bootstrapProperties The Bootstrap properties
+ * @return True if this SensitivePropertyProvider is supported
+ */
+ protected abstract boolean isSupported(BootstrapProperties bootstrapProperties);
+
+ @Override
+ public String getName() {
+ return getProtectionScheme().getName();
+ }
+
+ /**
+ * Default implementation to return the protection scheme identifier, with no args to populate the identifier key.
+ * Concrete classes may choose to override this in order to fill in the identifier with specific args.
+ * @return The identifier key
+ */
+ @Override
+ public String getIdentifierKey() {
+ return getProtectionScheme().getIdentifier();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return isSupported(bootstrapProperties);
+ }
+}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/ApplicationPropertiesProtector.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/ApplicationPropertiesProtector.java
new file mode 100644
index 0000000..3f03a9f
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/ApplicationPropertiesProtector.java
@@ -0,0 +1,340 @@
+/*
+ * 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.properties;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Class performing unprotection activities before returning a clean
+ * implementation of {@link ApplicationProperties}.
+ * This encapsulates the sensitive property access logic from external consumers
+ * of {@code ApplicationProperties}.
+ *
+ * @param <T> The type of protected application properties
+ * @param <U> The type of standard application properties that backs the protected application properties
+ */
+public class ApplicationPropertiesProtector<T extends ProtectedProperties<U>, U extends ApplicationProperties>
+ implements SensitivePropertyProtector<T, U> {
+ public static final String PROTECTED_KEY_SUFFIX = ".protected";
+
+ private static final Logger logger = LoggerFactory.getLogger(ApplicationPropertiesProtector.class);
+
+ private T protectedProperties;
+
+ private Map<String, SensitivePropertyProvider> localProviderCache = new HashMap<>();
+
+ /**
+ * Creates an instance containing the provided {@link ProtectedProperties}.
+ *
+ * @param protectedProperties the ProtectedProperties to contain
+ */
+ public ApplicationPropertiesProtector(final T protectedProperties) {
+ this.protectedProperties = protectedProperties;
+ logger.debug("Loaded {} properties (including {} protection schemes) into {}", getPropertyKeysIncludingProtectionSchemes().size(),
+ getProtectedPropertyKeys().size(), this.getClass().getName());
+ }
+
+ /**
+ * Returns the sibling property key which specifies the protection scheme for this key.
+ * <p>
+ * Example:
+ * <p>
+ * nifi.sensitive.key=ABCXYZ
+ * nifi.sensitive.key.protected=aes/gcm/256
+ * <p>
+ * nifi.sensitive.key -> nifi.sensitive.key.protected
+ *
+ * @param key the key identifying the sensitive property
+ * @return the key identifying the protection scheme for the sensitive property
+ */
+ public static String getProtectionKey(final String key) {
+ if (key == null || key.isEmpty()) {
+ throw new IllegalArgumentException("Cannot find protection key for null key");
+ }
+
+ return key + PROTECTED_KEY_SUFFIX;
+ }
+
+ /**
+ * Retrieves all known property keys.
+ *
+ * @return all known property keys
+ */
+ @Override
+ public Set<String> getPropertyKeys() {
+ Set<String> filteredKeys = getPropertyKeysIncludingProtectionSchemes();
+ filteredKeys.removeIf(p -> p.endsWith(PROTECTED_KEY_SUFFIX));
+ return filteredKeys;
+ }
+
+ @Override
+ public int size() {
+ return getPropertyKeys().size();
+ }
+
+ @Override
+ public Set<String> getPropertyKeysIncludingProtectionSchemes() {
+ return protectedProperties.getApplicationProperties().getPropertyKeys();
+ }
+
+ /**
+ * Splits a single string containing multiple property keys into a List. Delimited by ',' or ';' and ignores leading and trailing whitespace around delimiter.
+ *
+ * @param multipleProperties a single String containing multiple properties, i.e. "nifi.property.1; nifi.property.2, nifi.property.3"
+ * @return a List containing the split and trimmed properties
+ */
+ private static List<String> splitMultipleProperties(final String multipleProperties) {
+ if (multipleProperties == null || multipleProperties.trim().isEmpty()) {
+ return new ArrayList<>(0);
+ } else {
+ List<String> properties = new ArrayList<>(asList(multipleProperties.split("\\s*[,;]\\s*")));
+ for (int i = 0; i < properties.size(); i++) {
+ properties.set(i, properties.get(i).trim());
+ }
+ return properties;
+ }
+ }
+
+ private String getProperty(final String key) {
+ return protectedProperties.getApplicationProperties().getProperty(key);
+ }
+
+ private String getAdditionalSensitivePropertiesKeys() {
+ return getProperty(protectedProperties.getAdditionalSensitivePropertiesKeysName());
+ }
+
+ private String getAdditionalSensitivePropertiesKeysName() {
+ return protectedProperties.getAdditionalSensitivePropertiesKeysName();
+ }
+
+ @Override
+ public List<String> getSensitivePropertyKeys() {
+ final String additionalPropertiesString = getAdditionalSensitivePropertiesKeys();
+ final String additionalPropertiesKeyName = protectedProperties.getAdditionalSensitivePropertiesKeysName();
+ if (additionalPropertiesString == null || additionalPropertiesString.trim().isEmpty()) {
+ return protectedProperties.getDefaultSensitiveProperties();
+ } else {
+ List<String> additionalProperties = splitMultipleProperties(additionalPropertiesString);
+ /* Remove this key if it was accidentally provided as a sensitive key
+ * because we cannot protect it and read from it
+ */
+ if (additionalProperties.contains(additionalPropertiesKeyName)) {
+ logger.warn("The key '{}' contains itself. This is poor practice and should be removed", additionalPropertiesKeyName);
+ additionalProperties.remove(additionalPropertiesKeyName);
+ }
+ additionalProperties.addAll(protectedProperties.getDefaultSensitiveProperties());
+ return additionalProperties;
+ }
+ }
+
+ @Override
+ public List<String> getPopulatedSensitivePropertyKeys() {
+ List<String> allSensitiveKeys = getSensitivePropertyKeys();
+ return allSensitiveKeys.stream().filter(k -> StringUtils.isNotBlank(getProperty(k))).collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean hasProtectedKeys() {
+ final List<String> sensitiveKeys = getSensitivePropertyKeys();
+ for (String k : sensitiveKeys) {
+ if (isPropertyProtected(k)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Map<String, String> getProtectedPropertyKeys() {
+ final List<String> sensitiveKeys = getSensitivePropertyKeys();
+
+ final Map<String, String> traditionalProtectedProperties = new HashMap<>();
+ for (final String key : sensitiveKeys) {
+ final String protection = getProperty(getProtectionKey(key));
+ if (StringUtils.isNotBlank(protection) && StringUtils.isNotBlank(getProperty(key))) {
+ traditionalProtectedProperties.put(key, protection);
+ }
+ }
+
+ return traditionalProtectedProperties;
+ }
+
+ @Override
+ public Set<String> getProtectionSchemes() {
+ return new HashSet<>(getProtectedPropertyKeys().values());
+ }
+
+ @Override
+ public boolean isPropertySensitive(final String key) {
+ // If the explicit check for ADDITIONAL_SENSITIVE_PROPERTIES_KEY is not here, this will loop infinitely
+ return key != null && !key.equals(getAdditionalSensitivePropertiesKeysName()) && getSensitivePropertyKeys().contains(key.trim());
+ }
+
+ /**
+ * Returns true if the property identified by this key is considered protected in this instance of {@code NiFiProperties}.
+ * The property value is protected if the key is sensitive and the sibling key of key.protected is present.
+ *
+ * @param key the key
+ * @return true if it is currently marked as protected
+ * @see ApplicationPropertiesProtector#getSensitivePropertyKeys()
+ */
+ @Override
+ public boolean isPropertyProtected(final String key) {
+ return key != null && isPropertySensitive(key) && !StringUtils.isBlank(getProperty(getProtectionKey(key)));
+ }
+
+ @Override
+ public U getUnprotectedProperties() throws SensitivePropertyProtectionException {
+ if (hasProtectedKeys()) {
+ logger.debug("Protected Properties [{}] Sensitive Properties [{}]",
+ getProtectedPropertyKeys().size(),
+ getSensitivePropertyKeys().size());
+
+ final Properties rawProperties = new Properties();
+
+ final Set<String> failedKeys = new HashSet<>();
+
+ for (final String key : getPropertyKeys()) {
+ /* Three kinds of keys
+ * 1. protection schemes -- skip
+ * 2. protected keys -- unprotect and copy
+ * 3. normal keys -- copy over
+ */
+ if (key.endsWith(PROTECTED_KEY_SUFFIX)) {
+ // Do nothing
+ } else if (isPropertyProtected(key)) {
+ try {
+ rawProperties.setProperty(key, unprotectValue(key, getProperty(key)));
+ } catch (final SensitivePropertyProtectionException e) {
+ logger.warn("Failed to unprotect '{}'", key, e);
+ failedKeys.add(key);
+ }
+ } else {
+ rawProperties.setProperty(key, getProperty(key));
+ }
+ }
+
+ if (!failedKeys.isEmpty()) {
+ if (failedKeys.size() > 1) {
+ logger.warn("Combining {} failed keys [{}] into single exception", failedKeys.size(), StringUtils.join(failedKeys, ", "));
+ throw new MultipleSensitivePropertyProtectionException("Failed to unprotect keys", failedKeys);
+ } else {
+ throw new SensitivePropertyProtectionException("Failed to unprotect key " + failedKeys.iterator().next());
+ }
+ }
+
+ final U unprotected = protectedProperties.createApplicationProperties(rawProperties);
+
+ return unprotected;
+ } else {
+ logger.debug("No protected properties");
+ return protectedProperties.getApplicationProperties();
+ }
+ }
+
+ @Override
+ public void addSensitivePropertyProvider(final SensitivePropertyProvider sensitivePropertyProvider) {
+ Objects.requireNonNull(sensitivePropertyProvider, "Cannot add null SensitivePropertyProvider");
+ if (sensitivePropertyProvider == null) {
+ throw new IllegalArgumentException("Cannot add null SensitivePropertyProvider");
+ }
+
+ if (getSensitivePropertyProviders().containsKey(sensitivePropertyProvider.getIdentifierKey())) {
+ throw new UnsupportedOperationException("Cannot overwrite existing sensitive property provider registered for " + sensitivePropertyProvider.getIdentifierKey());
+ }
+
+ getSensitivePropertyProviders().put(sensitivePropertyProvider.getIdentifierKey(), sensitivePropertyProvider);
+ }
+
+ @Override
+ public String toString() {
+ final Set<String> providers = getSensitivePropertyProviders().keySet();
+ return new StringBuilder("ApplicationPropertiesProtector instance with ")
+ .append(size()).append(" properties (")
+ .append(getProtectedPropertyKeys().size())
+ .append(" protected) and ")
+ .append(providers.size())
+ .append(" sensitive property providers: ")
+ .append(StringUtils.join(providers, ", "))
+ .toString();
+ }
+
+ @Override
+ public Map<String, SensitivePropertyProvider> getSensitivePropertyProviders() {
+ if (localProviderCache == null) {
+ localProviderCache = new HashMap<>();
+ }
+
+ return localProviderCache;
+ }
+
+ private SensitivePropertyProvider getSensitivePropertyProvider(final String protectionScheme) {
+ if (isProviderAvailable(protectionScheme)) {
+ return getSensitivePropertyProviders().get(protectionScheme);
+ } else {
+ throw new SensitivePropertyProtectionException("No provider available for " + protectionScheme);
+ }
+ }
+
+ private boolean isProviderAvailable(final String protectionScheme) {
+ return getSensitivePropertyProviders().containsKey(protectionScheme);
+ }
+
+ /**
+ * If the value is protected, unprotects it and returns it. If not, returns the original value.
+ *
+ * @param key the retrieved property key
+ * @param retrievedValue the retrieved property value
+ * @return the unprotected value
+ */
+ private String unprotectValue(final String key, final String retrievedValue) {
+ // Checks if the key is sensitive and marked as protected
+ if (isPropertyProtected(key)) {
+ final String protectionScheme = getProperty(getProtectionKey(key));
+
+ // No provider registered for this scheme
+ if (!isProviderAvailable(protectionScheme)) {
+ throw new IllegalStateException(String.format("No provider available for " + key));
+ }
+
+ try {
+ final SensitivePropertyProvider sensitivePropertyProvider = getSensitivePropertyProvider(protectionScheme);
+ return sensitivePropertyProvider.unprotect(retrievedValue);
+ } catch (SensitivePropertyProtectionException e) {
+ logger.error("Error unprotecting value for " + key, e);
+ throw e;
+ } catch (IllegalArgumentException e) {
+ throw new SensitivePropertyProtectionException("Error unprotecting value for " + key, e);
+ }
+ }
+ return retrievedValue;
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/MultipleSensitivePropertyProtectionException.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/MultipleSensitivePropertyProtectionException.java
similarity index 100%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/MultipleSensitivePropertyProtectionException.java
rename to nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/MultipleSensitivePropertyProtectionException.java
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java
new file mode 100644
index 0000000..4017406
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/PropertyProtectionScheme.java
@@ -0,0 +1,79 @@
+/*
+ * 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.properties;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A scheme for protecting sensitive properties. Each scheme is intended to be backed by an implementation of
+ * SensitivePropertyProvider.
+ */
+public enum PropertyProtectionScheme {
+ AES_GCM("aes/gcm/(128|192|256)", "aes/gcm/%s", "AES Sensitive Property Provider", true);
+
+ PropertyProtectionScheme(final String identifierPattern, final String identifierFormat, final String name, final boolean requiresSecretKey) {
+ this.identifierPattern = identifierPattern;
+ this.identifierFormat = identifierFormat;
+ this.name = name;
+ this.requiresSecretKey = requiresSecretKey;
+ }
+
+ private final String identifierFormat;
+ private final String identifierPattern;
+ private final String name;
+ private final boolean requiresSecretKey;
+
+ /**
+ * Returns a the identifier of the PropertyProtectionScheme.
+ * @param args scheme-specific arguments used to fill in the formatted identifierPattern
+ * @return The identifier of the PropertyProtectionScheme
+ */
+ public String getIdentifier(final String... args) {
+ return String.format(identifierFormat, args);
+ }
+
+ /**
+ * Returns whether this scheme requires a secret key.
+ * @return True if this scheme requires a secret key
+ */
+ public boolean requiresSecretKey() {
+ return requiresSecretKey;
+ }
+
+ /**
+ * Returns the name of the PropertyProtectionScheme.
+ * @return The name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the PropertyProtectionScheme matching the provided name.
+ * @param identifier The unique PropertyProtectionScheme identifier
+ * @return The matching PropertyProtectionScheme
+ * @throws IllegalArgumentException If the name was not recognized
+ */
+ public static PropertyProtectionScheme fromIdentifier(final String identifier) {
+ Objects.requireNonNull(identifier, "Identifier must be specified");
+ return Arrays.stream(PropertyProtectionScheme.values())
+ .filter(scheme -> identifier.matches(scheme.identifierPattern))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Unrecognized protection scheme :" + identifier));
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProtectionException.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProtectionException.java
similarity index 100%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProtectionException.java
rename to nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProtectionException.java
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProtector.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProtector.java
new file mode 100644
index 0000000..b78dd6d
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProtector.java
@@ -0,0 +1,146 @@
+/*
+ * 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.properties;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Encapsulates methods needed to protect application properties.
+ * @param <T> The ProtectedProperties type
+ * @param <U> The ApplicationProperties type
+ */
+public interface SensitivePropertyProtector<T extends ProtectedProperties<U>, U extends ApplicationProperties> {
+
+ /**
+ * Returns the number of properties, excluding protection scheme properties.
+ * <p>
+ * Example:
+ * <p>
+ * key: E(value, key)
+ * key.protected: aes/gcm/256
+ * key2: value2
+ * <p>
+ * would return size 2
+ *
+ * @return the count of real properties
+ */
+ int size();
+
+ /**
+ * Retrieves all known property keys.
+ *
+ * @return all known property keys
+ */
+ Set<String> getPropertyKeys();
+
+ /**
+ * Returns the complete set of property keys, including any protection keys (i.e. 'x.y.z.protected').
+ *
+ * @return the set of property keys
+ */
+ Set<String> getPropertyKeysIncludingProtectionSchemes();
+
+ /**
+ * Returns a list of the keys identifying "sensitive" properties. There is a default list,
+ * and additional keys can be provided in the {@code nifi.sensitive.props.additional.keys} property in the ApplicationProperties.
+ *
+ * @return the list of sensitive property keys
+ */
+ List<String> getSensitivePropertyKeys();
+
+ /**
+ * Returns a list of the keys identifying "sensitive" properties. There is a default list,
+ * and additional keys can be provided in the {@code nifi.sensitive.props.additional.keys} property in the ApplicationProperties.
+ *
+ * @return the list of sensitive property keys
+ */
+ List<String> getPopulatedSensitivePropertyKeys();
+
+ /**
+ * Returns true if any sensitive keys are protected.
+ *
+ * @return true if any key is protected; false otherwise
+ */
+ boolean hasProtectedKeys();
+
+ /**
+ * Returns the unique set of all protection schemes currently in use for this instance.
+ *
+ * @return the set of protection schemes
+ */
+ Set<String> getProtectionSchemes();
+
+ /**
+ * Returns a Map of the keys identifying "sensitive" properties that are currently protected and the "protection" key for each.
+ * This may or may not include all properties marked as sensitive.
+ *
+ * @return the Map of protected property keys and the protection identifier for each
+ */
+ Map<String, String> getProtectedPropertyKeys();
+
+ /**
+ * Returns the local provider cache (null-safe) as a Map of protection schemes -> implementations.
+ *
+ * @return the map
+ */
+ Map<String, SensitivePropertyProvider> getSensitivePropertyProviders();
+
+ /**
+ * Returns true if the property identified by this key is considered sensitive in this instance of {@code ApplicationProperties}.
+ * Some properties are sensitive by default, while others can be specified by
+ * {@link ProtectedProperties#getAdditionalSensitivePropertiesKeys()}.
+ *
+ * @param key the key
+ * @return true if it is sensitive
+ * @see ApplicationPropertiesProtector#getSensitivePropertyKeys()
+ */
+ boolean isPropertySensitive(String key);
+
+ /**
+ * Returns the unprotected {@link ApplicationProperties} instance. If none of the properties
+ * loaded are marked as protected, it will simply pass through the internal instance.
+ * If any are protected, it will drop the protection scheme keys and translate each
+ * protected value (encrypted, HSM-retrieved, etc.) into the raw value and store it
+ * under the original key.
+ * <p>
+ * If any property fails to unprotect, it will save that key and continue. After
+ * attempting all properties, it will throw an exception containing all failed
+ * properties. This is necessary because the order is not enforced, so all failed
+ * properties should be gathered together.
+ *
+ * @return the ApplicationProperties instance with all raw values
+ * @throws SensitivePropertyProtectionException if there is a problem unprotecting one or more keys
+ */
+ boolean isPropertyProtected(String key);
+
+ /**
+ * Returns the unprotected ApplicationProperties.
+ * @return The unprotected properties
+ * @throws SensitivePropertyProtectionException if there is a problem unprotecting one or more keys
+ */
+ U getUnprotectedProperties() throws SensitivePropertyProtectionException;
+
+ /**
+ * Registers a new {@link SensitivePropertyProvider}. This method will throw a {@link UnsupportedOperationException}
+ * if a provider is already registered for the protection scheme.
+ *
+ * @param sensitivePropertyProvider the provider
+ */
+ void addSensitivePropertyProvider(SensitivePropertyProvider sensitivePropertyProvider);
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProvider.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProvider.java
similarity index 91%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProvider.java
rename to nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProvider.java
index b0c0be2..bb26ecf 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProvider.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProvider.java
@@ -33,6 +33,13 @@ public interface SensitivePropertyProvider {
String getIdentifierKey();
/**
+ * Returns whether this SensitivePropertyProvider is supported with the current system
+ * configuration.
+ * @return Whether this SensitivePropertyProvider is supported
+ */
+ boolean isSupported();
+
+ /**
* Returns the "protected" form of this value. This is a form which can safely be persisted in the {@code nifi.properties} file without compromising the value.
* An encryption-based provider would return a cipher text, while a remote-lookup provider could return a unique ID to retrieve the secured value.
*
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
similarity index 61%
copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
copy to nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
index c800b3a..834e708 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
@@ -16,8 +16,21 @@
*/
package org.apache.nifi.properties;
+import java.util.Collection;
+
public interface SensitivePropertyProviderFactory {
- SensitivePropertyProvider getProvider();
+ /**
+ * Gives the appropriate SensitivePropertyProvider, given a protection scheme.
+ * @param protectionScheme The protection scheme to use
+ * @return The appropriate SensitivePropertyProvider
+ */
+ SensitivePropertyProvider getProvider(PropertyProtectionScheme protectionScheme);
+
+ /**
+ * Returns a collection of all supported sensitive property providers.
+ * @return The supported sensitive property providers
+ */
+ Collection<SensitivePropertyProvider> getSupportedSensitivePropertyProviders();
}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactoryAware.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactoryAware.java
new file mode 100644
index 0000000..88bf1d6
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactoryAware.java
@@ -0,0 +1,46 @@
+/*
+ * 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.properties;
+
+import java.util.function.Supplier;
+
+/**
+ * Provides a default SensitivePropertyProviderFactory to subclasses.
+ */
+public class SensitivePropertyProviderFactoryAware {
+
+ private SensitivePropertyProviderFactory sensitivePropertyProviderFactory;
+
+ protected SensitivePropertyProviderFactory getSensitivePropertyProviderFactory() throws SensitivePropertyProtectionException {
+ if (sensitivePropertyProviderFactory == null) {
+ sensitivePropertyProviderFactory = StandardSensitivePropertyProviderFactory.withDefaults();
+ }
+ return sensitivePropertyProviderFactory;
+ }
+
+ /**
+ * Configures and sets the SensitivePropertyProviderFactory.
+ * @param keyHex An key in hex format, which some providers may use for encryption
+ * @param bootstrapPropertiesSupplier The bootstrap.conf properties supplier
+ * @return The configured SensitivePropertyProviderFactory
+ */
+ public SensitivePropertyProviderFactory configureSensitivePropertyProviderFactory(final String keyHex,
+ final Supplier<BootstrapProperties> bootstrapPropertiesSupplier) {
+ sensitivePropertyProviderFactory = StandardSensitivePropertyProviderFactory.withKeyAndBootstrapSupplier(keyHex, bootstrapPropertiesSupplier);
+ return sensitivePropertyProviderFactory;
+ }
+}
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java
new file mode 100644
index 0000000..ef59333
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/main/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactory.java
@@ -0,0 +1,121 @@
+/*
+ * 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.properties;
+
+import org.apache.nifi.util.NiFiBootstrapUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class StandardSensitivePropertyProviderFactory implements SensitivePropertyProviderFactory {
+ private static final Logger logger = LoggerFactory.getLogger(StandardSensitivePropertyProviderFactory.class);
+
+ private final Optional<String> keyHex;
+ private final Supplier<BootstrapProperties> bootstrapPropertiesSupplier;
+ private final Map<PropertyProtectionScheme, SensitivePropertyProvider> providerMap;
+
+ /**
+ * Creates a StandardSensitivePropertyProviderFactory using the default bootstrap.conf location and
+ * the keyHex extracted from this bootstrap.conf.
+ */
+ public static SensitivePropertyProviderFactory withDefaults() {
+ return withKeyAndBootstrapSupplier(null, null);
+ }
+
+ /**
+ * Creates a StandardSensitivePropertyProviderFactory using only the provided secret key hex. The default
+ * bootstrap.conf will be used for any providers that may require it, but the provided keyHex will be used instead
+ * of the one from the default bootstrap.conf.
+ * @param keyHex The secret key hex for encrypting properties
+ * @return A StandardSensitivePropertyProviderFactory
+ */
+ public static SensitivePropertyProviderFactory withKey(final String keyHex) {
+ return new StandardSensitivePropertyProviderFactory(keyHex, null);
+ }
+
+ /**
+ * Creates a new StandardSensitivePropertyProviderFactory using a separate keyHex and provided bootstrap.conf.
+ * The provided keyHex will be used instead of the one from the bootstrap.conf.
+ * @param keyHex The secret key hex for encrypting properties
+ * @param bootstrapPropertiesSupplier A supplier for the BootstrapProperties that represent bootstrap.conf.
+ * If the supplier returns null, the default bootstrap.conf will be used instead.
+ * @return A StandardSensitivePropertyProviderFactory
+ */
+ public static SensitivePropertyProviderFactory withKeyAndBootstrapSupplier(final String keyHex,
+ final Supplier<BootstrapProperties> bootstrapPropertiesSupplier) {
+ return new StandardSensitivePropertyProviderFactory(keyHex, bootstrapPropertiesSupplier);
+ }
+
+ private StandardSensitivePropertyProviderFactory(final String keyHex, final Supplier<BootstrapProperties> bootstrapPropertiesSupplier) {
+ this.keyHex = Optional.ofNullable(keyHex);
+ this.bootstrapPropertiesSupplier = bootstrapPropertiesSupplier == null ? () -> null : bootstrapPropertiesSupplier;
+ this.providerMap = new HashMap<>();
+ }
+
+ private String getKeyHex() {
+ return keyHex.orElseGet(() -> getBootstrapProperties().getBootstrapSensitiveKey()
+ .orElseThrow(() -> new SensitivePropertyProtectionException("Could not read root key from bootstrap.conf")));
+ }
+
+ /**
+ * Returns the configured bootstrap properties, or the default bootstrap.conf properties if
+ * not provided.
+ * @return The bootstrap.conf properties
+ */
+ private BootstrapProperties getBootstrapProperties() {
+ return Optional.ofNullable(bootstrapPropertiesSupplier.get()).orElseGet(() -> {
+ try {
+ return NiFiBootstrapUtils.loadBootstrapProperties();
+ } catch (final IOException e) {
+ logger.error("Error extracting root key from bootstrap.conf for login identity provider decryption", e);
+ throw new SensitivePropertyProtectionException("Could not read root key from bootstrap.conf");
+ }
+ });
+ }
+
+ @Override
+ public SensitivePropertyProvider getProvider(final PropertyProtectionScheme protectionScheme) throws SensitivePropertyProtectionException {
+ Objects.requireNonNull(protectionScheme, "Protection scheme is required");
+ // Only look up the secret key, which can perform a disk read, if this provider actually requires one
+ final String keyHex = protectionScheme.requiresSecretKey() ? getKeyHex() : null;
+ switch (protectionScheme) {
+ case AES_GCM:
+ return providerMap.computeIfAbsent(protectionScheme, s -> new AESSensitivePropertyProvider(keyHex));
+ // Other providers may choose to pass getBootstrapProperties() into the constructor
+ default:
+ throw new SensitivePropertyProtectionException("Unsupported protection scheme " + protectionScheme);
+ }
+ }
+
+ @Override
+ public Collection<SensitivePropertyProvider> getSupportedSensitivePropertyProviders() {
+ return Arrays.stream(PropertyProtectionScheme.values())
+ .map(this::getProvider)
+ .filter(SensitivePropertyProvider::isSupported)
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderTest.groovy b/nifi-commons/nifi-sensitive-property-provider/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderTest.groovy
similarity index 100%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderTest.groovy
rename to nifi-commons/nifi-sensitive-property-provider/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderTest.groovy
diff --git a/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactoryTest.java b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactoryTest.java
new file mode 100644
index 0000000..f36f259
--- /dev/null
+++ b/nifi-commons/nifi-sensitive-property-provider/src/test/java/org/apache/nifi/properties/StandardSensitivePropertyProviderFactoryTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.properties;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.internal.util.io.IOUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.Security;
+import java.util.Properties;
+import java.util.function.Supplier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class StandardSensitivePropertyProviderFactoryTest {
+
+ private static final String AES_GCM_128 = "aes/gcm/128";
+ private SensitivePropertyProviderFactory factory;
+
+ private static final String BOOTSTRAP_KEY_HEX = "0123456789ABCDEFFEDCBA9876543210";
+ private static final String AD_HOC_KEY_HEX = "123456789ABCDEFFEDCBA98765432101";
+
+ private static Path tempConfDir;
+ private static Path mockBootstrapConf;
+ private static Path mockNifiProperties;
+
+ private static NiFiProperties niFiProperties;
+
+ @BeforeClass
+ public static void initOnce() throws IOException {
+ Security.addProvider(new BouncyCastleProvider());
+ tempConfDir = Files.createTempDirectory("conf");
+ mockBootstrapConf = Files.createTempFile("bootstrap", ".conf").toAbsolutePath();
+
+ mockNifiProperties = Files.createTempFile("nifi", ".properties").toAbsolutePath();
+
+ mockBootstrapConf = Files.move(mockBootstrapConf, tempConfDir.resolve("bootstrap.conf"));
+ mockNifiProperties = Files.move(mockNifiProperties, tempConfDir.resolve("nifi.properties"));
+
+ IOUtil.writeText("nifi.bootstrap.sensitive.key=" + BOOTSTRAP_KEY_HEX, mockBootstrapConf.toFile());
+ System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, mockNifiProperties.toString());
+
+ niFiProperties = new NiFiProperties();
+ }
+
+ @AfterClass
+ public static void tearDownOnce() throws IOException {
+ Files.deleteIfExists(mockBootstrapConf);
+ Files.deleteIfExists(mockNifiProperties);
+ Files.deleteIfExists(tempConfDir);
+ System.clearProperty(NiFiProperties.PROPERTIES_FILE_PATH);
+ }
+
+ /**
+ * Configures the factory using the default bootstrap location.
+ */
+ private void configureDefaultFactory() {
+ factory = StandardSensitivePropertyProviderFactory.withDefaults();
+ }
+
+ /**
+ * Configures the factory using an ad hoc key hex.
+ */
+ private void configureAdHocKeyFactory() {
+ factory = StandardSensitivePropertyProviderFactory.withKey(AD_HOC_KEY_HEX);
+ }
+
+ /**
+ * Configures the factory using an ad hoc key hex and bootstrap.conf properties. The key should override
+ * the on in the bootstrap.conf.
+ */
+ private void configureAdHocKeyAndPropertiesFactory() throws IOException {
+ factory = StandardSensitivePropertyProviderFactory.withKeyAndBootstrapSupplier(AD_HOC_KEY_HEX, mockBootstrapProperties());
+ }
+
+ private Supplier<BootstrapProperties> mockBootstrapProperties() throws IOException {
+ final Properties bootstrapProperties = new Properties();
+ try (final InputStream inputStream = Files.newInputStream(mockBootstrapConf)) {
+ bootstrapProperties.load(inputStream);
+ return () -> new BootstrapProperties("nifi", bootstrapProperties, mockBootstrapConf);
+ }
+ }
+
+ @Test
+ public void testAES_GCM() throws IOException {
+ configureDefaultFactory();
+
+ final SensitivePropertyProvider spp = factory.getProvider(PropertyProtectionScheme.AES_GCM);
+ assertNotNull(spp);
+ assertTrue(spp.isSupported());
+
+ final String cleartext = "test";
+ assertEquals(cleartext, spp.unprotect(spp.protect(cleartext)));
+ assertNotEquals(cleartext, spp.protect(cleartext));
+ assertEquals(AES_GCM_128, spp.getIdentifierKey());
+
+ // Key is now different
+ configureAdHocKeyFactory();
+ final SensitivePropertyProvider sppAdHocKey = factory.getProvider(PropertyProtectionScheme.AES_GCM);
+ assertNotNull(sppAdHocKey);
+ assertTrue(sppAdHocKey.isSupported());
+ assertEquals(AES_GCM_128, sppAdHocKey.getIdentifierKey());
+
+ assertNotEquals(spp.protect(cleartext), sppAdHocKey.protect(cleartext));
+ assertEquals(cleartext, sppAdHocKey.unprotect(sppAdHocKey.protect(cleartext)));
+
+ // This should use the same keyHex as the second one
+ configureAdHocKeyAndPropertiesFactory();
+ final SensitivePropertyProvider sppKeyProperties = factory.getProvider(PropertyProtectionScheme.AES_GCM);
+ assertNotNull(sppKeyProperties);
+ assertTrue(sppKeyProperties.isSupported());
+ assertEquals(AES_GCM_128, sppKeyProperties.getIdentifierKey());
+
+ assertEquals(cleartext, sppKeyProperties.unprotect(sppKeyProperties.protect(cleartext)));
+ }
+}
diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml
index 43bcb2f..368a67a 100644
--- a/nifi-commons/pom.xml
+++ b/nifi-commons/pom.xml
@@ -35,7 +35,9 @@
<module>nifi-metrics</module>
<module>nifi-parameter</module>
<module>nifi-property-encryptor</module>
+ <module>nifi-property-utils</module>
<module>nifi-properties</module>
+ <module>nifi-sensitive-property-provider</module>
<module>nifi-record</module>
<module>nifi-record-path</module>
<module>nifi-rocksdb-utils</module>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java
index 5006ca5..0056ef9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java
@@ -16,29 +16,6 @@
*/
package org.apache.nifi.authorization;
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import javax.xml.XMLConstants;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.annotation.AuthorizerContext;
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
@@ -48,11 +25,9 @@ import org.apache.nifi.authorization.generated.Authorizers;
import org.apache.nifi.authorization.generated.Property;
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.nar.ExtensionManager;
-import org.apache.nifi.properties.AESSensitivePropertyProviderFactory;
+import org.apache.nifi.properties.PropertyProtectionScheme;
import org.apache.nifi.properties.SensitivePropertyProtectionException;
-import org.apache.nifi.properties.SensitivePropertyProvider;
-import org.apache.nifi.properties.SensitivePropertyProviderFactory;
-import org.apache.nifi.security.kms.CryptoUtils;
+import org.apache.nifi.properties.SensitivePropertyProviderFactoryAware;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.classloader.ClassLoaderUtils;
@@ -62,18 +37,41 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.xml.sax.SAXException;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
/**
* Factory bean for loading the configured authorizer.
*/
-public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup {
+public class AuthorizerFactoryBean extends SensitivePropertyProviderFactoryAware
+ implements FactoryBean, DisposableBean, UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup {
private static final Logger logger = LoggerFactory.getLogger(AuthorizerFactoryBean.class);
private static final String AUTHORIZERS_XSD = "/authorizers.xsd";
private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authorization.generated";
private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
- private static SensitivePropertyProviderFactory SENSITIVE_PROPERTY_PROVIDER_FACTORY;
- private static SensitivePropertyProvider SENSITIVE_PROPERTY_PROVIDER;
+ private NiFiProperties properties;
/**
* Load the JAXBContext.
@@ -87,12 +85,15 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserG
}
private Authorizer authorizer;
- private NiFiProperties properties;
private ExtensionManager extensionManager;
private final Map<String, UserGroupProvider> userGroupProviders = new HashMap<>();
private final Map<String, AccessPolicyProvider> accessPolicyProviders = new HashMap<>();
private final Map<String, Authorizer> authorizers = new HashMap<>();
+ public void setProperties(final NiFiProperties properties) {
+ this.properties = properties;
+ }
+
@Override
public UserGroupProvider getUserGroupProvider(String identifier) {
return userGroupProviders.get(identifier);
@@ -480,26 +481,8 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserG
};
}
- private String decryptValue(String cipherText, String encryptionScheme) throws SensitivePropertyProtectionException {
- initializeSensitivePropertyProvider(encryptionScheme);
- return SENSITIVE_PROPERTY_PROVIDER.unprotect(cipherText);
- }
-
- private static void initializeSensitivePropertyProvider(String encryptionScheme) throws SensitivePropertyProtectionException {
- if (SENSITIVE_PROPERTY_PROVIDER == null || !SENSITIVE_PROPERTY_PROVIDER.getIdentifierKey().equalsIgnoreCase(encryptionScheme)) {
- try {
- String keyHex = getRootKey();
- SENSITIVE_PROPERTY_PROVIDER_FACTORY = new AESSensitivePropertyProviderFactory(keyHex);
- SENSITIVE_PROPERTY_PROVIDER = SENSITIVE_PROPERTY_PROVIDER_FACTORY.getProvider();
- } catch (IOException e) {
- logger.error("Error extracting root key from bootstrap.conf for login identity provider decryption", e);
- throw new SensitivePropertyProtectionException("Could not read root key from bootstrap.conf");
- }
- }
- }
-
- private static String getRootKey() throws IOException {
- return CryptoUtils.extractKeyFromBootstrapFile();
+ private String decryptValue(final String cipherText, final String protectionScheme) throws SensitivePropertyProtectionException {
+ return getSensitivePropertyProviderFactory().getProvider(PropertyProtectionScheme.fromIdentifier(protectionScheme)).unprotect(cipherText);
}
@Override
@@ -555,10 +538,6 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserG
}
}
- public void setProperties(NiFiProperties properties) {
- this.properties = properties;
- }
-
public void setExtensionManager(ExtensionManager extensionManager) {
this.extensionManager = extensionManager;
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/groovy/org/apache/nifi/authorization/AuthorizerFactoryBeanTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/groovy/org/apache/nifi/authorization/AuthorizerFactoryBeanTest.groovy
index dccc46c..9a548ba 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/groovy/org/apache/nifi/authorization/AuthorizerFactoryBeanTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/groovy/org/apache/nifi/authorization/AuthorizerFactoryBeanTest.groovy
@@ -17,10 +17,7 @@
package org.apache.nifi.authorization
import org.apache.nifi.authorization.generated.Property
-import org.apache.nifi.properties.AESSensitivePropertyProvider
import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.junit.After
-import org.junit.AfterClass
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
@@ -52,8 +49,10 @@ class AuthorizerFactoryBeanTest extends GroovyTestCase {
private static final String PASSWORD = "thisIsABadPassword"
+ private AuthorizerFactoryBean bean
+
@BeforeClass
- public static void setUpOnce() throws Exception {
+ static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
@@ -61,29 +60,16 @@ class AuthorizerFactoryBeanTest extends GroovyTestCase {
}
}
- @AfterClass
- public static void tearDownOnce() throws Exception {
- }
-
@Before
- public void setUp() throws Exception {
- AuthorizerFactoryBean.SENSITIVE_PROPERTY_PROVIDER = new AESSensitivePropertyProvider(KEY_HEX)
- }
-
- @After
- public void tearDown() throws Exception {
- AuthorizerFactoryBean.SENSITIVE_PROPERTY_PROVIDER = null
- AuthorizerFactoryBean.SENSITIVE_PROPERTY_PROVIDER_FACTORY = null
+ void setUp() throws Exception {
+ bean = new AuthorizerFactoryBean()
+ bean.configureSensitivePropertyProviderFactory(KEY_HEX, null)
}
private static boolean isUnlimitedStrengthCryptoAvailable() {
Cipher.getMaxAllowedKeyLength("AES") > 128
}
- private static int getKeyLength(String keyHex = KEY_HEX) {
- keyHex?.size() * 4
- }
-
@Test
void testShouldDecryptValue() {
// Arrange
@@ -91,7 +77,7 @@ class AuthorizerFactoryBeanTest extends GroovyTestCase {
logger.info("Cipher text: ${CIPHER_TEXT}")
// Act
- String decrypted = new AuthorizerFactoryBean().decryptValue(CIPHER_TEXT, ENCRYPTION_SCHEME)
+ String decrypted = bean.decryptValue(CIPHER_TEXT, ENCRYPTION_SCHEME)
logger.info("Decrypted ${CIPHER_TEXT} -> ${decrypted}")
// Assert
@@ -107,7 +93,6 @@ class AuthorizerFactoryBeanTest extends GroovyTestCase {
List<Property> properties = [managerPasswordProperty]
logger.info("Manager Password property: ${managerPasswordProperty.dump()}")
- def bean = new AuthorizerFactoryBean()
// Act
def context = bean.loadAuthorizerConfiguration(identifier, properties)
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedRepositoryRecordSerdeFactoryTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedRepositoryRecordSerdeFactoryTest.groovy
index f12a695..fe690df 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedRepositoryRecordSerdeFactoryTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedRepositoryRecordSerdeFactoryTest.groovy
@@ -20,7 +20,6 @@ package org.apache.nifi.controller.repository
import org.apache.commons.lang3.SystemUtils
import org.apache.nifi.controller.repository.claim.ResourceClaimManager
import org.apache.nifi.controller.repository.claim.StandardResourceClaimManager
-import org.apache.nifi.properties.StandardNiFiProperties
import org.apache.nifi.security.kms.EncryptionException
import org.apache.nifi.util.NiFiProperties
import org.bouncycastle.jce.provider.BouncyCastleProvider
@@ -74,7 +73,7 @@ class EncryptedRepositoryRecordSerdeFactoryTest extends GroovyTestCase {
(NiFiProperties.FLOWFILE_REPOSITORY_ENCRYPTION_KEY) : KEY_1_HEX,
(NiFiProperties.FLOWFILE_REPOSITORY_ENCRYPTION_KEY_ID) : KEY_ID
]
- mockNiFiProperties = new StandardNiFiProperties(new Properties(flowfileEncryptionProps))
+ mockNiFiProperties = new NiFiProperties(new Properties(flowfileEncryptionProps))
}
@After
@@ -124,7 +123,7 @@ class EncryptedRepositoryRecordSerdeFactoryTest extends GroovyTestCase {
@Test
void testCreateSerDeShouldFailWithUnpopulatedNiFiProperties() {
// Arrange
- NiFiProperties emptyNiFiProperties = new StandardNiFiProperties(new Properties([:]))
+ NiFiProperties emptyNiFiProperties = new NiFiProperties(new Properties([:]))
// Act
def msg = shouldFail(EncryptionException) {
@@ -144,7 +143,7 @@ class EncryptedRepositoryRecordSerdeFactoryTest extends GroovyTestCase {
(NiFiProperties.FLOWFILE_REPOSITORY_ENCRYPTION_KEY) : KEY_1_HEX,
(NiFiProperties.FLOWFILE_REPOSITORY_ENCRYPTION_KEY_ID) : KEY_ID
]
- NiFiProperties invalidNiFiProperties = new StandardNiFiProperties(new Properties(invalidFlowfileEncryptionProps))
+ NiFiProperties invalidNiFiProperties = new NiFiProperties(new Properties(invalidFlowfileEncryptionProps))
// Act
def msg = shouldFail(EncryptionException) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/flow/PopularVoteFlowElectionFactoryBeanTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/flow/PopularVoteFlowElectionFactoryBeanTest.groovy
index 4e7f7f9..cb3dac8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/flow/PopularVoteFlowElectionFactoryBeanTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/flow/PopularVoteFlowElectionFactoryBeanTest.groovy
@@ -18,7 +18,7 @@ package org.apache.nifi.cluster.coordination.flow
import org.apache.nifi.encrypt.PropertyEncryptor
import org.apache.nifi.encrypt.PropertyEncryptorFactory
-import org.apache.nifi.properties.StandardNiFiProperties
+
import org.apache.nifi.security.util.EncryptionMethod
import org.apache.nifi.util.NiFiProperties
import org.junit.Before
@@ -51,7 +51,7 @@ class PopularVoteFlowElectionFactoryBeanTest extends GroovyTestCase {
}
NiFiProperties mockProperties(Map<String, String> defaults = [:]) {
- def mockProps = new StandardNiFiProperties(new Properties([
+ def mockProps = new NiFiProperties(new Properties([
(NiFiProperties.SENSITIVE_PROPS_ALGORITHM):DEFAULT_ENCRYPTION_METHOD.algorithm,
(NiFiProperties.SENSITIVE_PROPS_PROVIDER):DEFAULT_ENCRYPTION_METHOD.provider,
] + defaults))
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy
index d288aed..248c1a1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy
@@ -18,7 +18,7 @@
package org.apache.nifi.cluster.coordination.http.replication.okhttp
-import org.apache.nifi.properties.StandardNiFiProperties
+
import org.apache.nifi.util.NiFiProperties
import org.junit.BeforeClass
import org.junit.Test
@@ -38,13 +38,13 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
}
}
- private static StandardNiFiProperties mockNiFiProperties() {
+ private static NiFiProperties mockNiFiProperties() {
[getClusterNodeConnectionTimeout: { -> "10 ms" },
getClusterNodeReadTimeout : { -> "10 ms" },
getProperty : { String prop ->
logger.mock("Requested getProperty(${prop}) -> \"\"")
""
- }] as StandardNiFiProperties
+ }] as NiFiProperties
}
@Test
@@ -150,7 +150,7 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
]
- NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(propsMap))
+ NiFiProperties mockNiFiProperties = new NiFiProperties(new Properties(propsMap))
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
@@ -173,7 +173,7 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
]
- NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(flowfileEncryptionProps))
+ NiFiProperties mockNiFiProperties = new NiFiProperties(new Properties(flowfileEncryptionProps))
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
@@ -197,7 +197,7 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
]
- NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(propsMap))
+ NiFiProperties mockNiFiProperties = new NiFiProperties(new Properties(propsMap))
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
@@ -221,7 +221,7 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
]
- NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(propsMap))
+ NiFiProperties mockNiFiProperties = new NiFiProperties(new Properties(propsMap))
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
@@ -248,13 +248,13 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
] + propsMap
- NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(propsMap))
- NiFiProperties mockTLSNiFiProperties = new StandardNiFiProperties(new Properties(tlsPropsMap))
+ NiFiProperties mockNiFiProperties = new NiFiProperties(new Properties(propsMap))
+ NiFiProperties mockTLSNiFiProperties = new NiFiProperties(new Properties(tlsPropsMap))
// Remove the keystore password to create an invalid configuration
Map invalidTlsPropsMap = tlsPropsMap
invalidTlsPropsMap.remove(NiFiProperties.SECURITY_KEYSTORE_PASSWD)
- NiFiProperties mockInvalidTLSNiFiProperties = new StandardNiFiProperties(new Properties(invalidTlsPropsMap))
+ NiFiProperties mockInvalidTLSNiFiProperties = new NiFiProperties(new Properties(invalidTlsPropsMap))
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/cluster/ZooKeeperClientConfigTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/cluster/ZooKeeperClientConfigTest.java
index ef832c5..f54c10e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/cluster/ZooKeeperClientConfigTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/cluster/ZooKeeperClientConfigTest.java
@@ -17,18 +17,16 @@
package org.apache.nifi.cluster;
import org.apache.nifi.controller.cluster.ZooKeeperClientConfig;
-import org.apache.nifi.properties.StandardNiFiProperties;
import org.apache.nifi.util.NiFiProperties;
+import org.junit.Test;
+
+import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import org.junit.Test;
-
-import java.util.Properties;
-
public class ZooKeeperClientConfigTest {
private static final String LOCAL_CONNECT_STRING = "local:1234";
@@ -95,7 +93,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING, LOCAL_CONNECT_STRING);
properties.setProperty(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, Boolean.TRUE.toString());
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertTrue(zkClientConfig.isClientSecure());
assertEquals(zkClientConfig.getConnectionSocket(), ZooKeeperClientConfig.NETTY_CLIENT_CNXN_SOCKET);
}
@@ -106,7 +104,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING, LOCAL_CONNECT_STRING);
properties.setProperty(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, Boolean.FALSE.toString());
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertFalse(zkClientConfig.isClientSecure());
assertEquals(zkClientConfig.getConnectionSocket(), ZooKeeperClientConfig.NIO_CLIENT_CNXN_SOCKET);
}
@@ -117,7 +115,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING, LOCAL_CONNECT_STRING);
properties.setProperty(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "");
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertFalse(zkClientConfig.isClientSecure());
assertEquals(zkClientConfig.getConnectionSocket(), ZooKeeperClientConfig.NIO_CLIENT_CNXN_SOCKET);
}
@@ -128,7 +126,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING, LOCAL_CONNECT_STRING);
properties.setProperty(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, " true ");
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertTrue(zkClientConfig.isClientSecure());
assertEquals(zkClientConfig.getConnectionSocket(), ZooKeeperClientConfig.NETTY_CLIENT_CNXN_SOCKET);
}
@@ -138,9 +136,9 @@ public class ZooKeeperClientConfigTest {
final Properties properties = new Properties();
properties.setProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING, LOCAL_CONNECT_STRING);
properties.setProperty(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, Boolean.TRUE.toString());
- ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertTrue(zkClientConfig.isClientSecure());
assertEquals(zkClientConfig.getConnectionSocket(), ZooKeeperClientConfig.NETTY_CLIENT_CNXN_SOCKET);
}
@@ -150,7 +148,7 @@ public class ZooKeeperClientConfigTest {
final Properties properties = new Properties();
properties.setProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING, LOCAL_CONNECT_STRING);
properties.setProperty(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "meh");
- ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
}
@Test
@@ -161,7 +159,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, storeType);
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, storeType);
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertEquals(storeType, zkClientConfig.getKeyStoreType());
assertEquals(storeType, zkClientConfig.getTrustStoreType());
}
@@ -174,7 +172,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, storeType);
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, storeType);
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
final String expectedStoreType = "JKS";
assertEquals(expectedStoreType, zkClientConfig.getKeyStoreType());
assertEquals(expectedStoreType, zkClientConfig.getTrustStoreType());
@@ -187,7 +185,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, "");
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, "");
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertNull(zkClientConfig.getKeyStoreType());
assertNull(zkClientConfig.getTrustStoreType());
}
@@ -199,7 +197,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, " ");
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, " ");
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertNull(zkClientConfig.getKeyStoreType());
assertNull(zkClientConfig.getTrustStoreType());
}
@@ -219,7 +217,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE, ZOOKEEPER_TRUSTSTORE);
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, ZOOKEEPER_STORE_TYPE);
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertEquals(ZOOKEEPER_KEYSTORE, zkClientConfig.getKeyStore());
assertEquals(ZOOKEEPER_TRUSTSTORE, zkClientConfig.getTrustStore());
}
@@ -233,7 +231,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, DEFAULT_TRUSTSTORE);
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, DEFAULT_STORE_TYPE);
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertEquals(DEFAULT_KEYSTORE, zkClientConfig.getKeyStore());
assertEquals(DEFAULT_TRUSTSTORE, zkClientConfig.getTrustStore());
}
@@ -251,7 +249,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, DEFAULT_TRUSTSTORE);
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, DEFAULT_STORE_TYPE);
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertEquals(ZOOKEEPER_KEYSTORE, zkClientConfig.getKeyStore());
assertEquals(ZOOKEEPER_TRUSTSTORE, zkClientConfig.getTrustStore());
assertEquals(ZOOKEEPER_STORE_TYPE, zkClientConfig.getKeyStoreType());
@@ -271,7 +269,7 @@ public class ZooKeeperClientConfigTest {
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, DEFAULT_TRUSTSTORE);
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, DEFAULT_STORE_TYPE);
- final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new StandardNiFiProperties(properties));
+ final ZooKeeperClientConfig zkClientConfig = ZooKeeperClientConfig.createConfig(new NiFiProperties(properties));
assertEquals(DEFAULT_KEYSTORE, zkClientConfig.getKeyStore());
assertEquals(DEFAULT_TRUSTSTORE, zkClientConfig.getTrustStore());
assertEquals(DEFAULT_STORE_TYPE, zkClientConfig.getKeyStoreType());
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/state/server/TestZooKeeperStateServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/state/server/TestZooKeeperStateServer.java
index df0a08e..ff12c7e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/state/server/TestZooKeeperStateServer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/state/server/TestZooKeeperStateServer.java
@@ -16,19 +16,15 @@
*/
package org.apache.nifi.controller.state.server;
-import org.apache.curator.retry.RetryOneTime;
-import org.apache.curator.test.InstanceSpec;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
-
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.InstanceSpec;
import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.properties.StandardNiFiProperties;
-
import org.apache.zookeeper.client.FourLetterWordMain;
import org.apache.zookeeper.common.X509Exception.SSLContextException;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
-
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -73,7 +69,7 @@ public class TestZooKeeperStateServer {
properties.setProperty(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, zkServerConfig.toString());
properties.setProperty(NiFiProperties.STATE_MANAGEMENT_START_EMBEDDED_ZOOKEEPER, Boolean.TRUE.toString());
- zkServer = ZooKeeperStateServer.create(new StandardNiFiProperties(properties));
+ zkServer = ZooKeeperStateServer.create(new NiFiProperties(properties));
if (zkServer != null) zkServer.start();
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/leader/election/ITSecureClientZooKeeperFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/leader/election/ITSecureClientZooKeeperFactory.java
index 9e0a1ae..b0a4f22 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/leader/election/ITSecureClientZooKeeperFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/leader/election/ITSecureClientZooKeeperFactory.java
@@ -16,22 +16,18 @@
*/
package org.apache.nifi.leader.election;
-import org.apache.curator.retry.RetryOneTime;
-import org.apache.curator.test.InstanceSpec;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
-
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.properties.StandardNiFiProperties;
-import org.apache.nifi.controller.cluster.ZooKeeperClientConfig;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.InstanceSpec;
import org.apache.nifi.controller.cluster.SecureClientZooKeeperFactory;
+import org.apache.nifi.controller.cluster.ZooKeeperClientConfig;
import org.apache.nifi.security.util.CertificateUtils;
-
+import org.apache.nifi.util.NiFiProperties;
import org.apache.zookeeper.common.ClientX509Util;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.server.ServerCnxnFactory;
import org.apache.zookeeper.server.ZooKeeperServer;
-
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -42,7 +38,6 @@ import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.cert.CertificateException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -50,6 +45,7 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
@@ -218,7 +214,7 @@ public class ITSecureClientZooKeeperFactory {
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, trustStoreType);
properties.setProperty(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_PASSWD, trustStorePassword);
- return new StandardNiFiProperties(properties);
+ return new NiFiProperties(properties);
}
public static X509Certificate createKeyStore(final String alias,
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/pom.xml
index f49c66d..ad5058d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/pom.xml
@@ -46,6 +46,10 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-sensitive-property-provider</artifactId>
+ </dependency>
</dependencies>
<build>
<!-- Required to run Groovy tests without any Java tests -->
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProviderFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProviderFactory.java
deleted file mode 100644
index 56a1cc0..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/AESSensitivePropertyProviderFactory.java
+++ /dev/null
@@ -1,53 +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.properties;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import javax.crypto.NoSuchPaddingException;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AESSensitivePropertyProviderFactory implements SensitivePropertyProviderFactory {
- private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProviderFactory.class);
-
- private String keyHex;
-
- public AESSensitivePropertyProviderFactory(String keyHex) {
- this.keyHex = keyHex;
- }
-
- public SensitivePropertyProvider getProvider() throws SensitivePropertyProtectionException {
- try {
- if (keyHex != null && !StringUtils.isBlank(keyHex)) {
- return new AESSensitivePropertyProvider(keyHex);
- } else {
- throw new SensitivePropertyProtectionException("The provider factory cannot generate providers without a key");
- }
- } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
- String msg = "Error creating AES Sensitive Property Provider";
- logger.warn(msg, e);
- throw new SensitivePropertyProtectionException(msg, e);
- }
- }
-
- @Override
- public String toString() {
- return "SensitivePropertyProviderFactory for creating AESSensitivePropertyProviders";
- }
-}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/NiFiPropertiesLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/NiFiPropertiesLoader.java
index 250227f..480f801 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/NiFiPropertiesLoader.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/NiFiPropertiesLoader.java
@@ -16,6 +16,12 @@
*/
package org.apache.nifi.properties;
+import org.apache.nifi.util.NiFiBootstrapUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -25,20 +31,13 @@ import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;
import java.util.List;
import java.util.Properties;
-import java.util.stream.Collectors;
import java.util.Set;
-import javax.crypto.Cipher;
-import org.apache.nifi.security.kms.CryptoUtils;
-import org.apache.nifi.util.NiFiProperties;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.stream.Collectors;
public class NiFiPropertiesLoader {
@@ -49,12 +48,12 @@ public class NiFiPropertiesLoader {
private static final String MIGRATION_INSTRUCTIONS = "See Admin Guide section [Updating the Sensitive Properties Key]";
private static final String PROPERTIES_KEY_MESSAGE = String.format("Sensitive Properties Key [%s] not found: %s", NiFiProperties.SENSITIVE_PROPS_KEY, MIGRATION_INSTRUCTIONS);
- private final String defaultPropertiesFilePath = CryptoUtils.getDefaultFilePath();
+ private final String defaultPropertiesFilePath = NiFiBootstrapUtils.getDefaultApplicationPropertiesFilePath();
private NiFiProperties instance;
private String keyHex;
// Future enhancement: allow for external registration of new providers
- private static SensitivePropertyProviderFactory sensitivePropertyProviderFactory;
+ private SensitivePropertyProviderFactory sensitivePropertyProviderFactory;
public NiFiPropertiesLoader() {
}
@@ -69,20 +68,20 @@ public class NiFiPropertiesLoader {
* @param keyHex the key used to encrypt any sensitive properties
* @return the configured loader
*/
- public static NiFiPropertiesLoader withKey(String keyHex) {
- NiFiPropertiesLoader loader = new NiFiPropertiesLoader();
+ public static NiFiPropertiesLoader withKey(final String keyHex) {
+ final NiFiPropertiesLoader loader = new NiFiPropertiesLoader();
loader.setKeyHex(keyHex);
return loader;
}
/**
- * Sets the hexadecimal key used to unprotect properties encrypted with
- * {@link AESSensitivePropertyProvider}. If the key has already been set,
+ * Sets the hexadecimal key used to unprotect properties encrypted with a
+ * {@link SensitivePropertyProvider}. If the key has already been set,
* calling this method will throw a {@link RuntimeException}.
*
* @param keyHex the key in hexadecimal format
*/
- public void setKeyHex(String keyHex) {
+ public void setKeyHex(final String keyHex) {
if (this.keyHex == null || this.keyHex.trim().isEmpty()) {
this.keyHex = keyHex;
} else {
@@ -101,68 +100,25 @@ public class NiFiPropertiesLoader {
* or nifi.properties files
*/
public static NiFiProperties loadDefaultWithKeyFromBootstrap() throws IOException {
- // The nifi.properties file may not be encrypted, so attempt to naively load it first
try {
+ // The default behavior of StandardSensitivePropertiesFactory is to use the key
+ // from bootstrap.conf if no key is provided
return new NiFiPropertiesLoader().loadDefault();
} catch (Exception e) {
logger.warn("Encountered an error naively loading the nifi.properties file because one or more properties are protected: {}", e.getLocalizedMessage());
- }
-
- try {
- String keyHex = CryptoUtils.extractKeyFromBootstrapFile();
- return NiFiPropertiesLoader.withKey(keyHex).loadDefault();
- } catch (IOException e) {
- logger.error("Encountered an exception loading the default nifi.properties file {} with the key provided in bootstrap.conf", CryptoUtils.getDefaultFilePath(), e);
throw e;
}
}
- /**
- * Returns the key (if any) used to encrypt sensitive properties, extracted from {@code $NIFI_HOME/conf/bootstrap.conf}.
- *
- * @return the key in hexadecimal format
- * @throws IOException if the file is not readable
- * @deprecated Use {@link CryptoUtils#extractKeyFromBootstrapFile()} instead.
- */
- @Deprecated
- public static String extractKeyFromBootstrapFile() throws IOException {
- // TODO: Replace all existing uses with direct reference to CryptoUtils
- return extractKeyFromBootstrapFile("");
- }
-
- /**
- * Returns the key (if any) used to encrypt sensitive properties, extracted from {@code $NIFI_HOME/conf/bootstrap.conf}.
- *
- * @param bootstrapPath the path to the bootstrap file
- * @return the key in hexadecimal format
- * @throws IOException if the file is not readable
- * @deprecated Use {@link CryptoUtils#extractKeyFromBootstrapFile(String)} instead.
- */
- @Deprecated
- public static String extractKeyFromBootstrapFile(String bootstrapPath) throws IOException {
- // TODO: Replace all existing uses with direct reference to CryptoUtils
- return CryptoUtils.extractKeyFromBootstrapFile(bootstrapPath);
- }
-
private NiFiProperties loadDefault() {
return load(defaultPropertiesFilePath);
}
- static String getDefaultProviderKey() {
- try {
- return "aes/gcm/" + (Cipher.getMaxAllowedKeyLength("AES") > 128 ? "256" : "128");
- } catch (NoSuchAlgorithmException e) {
- return "aes/gcm/128";
+ private SensitivePropertyProviderFactory getSensitivePropertyProviderFactory() {
+ if (sensitivePropertyProviderFactory == null) {
+ sensitivePropertyProviderFactory = StandardSensitivePropertyProviderFactory.withKey(keyHex);
}
- }
-
- private void initializeSensitivePropertyProviderFactory() {
- sensitivePropertyProviderFactory = new AESSensitivePropertyProviderFactory(keyHex);
- }
-
- private SensitivePropertyProvider getSensitivePropertyProvider() {
- initializeSensitivePropertyProviderFactory();
- return sensitivePropertyProviderFactory.getProvider();
+ return sensitivePropertyProviderFactory;
}
/**
@@ -209,11 +165,13 @@ public class NiFiPropertiesLoader {
* @param file the File containing the serialized properties
* @return the NiFiProperties instance
*/
- public NiFiProperties load(File file) {
- ProtectedNiFiProperties protectedNiFiProperties = readProtectedPropertiesFromDisk(file);
+ public NiFiProperties load(final File file) {
+ final ProtectedNiFiProperties protectedNiFiProperties = readProtectedPropertiesFromDisk(file);
if (protectedNiFiProperties.hasProtectedKeys()) {
Security.addProvider(new BouncyCastleProvider());
- protectedNiFiProperties.addSensitivePropertyProvider(getSensitivePropertyProvider());
+ getSensitivePropertyProviderFactory()
+ .getSupportedSensitivePropertyProviders()
+ .forEach(protectedNiFiProperties::addSensitivePropertyProvider);
}
return protectedNiFiProperties.getUnprotectedProperties();
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java
index 16fb463..368ff03 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java
@@ -16,35 +16,33 @@
*/
package org.apache.nifi.properties;
-import static java.util.Arrays.asList;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import java.util.stream.Collectors;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.util.NiFiProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import static java.util.Arrays.asList;
/**
* Decorator class for intermediate phase when {@link NiFiPropertiesLoader} loads the
* raw properties file and performs unprotection activities before returning a clean
- * implementation of {@link NiFiProperties}, likely {@link StandardNiFiProperties}.
+ * implementation of {@link NiFiProperties}.
* This encapsulates the sensitive property access logic from external consumers
* of {@code NiFiProperties}.
*/
-class ProtectedNiFiProperties extends StandardNiFiProperties {
+class ProtectedNiFiProperties extends NiFiProperties implements ProtectedProperties<NiFiProperties>,
+ SensitivePropertyProtector<ProtectedNiFiProperties, NiFiProperties> {
private static final Logger logger = LoggerFactory.getLogger(ProtectedNiFiProperties.class);
- private NiFiProperties niFiProperties;
+ private SensitivePropertyProtector<ProtectedNiFiProperties, NiFiProperties> propertyProtectionDelegate;
- private Map<String, SensitivePropertyProvider> localProviderCache = new HashMap<>();
+ private NiFiProperties applicationProperties;
// Additional "sensitive" property key
public static final String ADDITIONAL_SENSITIVE_PROPERTIES_KEY = "nifi.sensitive.props.additional.keys";
@@ -54,7 +52,7 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
SECURITY_KEYSTORE_PASSWD, SECURITY_TRUSTSTORE_PASSWD, SENSITIVE_PROPS_KEY, PROVENANCE_REPO_ENCRYPTION_KEY));
public ProtectedNiFiProperties() {
- this(new StandardNiFiProperties());
+ this(new NiFiProperties());
}
/**
@@ -62,9 +60,11 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
*
* @param props the NiFiProperties to contain
*/
- public ProtectedNiFiProperties(NiFiProperties props) {
- this.niFiProperties = props;
- logger.debug("Loaded {} properties (including {} protection schemes) into ProtectedNiFiProperties", getPropertyKeysIncludingProtectionSchemes().size(), getProtectedPropertyKeys().size());
+ public ProtectedNiFiProperties(final NiFiProperties props) {
+ this.applicationProperties = props;
+ this.propertyProtectionDelegate = new ApplicationPropertiesProtector<>(this);
+ logger.debug("Loaded {} properties (including {} protection schemes) into ProtectedNiFiProperties", getApplicationProperties()
+ .getPropertyKeys().size(), getProtectedPropertyKeys().size());
}
/**
@@ -72,8 +72,44 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
*
* @param rawProps the Properties to contain
*/
- public ProtectedNiFiProperties(Properties rawProps) {
- this(new StandardNiFiProperties(rawProps));
+ public ProtectedNiFiProperties(final Properties rawProps) {
+ this(new NiFiProperties(rawProps));
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeys() {
+ return getProperty(getAdditionalSensitivePropertiesKeysName());
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeysName() {
+ return ADDITIONAL_SENSITIVE_PROPERTIES_KEY;
+ }
+
+ @Override
+ public List<String> getDefaultSensitiveProperties() {
+ return DEFAULT_SENSITIVE_PROPERTIES;
+ }
+
+ /**
+ * Returns the internal representation of the {@link NiFiProperties} -- protected
+ * or not as determined by the current state. No guarantee is made to the
+ * protection state of these properties. If the internal reference is null, a new
+ * {@link NiFiProperties} instance is created.
+ *
+ * @return the internal properties
+ */
+ public NiFiProperties getApplicationProperties() {
+ if (this.applicationProperties == null) {
+ this.applicationProperties = new NiFiProperties();
+ }
+
+ return this.applicationProperties;
+ }
+
+ @Override
+ public NiFiProperties createApplicationProperties(final Properties rawProperties) {
+ return new NiFiProperties(rawProperties);
}
/**
@@ -84,7 +120,7 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
*/
@Override
public String getProperty(String key) {
- return getInternalNiFiProperties().getProperty(key);
+ return getApplicationProperties().getProperty(key);
}
/**
@@ -94,25 +130,7 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
*/
@Override
public Set<String> getPropertyKeys() {
- Set<String> filteredKeys = getPropertyKeysIncludingProtectionSchemes();
- filteredKeys.removeIf(p -> p.endsWith(".protected"));
- return filteredKeys;
- }
-
- /**
- * Returns the internal representation of the {@link NiFiProperties} -- protected
- * or not as determined by the current state. No guarantee is made to the
- * protection state of these properties. If the internal reference is null, a new
- * {@link StandardNiFiProperties} instance is created.
- *
- * @return the internal properties
- */
- NiFiProperties getInternalNiFiProperties() {
- if (this.niFiProperties == null) {
- this.niFiProperties = new StandardNiFiProperties();
- }
-
- return this.niFiProperties;
+ return propertyProtectionDelegate.getPropertyKeys();
}
/**
@@ -130,317 +148,62 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
*/
@Override
public int size() {
- return getPropertyKeys().size();
+ return propertyProtectionDelegate.size();
}
- /**
- * Returns the complete set of property keys, including any protection keys (i.e. 'x.y.z.protected').
- *
- * @return the set of property keys
- */
- Set<String> getPropertyKeysIncludingProtectionSchemes() {
- return getInternalNiFiProperties().getPropertyKeys();
- }
-
- /**
- * Splits a single string containing multiple property keys into a List. Delimited by ',' or ';' and ignores leading and trailing whitespace around delimiter.
- *
- * @param multipleProperties a single String containing multiple properties, i.e. "nifi.property.1; nifi.property.2, nifi.property.3"
- * @return a List containing the split and trimmed properties
- */
- private static List<String> splitMultipleProperties(String multipleProperties) {
- if (multipleProperties == null || multipleProperties.trim().isEmpty()) {
- return new ArrayList<>(0);
- } else {
- List<String> properties = new ArrayList<>(asList(multipleProperties.split("\\s*[,;]\\s*")));
- for (int i = 0; i < properties.size(); i++) {
- properties.set(i, properties.get(i).trim());
- }
- return properties;
- }
+ @Override
+ public Set<String> getPropertyKeysIncludingProtectionSchemes() {
+ return propertyProtectionDelegate.getPropertyKeysIncludingProtectionSchemes();
}
- /**
- * Returns a list of the keys identifying "sensitive" properties. There is a default list,
- * and additional keys can be provided in the {@code nifi.sensitive.props.additional.keys} property in {@code nifi.properties}.
- *
- * @return the list of sensitive property keys
- */
+ @Override
public List<String> getSensitivePropertyKeys() {
- String additionalPropertiesString = getProperty(ADDITIONAL_SENSITIVE_PROPERTIES_KEY);
- if (additionalPropertiesString == null || additionalPropertiesString.trim().isEmpty()) {
- return DEFAULT_SENSITIVE_PROPERTIES;
- } else {
- List<String> additionalProperties = splitMultipleProperties(additionalPropertiesString);
- /* Remove this key if it was accidentally provided as a sensitive key
- * because we cannot protect it and read from it
- */
- if (additionalProperties.contains(ADDITIONAL_SENSITIVE_PROPERTIES_KEY)) {
- logger.warn("The key '{}' contains itself. This is poor practice and should be removed", ADDITIONAL_SENSITIVE_PROPERTIES_KEY);
- additionalProperties.remove(ADDITIONAL_SENSITIVE_PROPERTIES_KEY);
- }
- additionalProperties.addAll(DEFAULT_SENSITIVE_PROPERTIES);
- return additionalProperties;
- }
+ return propertyProtectionDelegate.getSensitivePropertyKeys();
}
- /**
- * Returns a list of the keys identifying "sensitive" properties. There is a default list,
- * and additional keys can be provided in the {@code nifi.sensitive.props.additional.keys} property in {@code nifi.properties}.
- *
- * @return the list of sensitive property keys
- */
+ @Override
public List<String> getPopulatedSensitivePropertyKeys() {
- List<String> allSensitiveKeys = getSensitivePropertyKeys();
- return allSensitiveKeys.stream().filter(k -> StringUtils.isNotBlank(getProperty(k))).collect(Collectors.toList());
+ return propertyProtectionDelegate.getPopulatedSensitivePropertyKeys();
}
- /**
- * Returns true if any sensitive keys are protected.
- *
- * @return true if any key is protected; false otherwise
- */
+ @Override
public boolean hasProtectedKeys() {
- List<String> sensitiveKeys = getSensitivePropertyKeys();
- for (String k : sensitiveKeys) {
- if (isPropertyProtected(k)) {
- return true;
- }
- }
- return false;
+ return propertyProtectionDelegate.hasProtectedKeys();
}
- /**
- * Returns a Map of the keys identifying "sensitive" properties that are currently protected and the "protection" key for each. This may or may not include all properties marked as sensitive.
- *
- * @return the Map of protected property keys and the protection identifier for each
- */
+ @Override
public Map<String, String> getProtectedPropertyKeys() {
- List<String> sensitiveKeys = getSensitivePropertyKeys();
-
- // This is the Java 8 way, but can likely be optimized (and not sure of correctness)
- // Map<String, String> protectedProperties = sensitiveKeys.stream().filter(key ->
- // getProperty(getProtectionKey(key)) != null).collect(Collectors.toMap(Function.identity(), key ->
- // getProperty(getProtectionKey(key))));
-
- // Groovy
- // Map<String, String> groovyProtectedProperties = sensitiveKeys.collectEntries { key ->
- // [(key): getProperty(getProtectionKey(key))] }.findAll { k, v -> v }
-
- // Traditional way
- Map<String, String> traditionalProtectedProperties = new HashMap<>();
- for (String key : sensitiveKeys) {
- String protection = getProperty(getProtectionKey(key));
- if (StringUtils.isNotBlank(protection) && StringUtils.isNotBlank(getProperty(key))) {
- traditionalProtectedProperties.put(key, protection);
- }
- }
-
- return traditionalProtectedProperties;
+ return propertyProtectionDelegate.getProtectedPropertyKeys();
}
- /**
- * Returns the unique set of all protection schemes currently in use for this instance.
- *
- * @return the set of protection schemes
- */
+ @Override
public Set<String> getProtectionSchemes() {
- return new HashSet<>(getProtectedPropertyKeys().values());
- }
-
- /**
- * Returns a percentage of the total number of populated properties marked as sensitive that are currently protected.
- *
- * @return the percent of sensitive properties marked as protected
- */
- public int getPercentOfSensitivePropertiesProtected() {
- return (int) Math.round(getProtectedPropertyKeys().size() / ((double) getPopulatedSensitivePropertyKeys().size()) * 100);
- }
-
- /**
- * Returns true if the property identified by this key is considered sensitive in this instance of {@code NiFiProperties}.
- * Some properties are sensitive by default, while others can be specified by
- * {@link ProtectedNiFiProperties#ADDITIONAL_SENSITIVE_PROPERTIES_KEY}.
- *
- * @param key the key
- * @return true if it is sensitive
- * @see ProtectedNiFiProperties#getSensitivePropertyKeys()
- */
- public boolean isPropertySensitive(String key) {
- // If the explicit check for ADDITIONAL_SENSITIVE_PROPERTIES_KEY is not here, this will loop infinitely
- return key != null && !key.equals(ADDITIONAL_SENSITIVE_PROPERTIES_KEY) && getSensitivePropertyKeys().contains(key.trim());
+ return propertyProtectionDelegate.getProtectionSchemes();
}
- /**
- * Returns true if the property identified by this key is considered protected in this instance of {@code NiFiProperties}.
- * The property value is protected if the key is sensitive and the sibling key of key.protected is present.
- *
- * @param key the key
- * @return true if it is currently marked as protected
- * @see ProtectedNiFiProperties#getSensitivePropertyKeys()
- */
- public boolean isPropertyProtected(String key) {
- return key != null && isPropertySensitive(key) && !StringUtils.isBlank(getProperty(getProtectionKey(key)));
+ @Override
+ public boolean isPropertySensitive(final String key) {
+ return propertyProtectionDelegate.isPropertySensitive(key);
}
- /**
- * Returns the sibling property key which specifies the protection scheme for this key.
- * <p>
- * Example:
- * <p>
- * nifi.sensitive.key=ABCXYZ
- * nifi.sensitive.key.protected=aes/gcm/256
- * <p>
- * nifi.sensitive.key -> nifi.sensitive.key.protected
- *
- * @param key the key identifying the sensitive property
- * @return the key identifying the protection scheme for the sensitive property
- */
- public static String getProtectionKey(String key) {
- if (key == null || key.isEmpty()) {
- throw new IllegalArgumentException("Cannot find protection key for null key");
- }
-
- return key + ".protected";
+ @Override
+ public boolean isPropertyProtected(final String key) {
+ return propertyProtectionDelegate.isPropertyProtected(key);
}
- /**
- * Returns the unprotected {@link NiFiProperties} instance. If none of the properties
- * loaded are marked as protected, it will simply pass through the internal instance.
- * If any are protected, it will drop the protection scheme keys and translate each
- * protected value (encrypted, HSM-retrieved, etc.) into the raw value and store it
- * under the original key.
- * <p>
- * If any property fails to unprotect, it will save that key and continue. After
- * attempting all properties, it will throw an exception containing all failed
- * properties. This is necessary because the order is not enforced, so all failed
- * properties should be gathered together.
- *
- * @return the NiFiProperties instance with all raw values
- * @throws SensitivePropertyProtectionException if there is a problem unprotecting one or more keys
- */
+ @Override
public NiFiProperties getUnprotectedProperties() throws SensitivePropertyProtectionException {
- if (hasProtectedKeys()) {
- logger.info("There are {} protected properties of {} sensitive properties ({}%)",
- getProtectedPropertyKeys().size(),
- getSensitivePropertyKeys().size(),
- getPercentOfSensitivePropertiesProtected());
-
- Properties rawProperties = new Properties();
-
- Set<String> failedKeys = new HashSet<>();
-
- for (String key : getPropertyKeys()) {
- /* Three kinds of keys
- * 1. protection schemes -- skip
- * 2. protected keys -- unprotect and copy
- * 3. normal keys -- copy over
- */
- if (key.endsWith(".protected")) {
- // Do nothing
- } else if (isPropertyProtected(key)) {
- try {
- rawProperties.setProperty(key, unprotectValue(key, getProperty(key)));
- } catch (SensitivePropertyProtectionException e) {
- logger.warn("Failed to unprotect '{}'", key, e);
- failedKeys.add(key);
- }
- } else {
- rawProperties.setProperty(key, getProperty(key));
- }
- }
-
- if (!failedKeys.isEmpty()) {
- if (failedKeys.size() > 1) {
- logger.warn("Combining {} failed keys [{}] into single exception", failedKeys.size(), StringUtils.join(failedKeys, ", "));
- throw new MultipleSensitivePropertyProtectionException("Failed to unprotect keys", failedKeys);
- } else {
- throw new SensitivePropertyProtectionException("Failed to unprotect key " + failedKeys.iterator().next());
- }
- }
-
- NiFiProperties unprotected = new StandardNiFiProperties(rawProperties);
-
- return unprotected;
- } else {
- logger.debug("No protected properties");
- return getInternalNiFiProperties();
- }
+ return propertyProtectionDelegate.getUnprotectedProperties();
}
- /**
- * Registers a new {@link SensitivePropertyProvider}. This method will throw a {@link UnsupportedOperationException} if a provider is already registered for the protection scheme.
- *
- * @param sensitivePropertyProvider the provider
- */
- void addSensitivePropertyProvider(SensitivePropertyProvider sensitivePropertyProvider) {
- if (sensitivePropertyProvider == null) {
- throw new IllegalArgumentException("Cannot add null SensitivePropertyProvider");
- }
-
- if (getSensitivePropertyProviders().containsKey(sensitivePropertyProvider.getIdentifierKey())) {
- throw new UnsupportedOperationException("Cannot overwrite existing sensitive property provider registered for " + sensitivePropertyProvider.getIdentifierKey());
- }
-
- getSensitivePropertyProviders().put(sensitivePropertyProvider.getIdentifierKey(), sensitivePropertyProvider);
- }
-
- private String getDefaultProtectionScheme() {
- if (!getSensitivePropertyProviders().isEmpty()) {
- List<String> schemes = new ArrayList<>(getSensitivePropertyProviders().keySet());
- Collections.sort(schemes);
- return schemes.get(0);
- } else {
- throw new IllegalStateException("No registered protection schemes");
- }
- }
-
- /**
- * Returns a new instance of {@link NiFiProperties} with all populated sensitive values protected by the default protection scheme. Plain non-sensitive values are copied directly.
- *
- * @return the protected properties in a {@link StandardNiFiProperties} object
- * @throws IllegalStateException if no protection schemes are registered
- */
- NiFiProperties protectPlainProperties() {
- try {
- return protectPlainProperties(getDefaultProtectionScheme());
- } catch (IllegalStateException e) {
- final String msg = "Cannot protect properties with default scheme if no protection schemes are registered";
- logger.warn(msg);
- throw new IllegalStateException(msg, e);
- }
+ @Override
+ public void addSensitivePropertyProvider(final SensitivePropertyProvider sensitivePropertyProvider) {
+ propertyProtectionDelegate.addSensitivePropertyProvider(sensitivePropertyProvider);
}
- /**
- * Returns a new instance of {@link NiFiProperties} with all populated sensitive values protected by the provided protection scheme. Plain non-sensitive values are copied directly.
- *
- * @param protectionScheme the identifier key of the {@link SensitivePropertyProvider} to use
- * @return the protected properties in a {@link StandardNiFiProperties} object
- */
- NiFiProperties protectPlainProperties(String protectionScheme) {
- SensitivePropertyProvider spp = getSensitivePropertyProvider(protectionScheme);
-
- // Make a new holder (settable)
- Properties protectedProperties = new Properties();
-
- // Copy over the plain keys
- Set<String> plainKeys = getPropertyKeys();
- plainKeys.removeAll(getSensitivePropertyKeys());
- for (String key : plainKeys) {
- protectedProperties.setProperty(key, getInternalNiFiProperties().getProperty(key));
- }
-
- // Add the protected keys and the protection schemes
- for (String key : getSensitivePropertyKeys()) {
- final String plainValue = getInternalNiFiProperties().getProperty(key);
- if (plainValue != null && !plainValue.trim().isEmpty()) {
- final String protectedValue = spp.protect(plainValue);
- protectedProperties.setProperty(key, protectedValue);
- protectedProperties.setProperty(getProtectionKey(key), protectionScheme);
- }
- }
-
- return new StandardNiFiProperties(protectedProperties);
+ @Override
+ public Map<String, SensitivePropertyProvider> getSensitivePropertyProviders() {
+ return propertyProtectionDelegate.getSensitivePropertyProviders();
}
/**
@@ -449,7 +212,7 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
* @param plainProperties the instance to count protected properties
* @return the number of protected properties
*/
- public static int countProtectedProperties(NiFiProperties plainProperties) {
+ public static int countProtectedProperties(final NiFiProperties plainProperties) {
return new ProtectedNiFiProperties(plainProperties).getProtectedPropertyKeys().size();
}
@@ -459,7 +222,7 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
* @param plainProperties the instance to count sensitive properties
* @return the number of sensitive properties
*/
- public static int countSensitiveProperties(NiFiProperties plainProperties) {
+ public static int countSensitiveProperties(final NiFiProperties plainProperties) {
return new ProtectedNiFiProperties(plainProperties).getSensitivePropertyKeys().size();
}
@@ -475,59 +238,4 @@ class ProtectedNiFiProperties extends StandardNiFiProperties {
.append(StringUtils.join(providers, ", "))
.toString();
}
-
- /**
- * Returns the local provider cache (null-safe) as a Map of protection schemes -> implementations.
- *
- * @return the map
- */
- private Map<String, SensitivePropertyProvider> getSensitivePropertyProviders() {
- if (localProviderCache == null) {
- localProviderCache = new HashMap<>();
- }
-
- return localProviderCache;
- }
-
- private SensitivePropertyProvider getSensitivePropertyProvider(String protectionScheme) {
- if (isProviderAvailable(protectionScheme)) {
- return getSensitivePropertyProviders().get(protectionScheme);
- } else {
- throw new SensitivePropertyProtectionException("No provider available for " + protectionScheme);
- }
- }
-
- private boolean isProviderAvailable(String protectionScheme) {
- return getSensitivePropertyProviders().containsKey(protectionScheme);
- }
-
- /**
- * If the value is protected, unprotects it and returns it. If not, returns the original value.
- *
- * @param key the retrieved property key
- * @param retrievedValue the retrieved property value
- * @return the unprotected value
- */
- private String unprotectValue(String key, String retrievedValue) {
- // Checks if the key is sensitive and marked as protected
- if (isPropertyProtected(key)) {
- final String protectionScheme = getProperty(getProtectionKey(key));
-
- // No provider registered for this scheme, so just return the value
- if (!isProviderAvailable(protectionScheme)) {
- logger.warn("No provider available for {} so passing the protected {} value back", protectionScheme, key);
- return retrievedValue;
- }
-
- try {
- SensitivePropertyProvider sensitivePropertyProvider = getSensitivePropertyProvider(protectionScheme);
- return sensitivePropertyProvider.unprotect(retrievedValue);
- } catch (SensitivePropertyProtectionException e) {
- throw new SensitivePropertyProtectionException("Error unprotecting value for " + key, e.getCause());
- } catch (IllegalArgumentException e) {
- throw new SensitivePropertyProtectionException("Error unprotecting value for " + key, e);
- }
- }
- return retrievedValue;
- }
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderFactoryTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderFactoryTest.groovy
deleted file mode 100644
index 26352c0..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/AESSensitivePropertyProviderFactoryTest.groovy
+++ /dev/null
@@ -1,105 +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.properties
-
-import org.apache.commons.lang3.SystemUtils
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.junit.After
-import org.junit.Assume
-import org.junit.Before
-import org.junit.BeforeClass
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import java.security.Security
-
-@RunWith(JUnit4.class)
-class AESSensitivePropertyProviderFactoryTest extends GroovyTestCase {
- private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProviderFactoryTest.class)
-
- private static final String KEY_HEX = "0123456789ABCDEFFEDCBA9876543210" * 2
-
- @BeforeClass
- public static void setUpOnce() throws Exception {
- Assume.assumeTrue("Test only runs on *nix", !SystemUtils.IS_OS_WINDOWS)
- Security.addProvider(new BouncyCastleProvider())
-
- logger.metaClass.methodMissing = { String name, args ->
- logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
- }
- }
-
- @Before
- public void setUp() throws Exception {
-
- }
-
- @After
- public void tearDown() throws Exception {
-
- }
-
- @Ignore("This is resolved in PR 1216")
- @Test
- public void testShouldNotGetProviderWithoutKey() throws Exception {
- // Arrange
- SensitivePropertyProviderFactory factory = new AESSensitivePropertyProviderFactory()
-
- // Act
- def msg = shouldFail(SensitivePropertyProtectionException) {
- SensitivePropertyProvider provider = factory.getProvider()
- }
- logger.expected(msg)
-
- // Assert
- assert msg == "The provider factory cannot generate providers without a key"
- }
-
- @Test
- public void testShouldGetProviderWithKey() throws Exception {
- // Arrange
- SensitivePropertyProviderFactory factory = new AESSensitivePropertyProviderFactory(KEY_HEX)
-
- // Act
- SensitivePropertyProvider provider = factory.getProvider()
-
- // Assert
- assert provider instanceof AESSensitivePropertyProvider
- assert provider.@key
- assert provider.@cipher
- }
-
- @Ignore("This is resolved in PR 1216")
- @Test
- public void testGetProviderShouldHandleEmptyKey() throws Exception {
- // Arrange
- SensitivePropertyProviderFactory factory = new AESSensitivePropertyProviderFactory("")
-
- // Act
- def msg = shouldFail(SensitivePropertyProtectionException) {
- SensitivePropertyProvider provider = factory.getProvider()
- }
- logger.expected(msg)
-
- // Assert
- assert msg == "The provider factory cannot generate providers without a key"
- }
-}
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/StandardNiFiPropertiesGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesGroovyTest.groovy
similarity index 92%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/StandardNiFiPropertiesGroovyTest.groovy
rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesGroovyTest.groovy
index b73ac42..485050b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/StandardNiFiPropertiesGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesGroovyTest.groovy
@@ -28,8 +28,8 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
@RunWith(JUnit4.class)
-class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
- private static final Logger logger = LoggerFactory.getLogger(StandardNiFiPropertiesGroovyTest.class)
+class NiFiPropertiesGroovyTest extends GroovyTestCase {
+ private static final Logger logger = LoggerFactory.getLogger(NiFiPropertiesGroovyTest.class)
private static String originalPropertiesPath = System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH)
private static final String PREK = NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY
@@ -63,18 +63,18 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
}
}
- private static StandardNiFiProperties loadFromFile(String propertiesFilePath) {
+ private static NiFiProperties loadFromFile(String propertiesFilePath) {
String filePath
try {
- filePath = StandardNiFiPropertiesGroovyTest.class.getResource(propertiesFilePath).toURI().getPath()
+ filePath = NiFiPropertiesGroovyTest.class.getResource(propertiesFilePath).toURI().getPath()
} catch (URISyntaxException ex) {
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " +
+ ex.getLocalizedMessage(), ex)
}
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, filePath)
- StandardNiFiProperties properties = new StandardNiFiProperties()
+ NiFiProperties properties = new NiFiProperties()
// clear out existing properties
for (String prop : properties.stringPropertyNames()) {
@@ -86,8 +86,8 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
inStream = new BufferedInputStream(new FileInputStream(filePath))
properties.load(inStream)
} catch (final Exception ex) {
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " +
+ ex.getLocalizedMessage(), ex)
} finally {
if (null != inStream) {
try {
@@ -108,7 +108,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
// Act
- NiFiProperties niFiProperties = new StandardNiFiProperties()
+ NiFiProperties niFiProperties = new NiFiProperties()
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Assert
@@ -125,7 +125,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
assert rawProperties.size() == 1
// Act
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Assert
@@ -142,9 +142,9 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
assert rawProperties.size() == 1
// Act
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
- NiFiProperties emptyProperties = new StandardNiFiProperties()
+ NiFiProperties emptyProperties = new NiFiProperties()
logger.info("emptyProperties has ${emptyProperties.size()} properties: ${emptyProperties.getPropertyKeys()}")
// Assert
@@ -163,7 +163,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
final String KEY_HEX = "0123456789ABCDEFFEDCBA9876543210"
rawProperties.setProperty(PREKID, KEY_ID)
rawProperties.setProperty(PREK, KEY_HEX)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -196,7 +196,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty(PREK, KEY_HEX)
rawProperties.setProperty("${PREK}.id.${KEY_ID_2}", KEY_HEX_2)
rawProperties.setProperty("${PREK}.id.${KEY_ID_3}", KEY_HEX_3)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -236,7 +236,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty(FFREK, "")
rawProperties.setProperty("${FFREK}.id.${KEY_ID}", KEY_HEX)
rawProperties.setProperty("${FFREK}.id.${KEY_ID_2}", KEY_HEX_2)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -276,7 +276,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// rawProperties.setProperty(FFREK, "")
rawProperties.setProperty("${FFREK}.id.${KEY_ID}", KEY_HEX)
rawProperties.setProperty("${FFREK}.id.${KEY_ID_2}", KEY_HEX_2)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -318,7 +318,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// rawProperties.setProperty(FFREK, "")
rawProperties.setProperty("${FFREK}.${KEY_ID}", KEY_HEX)
rawProperties.setProperty("${FFREK}.${KEY_ID_2}", KEY_HEX_2)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -365,7 +365,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty(FFREK, KEY_HEX_DUP)
rawProperties.setProperty("${FFREK}.id.${KEY_ID}", KEY_HEX)
rawProperties.setProperty("${FFREK}.id.${KEY_ID_2}", KEY_HEX_2)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -412,7 +412,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty("${FFREK}.id.${KEY_ID}", KEY_HEX)
rawProperties.setProperty(FFREK, KEY_HEX_DUP)
rawProperties.setProperty(FFREKID, KEY_ID_2)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -445,7 +445,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty("${PREK}.id.${KEY_ID}", KEY_HEX)
rawProperties.setProperty("${PREK}.id.${KEY_ID_2}", KEY_HEX_2)
rawProperties.setProperty("${PREK}.id.${KEY_ID_3}", KEY_HEX_3)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -467,7 +467,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
void testShouldGetProvenanceRepoEncryptionKeysWithNoneDefined() throws Exception {
// Arrange
Properties rawProperties = new Properties()
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -492,7 +492,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
final String KEY_ID = "arbitraryKeyId"
rawProperties.setProperty(PREKID, KEY_ID)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -527,7 +527,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty("${PREK}.id.${KEY_ID_3}", KEY_HEX_3)
rawProperties.setProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS, "some.class.provider")
rawProperties.setProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_LOCATION, "some://url")
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -553,7 +553,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
final String KEY_HEX = "0123456789ABCDEFFEDCBA9876543210"
rawProperties.setProperty(CREKID, KEY_ID)
rawProperties.setProperty(CREK, KEY_HEX)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -586,7 +586,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty(CREK, KEY_HEX)
rawProperties.setProperty("${CREK}.id.${KEY_ID_2}", KEY_HEX_2)
rawProperties.setProperty("${CREK}.id.${KEY_ID_3}", KEY_HEX_3)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -619,7 +619,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty("${CREK}.id.${KEY_ID}", KEY_HEX)
rawProperties.setProperty("${CREK}.id.${KEY_ID_2}", KEY_HEX_2)
rawProperties.setProperty("${CREK}.id.${KEY_ID_3}", KEY_HEX_3)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -641,7 +641,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
void testShouldGetContentRepositoryEncryptionKeysWithNoneDefined() throws Exception {
// Arrange
Properties rawProperties = new Properties()
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -666,7 +666,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
final String KEY_ID = "arbitraryKeyId"
rawProperties.setProperty(CREKID, KEY_ID)
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -701,7 +701,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
rawProperties.setProperty("${CREK}.id.${KEY_ID_3}", KEY_HEX_3)
rawProperties.setProperty(NiFiProperties.CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS, "some.class.provider")
rawProperties.setProperty(NiFiProperties.CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_LOCATION, "some://url")
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Act
@@ -724,7 +724,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String noLeadingSlash = "some/context/path"
Properties rawProps = new Properties(["nifi.web.proxy.context.path": noLeadingSlash])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw context path property [${noLeadingSlash}]")
// Act
@@ -740,7 +740,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String leadingSlash = "/some/context/path"
Properties rawProps = new Properties(["nifi.web.proxy.context.path": leadingSlash])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw context path property [${leadingSlash}]")
// Act
@@ -760,7 +760,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
List<String> paths = [noLeadingSlash, leadingSlash, leadingAndTrailingSlash]
String combinedPaths = paths.join(",")
Properties rawProps = new Properties(["nifi.web.proxy.context.path": combinedPaths])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw context path property [${noLeadingSlash}]")
// Act
@@ -780,7 +780,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String leadingSlash = "/some/context/path"
Properties rawProps = new Properties(["nifi.web.proxy.context.path": leadingSlash])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw context path property [${leadingSlash}]")
// Act
@@ -801,7 +801,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
List<String> paths = [noLeadingSlash, leadingSlash, leadingAndTrailingSlash]
String combinedPaths = paths.join(",")
Properties rawProps = new Properties(["nifi.web.proxy.context.path": combinedPaths])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw context path property [${noLeadingSlash}]")
// Act
@@ -818,7 +818,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String empty = ""
Properties rawProps = new Properties(["nifi.web.proxy.context.path": empty])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw context path property [${empty}]")
// Act
@@ -834,7 +834,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String extraSpaceHostname = "somehost.com "
Properties rawProps = new Properties(["nifi.web.proxy.host": extraSpaceHostname])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${extraSpaceHostname}]")
// Act
@@ -851,7 +851,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String hostname = "somehost.com"
Properties rawProps = new Properties(["nifi.web.proxy.host": hostname])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${hostname}]")
// Act
@@ -872,7 +872,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
List<String> hosts = [extraSpaceHostname, normalHostname, hostnameWithPort, extraSpaceHostnameWithPort]
String combinedHosts = hosts.join(",")
Properties rawProps = new Properties(["nifi.web.proxy.host": combinedHosts])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${combinedHosts}]")
// Act
@@ -893,7 +893,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String normalHostname = "someotherhost.com"
Properties rawProps = new Properties(["nifi.web.proxy.host": normalHostname])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${normalHostname}]")
// Act
@@ -915,7 +915,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
List<String> hosts = [extraSpaceHostname, normalHostname, hostnameWithPort, extraSpaceHostnameWithPort]
String combinedHosts = hosts.join(",")
Properties rawProps = new Properties(["nifi.web.proxy.host": combinedHosts])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${combinedHosts}]")
// Act
@@ -932,7 +932,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String empty = ""
Properties rawProps = new Properties(["nifi.web.proxy.host": empty])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${empty}]")
// Act
@@ -948,7 +948,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
String empty = ""
Properties rawProps = new Properties(["nifi.web.proxy.host": empty])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with raw proxy host property [${empty}]")
// Act
@@ -997,7 +997,7 @@ class StandardNiFiPropertiesGroovyTest extends GroovyTestCase {
void testWebMaxContentSizeShouldDefaultToEmpty() {
// Arrange
Properties rawProps = new Properties(["nifi.web.max.content.size": ""])
- NiFiProperties props = new StandardNiFiProperties(rawProps)
+ NiFiProperties props = new NiFiProperties(rawProps)
logger.info("Created a NiFiProperties instance with empty web max content size property")
// Act
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesLoaderGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesLoaderGroovyTest.groovy
index ef9ff4b..b73a970 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesLoaderGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/NiFiPropertiesLoaderGroovyTest.groovy
@@ -17,6 +17,7 @@
package org.apache.nifi.properties
import org.apache.commons.lang3.SystemUtils
+import org.apache.nifi.util.NiFiBootstrapUtils
import org.apache.nifi.util.NiFiProperties
import org.apache.nifi.util.file.FileUtils
import org.bouncycastle.jce.provider.BouncyCastleProvider
@@ -89,7 +90,6 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
// if (ProtectedNiFiProperties.@localProviderCache) {
// ProtectedNiFiProperties.@localProviderCache = [:]
// }
- NiFiPropertiesLoader.@sensitivePropertyProviderFactory = null
}
@AfterClass
@@ -124,31 +124,6 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
}
@Test
- void testShouldGetDefaultProviderKey() throws Exception {
- // Arrange
- final String EXPECTED_PROVIDER_KEY = "aes/gcm/${Cipher.getMaxAllowedKeyLength("AES") > 128 ? 256 : 128}"
- logger.info("Expected provider key: ${EXPECTED_PROVIDER_KEY}")
-
- // Act
- String defaultKey = NiFiPropertiesLoader.getDefaultProviderKey()
- logger.info("Default key: ${defaultKey}")
- // Assert
- assert defaultKey == EXPECTED_PROVIDER_KEY
- }
-
- @Test
- void testShouldInitializeSensitivePropertyProviderFactory() throws Exception {
- // Arrange
- NiFiPropertiesLoader niFiPropertiesLoader = new NiFiPropertiesLoader()
-
- // Act
- niFiPropertiesLoader.initializeSensitivePropertyProviderFactory()
-
- // Assert
- assert niFiPropertiesLoader.@sensitivePropertyProviderFactory
- }
-
- @Test
void testShouldLoadUnprotectedPropertiesFromFile() throws Exception {
// Arrange
File unprotectedFile = new File("src/test/resources/conf/nifi.properties")
@@ -161,7 +136,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
assert niFiProperties.size() > 0
// Ensure it is not a ProtectedNiFiProperties
- assert niFiProperties instanceof StandardNiFiProperties
+ assert !(niFiProperties instanceof ProtectedNiFiProperties)
}
@Test
@@ -180,7 +155,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
assert niFiProperties.size() > 0
// Ensure it is not a ProtectedNiFiProperties
- assert niFiProperties instanceof StandardNiFiProperties
+ assert !(niFiProperties instanceof ProtectedNiFiProperties)
}
@Test
@@ -193,13 +168,13 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
logger.info("Set ${NiFiProperties.PROPERTIES_FILE_PATH} to ${protectedFile.absolutePath}")
// Act
- def msg = shouldFail(IOException) {
+ def msg = shouldFail(SensitivePropertyProtectionException) {
NiFiProperties niFiProperties = NiFiPropertiesLoader.loadDefaultWithKeyFromBootstrap()
}
logger.expected(msg)
// Assert
- assert msg =~ "Cannot read from bootstrap.conf"
+ assert msg =~ "Could not read root key from bootstrap.conf"
}
@Test
@@ -308,7 +283,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
assert niFiProperties.size() > 0
// Ensure it is not a ProtectedNiFiProperties
- assert niFiProperties instanceof StandardNiFiProperties
+ assert !(niFiProperties instanceof ProtectedNiFiProperties)
}
@Test
@@ -348,7 +323,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
}
// Ensure it is not a ProtectedNiFiProperties
- assert niFiProperties instanceof StandardNiFiProperties
+ assert !(niFiProperties instanceof ProtectedNiFiProperties)
}
@Test
@@ -358,7 +333,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, defaultNiFiPropertiesFilePath)
// Act
- String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile()
+ String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile()
// Assert
assert key == KEY_HEX
@@ -371,7 +346,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, defaultNiFiPropertiesFilePath)
// Act
- String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile()
+ String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile()
// Assert
assert key == ""
@@ -384,7 +359,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, defaultNiFiPropertiesFilePath)
// Act
- String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile()
+ String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile()
// Assert
assert key == ""
@@ -398,7 +373,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
// Act
def msg = shouldFail(IOException) {
- String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile()
+ String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile()
}
logger.expected(msg)
@@ -419,7 +394,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
// Act
def msg = shouldFail(IOException) {
- String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile()
+ String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile()
}
logger.expected(msg)
@@ -444,7 +419,7 @@ class NiFiPropertiesLoaderGroovyTest extends GroovyTestCase {
// Act
def msg = shouldFail(IOException) {
- String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile()
+ String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile()
}
logger.expected(msg)
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy
index 034c6ca..f902dbc 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy
@@ -55,6 +55,9 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
private static String originalPropertiesPath = System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH)
+ private static SensitivePropertyProviderFactory sensitivePropertyProviderFactory =
+ StandardSensitivePropertyProviderFactory.withKey(KEY_HEX)
+
@BeforeClass
static void setUpOnce() throws Exception {
Assume.assumeTrue("Test only runs on *nix", !SystemUtils.IS_OS_WINDOWS)
@@ -85,8 +88,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
try {
filePath = ProtectedNiFiPropertiesGroovyTest.class.getResource(propertiesFilePath).toURI().getPath()
} catch (URISyntaxException ex) {
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " + ex.getLocalizedMessage(), ex)
}
File file = new File(filePath)
@@ -109,14 +111,14 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// If it has protected keys, inject the SPP
if (protectedNiFiProperties.hasProtectedKeys()) {
- protectedNiFiProperties.addSensitivePropertyProvider(new AESSensitivePropertyProvider(KEY_HEX))
+ protectedNiFiProperties.addSensitivePropertyProvider(sensitivePropertyProviderFactory
+ .getProvider(PropertyProtectionScheme.AES_GCM))
}
return protectedNiFiProperties
} catch (final Exception ex) {
logger.error("Cannot load properties file due to " + ex.getLocalizedMessage())
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " + ex.getLocalizedMessage(), ex)
} finally {
if (null != inStream) {
try {
@@ -135,7 +137,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Arrange
// Act
- NiFiProperties niFiProperties = new StandardNiFiProperties()
+ NiFiProperties niFiProperties = new NiFiProperties()
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Assert
@@ -152,7 +154,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
assert rawProperties.size() == 1
// Act
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
// Assert
@@ -166,7 +168,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
Properties rawProperties = new Properties()
rawProperties.setProperty("key", "value")
rawProperties.setProperty("key.protected", "value2")
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
assert niFiProperties.size() == 2
@@ -190,9 +192,9 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
assert rawProperties.size() == 1
// Act
- NiFiProperties niFiProperties = new StandardNiFiProperties(rawProperties)
+ NiFiProperties niFiProperties = new NiFiProperties(rawProperties)
logger.info("niFiProperties has ${niFiProperties.size()} properties: ${niFiProperties.getPropertyKeys()}")
- NiFiProperties emptyProperties = new StandardNiFiProperties()
+ NiFiProperties emptyProperties = new NiFiProperties()
logger.info("emptyProperties has ${emptyProperties.size()} properties: ${emptyProperties.getPropertyKeys()}")
// Assert
@@ -360,7 +362,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
* @throws Exception
*/
@Test
- void testGetValueOfSensitivePropertyShouldHandleUnknownProtectionScheme() throws Exception {
+ void testGetValueOfSensitivePropertyShouldFailOnUnknownProtectionScheme() throws Exception {
// Arrange
final String KEYSTORE_PASSWORD_KEY = "nifi.security.keystorePasswd"
@@ -375,16 +377,18 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
boolean isSensitive = properties.isPropertySensitive(KEYSTORE_PASSWORD_KEY)
boolean isProtected = properties.isPropertyProtected(KEYSTORE_PASSWORD_KEY)
- // While the value is "protected", the scheme is not registered, so treat it as raw
+ // While the value is "protected", the scheme is not registered, so throw an exception
logger.info("The property is ${isSensitive ? "sensitive" : "not sensitive"} and ${isProtected ? "protected" : "not protected"}")
// Act
- NiFiProperties unprotectedProperties = properties.getUnprotectedProperties()
- String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
- logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ def msg = shouldFail(IllegalStateException) {
+ NiFiProperties unprotectedProperties = properties.getUnprotectedProperties()
+ String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
+ logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ }
// Assert
- assert retrievedKeystorePassword == RAW_KEYSTORE_PASSWORD
+ assert msg == "No provider available for nifi.sensitive.props.key"
assert isSensitive
assert isProtected
}
@@ -439,7 +443,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
ProtectedNiFiProperties properties = loadFromFile("/conf/nifi_with_sensitive_properties_protected_aes_multiple_malformed.properties")
// Iterate over the protected keys and track the ones that fail to decrypt
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(KEY_HEX)
+ SensitivePropertyProvider spp = sensitivePropertyProviderFactory.getProvider(PropertyProtectionScheme.AES_GCM)
Set<String> malformedKeys = properties.getProtectedPropertyKeys()
.findAll { String key, String scheme -> scheme == spp.identifierKey }
.keySet().collect { String key ->
@@ -538,11 +542,11 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// TODO: Test getProtected with multiple providers
/**
- * In the protection enabled scenario, a call to retrieve a sensitive property should handle if the internal cache of providers is empty.
+ * In the protection enabled scenario, a call to retrieve a sensitive property should fail if the internal cache of providers is empty.
* @throws Exception
*/
@Test
- void testGetValueOfSensitivePropertyShouldHandleInvalidatedInternalCache() throws Exception {
+ void testGetValueOfSensitivePropertyShouldFailOnInvalidatedInternalCache() throws Exception {
// Arrange
final String KEYSTORE_PASSWORD_KEY = "nifi.security.keystorePasswd"
final String EXPECTED_KEYSTORE_PASSWORD = "thisIsABadKeystorePassword"
@@ -553,19 +557,21 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Read raw value from properties: ${RAW_PASSWORD}")
// Overwrite the internal cache
- properties.localProviderCache = [:]
+ properties.getSensitivePropertyProviders().clear()
boolean isSensitive = properties.isPropertySensitive(KEYSTORE_PASSWORD_KEY)
boolean isProtected = properties.isPropertyProtected(KEYSTORE_PASSWORD_KEY)
logger.info("The property is ${isSensitive ? "sensitive" : "not sensitive"} and ${isProtected ? "protected" : "not protected"}")
// Act
- NiFiProperties unprotectedProperties = properties.getUnprotectedProperties()
- String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
- logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ def msg = shouldFail(IllegalStateException) {
+ NiFiProperties unprotectedProperties = properties.getUnprotectedProperties()
+ String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
+ logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ }
// Assert
- assert retrievedKeystorePassword == RAW_PASSWORD
+ assert msg == "No provider available for nifi.sensitive.props.key"
assert isSensitive
assert isProtected
}
@@ -614,6 +620,10 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
assert !unprotectedPasswordIsProtected
}
+ private static double getPercentOfSensitivePropertiesProtected(final ProtectedNiFiProperties properties) {
+ return (int) Math.round(properties.getProtectedPropertyKeys().size() / ((double) properties.getPopulatedSensitivePropertyKeys().size()) * 100);
+ }
+
@Test
void testShouldGetPercentageOfSensitivePropertiesProtected_0() throws Exception {
// Arrange
@@ -623,7 +633,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Protected property keys: ${properties.getProtectedPropertyKeys().keySet()}")
// Act
- double percentProtected = properties.getPercentOfSensitivePropertiesProtected()
+ double percentProtected = getPercentOfSensitivePropertiesProtected(properties)
logger.info("${percentProtected}% (${properties.getProtectedPropertyKeys().size()} of ${properties.getPopulatedSensitivePropertyKeys().size()}) protected")
// Assert
@@ -639,7 +649,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Protected property keys: ${properties.getProtectedPropertyKeys().keySet()}")
// Act
- double percentProtected = properties.getPercentOfSensitivePropertiesProtected()
+ double percentProtected = getPercentOfSensitivePropertiesProtected(properties)
logger.info("${percentProtected}% (${properties.getProtectedPropertyKeys().size()} of ${properties.getPopulatedSensitivePropertyKeys().size()}) protected")
// Assert
@@ -655,7 +665,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Protected property keys: ${properties.getProtectedPropertyKeys().keySet()}")
// Act
- double percentProtected = properties.getPercentOfSensitivePropertiesProtected()
+ double percentProtected = getPercentOfSensitivePropertiesProtected(properties)
logger.info("${percentProtected}% (${properties.getProtectedPropertyKeys().size()} of ${properties.getPopulatedSensitivePropertyKeys().size()}) protected")
// Assert
@@ -666,13 +676,13 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
void testInstanceWithNoProtectedPropertiesShouldNotLoadSPP() throws Exception {
// Arrange
ProtectedNiFiProperties properties = loadFromFile("/conf/nifi.properties")
- assert properties.@localProviderCache?.isEmpty()
+ assert properties.getSensitivePropertyProviders().isEmpty()
logger.info("Has protected properties: ${properties.hasProtectedKeys()}")
assert !properties.hasProtectedKeys()
// Act
- Map localCache = properties.@localProviderCache
+ Map localCache = properties.getSensitivePropertyProviders()
logger.info("Internal cache ${localCache} has ${localCache.size()} providers loaded")
// Assert
@@ -706,7 +716,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
assert properties.getSensitivePropertyProviders().isEmpty()
// Act
- def msg = shouldFail(IllegalArgumentException) {
+ def msg = shouldFail(NullPointerException) {
properties.addSensitivePropertyProvider(null)
}
logger.expected(msg)
@@ -756,7 +766,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
ProtectedNiFiProperties protectedNiFiProperties = loadFromFile(noProtectedPropertiesPath)
logger.info("Loaded ${protectedNiFiProperties.size()} properties from ${noProtectedPropertiesPath}")
- int hashCode = protectedNiFiProperties.internalNiFiProperties.hashCode()
+ int hashCode = protectedNiFiProperties.getApplicationProperties().hashCode()
logger.info("Hash code of internal instance: ${hashCode}")
// Act
@@ -766,7 +776,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Assert
assert unprotectedNiFiProperties.size() == protectedNiFiProperties.size()
assert unprotectedNiFiProperties.getPropertyKeys().every {
- !unprotectedNiFiProperties.getProperty(it).endsWith(".protected")
+ !unprotectedNiFiProperties.getProperty(it).endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)
}
logger.info("Hash code from returned unprotected instance: ${unprotectedNiFiProperties.hashCode()}")
assert unprotectedNiFiProperties.hashCode() == hashCode
@@ -781,7 +791,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
int protectedPropertyCount = protectedNiFiProperties.getProtectedPropertyKeys().size()
int protectionSchemeCount = protectedNiFiProperties
- .getPropertyKeys().findAll { it.endsWith(".protected") }
+ .getPropertyKeys().findAll { it.endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX) }
.size()
int expectedUnprotectedPropertyCount = protectedNiFiProperties.size() - protectionSchemeCount
@@ -797,7 +807,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Expected unprotected property count: ${expectedUnprotectedPropertyCount}")
- int hashCode = protectedNiFiProperties.internalNiFiProperties.hashCode()
+ int hashCode = protectedNiFiProperties.getApplicationProperties().hashCode()
logger.info("Hash code of internal instance: ${hashCode}")
// Act
@@ -807,7 +817,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Assert
assert unprotectedNiFiProperties.size() == expectedUnprotectedPropertyCount
assert unprotectedNiFiProperties.getPropertyKeys().every {
- !unprotectedNiFiProperties.getProperty(it).endsWith(".protected")
+ !unprotectedNiFiProperties.getProperty(it).endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)
}
logger.info("Hash code from returned unprotected instance: ${unprotectedNiFiProperties.hashCode()}")
assert unprotectedNiFiProperties.hashCode() != hashCode
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/test/groovy/org/apache/nifi/NiFiGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/test/groovy/org/apache/nifi/NiFiGroovyTest.groovy
index 69dc5b0..5dda4e1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/test/groovy/org/apache/nifi/NiFiGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/test/groovy/org/apache/nifi/NiFiGroovyTest.groovy
@@ -18,9 +18,11 @@ package org.apache.nifi
import ch.qos.logback.classic.spi.LoggingEvent
import ch.qos.logback.core.AppenderBase
-import org.apache.nifi.properties.AESSensitivePropertyProvider
+import org.apache.nifi.properties.ApplicationPropertiesProtector
import org.apache.nifi.properties.NiFiPropertiesLoader
-import org.apache.nifi.properties.StandardNiFiProperties
+import org.apache.nifi.properties.PropertyProtectionScheme
+import org.apache.nifi.properties.SensitivePropertyProvider
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory
import org.apache.nifi.util.NiFiProperties
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.After
@@ -34,7 +36,6 @@ import org.slf4j.LoggerFactory
import org.slf4j.bridge.SLF4JBridgeHandler
import javax.crypto.Cipher
-import java.nio.file.Paths
import java.security.Security
@RunWith(JUnit4.class)
@@ -64,7 +65,6 @@ class NiFiGroovyTest extends GroovyTestCase {
@After
void tearDown() throws Exception {
- NiFiPropertiesLoader.@sensitivePropertyProviderFactory = null
TestAppender.reset()
System.setIn(System.in)
}
@@ -166,7 +166,7 @@ class NiFiGroovyTest extends GroovyTestCase {
System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, testPropertiesPath)
def protectedNiFiProperties = new NiFiPropertiesLoader().readProtectedPropertiesFromDisk(new File(testPropertiesPath))
- NiFiProperties unprocessedProperties = protectedNiFiProperties.internalNiFiProperties
+ NiFiProperties unprocessedProperties = protectedNiFiProperties.getApplicationProperties()
def protectedKeys = getProtectedKeys(unprocessedProperties)
logger.info("Reading from raw properties file gives protected properties: ${protectedKeys}")
@@ -198,29 +198,30 @@ class NiFiGroovyTest extends GroovyTestCase {
}
private static boolean hasProtectedKeys(NiFiProperties properties) {
- properties.getPropertyKeys().any { it.endsWith(".protected") }
+ properties.getPropertyKeys().any { it.endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX) }
}
private static Map<String, String> getProtectedPropertyKeys(NiFiProperties properties) {
getProtectedKeys(properties).collectEntries { String key ->
- [(key): properties.getProperty(key + ".protected")]
+ [(key): properties.getProperty(key + ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)]
}
}
private static Set<String> getProtectedKeys(NiFiProperties properties) {
- properties.getPropertyKeys().findAll { it.endsWith(".protected") }.collect { it - ".protected" }
+ properties.getPropertyKeys().findAll { it.endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX) }.collect { it - ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX }
}
private static NiFiProperties decrypt(NiFiProperties encryptedProperties, String keyHex) {
- AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(keyHex)
+ SensitivePropertyProvider spp = StandardSensitivePropertyProviderFactory.withKey(keyHex)
+ .getProvider(PropertyProtectionScheme.AES_GCM)
def map = encryptedProperties.getPropertyKeys().collectEntries { String key ->
- if (encryptedProperties.getProperty(key + ".protected") == spp.getIdentifierKey()) {
+ if (encryptedProperties.getProperty(key + ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX) == spp.getIdentifierKey()) {
[(key): spp.unprotect(encryptedProperties.getProperty(key))]
- } else if (!key.endsWith(".protected")) {
+ } else if (!key.endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)) {
[(key): encryptedProperties.getProperty(key)]
}
}
- new StandardNiFiProperties(map as Properties)
+ new NiFiProperties(map as Properties)
}
@Test
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/groovy/org/apache/nifi/remote/StandardPublicPortGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/groovy/org/apache/nifi/remote/StandardPublicPortGroovyTest.groovy
index e9e9b6c..3a09a78 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/groovy/org/apache/nifi/remote/StandardPublicPortGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/groovy/org/apache/nifi/remote/StandardPublicPortGroovyTest.groovy
@@ -16,12 +16,10 @@
*/
package org.apache.nifi.remote
-
import org.apache.nifi.authorization.Authorizer
import org.apache.nifi.connectable.Connectable
import org.apache.nifi.connectable.ConnectableType
import org.apache.nifi.controller.ProcessScheduler
-import org.apache.nifi.properties.StandardNiFiProperties
import org.apache.nifi.remote.protocol.CommunicationsSession
import org.apache.nifi.remote.protocol.ServerProtocol
import org.apache.nifi.reporting.BulletinRepository
@@ -87,7 +85,7 @@ class StandardPublicPortGroovyTest extends GroovyTestCase {
logger.mock("getProperty(${prop}) -> ${value}")
value
},
- ] as StandardNiFiProperties
+ ] as NiFiProperties
StandardPublicPort port = createPublicPort(mockProps)
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestHttpRemoteSiteListener.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestHttpRemoteSiteListener.java
index fbad70f..099efab 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestHttpRemoteSiteListener.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestHttpRemoteSiteListener.java
@@ -22,7 +22,6 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.apache.nifi.processor.ProcessSession;
-import org.apache.nifi.properties.StandardNiFiProperties;
import org.apache.nifi.remote.protocol.FlowFileTransaction;
import org.apache.nifi.remote.protocol.HandshakeProperties;
import org.apache.nifi.util.NiFiProperties;
@@ -40,7 +39,7 @@ public class TestHttpRemoteSiteListener {
@Test
public void testNormalTransactionProgress() {
- HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(new StandardNiFiProperties());
+ HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(new NiFiProperties());
String transactionId = transactionManager.createTransaction();
assertTrue("Transaction should be active.", transactionManager.isTransactionActive(transactionId));
@@ -60,7 +59,7 @@ public class TestHttpRemoteSiteListener {
@Test
public void testDuplicatedTransactionId() {
- HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(new StandardNiFiProperties());
+ HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(new NiFiProperties());
String transactionId = transactionManager.createTransaction();
assertTrue("Transaction should be active.", transactionManager.isTransactionActive(transactionId));
@@ -79,7 +78,7 @@ public class TestHttpRemoteSiteListener {
@Test
public void testNoneExistingTransaction() {
- HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(new StandardNiFiProperties());
+ HttpRemoteSiteListener transactionManager = HttpRemoteSiteListener.getInstance(new NiFiProperties());
String transactionId = "does-not-exist-1";
assertFalse("Transaction should not be active.", transactionManager.isTransactionActive(transactionId));
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestPeerDescriptionModifier.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestPeerDescriptionModifier.java
index cf6639f..122cc3f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestPeerDescriptionModifier.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/TestPeerDescriptionModifier.java
@@ -17,7 +17,6 @@
package org.apache.nifi.remote;
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException;
-import org.apache.nifi.properties.StandardNiFiProperties;
import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol;
import org.apache.nifi.util.NiFiProperties;
import org.junit.Test;
@@ -37,7 +36,7 @@ public class TestPeerDescriptionModifier {
@Test
public void testNoConfiguration() {
Properties props = new Properties();
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
final PeerDescriptionModifier modifier = new PeerDescriptionModifier(properties);
assertFalse(modifier.isModificationNeeded(SiteToSiteTransportProtocol.RAW));
assertFalse(modifier.isModificationNeeded(SiteToSiteTransportProtocol.HTTP));
@@ -47,7 +46,7 @@ public class TestPeerDescriptionModifier {
public void testInvalidNoHostname() {
Properties props = new Properties();
props.put("nifi.remote.route.raw.no-host.when", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
try {
new PeerDescriptionModifier(properties);
fail("Should throw an Exception");
@@ -61,7 +60,7 @@ public class TestPeerDescriptionModifier {
Properties props = new Properties();
props.put("nifi.remote.route.raw.no-port.when", "true");
props.put("nifi.remote.route.raw.no-port.hostname", "proxy.example.com");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
try {
new PeerDescriptionModifier(properties);
fail("Should throw an Exception");
@@ -78,7 +77,7 @@ public class TestPeerDescriptionModifier {
props.put("nifi.remote.route.raw.invalid-name.port", "8081");
props.put("nifi.remote.route.raw.invalid-name.secure", "true");
props.put("nifi.remote.route.raw.invalid-name.unsupported", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
try {
new PeerDescriptionModifier(properties);
fail("Should throw an Exception");
@@ -93,7 +92,7 @@ public class TestPeerDescriptionModifier {
public void testInvalidPropertyKeyNoProtocol() {
Properties props = new Properties();
props.put("nifi.remote.route.", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
try {
new PeerDescriptionModifier(properties);
fail("Should throw an Exception");
@@ -108,7 +107,7 @@ public class TestPeerDescriptionModifier {
public void testInvalidPropertyKeyNoName() {
Properties props = new Properties();
props.put("nifi.remote.route.http.", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
try {
new PeerDescriptionModifier(properties);
fail("Should throw an Exception");
@@ -125,7 +124,7 @@ public class TestPeerDescriptionModifier {
props.put("nifi.remote.route.raw.invalid-el.when", "${nonExistingFunction()}");
props.put("nifi.remote.route.raw.invalid-el.hostname", "proxy.example.com");
props.put("nifi.remote.route.raw.invalid-el.port", "8081");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
final PeerDescriptionModifier modifier = new PeerDescriptionModifier(properties);
final PeerDescription source = new PeerDescription("client", 12345, true);
@@ -146,7 +145,7 @@ public class TestPeerDescriptionModifier {
props.put("nifi.remote.route.raw.no-port.when", "true");
props.put("nifi.remote.route.raw.no-port.hostname", "proxy.example.com");
props.put("nifi.remote.route.raw.no-port.port", "8443");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
final PeerDescriptionModifier modifier = new PeerDescriptionModifier(properties);
final PeerDescription source = new PeerDescription("client", 12345, true);
@@ -178,7 +177,7 @@ public class TestPeerDescriptionModifier {
props.put("nifi.remote.input.socket.port", "8081");
props.put("nifi.remote.input.http.enabled", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
final PeerDescriptionModifier modifier = new PeerDescriptionModifier(properties);
// For requests coming from the proxy server, modify target description,
@@ -230,7 +229,7 @@ public class TestPeerDescriptionModifier {
props.put("nifi.remote.input.socket.port", "8081");
props.put("nifi.remote.input.http.enabled", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
final PeerDescriptionModifier modifier = new PeerDescriptionModifier(properties);
// For requests coming from the proxy server, modify target description,
@@ -284,7 +283,7 @@ public class TestPeerDescriptionModifier {
props.put("nifi.remote.input.http.enabled", "true");
- final NiFiProperties properties = new StandardNiFiProperties(props);
+ final NiFiProperties properties = new NiFiProperties(props);
final PeerDescriptionModifier modifier = new PeerDescriptionModifier(properties);
// For requests coming from the proxy server, modify target description,
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpFlowFileServerProtocol.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpFlowFileServerProtocol.java
index 529dfe6..e63e901 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpFlowFileServerProtocol.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/java/org/apache/nifi/remote/protocol/http/TestHttpFlowFileServerProtocol.java
@@ -25,7 +25,6 @@ import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.processor.Relationship;
-import org.apache.nifi.properties.StandardNiFiProperties;
import org.apache.nifi.provenance.ProvenanceEventRecord;
import org.apache.nifi.provenance.ProvenanceEventType;
import org.apache.nifi.remote.HttpRemoteSiteListener;
@@ -109,7 +108,7 @@ public class TestHttpFlowFileServerProtocol {
private HttpFlowFileServerProtocol getDefaultHttpFlowFileServerProtocol() {
final StandardVersionNegotiator versionNegotiator = new StandardVersionNegotiator(5, 4, 3, 2, 1);
- return new StandardHttpFlowFileServerProtocol(versionNegotiator, new StandardNiFiProperties());
+ return new StandardHttpFlowFileServerProtocol(versionNegotiator, new NiFiProperties());
}
@Test
@@ -367,7 +366,7 @@ public class TestHttpFlowFileServerProtocol {
sessionState.getFlowFileQueue().offer(flowFile);
}
- final HttpRemoteSiteListener remoteSiteListener = HttpRemoteSiteListener.getInstance(new StandardNiFiProperties());
+ final HttpRemoteSiteListener remoteSiteListener = HttpRemoteSiteListener.getInstance(new NiFiProperties());
serverProtocol.handshake(peer);
assertTrue(serverProtocol.isHandshakeSuccessful());
@@ -522,7 +521,7 @@ public class TestHttpFlowFileServerProtocol {
}
private void receiveFlowFiles(final HttpFlowFileServerProtocol serverProtocol, final String transactionId, final Peer peer, final DataPacket ... dataPackets) throws IOException {
- final HttpRemoteSiteListener remoteSiteListener = HttpRemoteSiteListener.getInstance(new StandardNiFiProperties());
+ final HttpRemoteSiteListener remoteSiteListener = HttpRemoteSiteListener.getInstance(new NiFiProperties());
final HttpServerCommunicationsSession commsSession = (HttpServerCommunicationsSession) peer.getCommunicationsSession();
serverProtocol.handshake(peer);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy
index 6842e4c..d00ed76 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy
@@ -23,7 +23,6 @@ import org.apache.nifi.nar.ExtensionManagerHolder
import org.apache.nifi.nar.ExtensionMapping
import org.apache.nifi.nar.SystemBundle
import org.apache.nifi.processor.DataUnit
-import org.apache.nifi.properties.StandardNiFiProperties
import org.apache.nifi.security.util.StandardTlsConfiguration
import org.apache.nifi.security.util.TlsConfiguration
import org.apache.nifi.security.util.TlsPlatform
@@ -50,6 +49,11 @@ import org.junit.contrib.java.lang.system.SystemErrRule
import org.junit.contrib.java.lang.system.SystemOutRule
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.stubbing.Answer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -87,7 +91,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
// These protocol versions should not ever be supported
static private final List<String> LEGACY_TLS_PROTOCOLS = ["TLS", "TLSv1", "TLSv1.1", "SSL", "SSLv2", "SSLv2Hello", "SSLv3"]
- NiFiProperties httpsProps = new StandardNiFiProperties(rawProperties: new Properties([
+ NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT) : HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST) : HTTPS_HOSTNAME,
(NiFiProperties.SECURITY_KEYSTORE) : KEYSTORE_PATH,
@@ -134,15 +138,16 @@ class JettyServerGroovyTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
(NiFiProperties.WEB_THREADS) : NiFiProperties.DEFAULT_WEB_THREADS
]
- NiFiProperties mockProps = [
- getPort : { -> 8080 },
- getSslPort : { -> 8443 },
- getProperty: { String prop ->
- String value = badProps[prop] ?: "no_value"
- logger.mock("getProperty(${prop}) -> ${value}")
- value
- },
- ] as StandardNiFiProperties
+ NiFiProperties mockProps = Mockito.mock(NiFiProperties.class)
+ Mockito.when(mockProps.getPort()).thenReturn(8080)
+ Mockito.when(mockProps.getSslPort()).thenReturn(8443)
+
+ Mockito.when(mockProps.getProperty(ArgumentMatchers.anyString())).thenAnswer(new Answer<Object>() {
+ @Override
+ Object answer(InvocationOnMock invocation) throws Throwable {
+ badProps[(String) invocation.getArgument(0)] ?: "no_value"
+ }
+ })
// Act
boolean bothConfigsPresent = JettyServer.bothHttpAndHttpsConnectorsConfigured(mockProps)
@@ -170,7 +175,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
logger.mock("getProperty(${prop}) -> ${value}")
value
},
- ] as StandardNiFiProperties
+ ] as NiFiProperties
Map httpsMap = [
(NiFiProperties.WEB_HTTP_HOST) : null,
@@ -184,7 +189,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
logger.mock("getProperty(${prop}) -> ${value}")
value
},
- ] as StandardNiFiProperties
+ ] as NiFiProperties
// Act
boolean bothConfigsPresentForHttp = JettyServer.bothHttpAndHttpsConnectorsConfigured(httpProps)
@@ -221,7 +226,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
getWebThreads : { -> NiFiProperties.DEFAULT_WEB_THREADS },
getWebMaxHeaderSize: { -> NiFiProperties.DEFAULT_WEB_MAX_HEADER_SIZE },
isHTTPSConfigured : { -> true }
- ] as StandardNiFiProperties
+ ] as NiFiProperties
// The web server should fail to start and exit Java
exit.expectSystemExitWithStatus(1)
@@ -253,7 +258,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
// Arrange
final String externalHostname = "localhost"
- NiFiProperties httpsProps = new StandardNiFiProperties(rawProperties: new Properties([
+ NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT): HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST): externalHostname,
(NiFiProperties.SECURITY_KEYSTORE): "src/test/resources/multiple_cert_keystore.p12",
@@ -285,7 +290,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
// Arrange
final String externalHostname = "localhost"
- NiFiProperties httpsProps = new StandardNiFiProperties(rawProperties: new Properties([
+ NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT): HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST): externalHostname,
(NiFiProperties.SECURITY_KEYSTORE): "src/test/resources/multiple_cert_keystore.jks",
@@ -308,7 +313,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
jetty.stop()
}
- private static JettyServer createJettyServer(StandardNiFiProperties httpsProps) {
+ private static JettyServer createJettyServer(NiFiProperties httpsProps) {
Server internalServer = new Server()
JettyServer jetty = new JettyServer(internalServer, httpsProps)
jetty.systemBundle = SystemBundle.create(httpsProps)
@@ -323,7 +328,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
// Arrange
final String externalHostname = "localhost"
- NiFiProperties httpsProps = new StandardNiFiProperties(rawProperties: new Properties([
+ NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT): HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST): externalHostname,
]))
@@ -462,7 +467,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTP_HOST) : "localhost",
(NiFiProperties.WEB_MAX_CONTENT_SIZE): "1 MB",
]
- NiFiProperties mockProps = new StandardNiFiProperties(new Properties(defaultProps))
+ NiFiProperties mockProps = new NiFiProperties(new Properties(defaultProps))
List<FilterHolder> filters = []
def mockWebContext = [
@@ -505,7 +510,7 @@ class JettyServerGroovyTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTP_PORT): "8080",
(NiFiProperties.WEB_HTTP_HOST): "localhost",
]
- NiFiProperties mockProps = new StandardNiFiProperties(new Properties(defaultProps))
+ NiFiProperties mockProps = new NiFiProperties(new Properties(defaultProps))
List<FilterHolder> filters = []
def mockWebContext = [
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/HostHeaderHandlerTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/HostHeaderHandlerTest.groovy
index a08be9d..80a3c58 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/HostHeaderHandlerTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/HostHeaderHandlerTest.groovy
@@ -17,7 +17,7 @@
package org.apache.nifi.web.server
import org.apache.commons.lang3.StringUtils
-import org.apache.nifi.properties.StandardNiFiProperties
+
import org.apache.nifi.util.NiFiProperties
import org.junit.After
import org.junit.Before
@@ -112,7 +112,7 @@ class HostHeaderHandlerTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_HOST): DEFAULT_HOSTNAME,
(NiFiProperties.WEB_HTTPS_PORT): "${DEFAULT_PORT}".toString(),
])
- NiFiProperties simpleProperties = new StandardNiFiProperties(rawProps)
+ NiFiProperties simpleProperties = new NiFiProperties(rawProps)
// Act
HostHeaderHandler handler = new HostHeaderHandler(simpleProperties)
@@ -141,7 +141,7 @@ class HostHeaderHandlerTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_PORT): "${DEFAULT_PORT}".toString(),
(NiFiProperties.WEB_PROXY_HOST): concatenatedHosts
])
- NiFiProperties simpleProperties = new StandardNiFiProperties(rawProps)
+ NiFiProperties simpleProperties = new NiFiProperties(rawProps)
HostHeaderHandler handler = new HostHeaderHandler(simpleProperties)
logger.info("Handler: ${handler}")
@@ -190,7 +190,7 @@ class HostHeaderHandlerTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_PORT): "${DEFAULT_PORT}".toString(),
(NiFiProperties.WEB_PROXY_HOST): concatenatedHosts
])
- NiFiProperties simpleProperties = new StandardNiFiProperties(rawProps)
+ NiFiProperties simpleProperties = new NiFiProperties(rawProps)
HostHeaderHandler handler = new HostHeaderHandler(simpleProperties)
logger.info("Handler: ${handler}")
@@ -236,7 +236,7 @@ class HostHeaderHandlerTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_PORT): "${DEFAULT_PORT}".toString(),
(NiFiProperties.WEB_PROXY_HOST): concatenatedHosts
])
- NiFiProperties simpleProperties = new StandardNiFiProperties(rawProps)
+ NiFiProperties simpleProperties = new NiFiProperties(rawProps)
HostHeaderHandler handler = new HostHeaderHandler(simpleProperties)
logger.info("Handler: ${handler}")
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy
index 2d4b496..356b067 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy
@@ -16,7 +16,7 @@
*/
package org.apache.nifi.web.api
-import org.apache.nifi.properties.StandardNiFiProperties
+
import org.apache.nifi.util.NiFiProperties
import org.glassfish.jersey.uri.internal.JerseyUriBuilder
import org.junit.After
@@ -109,7 +109,7 @@ class ApplicationResourceTest extends GroovyTestCase {
resource.setHttpServletRequest(mockRequest)
resource.setUriInfo(mockUriInfo)
- resource.properties = new StandardNiFiProperties()
+ resource.properties = new NiFiProperties()
resource
}
@@ -136,7 +136,7 @@ class ApplicationResourceTest extends GroovyTestCase {
// Arrange
ApplicationResource resource = buildApplicationResource()
logger.info("Allowed path(s): ${ALLOWED_PATH}")
- NiFiProperties niFiProperties = new StandardNiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties)
+ NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties)
resource.properties = niFiProperties
// Act
@@ -153,7 +153,7 @@ class ApplicationResourceTest extends GroovyTestCase {
ApplicationResource resource = buildApplicationResource()
String multipleAllowedPaths = [ALLOWED_PATH, "another/path", "a/third/path"].join(",")
logger.info("Allowed path(s): ${multipleAllowedPaths}")
- NiFiProperties niFiProperties = new StandardNiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties)
+ NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties)
resource.properties = niFiProperties
// Act
@@ -203,7 +203,7 @@ class ApplicationResourceTest extends GroovyTestCase {
// Arrange
ApplicationResource resource = buildApplicationResource([FORWARDED_CONTEXT_HTTP_HEADER])
logger.info("Allowed path(s): ${ALLOWED_PATH}")
- NiFiProperties niFiProperties = new StandardNiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties)
+ NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties)
resource.properties = niFiProperties
// Act
@@ -219,7 +219,7 @@ class ApplicationResourceTest extends GroovyTestCase {
// Arrange
ApplicationResource resource = buildApplicationResource([FORWARDED_PREFIX_HTTP_HEADER])
logger.info("Allowed path(s): ${ALLOWED_PATH}")
- NiFiProperties niFiProperties = new StandardNiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties)
+ NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties)
resource.properties = niFiProperties
// Act
@@ -236,7 +236,7 @@ class ApplicationResourceTest extends GroovyTestCase {
ApplicationResource resource = buildApplicationResource([FORWARDED_CONTEXT_HTTP_HEADER])
String multipleAllowedPaths = [ALLOWED_PATH, "another/path", "a/third/path"].join(",")
logger.info("Allowed path(s): ${multipleAllowedPaths}")
- NiFiProperties niFiProperties = new StandardNiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties)
+ NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties)
resource.properties = niFiProperties
// Act
@@ -253,7 +253,7 @@ class ApplicationResourceTest extends GroovyTestCase {
ApplicationResource resource = buildApplicationResource([FORWARDED_PREFIX_HTTP_HEADER])
String multipleAllowedPaths = [ALLOWED_PATH, "another/path", "a/third/path"].join(",")
logger.info("Allowed path(s): ${multipleAllowedPaths}")
- NiFiProperties niFiProperties = new StandardNiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties)
+ NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties)
resource.properties = niFiProperties
// Act
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
index 1fdab5d..1ef38b5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
@@ -168,7 +168,8 @@ public class NiFiTestServer {
}
public Client getClient() throws TlsException {
- return WebUtils.createClient(null, org.apache.nifi.security.util.SslContextFactory.createSslContext(StandardTlsConfiguration.fromNiFiProperties(properties)));
+ return WebUtils.createClient(null, org.apache.nifi.security.util.SslContextFactory.createSslContext(StandardTlsConfiguration
+ .fromNiFiProperties(properties)));
}
/**
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
index c6e433a..3e581c2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
@@ -16,24 +16,6 @@
*/
package org.apache.nifi.web.security.spring;
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.xml.XMLConstants;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.stream.XMLStreamReader;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authentication.AuthenticationResponse;
import org.apache.nifi.authentication.LoginCredentials;
@@ -50,11 +32,9 @@ import org.apache.nifi.authentication.generated.Provider;
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarCloseable;
-import org.apache.nifi.properties.AESSensitivePropertyProviderFactory;
+import org.apache.nifi.properties.PropertyProtectionScheme;
import org.apache.nifi.properties.SensitivePropertyProtectionException;
-import org.apache.nifi.properties.SensitivePropertyProvider;
-import org.apache.nifi.properties.SensitivePropertyProviderFactory;
-import org.apache.nifi.security.kms.CryptoUtils;
+import org.apache.nifi.properties.SensitivePropertyProviderFactoryAware;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
@@ -63,18 +43,36 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.xml.sax.SAXException;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/**
*
*/
-public class LoginIdentityProviderFactoryBean implements FactoryBean, DisposableBean, LoginIdentityProviderLookup {
+public class LoginIdentityProviderFactoryBean extends SensitivePropertyProviderFactoryAware
+ implements FactoryBean, DisposableBean, LoginIdentityProviderLookup {
private static final Logger logger = LoggerFactory.getLogger(LoginIdentityProviderFactoryBean.class);
private static final String LOGIN_IDENTITY_PROVIDERS_XSD = "/login-identity-providers.xsd";
private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authentication.generated";
private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
- private static SensitivePropertyProviderFactory SENSITIVE_PROPERTY_PROVIDER_FACTORY;
- private static SensitivePropertyProvider SENSITIVE_PROPERTY_PROVIDER;
+ private NiFiProperties properties;
/**
* Load the JAXBContext.
@@ -87,11 +85,14 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
}
}
- private NiFiProperties properties;
private ExtensionManager extensionManager;
private LoginIdentityProvider loginIdentityProvider;
private final Map<String, LoginIdentityProvider> loginIdentityProviders = new HashMap<>();
+ public void setProperties(NiFiProperties properties) {
+ this.properties = properties;
+ }
+
@Override
public LoginIdentityProvider getLoginIdentityProvider(String identifier) {
return loginIdentityProviders.get(identifier);
@@ -218,26 +219,9 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
return new StandardLoginIdentityProviderConfigurationContext(provider.getIdentifier(), providerProperties);
}
- private String decryptValue(String cipherText, String encryptionScheme) throws SensitivePropertyProtectionException {
- initializeSensitivePropertyProvider(encryptionScheme);
- return SENSITIVE_PROPERTY_PROVIDER.unprotect(cipherText);
- }
-
- private static void initializeSensitivePropertyProvider(String encryptionScheme) throws SensitivePropertyProtectionException {
- if (SENSITIVE_PROPERTY_PROVIDER == null || !SENSITIVE_PROPERTY_PROVIDER.getIdentifierKey().equalsIgnoreCase(encryptionScheme)) {
- try {
- String keyHex = getRootKey();
- SENSITIVE_PROPERTY_PROVIDER_FACTORY = new AESSensitivePropertyProviderFactory(keyHex);
- SENSITIVE_PROPERTY_PROVIDER = SENSITIVE_PROPERTY_PROVIDER_FACTORY.getProvider();
- } catch (IOException e) {
- logger.error("Error extracting master key from bootstrap.conf for login identity provider decryption", e);
- throw new SensitivePropertyProtectionException("Could not read root key from bootstrap.conf");
- }
- }
- }
-
- private static String getRootKey() throws IOException {
- return CryptoUtils.extractKeyFromBootstrapFile();
+ private String decryptValue(final String cipherText, final String protectionScheme) throws SensitivePropertyProtectionException {
+ return getSensitivePropertyProviderFactory().getProvider(PropertyProtectionScheme.fromIdentifier(protectionScheme))
+ .unprotect(cipherText);
}
private void performMethodInjection(final LoginIdentityProvider instance, final Class loginIdentityProviderClass)
@@ -356,10 +340,6 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
}
}
- public void setProperties(NiFiProperties properties) {
- this.properties = properties;
- }
-
public void setExtensionManager(ExtensionManager extensionManager) {
this.extensionManager = extensionManager;
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/OidcServiceGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/OidcServiceGroovyTest.groovy
index 5480ad4..011788f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/OidcServiceGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/OidcServiceGroovyTest.groovy
@@ -45,16 +45,15 @@ class OidcServiceGroovyTest extends GroovyTestCase {
private static final Key SIGNING_KEY = new Key(id: 1, identity: "signingKey", key: "mock-signing-key-value")
private static final Map<String, Object> DEFAULT_NIFI_PROPERTIES = [
- isOidcEnabled : true,
- getOidcDiscoveryUrl : "https://localhost/oidc",
- isLoginIdentityProviderEnabled: false,
- isKnoxSsoEnabled : false,
- getOidcConnectTimeout : "1000",
- getOidcReadTimeout : "1000",
- getOidcClientId : "expected_client_id",
- getOidcClientSecret : "expected_client_secret",
- getOidcClaimIdentifyingUser : "username",
- getOidcPreferredJwsAlgorithm : ""
+ "nifi.security.user.oidc.discovery.url" : "https://localhost/oidc",
+ "nifi.security.user.login.identity.provider" : "provider",
+ "nifi.security.user.knox.url" : "url",
+ "nifi.security.user.oidc.connect.timeout" : "1000",
+ "nifi.security.user.oidc.read.timeout" : "1000",
+ "nifi.security.user.oidc.client.id" : "expected_client_id",
+ "nifi.security.user.oidc.client.secret" : "expected_client_secret",
+ "nifi.security.user.oidc.claim.identifying.user" : "username",
+ "nifi.security.user.oidc.preferred.jwsalgorithm" : ""
]
// Mock collaborators
@@ -89,10 +88,7 @@ class OidcServiceGroovyTest extends GroovyTestCase {
private static NiFiProperties buildNiFiProperties(Map<String, Object> props = [:]) {
def combinedProps = DEFAULT_NIFI_PROPERTIES + props
- def mockNFP = combinedProps.collectEntries { String k, def v ->
- [k, { -> return v }]
- }
- mockNFP as NiFiProperties
+ new NiFiProperties(combinedProps)
}
private static JwtService buildJwtService() {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/StandardOidcIdentityProviderGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/StandardOidcIdentityProviderGroovyTest.groovy
index c52d4cd..089f28a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/StandardOidcIdentityProviderGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/oidc/StandardOidcIdentityProviderGroovyTest.groovy
@@ -68,17 +68,15 @@ class StandardOidcIdentityProviderGroovyTest extends GroovyTestCase {
private static final Key SIGNING_KEY = new Key(id: 1, identity: "signingKey", key: "mock-signing-key-value")
private static final Map<String, Object> DEFAULT_NIFI_PROPERTIES = [
-// isOidcEnabled : false,
- isOidcEnabled : true,
- getOidcDiscoveryUrl : "https://localhost/oidc",
- isLoginIdentityProviderEnabled: false,
- isKnoxSsoEnabled : false,
- getOidcConnectTimeout : 1000,
- getOidcReadTimeout : 1000,
- getOidcClientId : "expected_client_id",
- getOidcClientSecret : "expected_client_secret",
- getOidcClaimIdentifyingUser : "username",
- getOidcPreferredJwsAlgorithm : ""
+ "nifi.security.user.oidc.discovery.url" : "https://localhost/oidc",
+ "nifi.security.user.login.identity.provider" : "provider",
+ "nifi.security.user.knox.url" : "url",
+ "nifi.security.user.oidc.connect.timeout" : "1000",
+ "nifi.security.user.oidc.read.timeout" : "1000",
+ "nifi.security.user.oidc.client.id" : "expected_client_id",
+ "nifi.security.user.oidc.client.secret" : "expected_client_secret",
+ "nifi.security.user.oidc.claim.identifying.user" : "username",
+ "nifi.security.user.oidc.preferred.jwsalgorithm" : ""
]
// Mock collaborators
@@ -108,10 +106,7 @@ class StandardOidcIdentityProviderGroovyTest extends GroovyTestCase {
private static NiFiProperties buildNiFiProperties(Map<String, Object> props = [:]) {
def combinedProps = DEFAULT_NIFI_PROPERTIES + props
- def mockNFP = combinedProps.collectEntries { String k, def v ->
- [k, { -> return v }]
- }
- mockNFP as NiFiProperties
+ new NiFiProperties(combinedProps)
}
private static JwtService buildJwtService() {
@@ -414,7 +409,9 @@ class StandardOidcIdentityProviderGroovyTest extends GroovyTestCase {
@Test
void testConvertOIDCTokenToLoginAuthenticationTokenShouldHandleNoEmailClaimHasFallbackClaims() {
// Arrange
- StandardOidcIdentityProvider soip = buildIdentityProviderWithMockTokenValidator(["getOidcClaimIdentifyingUser": "email", "getOidcFallbackClaimsIdentifyingUser": ["upn"] ])
+ StandardOidcIdentityProvider soip = buildIdentityProviderWithMockTokenValidator(
+ ["nifi.security.user.oidc.claim.identifying.user": "email",
+ "nifi.security.user.oidc.fallback.claims.identifying.user": "upn" ])
String expectedUpn = "xxx@aaddomain";
OIDCTokenResponse mockResponse = mockOIDCTokenResponse(["email": null, "upn": expectedUpn])
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBeanTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBeanTest.groovy
index 13eab19..eca7efb 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBeanTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/groovy/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBeanTest.groovy
@@ -18,13 +18,9 @@ package org.apache.nifi.web.security.spring
import org.apache.nifi.authentication.generated.Property
import org.apache.nifi.authentication.generated.Provider
-import org.apache.nifi.properties.AESSensitivePropertyProvider
import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.junit.After
-import org.junit.AfterClass
import org.junit.Before
import org.junit.BeforeClass
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -54,8 +50,10 @@ class LoginIdentityProviderFactoryBeanTest extends GroovyTestCase {
private static final String PASSWORD = "thisIsABadPassword"
+ private LoginIdentityProviderFactoryBean bean
+
@BeforeClass
- public static void setUpOnce() throws Exception {
+ static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
@@ -63,47 +61,16 @@ class LoginIdentityProviderFactoryBeanTest extends GroovyTestCase {
}
}
- @AfterClass
- public static void tearDownOnce() throws Exception {
- }
-
@Before
- public void setUp() throws Exception {
- LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER = new AESSensitivePropertyProvider(KEY_HEX)
- }
-
- @After
- public void tearDown() throws Exception {
- LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER = null
- LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER_FACTORY = null
+ void setUp() {
+ bean = new LoginIdentityProviderFactoryBean()
+ bean.configureSensitivePropertyProviderFactory(KEY_HEX, null)
}
private static boolean isUnlimitedStrengthCryptoAvailable() {
Cipher.getMaxAllowedKeyLength("AES") > 128
}
- private static int getKeyLength(String keyHex = KEY_HEX) {
- keyHex?.size() * 4
- }
-
- @Ignore("Can't test without overloading static metaClass method")
- @Test
- void testShouldInitializeSensitivePropertyProvider() {
- // Arrange
- assert !LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER
- assert !LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER_FACTORY
-
- logger.info("Encryption scheme: ${ENCRYPTION_SCHEME}")
-
- // Act
- LoginIdentityProviderFactoryBean.initializeSensitivePropertyProvider(ENCRYPTION_SCHEME)
-
- // Assert
- assert LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER
- assert LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER_FACTORY
- assert LoginIdentityProviderFactoryBean.SENSITIVE_PROPERTY_PROVIDER.getIdentifierKey() == ENCRYPTION_SCHEME
- }
-
@Test
void testShouldDecryptValue() {
// Arrange
@@ -111,7 +78,7 @@ class LoginIdentityProviderFactoryBeanTest extends GroovyTestCase {
logger.info("Cipher text: ${CIPHER_TEXT}")
// Act
- String decrypted = new LoginIdentityProviderFactoryBean().decryptValue(CIPHER_TEXT, ENCRYPTION_SCHEME)
+ String decrypted = bean.decryptValue(CIPHER_TEXT, ENCRYPTION_SCHEME)
logger.info("Decrypted ${CIPHER_TEXT} -> ${decrypted}")
// Assert
@@ -129,7 +96,6 @@ class LoginIdentityProviderFactoryBeanTest extends GroovyTestCase {
encryptedProvider.property = [managerPasswordProperty]
logger.info("Manager Password property: ${managerPasswordProperty.dump()}")
- def bean = new LoginIdentityProviderFactoryBean()
// Act
def context = bean.loadLoginIdentityProviderConfiguration(encryptedProvider)
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProviderTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProviderTest.java
index e1232fa..b4a85f3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProviderTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProviderTest.java
@@ -28,7 +28,6 @@ import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserDetails;
import org.apache.nifi.idp.IdpType;
import org.apache.nifi.idp.IdpUserGroup;
-import org.apache.nifi.properties.StandardNiFiProperties;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.InvalidAuthenticationException;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
@@ -87,7 +86,7 @@ public class JwtAuthenticationProviderTest {
Properties props = new Properties();
props.put(properties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX, "^(.*?)@(.*?)$");
props.put(properties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX, "$1");
- properties = new StandardNiFiProperties(props);
+ properties = new NiFiProperties(props);
jwtAuthenticationProvider = new JwtAuthenticationProvider(jwtService, properties, authorizer, idpUserGroupService);
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
index 513eb0c..a89082f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/JwtServiceTest.java
@@ -25,7 +25,7 @@ import org.apache.nifi.authorization.user.StandardNiFiUser;
import org.apache.nifi.authorization.util.IdentityMapping;
import org.apache.nifi.authorization.util.IdentityMappingUtil;
import org.apache.nifi.key.Key;
-import org.apache.nifi.properties.StandardNiFiProperties;
+import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
import org.codehaus.jettison.json.JSONObject;
import org.junit.After;
@@ -244,7 +244,7 @@ public class JwtServiceTest {
Properties props = new Properties();
props.setProperty(SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX+"kerb", "^(.*?)@(.*?)$");
props.setProperty(SECURITY_IDENTITY_MAPPING_VALUE_PREFIX+"kerb", "$1");
- identityMappings = IdentityMappingUtil.getIdentityMappings(new StandardNiFiProperties(props));
+ identityMappings = IdentityMappingUtil.getIdentityMappings(new NiFiProperties(props));
}
@After
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml/impl/TestStandardSAMLService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml/impl/TestStandardSAMLService.java
index 745bcec..8ad2fc4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml/impl/TestStandardSAMLService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/saml/impl/TestStandardSAMLService.java
@@ -28,6 +28,8 @@ import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -72,6 +74,15 @@ public class TestStandardSAMLService {
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE)).thenReturn("src/test/resources/saml/truststore.jks");
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD)).thenReturn("passwordpassword");
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE)).thenReturn("JKS");
+ when(properties.getPropertyKeys()).thenReturn(new HashSet<>(Arrays.asList(
+ NiFiProperties.SECURITY_KEYSTORE,
+ NiFiProperties.SECURITY_KEYSTORE_PASSWD,
+ NiFiProperties.SECURITY_KEY_PASSWD,
+ NiFiProperties.SECURITY_KEYSTORE_TYPE,
+ NiFiProperties.SECURITY_TRUSTSTORE,
+ NiFiProperties.SECURITY_TRUSTSTORE_PASSWD,
+ NiFiProperties.SECURITY_TRUSTSTORE_TYPE
+ )));
when(properties.isSamlEnabled()).thenReturn(true);
when(properties.getSamlServiceProviderEntityId()).thenReturn(spEntityId);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/pom.xml
index bce4655..452b843 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/pom.xml
@@ -147,6 +147,11 @@
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-sensitive-property-provider</artifactId>
+ <version>1.14.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-authorization</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
index 24ee0b5..24d80d6 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
@@ -16,8 +16,6 @@
*/
package org.apache.nifi.provenance;
-import org.apache.commons.codec.DecoderException;
-import org.apache.commons.codec.binary.Hex;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.provenance.serialization.RecordReaders;
@@ -35,7 +33,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.KeyManagementException;
@@ -89,7 +86,7 @@ public class EncryptedWriteAheadProvenanceRepository extends WriteAheadProvenanc
try {
KeyProvider keyProvider;
if (KeyProviderFactory.requiresRootKey(getConfig().getKeyProviderImplementation())) {
- SecretKey rootKey = getRootKey();
+ SecretKey rootKey = CryptoUtils.getRootKey();
keyProvider = buildKeyProvider(rootKey);
} else {
keyProvider = buildKeyProvider();
@@ -151,15 +148,4 @@ public class EncryptedWriteAheadProvenanceRepository extends WriteAheadProvenanc
return KeyProviderFactory.buildKeyProvider(implementationClassName, config.getKeyProviderLocation(), config.getKeyId(), config.getEncryptionKeys(), rootKey);
}
-
- private static SecretKey getRootKey() throws KeyManagementException {
- try {
- // Get the root encryption key from bootstrap.conf
- String rootKeyHex = CryptoUtils.extractKeyFromBootstrapFile();
- return new SecretKeySpec(Hex.decodeHex(rootKeyHex.toCharArray()), "AES");
- } catch (IOException | DecoderException e) {
- logger.error("Encountered an error: ", e);
- throw new KeyManagementException(e);
- }
- }
}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authentication/IdentityProviderFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authentication/IdentityProviderFactory.java
index 3c2a3f4..9a06d59 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authentication/IdentityProviderFactory.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authentication/IdentityProviderFactory.java
@@ -17,10 +17,10 @@
package org.apache.nifi.registry.security.authentication;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProvider;
import org.apache.nifi.registry.extension.ExtensionManager;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
-import org.apache.nifi.registry.properties.SensitivePropertyProtectionException;
-import org.apache.nifi.registry.properties.SensitivePropertyProvider;
import org.apache.nifi.registry.security.authentication.annotation.IdentityProviderContext;
import org.apache.nifi.registry.security.authentication.generated.IdentityProviders;
import org.apache.nifi.registry.security.authentication.generated.Property;
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
index 1fb3d90..3d1e790 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
@@ -18,12 +18,12 @@ package org.apache.nifi.registry.security.authorization;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProvider;
import org.apache.nifi.registry.extension.ExtensionClassLoader;
import org.apache.nifi.registry.extension.ExtensionCloseable;
import org.apache.nifi.registry.extension.ExtensionManager;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
-import org.apache.nifi.registry.properties.SensitivePropertyProtectionException;
-import org.apache.nifi.registry.properties.SensitivePropertyProvider;
import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext;
import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/crypto/SensitivePropertyProviderConfiguration.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/crypto/SensitivePropertyProviderConfiguration.java
index 7859492..055b71e 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/crypto/SensitivePropertyProviderConfiguration.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/crypto/SensitivePropertyProviderConfiguration.java
@@ -16,25 +16,25 @@
*/
package org.apache.nifi.registry.security.crypto;
-import org.apache.nifi.registry.properties.AESSensitivePropertyProvider;
-import org.apache.nifi.registry.properties.SensitivePropertyProtectionException;
-import org.apache.nifi.registry.properties.SensitivePropertyProvider;
-import org.apache.nifi.registry.properties.SensitivePropertyProviderFactory;
+import org.apache.nifi.properties.PropertyProtectionScheme;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory;
+import org.apache.nifi.registry.properties.util.NiFiRegistryBootstrapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import javax.crypto.NoSuchPaddingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
+import java.io.IOException;
@Configuration
-public class SensitivePropertyProviderConfiguration implements SensitivePropertyProviderFactory {
-
+public class SensitivePropertyProviderConfiguration {
private static final Logger logger = LoggerFactory.getLogger(SensitivePropertyProviderConfiguration.class);
+ private static final PropertyProtectionScheme DEFAULT_SCHEME = PropertyProtectionScheme.AES_GCM;
+
@Autowired(required = false)
private CryptoKeyProvider masterKeyProvider;
@@ -43,7 +43,6 @@ public class SensitivePropertyProviderConfiguration implements SensitiveProperty
* or null if the master key is not present.
*/
@Bean
- @Override
public SensitivePropertyProvider getProvider() {
if (masterKeyProvider == null || masterKeyProvider.isEmpty()) {
// This NiFi Registry was not configured with a master key, so the assumption is
@@ -56,11 +55,18 @@ public class SensitivePropertyProviderConfiguration implements SensitiveProperty
// returned provider, which has a copy of the sensitive master key material
// to be reaped when it goes out of scope in order to decrease the time
// key material is held in memory.
- String key = masterKeyProvider.getKey();
- return new AESSensitivePropertyProvider(masterKeyProvider.getKey());
- } catch (MissingCryptoKeyException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
- logger.warn("Error creating AES Sensitive Property Provider", e);
- throw new SensitivePropertyProtectionException("Error creating AES Sensitive Property Provider", e);
+ return StandardSensitivePropertyProviderFactory
+ .withKeyAndBootstrapSupplier(masterKeyProvider.getKey(), () -> {
+ try {
+ return NiFiRegistryBootstrapUtils.loadBootstrapProperties();
+ } catch (IOException e) {
+ throw new SensitivePropertyProtectionException("Error creating Sensitive Property Provider", e);
+ }
+ })
+ .getProvider(DEFAULT_SCHEME);
+ } catch (final MissingCryptoKeyException e) {
+ logger.warn("Error creating Sensitive Property Provider", e);
+ throw new SensitivePropertyProtectionException("Error creating Sensitive Property Provider", e);
}
}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/TestStandardProviderFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/TestStandardProviderFactory.java
index 7fb8dee..75e10c6 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/TestStandardProviderFactory.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/TestStandardProviderFactory.java
@@ -26,6 +26,7 @@ import org.mockito.Mockito;
import javax.sql.DataSource;
import java.net.URL;
+import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -36,8 +37,9 @@ public class TestStandardProviderFactory {
@Test
public void testGetProvidersSuccess() {
- final NiFiRegistryProperties props = new NiFiRegistryProperties();
- props.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-good.xml");
+ final Properties properties = new Properties();
+ properties.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-good.xml");
+ final NiFiRegistryProperties props = new NiFiRegistryProperties(properties);
final ExtensionManager extensionManager = Mockito.mock(ExtensionManager.class);
when(extensionManager.getExtensionClassLoader(any(String.class)))
@@ -67,8 +69,9 @@ public class TestStandardProviderFactory {
@Test(expected = ProviderFactoryException.class)
public void testGetFlowProviderBeforeInitializingShouldThrowException() {
- final NiFiRegistryProperties props = new NiFiRegistryProperties();
- props.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-good.xml");
+ final Properties properties = new Properties();
+ properties.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-good.xml");
+ final NiFiRegistryProperties props = new NiFiRegistryProperties(properties);
final ExtensionManager extensionManager = Mockito.mock(ExtensionManager.class);
when(extensionManager.getExtensionClassLoader(any(String.class)))
@@ -82,8 +85,9 @@ public class TestStandardProviderFactory {
@Test(expected = ProviderFactoryException.class)
public void testProvidersConfigDoesNotExist() {
- final NiFiRegistryProperties props = new NiFiRegistryProperties();
- props.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-does-not-exist.xml");
+ final Properties properties = new Properties();
+ properties.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-does-not-exist.xml");
+ final NiFiRegistryProperties props = new NiFiRegistryProperties(properties);
final ExtensionManager extensionManager = Mockito.mock(ExtensionManager.class);
when(extensionManager.getExtensionClassLoader(any(String.class)))
@@ -97,8 +101,9 @@ public class TestStandardProviderFactory {
@Test(expected = ProviderFactoryException.class)
public void testFlowProviderClassNotFound() {
- final NiFiRegistryProperties props = new NiFiRegistryProperties();
- props.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-class-not-found.xml");
+ final Properties properties = new Properties();
+ properties.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/providers-class-not-found.xml");
+ final NiFiRegistryProperties props = new NiFiRegistryProperties(properties);
final ExtensionManager extensionManager = Mockito.mock(ExtensionManager.class);
when(extensionManager.getExtensionClassLoader(any(String.class)))
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/hook/TestScriptEventHookProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/hook/TestScriptEventHookProvider.java
index 39d45ae..0085bba 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/hook/TestScriptEventHookProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/hook/TestScriptEventHookProvider.java
@@ -28,6 +28,7 @@ import org.mockito.Mockito;
import javax.sql.DataSource;
import java.net.URL;
+import java.util.Properties;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@@ -36,8 +37,9 @@ public class TestScriptEventHookProvider {
@Test(expected = ProviderCreationException.class)
public void testBadScriptProvider() {
- final NiFiRegistryProperties props = new NiFiRegistryProperties();
- props.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/hook/bad-script-provider.xml");
+ final Properties properties = new Properties();
+ properties.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, "src/test/resources/provider/hook/bad-script-provider.xml");
+ final NiFiRegistryProperties props = new NiFiRegistryProperties(properties);
final ExtensionManager extensionManager = Mockito.mock(ExtensionManager.class);
when(extensionManager.getExtensionClassLoader(any(String.class)))
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseAccessPolicyProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseAccessPolicyProvider.java
index 7f522ff..9c2cb60 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseAccessPolicyProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseAccessPolicyProvider.java
@@ -45,6 +45,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
import static org.junit.Assert.assertEquals;
@@ -286,9 +287,11 @@ public class TestDatabaseAccessPolicyProvider extends DatabaseBaseTest {
@Test
public void testOnConfiguredAppliesIdentityMappings() {
+ final Properties props = new Properties();
// Set up an identity mapping for kerberos principals
- properties.setProperty("nifi.registry.security.identity.mapping.pattern.kerb", "^(.*?)@(.*?)$");
- properties.setProperty("nifi.registry.security.identity.mapping.value.kerb", "$1");
+ props.setProperty("nifi.registry.security.identity.mapping.pattern.kerb", "^(.*?)@(.*?)$");
+ props.setProperty("nifi.registry.security.identity.mapping.value.kerb", "$1");
+ properties = new NiFiRegistryProperties(props);
identityMapper = new DefaultIdentityMapper(properties);
((DatabaseAccessPolicyProvider)policyProvider).setIdentityMapper(identityMapper);
@@ -311,10 +314,12 @@ public class TestDatabaseAccessPolicyProvider extends DatabaseBaseTest {
@Test
public void testOnConfiguredAppliesGroupMappings() {
+ final Properties props = new Properties();
// Set up an identity mapping for kerberos principals
- properties.setProperty("nifi.registry.security.group.mapping.pattern.anyGroup", "^(.*)$");
- properties.setProperty("nifi.registry.security.group.mapping.value.anyGroup", "$1");
- properties.setProperty("nifi.registry.security.group.mapping.transform.anyGroup", "LOWER");
+ props.setProperty("nifi.registry.security.group.mapping.pattern.anyGroup", "^(.*)$");
+ props.setProperty("nifi.registry.security.group.mapping.value.anyGroup", "$1");
+ props.setProperty("nifi.registry.security.group.mapping.transform.anyGroup", "LOWER");
+ properties = new NiFiRegistryProperties(props);
identityMapper = new DefaultIdentityMapper(properties);
((DatabaseAccessPolicyProvider)policyProvider).setIdentityMapper(identityMapper);
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseUserGroupProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseUserGroupProvider.java
index 0f8d432..dc14e66 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseUserGroupProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/database/TestDatabaseUserGroupProvider.java
@@ -35,6 +35,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
import java.util.UUID;
@@ -179,9 +180,11 @@ public class TestDatabaseUserGroupProvider extends DatabaseBaseTest {
@Test
public void testOnConfiguredAppliesIdentityMappingsToInitialUsers() {
+ final Properties props = new Properties();
// Set up an identity mapping for kerberos principals
- properties.setProperty("nifi.registry.security.identity.mapping.pattern.kerb", "^(.*?)@(.*?)$");
- properties.setProperty("nifi.registry.security.identity.mapping.value.kerb", "$1");
+ props.setProperty("nifi.registry.security.identity.mapping.pattern.kerb", "^(.*?)@(.*?)$");
+ props.setProperty("nifi.registry.security.identity.mapping.value.kerb", "$1");
+ properties = new NiFiRegistryProperties(props);
identityMapper = new DefaultIdentityMapper(properties);
((DatabaseUserGroupProvider)userGroupProvider).setIdentityMapper(identityMapper);
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml b/nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml
index 6458aec..77c7c14 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/pom.xml
@@ -72,5 +72,11 @@
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-sensitive-property-provider</artifactId>
+ <version>1.14.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/AESSensitivePropertyProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/AESSensitivePropertyProvider.java
deleted file mode 100644
index b7d1d2e..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/AESSensitivePropertyProvider.java
+++ /dev/null
@@ -1,265 +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.registry.properties;
-
-import org.apache.commons.lang3.StringUtils;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.encoders.DecoderException;
-import org.bouncycastle.util.encoders.EncoderException;
-import org.bouncycastle.util.encoders.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import java.nio.charset.StandardCharsets;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public class AESSensitivePropertyProvider implements SensitivePropertyProvider {
- private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProvider.class);
-
- private static final String IMPLEMENTATION_NAME = "AES Sensitive Property Provider";
- private static final String IMPLEMENTATION_KEY = "aes/gcm/";
- private static final String ALGORITHM = "AES/GCM/NoPadding";
- private static final String PROVIDER = "BC";
- private static final String DELIMITER = "||"; // "|" is not a valid Base64 character, so ensured not to be present in cipher text
- private static final int IV_LENGTH = 12;
- private static final int MIN_CIPHER_TEXT_LENGTH = IV_LENGTH * 4 / 3 + DELIMITER.length() + 1;
-
- private Cipher cipher;
- private final SecretKey key;
-
- public AESSensitivePropertyProvider(String keyHex) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
- byte[] key = validateKey(keyHex);
-
- try {
- Security.addProvider(new BouncyCastleProvider());
- cipher = Cipher.getInstance(ALGORITHM, PROVIDER);
- // Only store the key if the cipher was initialized successfully
- this.key = new SecretKeySpec(key, "AES");
- } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
- logger.error("Encountered an error initializing the {}: {}", IMPLEMENTATION_NAME, e.getMessage());
- throw new SensitivePropertyProtectionException("Error initializing the protection cipher", e);
- }
- }
-
- private byte[] validateKey(String keyHex) {
- if (keyHex == null || StringUtils.isBlank(keyHex)) {
- throw new SensitivePropertyProtectionException("The key cannot be empty");
- }
- keyHex = formatHexKey(keyHex);
- if (!isHexKeyValid(keyHex)) {
- throw new SensitivePropertyProtectionException("The key must be a valid hexadecimal key");
- }
- byte[] key = Hex.decode(keyHex);
- final List<Integer> validKeyLengths = getValidKeyLengths();
- if (!validKeyLengths.contains(key.length * 8)) {
- List<String> validKeyLengthsAsStrings = validKeyLengths.stream().map(i -> Integer.toString(i)).collect(Collectors.toList());
- throw new SensitivePropertyProtectionException("The key (" + key.length * 8 + " bits) must be a valid length: " + StringUtils.join(validKeyLengthsAsStrings, ", "));
- }
- return key;
- }
-
- public AESSensitivePropertyProvider(byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
- this(key == null ? "" : Hex.toHexString(key));
- }
-
- private static String formatHexKey(String input) {
- if (input == null || StringUtils.isBlank(input)) {
- return "";
- }
- return input.replaceAll("[^0-9a-fA-F]", "").toLowerCase();
- }
-
- private static boolean isHexKeyValid(String key) {
- if (key == null || StringUtils.isBlank(key)) {
- return false;
- }
- // Key length is in "nibbles" (i.e. one hex char = 4 bits)
- return getValidKeyLengths().contains(key.length() * 4) && key.matches("^[0-9a-fA-F]*$");
- }
-
- private static List<Integer> getValidKeyLengths() {
- List<Integer> validLengths = new ArrayList<>();
- validLengths.add(128);
-
- try {
- if (Cipher.getMaxAllowedKeyLength("AES") > 128) {
- validLengths.add(192);
- validLengths.add(256);
- } else {
- logger.warn("JCE Unlimited Strength Cryptography Jurisdiction policies are not available, so the max key length is 128 bits");
- }
- } catch (NoSuchAlgorithmException e) {
- logger.warn("Encountered an error determining the max key length", e);
- }
-
- return validLengths;
- }
-
- /**
- * Returns the name of the underlying implementation.
- *
- * @return the name of this sensitive property provider
- */
- @Override
- public String getName() {
- return IMPLEMENTATION_NAME;
- }
-
- /**
- * Returns the key used to identify the provider implementation in {@code nifi.properties}.
- *
- * @return the key to persist in the sibling property
- */
- @Override
- public String getIdentifierKey() {
- return IMPLEMENTATION_KEY + getKeySize(Hex.toHexString(key.getEncoded()));
- }
-
- private int getKeySize(String key) {
- if (StringUtils.isBlank(key)) {
- return 0;
- } else {
- // A key in hexadecimal format has one char per nibble (4 bits)
- return formatHexKey(key).length() * 4;
- }
- }
-
- /**
- * Returns the encrypted cipher text.
- *
- * @param unprotectedValue the sensitive value
- * @return the value to persist in the {@code nifi.properties} file
- * @throws SensitivePropertyProtectionException if there is an exception encrypting the value
- */
- @Override
- public String protect(String unprotectedValue) throws SensitivePropertyProtectionException {
- if (unprotectedValue == null || unprotectedValue.trim().length() == 0) {
- throw new IllegalArgumentException("Cannot encrypt an empty value");
- }
-
- // Generate IV
- byte[] iv = generateIV();
- if (iv.length < IV_LENGTH) {
- throw new IllegalArgumentException("The IV (" + iv.length + " bytes) must be at least " + IV_LENGTH + " bytes");
- }
-
- try {
- // Initialize cipher for encryption
- cipher.init(Cipher.ENCRYPT_MODE, this.key, new IvParameterSpec(iv));
-
- byte[] plainBytes = unprotectedValue.getBytes(StandardCharsets.UTF_8);
- byte[] cipherBytes = cipher.doFinal(plainBytes);
- logger.info(getName() + " encrypted a sensitive value successfully");
- return base64Encode(iv) + DELIMITER + base64Encode(cipherBytes);
- // return Base64.toBase64String(iv) + DELIMITER + Base64.toBase64String(cipherBytes);
- } catch (BadPaddingException | IllegalBlockSizeException | EncoderException | InvalidAlgorithmParameterException | InvalidKeyException e) {
- final String msg = "Error encrypting a protected value";
- logger.error(msg, e);
- throw new SensitivePropertyProtectionException(msg, e);
- }
- }
-
- private String base64Encode(byte[] input) {
- return Base64.toBase64String(input).replaceAll("=", "");
- }
-
- /**
- * Generates a new random IV of 12 bytes using {@link SecureRandom}.
- *
- * @return the IV
- */
- private byte[] generateIV() {
- byte[] iv = new byte[IV_LENGTH];
- new SecureRandom().nextBytes(iv);
- return iv;
- }
-
- /**
- * Returns the decrypted plaintext.
- *
- * @param protectedValue the cipher text read from the {@code nifi.properties} file
- * @return the raw value to be used by the application
- * @throws SensitivePropertyProtectionException if there is an error decrypting the cipher text
- */
- @Override
- public String unprotect(String protectedValue) throws SensitivePropertyProtectionException {
- if (protectedValue == null || protectedValue.trim().length() < MIN_CIPHER_TEXT_LENGTH) {
- throw new IllegalArgumentException("Cannot decrypt a cipher text shorter than " + MIN_CIPHER_TEXT_LENGTH + " chars");
- }
-
- if (!protectedValue.contains(DELIMITER)) {
- throw new IllegalArgumentException("The cipher text does not contain the delimiter " + DELIMITER + " -- it should be of the form Base64(IV) || Base64(cipherText)");
- }
-
- protectedValue = protectedValue.trim();
-
- final String IV_B64 = protectedValue.substring(0, protectedValue.indexOf(DELIMITER));
- byte[] iv = Base64.decode(IV_B64);
- if (iv.length < IV_LENGTH) {
- throw new IllegalArgumentException("The IV (" + iv.length + " bytes) must be at least " + IV_LENGTH + " bytes");
- }
-
- String CIPHERTEXT_B64 = protectedValue.substring(protectedValue.indexOf(DELIMITER) + 2);
-
- // Restore the = padding if necessary to reconstitute the GCM MAC check
- if (CIPHERTEXT_B64.length() % 4 != 0) {
- final int paddedLength = CIPHERTEXT_B64.length() + 4 - (CIPHERTEXT_B64.length() % 4);
- CIPHERTEXT_B64 = StringUtils.rightPad(CIPHERTEXT_B64, paddedLength, '=');
- }
-
- try {
- byte[] cipherBytes = Base64.decode(CIPHERTEXT_B64);
-
- cipher.init(Cipher.DECRYPT_MODE, this.key, new IvParameterSpec(iv));
- byte[] plainBytes = cipher.doFinal(cipherBytes);
- logger.debug(getName() + " decrypted a sensitive value successfully");
- return new String(plainBytes, StandardCharsets.UTF_8);
- } catch (BadPaddingException | IllegalBlockSizeException | DecoderException | InvalidAlgorithmParameterException | InvalidKeyException e) {
- final String msg = "Error decrypting a protected value";
- logger.error(msg, e);
- throw new SensitivePropertyProtectionException(msg, e);
- }
- }
-
- public static int getIvLength() {
- return IV_LENGTH;
- }
-
- public static int getMinCipherTextLength() {
- return MIN_CIPHER_TEXT_LENGTH;
- }
-
- public static String getDelimiter() {
- return DELIMITER;
- }
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/AESSensitivePropertyProviderFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/AESSensitivePropertyProviderFactory.java
deleted file mode 100644
index 5c24a73..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/AESSensitivePropertyProviderFactory.java
+++ /dev/null
@@ -1,54 +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.registry.properties;
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.crypto.NoSuchPaddingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-
-public class AESSensitivePropertyProviderFactory implements SensitivePropertyProviderFactory {
- private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProviderFactory.class);
-
- private String keyHex;
-
- public AESSensitivePropertyProviderFactory(String keyHex) {
- this.keyHex = keyHex;
- }
-
- public SensitivePropertyProvider getProvider() throws SensitivePropertyProtectionException {
- try {
- if (keyHex != null && !StringUtils.isBlank(keyHex)) {
- return new AESSensitivePropertyProvider(keyHex);
- } else {
- throw new SensitivePropertyProtectionException("The provider factory cannot generate providers without a key");
- }
- } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
- String msg = "Error creating AES Sensitive Property Provider";
- logger.warn(msg, e);
- throw new SensitivePropertyProtectionException(msg, e);
- }
- }
-
- @Override
- public String toString() {
- return "SensitivePropertyProviderFactory for creating AESSensitivePropertyProviders";
- }
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/MultipleSensitivePropertyProtectionException.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/MultipleSensitivePropertyProtectionException.java
deleted file mode 100644
index df4047f..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/MultipleSensitivePropertyProtectionException.java
+++ /dev/null
@@ -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.registry.properties;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-public class MultipleSensitivePropertyProtectionException extends SensitivePropertyProtectionException {
-
- private Set<String> failedKeys;
-
- /**
- * Constructs a new throwable with {@code null} as its detail message.
- * The cause is not initialized, and may subsequently be initialized by a
- * call to {@link #initCause}.
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- */
- public MultipleSensitivePropertyProtectionException() {
- }
-
- /**
- * Constructs a new throwable with the specified detail message. The
- * cause is not initialized, and may subsequently be initialized by
- * a call to {@link #initCause}.
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- *
- * @param message the detail message. The detail message is saved for
- * later retrieval by the {@link #getMessage()} method.
- */
- public MultipleSensitivePropertyProtectionException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new throwable with the specified detail message and
- * cause. <p>Note that the detail message associated with
- * {@code cause} is <i>not</i> automatically incorporated in
- * this throwable's detail message.
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- *
- * @param message the detail message (which is saved for later retrieval
- * by the {@link #getMessage()} method).
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A {@code null} value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- * @since 1.4
- */
- public MultipleSensitivePropertyProtectionException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Constructs a new throwable with the specified cause and a detail
- * message of {@code (cause==null ? null : cause.toString())} (which
- * typically contains the class and detail message of {@code cause}).
- * This constructor is useful for throwables that are little more than
- * wrappers for other throwables (for example, PrivilegedActionException).
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- *
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A {@code null} value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- * @since 1.4
- */
- public MultipleSensitivePropertyProtectionException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Constructs a new exception with the provided message and a unique set of the keys that caused the error.
- *
- * @param message the message
- * @param failedKeys any failed keys
- */
- public MultipleSensitivePropertyProtectionException(String message, Collection<String> failedKeys) {
- this(message, failedKeys, null);
- }
-
- /**
- * Constructs a new exception with the provided message and a unique set of the keys that caused the error.
- *
- * @param message the message
- * @param failedKeys any failed keys
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A {@code null} value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- */
- public MultipleSensitivePropertyProtectionException(String message, Collection<String> failedKeys, Throwable cause) {
- super(message, cause);
- this.failedKeys = new HashSet<>(failedKeys);
- }
-
- public Set<String> getFailedKeys() {
- return this.failedKeys;
- }
-
- @Override
- public String toString() {
- return "SensitivePropertyProtectionException for [" + StringUtils.join(this.failedKeys, ", ") + "]: " + getLocalizedMessage();
- }
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
index 48b90e5..3f8ec6b 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
@@ -17,13 +17,14 @@
package org.apache.nifi.registry.properties;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.properties.ApplicationProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Enumeration;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -31,11 +32,20 @@ import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
-public class NiFiRegistryProperties extends Properties {
+public class NiFiRegistryProperties extends ApplicationProperties {
private static final Logger logger = LoggerFactory.getLogger(NiFiRegistryProperties.class);
+ public static final String NIFI_REGISTRY_PROPERTIES_FILE_PATH_PROPERTY = "nifi.registry.properties.file.path";
+ public static final String NIFI_REGISTRY_BOOTSTRAP_FILE_PATH_PROPERTY = "nifi.registry.bootstrap.config.file.path";
+ public static final String NIFI_REGISTRY_BOOTSTRAP_DOCS_DIR_PROPERTY = "nifi.registry.bootstrap.config.docs.dir";
+
+ public static final String RELATIVE_BOOTSTRAP_FILE_LOCATION = "conf/bootstrap.conf";
+ public static final String RELATIVE_PROPERTIES_FILE_LOCATION = "conf/nifi-registry.properties";
+ public static final String RELATIVE_DOCS_LOCATION = "docs";
+
// Keys
+ public static final String PROPERTIES_FILE_PATH = "nifi.registry.properties.file.path";
public static final String WEB_WAR_DIR = "nifi.registry.web.war.directory";
public static final String WEB_HTTP_PORT = "nifi.registry.web.http.port";
public static final String WEB_HTTP_HOST = "nifi.registry.web.http.host";
@@ -119,11 +129,15 @@ public class NiFiRegistryProperties extends Properties {
public static final String DEFAULT_SECURITY_USER_OIDC_READ_TIMEOUT = "5 secs";
public NiFiRegistryProperties() {
- super();
+ this(Collections.EMPTY_MAP);
+ }
+
+ public NiFiRegistryProperties(final Map<String, String> props) {
+ super(props);
}
- public NiFiRegistryProperties(Map<String, String> props) {
- this.putAll(props);
+ public NiFiRegistryProperties(final Properties props) {
+ super(props);
}
public int getWebThreads() {
@@ -297,7 +311,7 @@ public class NiFiRegistryProperties extends Properties {
public Set<String> getExtensionsDirs() {
final Set<String> extensionDirs = new HashSet<>();
- stringPropertyNames().stream().filter(key -> key.startsWith(EXTENSION_DIR_PREFIX)).forEach(key -> extensionDirs.add(getProperty(key)));
+ getPropertyKeys().stream().filter(key -> key.startsWith(EXTENSION_DIR_PREFIX)).forEach(key -> extensionDirs.add(getProperty(key)));
return extensionDirs;
}
@@ -305,21 +319,6 @@ public class NiFiRegistryProperties extends Properties {
return Boolean.parseBoolean(getPropertyAsTrimmedString(REVISIONS_ENABLED));
}
- /**
- * Retrieves all known property keys.
- *
- * @return all known property keys
- */
- public Set<String> getPropertyKeys() {
- Set<String> propertyNames = new HashSet<>();
- Enumeration e = this.propertyNames();
- for (; e.hasMoreElements(); ){
- propertyNames.add((String) e.nextElement());
- }
-
- return propertyNames;
- }
-
// Helper functions for common ways of interpreting property values
private String getPropertyAsTrimmedString(String key) {
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoader.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoader.java
index 5ceffd1..be9e282 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoader.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoader.java
@@ -16,25 +16,33 @@
*/
package org.apache.nifi.registry.properties;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.apache.nifi.properties.SensitivePropertyProviderFactory;
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory;
+import org.apache.nifi.registry.properties.util.NiFiRegistryBootstrapUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.crypto.Cipher;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.util.Properties;
public class NiFiRegistryPropertiesLoader {
private static final Logger logger = LoggerFactory.getLogger(NiFiRegistryPropertiesLoader.class);
+ private static final String APPLICATION_PATH = "nifi.registry";
+
private static final String RELATIVE_PATH = "conf/nifi-registry.properties";
private String keyHex;
// Future enhancement: allow for external registration of new providers
- private static SensitivePropertyProviderFactory sensitivePropertyProviderFactory;
+ private SensitivePropertyProviderFactory sensitivePropertyProviderFactory;
/**
* Returns an instance of the loader configured with the key.
@@ -46,7 +54,7 @@ public class NiFiRegistryPropertiesLoader {
* @param keyHex the key used to encrypt any sensitive properties
* @return the configured loader
*/
- public static NiFiRegistryPropertiesLoader withKey(String keyHex) {
+ public static NiFiRegistryPropertiesLoader withKey(final String keyHex) {
NiFiRegistryPropertiesLoader loader = new NiFiRegistryPropertiesLoader();
loader.setKeyHex(keyHex);
return loader;
@@ -54,12 +62,12 @@ public class NiFiRegistryPropertiesLoader {
/**
* Sets the hexadecimal key used to unprotect properties encrypted with
- * {@link AESSensitivePropertyProvider}. If the key has already been set,
+ * {@link SensitivePropertyProvider}. If the key has already been set,
* calling this method will throw a {@link RuntimeException}.
*
* @param keyHex the key in hexadecimal format
*/
- public void setKeyHex(String keyHex) {
+ public void setKeyHex(final String keyHex) {
if (this.keyHex == null || this.keyHex.trim().isEmpty()) {
this.keyHex = keyHex;
} else {
@@ -67,21 +75,18 @@ public class NiFiRegistryPropertiesLoader {
}
}
- private static String getDefaultProviderKey() {
- try {
- return "aes/gcm/" + (Cipher.getMaxAllowedKeyLength("AES") > 128 ? "256" : "128");
- } catch (NoSuchAlgorithmException e) {
- return "aes/gcm/128";
+ private SensitivePropertyProviderFactory getSensitivePropertyProviderFactory() {
+ if (sensitivePropertyProviderFactory == null) {
+ sensitivePropertyProviderFactory = StandardSensitivePropertyProviderFactory
+ .withKeyAndBootstrapSupplier(keyHex, () -> {
+ try {
+ return NiFiRegistryBootstrapUtils.loadBootstrapProperties();
+ } catch (IOException e) {
+ throw new SensitivePropertyProtectionException("Could not load bootstrap.conf for sensitive property provider configuration.", e);
+ }
+ });
}
- }
-
- private void initializeSensitivePropertyProviderFactory() {
- sensitivePropertyProviderFactory = new AESSensitivePropertyProviderFactory(keyHex);
- }
-
- private SensitivePropertyProvider getSensitivePropertyProvider() {
- initializeSensitivePropertyProviderFactory();
- return sensitivePropertyProviderFactory.getProvider();
+ return sensitivePropertyProviderFactory;
}
/**
@@ -100,11 +105,12 @@ public class NiFiRegistryPropertiesLoader {
throw new IllegalArgumentException("NiFi Registry properties file missing or unreadable");
}
- final NiFiRegistryProperties rawProperties = new NiFiRegistryProperties();
+ final Properties rawProperties = new Properties();
try (final FileReader reader = new FileReader(file)) {
rawProperties.load(reader);
+ final NiFiRegistryProperties innerProperties = new NiFiRegistryProperties(rawProperties);
logger.info("Loaded {} properties from {}", rawProperties.size(), file.getAbsolutePath());
- ProtectedNiFiRegistryProperties protectedNiFiRegistryProperties = new ProtectedNiFiRegistryProperties(rawProperties);
+ ProtectedNiFiRegistryProperties protectedNiFiRegistryProperties = new ProtectedNiFiRegistryProperties(innerProperties);
return protectedNiFiRegistryProperties;
} catch (final IOException ioe) {
logger.error("Cannot load properties file due to " + ioe.getLocalizedMessage());
@@ -120,13 +126,16 @@ public class NiFiRegistryPropertiesLoader {
* @param file the File containing the serialized properties
* @return the NiFiProperties instance
*/
- public NiFiRegistryProperties load(File file) {
- ProtectedNiFiRegistryProperties protectedNiFiRegistryProperties = readProtectedPropertiesFromDisk(file);
- if (protectedNiFiRegistryProperties.hasProtectedKeys()) {
- protectedNiFiRegistryProperties.addSensitivePropertyProvider(getSensitivePropertyProvider());
+ public NiFiRegistryProperties load(final File file) {
+ final ProtectedNiFiRegistryProperties protectedNiFiProperties = readProtectedPropertiesFromDisk(file);
+ if (protectedNiFiProperties.hasProtectedKeys()) {
+ Security.addProvider(new BouncyCastleProvider());
+ getSensitivePropertyProviderFactory()
+ .getSupportedSensitivePropertyProviders()
+ .forEach(protectedNiFiProperties::addSensitivePropertyProvider);
}
- return protectedNiFiRegistryProperties.getUnprotectedProperties();
+ return protectedNiFiProperties.getUnprotectedProperties();
}
/**
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/ProtectedNiFiRegistryProperties.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/ProtectedNiFiRegistryProperties.java
index 5debc4a..57e1af2 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/ProtectedNiFiRegistryProperties.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/ProtectedNiFiRegistryProperties.java
@@ -17,31 +17,36 @@
package org.apache.nifi.registry.properties;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.properties.ApplicationPropertiesProtector;
+import org.apache.nifi.properties.ProtectedProperties;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProtector;
+import org.apache.nifi.properties.SensitivePropertyProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
-import java.util.stream.Collectors;
import static java.util.Arrays.asList;
/**
- * Wrapper class of {@link NiFiRegistryProperties} for intermediate phase when
- * {@link NiFiRegistryPropertiesLoader} loads the raw properties file and performs
- * unprotection activities before returning an instance of {@link NiFiRegistryProperties}.
+ * Decorator class for intermediate phase when {@link NiFiRegistryPropertiesLoader} loads the
+ * raw properties file and performs unprotection activities before returning a clean
+ * implementation of {@link NiFiRegistryProperties}.
+ * This encapsulates the sensitive property access logic from external consumers
+ * of {@code NiFiRegistryProperties}.
*/
-class ProtectedNiFiRegistryProperties {
+class ProtectedNiFiRegistryProperties extends NiFiRegistryProperties implements ProtectedProperties<NiFiRegistryProperties>,
+ SensitivePropertyProtector<ProtectedNiFiRegistryProperties, NiFiRegistryProperties> {
private static final Logger logger = LoggerFactory.getLogger(ProtectedNiFiRegistryProperties.class);
- private NiFiRegistryProperties properties;
+ private SensitivePropertyProtector<ProtectedNiFiRegistryProperties, NiFiRegistryProperties> propertyProtectionDelegate;
- private Map<String, SensitivePropertyProvider> localProviderCache = new HashMap<>();
+ private NiFiRegistryProperties applicationProperties;
// Additional "sensitive" property key
public static final String ADDITIONAL_SENSITIVE_PROPERTIES_KEY = "nifi.registry.sensitive.props.additional.keys";
@@ -53,32 +58,34 @@ class ProtectedNiFiRegistryProperties {
NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD));
public ProtectedNiFiRegistryProperties() {
- this(null);
+ this(new NiFiRegistryProperties());
}
/**
* Creates an instance containing the provided {@link NiFiRegistryProperties}.
*
- * @param props the NiFiProperties to contain
+ * @param props the NiFiRegistryProperties to contain
*/
- public ProtectedNiFiRegistryProperties(NiFiRegistryProperties props) {
- if (props == null) {
- props = new NiFiRegistryProperties();
- }
- this.properties = props;
- logger.debug("Loaded {} properties (including {} protection schemes) into ProtectedNiFiProperties",
- getPropertyKeysIncludingProtectionSchemes().size(), getProtectedPropertyKeys().size());
+ public ProtectedNiFiRegistryProperties(final NiFiRegistryProperties props) {
+ this.applicationProperties = props;
+ this.propertyProtectionDelegate = new ApplicationPropertiesProtector<>(this);
+ logger.debug("Loaded {} properties (including {} protection schemes) into ProtectedNiFiRegistryProperties", getApplicationProperties()
+ .getPropertyKeys().size(), getProtectedPropertyKeys().size());
}
- /**
- * Retrieves the property value for the given property key.
- *
- * @param key the key of property value to lookup
- * @return value of property at given key or null if not found
- */
- // @Override
- public String getProperty(String key) {
- return getInternalNiFiProperties().getProperty(key);
+ @Override
+ public String getAdditionalSensitivePropertiesKeys() {
+ return getProperty(getAdditionalSensitivePropertiesKeysName());
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeysName() {
+ return ADDITIONAL_SENSITIVE_PROPERTIES_KEY;
+ }
+
+ @Override
+ public List<String> getDefaultSensitiveProperties() {
+ return DEFAULT_SENSITIVE_PROPERTIES;
}
/**
@@ -89,382 +96,119 @@ class ProtectedNiFiRegistryProperties {
*
* @return the internal properties
*/
- NiFiRegistryProperties getInternalNiFiProperties() {
- if (this.properties == null) {
- this.properties = new NiFiRegistryProperties();
+ @Override
+ public NiFiRegistryProperties getApplicationProperties() {
+ if (this.applicationProperties == null) {
+ this.applicationProperties = new NiFiRegistryProperties();
}
- return this.properties;
+ return this.applicationProperties;
}
- /**
- * Returns the number of properties in the NiFiRegistryProperties,
- * excluding protection scheme properties.
- *
- * <p>
- * Example:
- * <p>
- * key: E(value, key)
- * key.protected: aes/gcm/256
- * key2: value2
- * <p>
- * would return size 2
- *
- * @return the count of real properties
- */
- int size() {
- return getPropertyKeysExcludingProtectionSchemes().size();
+ @Override
+ public NiFiRegistryProperties createApplicationProperties(final Properties rawProperties) {
+ return new NiFiRegistryProperties(rawProperties);
}
/**
- * Returns the complete set of property keys in the NiFiRegistryProperties,
- * including any protection keys (i.e. 'x.y.z.protected').
+ * Retrieves the property value for the given property key.
*
- * @return the set of property keys
+ * @param key the key of property value to lookup
+ * @return value of property at given key or null if not found
*/
- Set<String> getPropertyKeysIncludingProtectionSchemes() {
- return getInternalNiFiProperties().getPropertyKeys();
+ @Override
+ public String getProperty(String key) {
+ return getApplicationProperties().getProperty(key);
}
/**
- * Returns the set of property keys in the NiFiRegistryProperties,
- * excluding any protection keys (i.e. 'x.y.z.protected').
+ * Retrieves all known property keys.
*
- * @return the set of property keys
+ * @return all known property keys
*/
- Set<String> getPropertyKeysExcludingProtectionSchemes() {
- Set<String> filteredKeys = getPropertyKeysIncludingProtectionSchemes();
- filteredKeys.removeIf(p -> p.endsWith(".protected"));
- return filteredKeys;
+ @Override
+ public Set<String> getPropertyKeys() {
+ return propertyProtectionDelegate.getPropertyKeys();
}
/**
- * Splits a single string containing multiple property keys into a List.
- *
- * Delimited by ',' or ';' and ignores leading and trailing whitespace around delimiter.
+ * Returns the number of properties, excluding protection scheme properties.
+ * <p>
+ * Example:
+ * <p>
+ * key: E(value, key)
+ * key.protected: aes/gcm/256
+ * key2: value2
+ * <p>
+ * would return size 2
*
- * @param multipleProperties a single String containing multiple properties, i.e.
- * "nifi.registry.property.1; nifi.registry.property.2, nifi.registry.property.3"
- * @return a List containing the split and trimmed properties
+ * @return the count of real properties
*/
- private static List<String> splitMultipleProperties(String multipleProperties) {
- if (multipleProperties == null || multipleProperties.trim().isEmpty()) {
- return new ArrayList<>(0);
- } else {
- List<String> properties = new ArrayList<>(asList(multipleProperties.split("\\s*[,;]\\s*")));
- for (int i = 0; i < properties.size(); i++) {
- properties.set(i, properties.get(i).trim());
- }
- return properties;
- }
+ @Override
+ public int size() {
+ return propertyProtectionDelegate.size();
}
- /**
- * Returns a list of the keys identifying "sensitive" properties.
- *
- * There is a default list, and additional keys can be provided in the
- * {@code nifi.registry.sensitive.props.additional.keys} property in {@code nifi-registry.properties}.
- *
- * @return the list of sensitive property keys
- */
+ @Override
+ public Set<String> getPropertyKeysIncludingProtectionSchemes() {
+ return propertyProtectionDelegate.getPropertyKeysIncludingProtectionSchemes();
+ }
+
+ @Override
public List<String> getSensitivePropertyKeys() {
- String additionalPropertiesString = getProperty(ADDITIONAL_SENSITIVE_PROPERTIES_KEY);
- if (additionalPropertiesString == null || additionalPropertiesString.trim().isEmpty()) {
- return DEFAULT_SENSITIVE_PROPERTIES;
- } else {
- List<String> additionalProperties = splitMultipleProperties(additionalPropertiesString);
- /* Remove this key if it was accidentally provided as a sensitive key
- * because we cannot protect it and read from it
- */
- if (additionalProperties.contains(ADDITIONAL_SENSITIVE_PROPERTIES_KEY)) {
- logger.warn("The key '{}' contains itself. This is poor practice and should be removed", ADDITIONAL_SENSITIVE_PROPERTIES_KEY);
- additionalProperties.remove(ADDITIONAL_SENSITIVE_PROPERTIES_KEY);
- }
- additionalProperties.addAll(DEFAULT_SENSITIVE_PROPERTIES);
- return additionalProperties;
- }
+ return propertyProtectionDelegate.getSensitivePropertyKeys();
}
- /**
- * Returns a list of the keys identifying "sensitive" properties. There is a default list,
- * and additional keys can be provided in the {@code nifi.sensitive.props.additional.keys} property in {@code nifi.properties}.
- *
- * @return the list of sensitive property keys
- */
+ @Override
public List<String> getPopulatedSensitivePropertyKeys() {
- List<String> allSensitiveKeys = getSensitivePropertyKeys();
- return allSensitiveKeys.stream().filter(k -> StringUtils.isNotBlank(getProperty(k))).collect(Collectors.toList());
+ return propertyProtectionDelegate.getPopulatedSensitivePropertyKeys();
}
- /**
- * Returns true if any sensitive keys are protected.
- *
- * @return true if any key is protected; false otherwise
- */
+ @Override
public boolean hasProtectedKeys() {
- List<String> sensitiveKeys = getSensitivePropertyKeys();
- for (String k : sensitiveKeys) {
- if (isPropertyProtected(k)) {
- return true;
- }
- }
- return false;
+ return propertyProtectionDelegate.hasProtectedKeys();
}
- /**
- * Returns a Map of the keys identifying "sensitive" properties that are currently protected and the "protection" key for each.
- *
- * This may or may not include all properties marked as sensitive.
- *
- * @return the Map of protected property keys and the protection identifier for each
- */
+ @Override
public Map<String, String> getProtectedPropertyKeys() {
- List<String> sensitiveKeys = getSensitivePropertyKeys();
-
- Map<String, String> traditionalProtectedProperties = new HashMap<>();
- for (String key : sensitiveKeys) {
- String protection = getProperty(getProtectionKey(key));
- if (StringUtils.isNotBlank(protection) && StringUtils.isNotBlank(getProperty(key))) {
- traditionalProtectedProperties.put(key, protection);
- }
- }
-
- return traditionalProtectedProperties;
+ return propertyProtectionDelegate.getProtectedPropertyKeys();
}
- /**
- * Returns the unique set of all protection schemes currently in use for this instance.
- *
- * @return the set of protection schemes
- */
+ @Override
public Set<String> getProtectionSchemes() {
- return new HashSet<>(getProtectedPropertyKeys().values());
- }
-
- /**
- * Returns a percentage of the total number of populated properties marked as sensitive that are currently protected.
- *
- * @return the percent of sensitive properties marked as protected
- */
- public int getPercentOfSensitivePropertiesProtected() {
- return (int) Math.round(getProtectedPropertyKeys().size() / ((double) getPopulatedSensitivePropertyKeys().size()) * 100);
- }
-
- /**
- * Returns true if the property identified by this key is considered sensitive in this instance of {@code NiFiProperties}.
- * Some properties are sensitive by default, while others can be specified by
- * {@link ProtectedNiFiRegistryProperties#ADDITIONAL_SENSITIVE_PROPERTIES_KEY}.
- *
- * @param key the key
- * @return true if it is sensitive
- * @see ProtectedNiFiRegistryProperties#getSensitivePropertyKeys()
- */
- public boolean isPropertySensitive(String key) {
- // If the explicit check for ADDITIONAL_SENSITIVE_PROPERTIES_KEY is not here, this could loop infinitely
- return key != null && !key.equals(ADDITIONAL_SENSITIVE_PROPERTIES_KEY) && getSensitivePropertyKeys().contains(key.trim());
+ return propertyProtectionDelegate.getProtectionSchemes();
}
- /**
- * Returns true if the property identified by this key is considered protected in this instance of {@code NiFiProperties}.
- * The property value is protected if the key is sensitive and the sibling key of key.protected is present.
- *
- * @param key the key
- * @return true if it is currently marked as protected
- * @see ProtectedNiFiRegistryProperties#getSensitivePropertyKeys()
- */
- public boolean isPropertyProtected(String key) {
- return key != null && isPropertySensitive(key) && !StringUtils.isBlank(getProperty(getProtectionKey(key)));
+ @Override
+ public boolean isPropertySensitive(final String key) {
+ return propertyProtectionDelegate.isPropertySensitive(key);
}
- /**
- * Returns the sibling property key which specifies the protection scheme for this key.
- * <p>
- * Example:
- * <p>
- * nifi.registry.sensitive.key=ABCXYZ
- * nifi.registry.sensitive.key.protected=aes/gcm/256
- * <p>
- * nifi.registry.sensitive.key -> nifi.sensitive.key.protected
- *
- * @param key the key identifying the sensitive property
- * @return the key identifying the protection scheme for the sensitive property
- */
- public static String getProtectionKey(String key) {
- if (key == null || key.isEmpty()) {
- throw new IllegalArgumentException("Cannot find protection key for null key");
- }
-
- return key + ".protected";
+ @Override
+ public boolean isPropertyProtected(final String key) {
+ return propertyProtectionDelegate.isPropertyProtected(key);
}
- /**
- * Returns the unprotected {@link NiFiRegistryProperties} instance. If none of the
- * properties loaded are marked as protected, it will simply pass through the
- * internal instance. If any are protected, it will drop the protection scheme keys
- * and translate each protected value (encrypted, HSM-retrieved, etc.) into the raw
- * value and store it under the original key.
- * <p>
- * If any property fails to unprotect, it will save that key and continue. After
- * attempting all properties, it will throw an exception containing all failed
- * properties. This is necessary because the order is not enforced, so all failed
- * properties should be gathered together.
- *
- * @return the NiFiRegistryProperties instance with all raw values
- * @throws SensitivePropertyProtectionException if there is a problem unprotecting one or more keys
- */
+ @Override
public NiFiRegistryProperties getUnprotectedProperties() throws SensitivePropertyProtectionException {
- if (hasProtectedKeys()) {
- logger.debug("There are {} protected properties of {} sensitive properties ({}%)",
- getProtectedPropertyKeys().size(),
- getPopulatedSensitivePropertyKeys().size(),
- getPercentOfSensitivePropertiesProtected());
-
- NiFiRegistryProperties unprotectedProperties = new NiFiRegistryProperties();
-
- Set<String> failedKeys = new HashSet<>();
-
- for (String key : getPropertyKeysExcludingProtectionSchemes()) {
- /* Three kinds of keys
- * 1. protection schemes -- skip
- * 2. protected keys -- unprotect and copy
- * 3. normal keys -- copy over
- */
- if (key.endsWith(".protected")) {
- // Do nothing
- } else if (isPropertyProtected(key)) {
- try {
- unprotectedProperties.setProperty(key, unprotectValue(key, getProperty(key)));
- } catch (SensitivePropertyProtectionException e) {
- logger.warn("Failed to unprotect '{}'", key, e);
- failedKeys.add(key);
- }
- } else {
- unprotectedProperties.setProperty(key, getProperty(key));
- }
- }
-
- if (!failedKeys.isEmpty()) {
- if (failedKeys.size() > 1) {
- logger.warn("Combining {} failed keys [{}] into single exception", failedKeys.size(), StringUtils.join(failedKeys, ", "));
- throw new MultipleSensitivePropertyProtectionException("Failed to unprotect keys", failedKeys);
- } else {
- throw new SensitivePropertyProtectionException("Failed to unprotect key " + failedKeys.iterator().next());
- }
- }
-
- return unprotectedProperties;
- } else {
- logger.debug("No protected properties");
- return getInternalNiFiProperties();
- }
- }
-
- /**
- * Registers a new {@link SensitivePropertyProvider}. This method will throw a {@link UnsupportedOperationException} if a provider is already registered for the protection scheme.
- *
- * @param sensitivePropertyProvider the provider
- */
- void addSensitivePropertyProvider(SensitivePropertyProvider sensitivePropertyProvider) {
- if (sensitivePropertyProvider == null) {
- throw new IllegalArgumentException("Cannot add null SensitivePropertyProvider");
- }
-
- if (getSensitivePropertyProviders().containsKey(sensitivePropertyProvider.getIdentifierKey())) {
- throw new UnsupportedOperationException("Cannot overwrite existing sensitive property provider registered for " + sensitivePropertyProvider.getIdentifierKey());
- }
-
- getSensitivePropertyProviders().put(sensitivePropertyProvider.getIdentifierKey(), sensitivePropertyProvider);
- }
-
- private String getDefaultProtectionScheme() {
- if (!getSensitivePropertyProviders().isEmpty()) {
- List<String> schemes = new ArrayList<>(getSensitivePropertyProviders().keySet());
- Collections.sort(schemes);
- return schemes.get(0);
- } else {
- throw new IllegalStateException("No registered protection schemes");
- }
- }
-
- /**
- * Returns a new instance of {@link NiFiRegistryProperties} with all populated sensitive values protected by the default protection scheme.
- *
- * Plain non-sensitive values are copied directly.
- *
- * @return the protected properties in a {@link NiFiRegistryProperties} object
- * @throws IllegalStateException if no protection schemes are registered
- */
- NiFiRegistryProperties protectPlainProperties() {
- try {
- return protectPlainProperties(getDefaultProtectionScheme());
- } catch (IllegalStateException e) {
- final String msg = "Cannot protect properties with default scheme if no protection schemes are registered";
- logger.warn(msg);
- throw new IllegalStateException(msg, e);
- }
- }
-
- /**
- * Returns a new instance of {@link NiFiRegistryProperties} with all populated sensitive values protected by the provided protection scheme.
- *
- * Plain non-sensitive values are copied directly.
- *
- * @param protectionScheme the identifier key of the {@link SensitivePropertyProvider} to use
- * @return the protected properties in a {@link NiFiRegistryProperties} object
- */
- NiFiRegistryProperties protectPlainProperties(String protectionScheme) {
- SensitivePropertyProvider spp = getSensitivePropertyProvider(protectionScheme);
-
- NiFiRegistryProperties protectedProperties = new NiFiRegistryProperties();
-
- // Copy over the plain keys
- Set<String> plainKeys = getPropertyKeysExcludingProtectionSchemes();
- plainKeys.removeAll(getSensitivePropertyKeys());
- for (String key : plainKeys) {
- protectedProperties.setProperty(key, getInternalNiFiProperties().getProperty(key));
- }
-
- // Add the protected keys and the protection schemes
- for (String key : getSensitivePropertyKeys()) {
- final String plainValue = getProperty(key);
- if (plainValue != null && !plainValue.trim().isEmpty()) {
- final String protectedValue = spp.protect(plainValue);
- protectedProperties.setProperty(key, protectedValue);
- protectedProperties.setProperty(getProtectionKey(key), protectionScheme);
- }
- }
-
- return protectedProperties;
+ return propertyProtectionDelegate.getUnprotectedProperties();
}
- /**
- * Returns the number of properties that are marked as protected in the provided {@link NiFiRegistryProperties} instance
- * without requiring external creation of a {@link ProtectedNiFiRegistryProperties} instance.
- *
- * @param plainProperties the instance to count protected properties
- * @return the number of protected properties
- */
- public static int countProtectedProperties(NiFiRegistryProperties plainProperties) {
- return new ProtectedNiFiRegistryProperties(plainProperties).getProtectedPropertyKeys().size();
+ @Override
+ public void addSensitivePropertyProvider(final SensitivePropertyProvider sensitivePropertyProvider) {
+ propertyProtectionDelegate.addSensitivePropertyProvider(sensitivePropertyProvider);
}
- /**
- * Returns the number of properties that are marked as sensitive in the provided {@link NiFiRegistryProperties} instance
- * without requiring external creation of a {@link ProtectedNiFiRegistryProperties} instance.
- *
- * @param plainProperties the instance to count sensitive properties
- * @return the number of sensitive properties
- */
- public static int countSensitiveProperties(NiFiRegistryProperties plainProperties) {
- return new ProtectedNiFiRegistryProperties(plainProperties).getSensitivePropertyKeys().size();
+ @Override
+ public Map<String, SensitivePropertyProvider> getSensitivePropertyProviders() {
+ return propertyProtectionDelegate.getSensitivePropertyProviders();
}
@Override
public String toString() {
final Set<String> providers = getSensitivePropertyProviders().keySet();
- return new StringBuilder("ProtectedNiFiProperties instance with ")
- .append(getPropertyKeysIncludingProtectionSchemes().size())
- .append(" properties (")
+ return new StringBuilder("ProtectedNiFiRegistryProperties instance with ")
+ .append(size()).append(" properties (")
.append(getProtectedPropertyKeys().size())
.append(" protected) and ")
.append(providers.size())
@@ -472,57 +216,4 @@ class ProtectedNiFiRegistryProperties {
.append(StringUtils.join(providers, ", "))
.toString();
}
-
- /**
- * Returns the local provider cache (null-safe) as a Map of protection schemes -> implementations.
- *
- * @return the map
- */
- private Map<String, SensitivePropertyProvider> getSensitivePropertyProviders() {
- if (localProviderCache == null) {
- localProviderCache = new HashMap<>();
- }
-
- return localProviderCache;
- }
-
- private SensitivePropertyProvider getSensitivePropertyProvider(String protectionScheme) {
- if (isProviderAvailable(protectionScheme)) {
- return getSensitivePropertyProviders().get(protectionScheme);
- } else {
- throw new SensitivePropertyProtectionException("No provider available for " + protectionScheme);
- }
- }
-
- private boolean isProviderAvailable(String protectionScheme) {
- return getSensitivePropertyProviders().containsKey(protectionScheme);
- }
-
- /**
- * If the value is protected, unprotects it and returns it. If not, returns the original value.
- *
- * @param key the retrieved property key
- * @param retrievedValue the retrieved property value
- * @return the unprotected value
- */
- private String unprotectValue(String key, String retrievedValue) {
- // Checks if the key is sensitive and marked as protected
- if (isPropertyProtected(key)) {
- final String protectionScheme = getProperty(getProtectionKey(key));
-
- // No provider registered for this scheme, so just return the value
- if (!isProviderAvailable(protectionScheme)) {
- logger.warn("No provider available for {} so passing the protected {} value back", protectionScheme, key);
- return retrievedValue;
- }
-
- try {
- SensitivePropertyProvider sensitivePropertyProvider = getSensitivePropertyProvider(protectionScheme);
- return sensitivePropertyProvider.unprotect(retrievedValue);
- } catch (SensitivePropertyProtectionException e) {
- throw new SensitivePropertyProtectionException("Error unprotecting value for " + key, e.getCause());
- }
- }
- return retrievedValue;
- }
}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProtectionException.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProtectionException.java
deleted file mode 100644
index 2ffa902..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProtectionException.java
+++ /dev/null
@@ -1,89 +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.registry.properties;
-
-public class SensitivePropertyProtectionException extends RuntimeException {
- /**
- * Constructs a new throwable with {@code null} as its detail message.
- * The cause is not initialized, and may subsequently be initialized by a
- * call to {@link #initCause}.
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- */
- public SensitivePropertyProtectionException() {
- }
-
- /**
- * Constructs a new throwable with the specified detail message. The
- * cause is not initialized, and may subsequently be initialized by
- * a call to {@link #initCause}.
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- *
- * @param message the detail message. The detail message is saved for
- * later retrieval by the {@link #getMessage()} method.
- */
- public SensitivePropertyProtectionException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new throwable with the specified detail message and
- * cause. <p>Note that the detail message associated with
- * {@code cause} is <i>not</i> automatically incorporated in
- * this throwable's detail message.
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- *
- * @param message the detail message (which is saved for later retrieval
- * by the {@link #getMessage()} method).
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A {@code null} value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- */
- public SensitivePropertyProtectionException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Constructs a new throwable with the specified cause and a detail
- * message of {@code (cause==null ? null : cause.toString())} (which
- * typically contains the class and detail message of {@code cause}).
- * This constructor is useful for throwables that are little more than
- * wrappers for other throwables (for example, PrivilegedActionException).
- * <p>
- * <p>The {@link #fillInStackTrace()} method is called to initialize
- * the stack trace data in the newly created throwable.
- *
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A {@code null} value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- */
- public SensitivePropertyProtectionException(Throwable cause) {
- super(cause);
- }
-
- @Override
- public String toString() {
- return "SensitivePropertyProtectionException: " + getLocalizedMessage();
- }
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProvider.java
deleted file mode 100644
index c0dd43c6..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/SensitivePropertyProvider.java
+++ /dev/null
@@ -1,52 +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.registry.properties;
-
-public interface SensitivePropertyProvider {
-
- /**
- * Returns the name of the underlying implementation.
- *
- * @return the name of this sensitive property provider
- */
- String getName();
-
- /**
- * Returns the key used to identify the provider implementation in {@code nifi.properties}.
- *
- * @return the key to persist in the sibling property
- */
- String getIdentifierKey();
-
- /**
- * Returns the "protected" form of this value. This is a form which can safely be persisted in the {@code nifi.properties} file without compromising the value.
- * An encryption-based provider would return a cipher text, while a remote-lookup provider could return a unique ID to retrieve the secured value.
- *
- * @param unprotectedValue the sensitive value
- * @return the value to persist in the {@code nifi.properties} file
- */
- String protect(String unprotectedValue) throws SensitivePropertyProtectionException;
-
- /**
- * Returns the "unprotected" form of this value. This is the raw sensitive value which is used by the application logic.
- * An encryption-based provider would decrypt a cipher text and return the plaintext, while a remote-lookup provider could retrieve the secured value.
- *
- * @param protectedValue the protected value read from the {@code nifi.properties} file
- * @return the raw value to be used by the application
- */
- String unprotect(String protectedValue) throws SensitivePropertyProtectionException;
-}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapPropertiesLoader.java
similarity index 55%
rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
rename to nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapPropertiesLoader.java
index c800b3a..1a8be9c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/SensitivePropertyProviderFactory.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapPropertiesLoader.java
@@ -14,10 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.nifi.properties;
+package org.apache.nifi.registry.properties.util;
-public interface SensitivePropertyProviderFactory {
+import org.apache.nifi.properties.AbstractBootstrapPropertiesLoader;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
- SensitivePropertyProvider getProvider();
+public class NiFiRegistryBootstrapPropertiesLoader extends AbstractBootstrapPropertiesLoader {
+ @Override
+ protected String getApplicationPrefix() {
+ return "nifi.registry";
+ }
+ @Override
+ protected String getApplicationPropertiesFilename() {
+ return "nifi-registry.properties";
+ }
+
+ @Override
+ protected String getApplicationPropertiesFilePathSystemProperty() {
+ return NiFiRegistryProperties.PROPERTIES_FILE_PATH;
+ }
}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapUtils.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapUtils.java
new file mode 100644
index 0000000..8494915
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapUtils.java
@@ -0,0 +1,82 @@
+/*
+ * 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.registry.properties.util;
+
+import org.apache.nifi.properties.AbstractBootstrapPropertiesLoader;
+import org.apache.nifi.properties.BootstrapProperties;
+
+import java.io.IOException;
+
+/**
+ * Encapsulates utility methods for dealing with bootstrap.conf or nifi-registry.properties.
+ */
+public class NiFiRegistryBootstrapUtils {
+ private static final AbstractBootstrapPropertiesLoader BOOTSTRAP_PROPERTIES_LOADER = new NiFiRegistryBootstrapPropertiesLoader();
+
+ /**
+ * Returns the key (if any) used to encrypt sensitive properties, extracted from
+ * {@code $NIFI_REGISTRY_HOME/conf/bootstrap.conf}.
+ *
+ * @return the key in hexadecimal format
+ * @throws IOException if the file is not readable
+ */
+ public static String extractKeyFromBootstrapFile() throws IOException {
+ return BOOTSTRAP_PROPERTIES_LOADER.extractKeyFromBootstrapFile();
+ }
+
+ /**
+ * Loads the default bootstrap.conf file into a BootstrapProperties object.
+ * @return The default bootstrap.conf as a BootstrapProperties object
+ * @throws IOException If the file is not readable
+ */
+ public static BootstrapProperties loadBootstrapProperties() throws IOException {
+ return loadBootstrapProperties(null);
+ }
+
+ /**
+ * Loads the bootstrap.conf file into a BootstrapProperties object.
+ * @param bootstrapPath the path to the bootstrap file
+ * @return The bootstrap.conf as a BootstrapProperties object
+ * @throws IOException If the file is not readable
+ */
+ public static BootstrapProperties loadBootstrapProperties(final String bootstrapPath) throws IOException {
+ return BOOTSTRAP_PROPERTIES_LOADER.loadBootstrapProperties(bootstrapPath);
+ }
+
+ /**
+ * Returns the key (if any) used to encrypt sensitive properties, extracted from
+ * {@code $NIFI_REGISTRY_HOME/conf/bootstrap.conf}.
+ *
+ * @param bootstrapPath the path to the bootstrap file (if null, returns the sensitive key
+ * found in $NIFI_REGISTRY_HOME/conf/bootstrap.conf)
+ * @return the key in hexadecimal format
+ * @throws IOException if the file is not readable
+ */
+ public static String extractKeyFromBootstrapFile(final String bootstrapPath) throws IOException {
+ return BOOTSTRAP_PROPERTIES_LOADER.extractKeyFromBootstrapFile(bootstrapPath);
+ }
+
+ /**
+ * Returns the default file path to {@code $NIFI_REGISTRY_HOME/conf/nifi-registry.properties}. If the system
+ * property nifi-registry.properties.file.path is not set, it will be set to the relative conf/nifi-registry.properties
+ *
+ * @return the path to the nifi-registry.properties file
+ */
+ public static String getDefaultApplicationPropertiesFilePath() {
+ return BOOTSTRAP_PROPERTIES_LOADER.getDefaultApplicationPropertiesFilePath();
+ }
+}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/BootstrapFileCryptoKeyProvider.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/BootstrapFileCryptoKeyProvider.java
index 191b5e2..c277a9c 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/BootstrapFileCryptoKeyProvider.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/BootstrapFileCryptoKeyProvider.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.registry.security.crypto;
+import org.apache.nifi.registry.properties.util.NiFiRegistryBootstrapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,7 +63,7 @@ public class BootstrapFileCryptoKeyProvider implements CryptoKeyProvider {
@Override
public String getKey() throws MissingCryptoKeyException {
try {
- return CryptoKeyLoader.extractKeyFromBootstrapFile(this.bootstrapFile);
+ return NiFiRegistryBootstrapUtils.extractKeyFromBootstrapFile(this.bootstrapFile);
} catch (IOException ioe) {
final String errMsg = "Loading the master crypto key from bootstrap file '" + bootstrapFile + "' failed due to IOException.";
logger.warn(errMsg);
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/CryptoKeyLoader.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/CryptoKeyLoader.java
deleted file mode 100644
index d828773..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/security/crypto/CryptoKeyLoader.java
+++ /dev/null
@@ -1,87 +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.registry.security.crypto;
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-public class CryptoKeyLoader {
-
- private static final Logger logger = LoggerFactory.getLogger(CryptoKeyLoader.class);
-
- private static final String BOOTSTRAP_KEY_PREFIX = "nifi.registry.bootstrap.sensitive.key=";
-
- /**
- * Returns the key (if any) used to encrypt sensitive properties.
- * The key extracted from the bootstrap.conf file at the specified location.
- *
- * @param bootstrapPath the path to the bootstrap file
- * @return the key in hexadecimal format, or {@link CryptoKeyProvider#EMPTY_KEY} if the key is null or empty
- * @throws IOException if the file is not readable
- */
- public static String extractKeyFromBootstrapFile(String bootstrapPath) throws IOException {
- File bootstrapFile;
- if (StringUtils.isBlank(bootstrapPath)) {
- logger.error("Cannot read from bootstrap.conf file to extract encryption key; location not specified");
- throw new IOException("Cannot read from bootstrap.conf without file location");
- } else {
- bootstrapFile = new File(bootstrapPath);
- }
-
- String keyValue;
- if (bootstrapFile.exists() && bootstrapFile.canRead()) {
- try (Stream<String> stream = Files.lines(Paths.get(bootstrapFile.getAbsolutePath()))) {
- Optional<String> keyLine = stream.filter(l -> l.startsWith(BOOTSTRAP_KEY_PREFIX)).findFirst();
- if (keyLine.isPresent()) {
- keyValue = keyLine.get().split("=", 2)[1];
- keyValue = checkHexKey(keyValue);
- } else {
- keyValue = CryptoKeyProvider.EMPTY_KEY;
- }
- } catch (IOException e) {
- logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key", bootstrapFile.getAbsolutePath());
- throw new IOException("Cannot read from bootstrap.conf", e);
- }
- } else {
- logger.error("Cannot read from bootstrap.conf file at {} to extract encryption key -- file is missing or permissions are incorrect", bootstrapFile.getAbsolutePath());
- throw new IOException("Cannot read from bootstrap.conf");
- }
-
- if (CryptoKeyProvider.EMPTY_KEY.equals(keyValue)) {
- logger.info("No encryption key present in the bootstrap.conf file at {}", bootstrapFile.getAbsolutePath());
- }
-
- return keyValue;
- }
-
- private static String checkHexKey(String input) {
- if (input == null || input.trim().isEmpty()) {
- logger.debug("Checking the hex key value that was loaded determined the key is empty.");
- return CryptoKeyProvider.EMPTY_KEY;
- }
- return input;
- }
-
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/AESSensitivePropertyProviderFactoryTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/AESSensitivePropertyProviderFactoryTest.groovy
deleted file mode 100644
index 0d1d5e2..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/AESSensitivePropertyProviderFactoryTest.groovy
+++ /dev/null
@@ -1,81 +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.registry.properties
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.junit.*
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import javax.crypto.Cipher
-import java.security.Security
-
-@RunWith(JUnit4.class)
-class AESSensitivePropertyProviderFactoryTest extends GroovyTestCase {
- private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProviderFactoryTest.class)
-
- private static final String KEY_HEX_128 = "0123456789ABCDEFFEDCBA9876543210"
- private static final String KEY_HEX_256 = KEY_HEX_128 * 2
-
- @BeforeClass
- public static void setUpOnce() throws Exception {
- Security.addProvider(new BouncyCastleProvider())
-
- logger.metaClass.methodMissing = { String name, args ->
- logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
- }
- }
-
- @Before
- public void setUp() throws Exception {
- }
-
- @After
- public void tearDown() throws Exception {
- }
-
- @Test
- public void testShouldGetProviderWithKey() throws Exception {
- // Arrange
- SensitivePropertyProviderFactory factory = new AESSensitivePropertyProviderFactory(KEY_HEX_128)
-
- // Act
- SensitivePropertyProvider provider = factory.getProvider()
-
- // Assert
- assert provider instanceof AESSensitivePropertyProvider
- assert provider.@key
- assert provider.@cipher
- }
-
- @Test
- public void testShouldGetProviderWith256BitKey() throws Exception {
- // Arrange
- Assume.assumeTrue("JCE unlimited strength crypto policy must be installed for this test", Cipher.getMaxAllowedKeyLength("AES") > 128)
- SensitivePropertyProviderFactory factory = new AESSensitivePropertyProviderFactory(KEY_HEX_256)
-
- // Act
- SensitivePropertyProvider provider = factory.getProvider()
-
- // Assert
- assert provider instanceof AESSensitivePropertyProvider
- assert provider.@key
- assert provider.@cipher
- }
-}
\ No newline at end of file
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/AESSensitivePropertyProviderTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/AESSensitivePropertyProviderTest.groovy
deleted file mode 100644
index bad659f..0000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/AESSensitivePropertyProviderTest.groovy
+++ /dev/null
@@ -1,471 +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.registry.properties
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.bouncycastle.util.encoders.Hex
-import org.junit.*
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import javax.crypto.Cipher
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-import java.nio.charset.StandardCharsets
-import java.security.SecureRandom
-import java.security.Security
-
-@RunWith(JUnit4.class)
-class AESSensitivePropertyProviderTest extends GroovyTestCase {
- private static final Logger logger = LoggerFactory.getLogger(AESSensitivePropertyProviderTest.class)
-
- private static final String KEY_128_HEX = "0123456789ABCDEFFEDCBA9876543210"
- private static final String KEY_256_HEX = KEY_128_HEX * 2
- private static final int IV_LENGTH = AESSensitivePropertyProvider.getIvLength()
-
- private static final List<Integer> KEY_SIZES = getAvailableKeySizes()
-
- private static final SecureRandom secureRandom = new SecureRandom()
-
- private static final Base64.Encoder encoder = Base64.encoder
- private static final Base64.Decoder decoder = Base64.decoder
-
- @BeforeClass
- static void setUpOnce() throws Exception {
- Security.addProvider(new BouncyCastleProvider())
-
- logger.metaClass.methodMissing = { String name, args ->
- logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
- }
- }
-
- @Before
- void setUp() throws Exception {
-
- }
-
- @After
- void tearDown() throws Exception {
-
- }
-
- private static Cipher getCipher(boolean encrypt = true, int keySize = 256, byte[] iv = [0x00] * IV_LENGTH) {
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding")
- String key = getKeyOfSize(keySize)
- cipher.init((encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE) as int, new SecretKeySpec(Hex.decode(key), "AES"), new IvParameterSpec(iv))
- logger.setup("Initialized a cipher in ${encrypt ? "encrypt" : "decrypt"} mode with a key of length ${keySize} bits")
- cipher
- }
-
- private static String getKeyOfSize(int keySize = 256) {
- switch (keySize) {
- case 128:
- return KEY_128_HEX
- case 192:
- case 256:
- if (Cipher.getMaxAllowedKeyLength("AES") < keySize) {
- throw new IllegalArgumentException("The JCE unlimited strength cryptographic jurisdiction policies are not installed, so the max key size is 128 bits")
- }
- return KEY_256_HEX[0..<keySize.intdiv(4)]
- default:
- throw new IllegalArgumentException("Key size ${keySize} bits is not valid")
- }
- }
-
- private static List<Integer> getAvailableKeySizes() {
- if (Cipher.getMaxAllowedKeyLength("AES") > 128) {
- [128, 192, 256]
- } else {
- [128]
- }
- }
-
- private static String manipulateString(String input, int start = 0, int end = input?.length()) {
- if ((input[start..end] as List).unique().size() == 1) {
- throw new IllegalArgumentException("Can't manipulate a String where the entire range is identical [${input[start..end]}]")
- }
- List shuffled = input[start..end] as List
- Collections.shuffle(shuffled)
- String reconstituted = input[0..<start] + shuffled.join() + input[end + 1..-1]
- return reconstituted != input ? reconstituted : manipulateString(input, start, end)
- }
-
- @Test
- void testShouldProtectValue() throws Exception {
- final String PLAINTEXT = "This is a plaintext value"
-
- // Act
- Map<Integer, String> CIPHER_TEXTS = KEY_SIZES.collectEntries { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- [(keySize): spp.protect(PLAINTEXT)]
- }
- CIPHER_TEXTS.each { ks, ct -> logger.info("Encrypted for ${ks} length key: ${ct}") }
-
- // Assert
-
- // The IV generation is part of #protect, so the expected cipher text values must be generated after #protect has run
- Map<Integer, Cipher> decryptionCiphers = CIPHER_TEXTS.collectEntries { int keySize, String cipherText ->
- // The 12 byte IV is the first 16 Base64-encoded characters of the "complete" cipher text
- byte[] iv = decoder.decode(cipherText[0..<16])
- [(keySize): getCipher(false, keySize, iv)]
- }
- Map<Integer, String> plaintexts = decryptionCiphers.collectEntries { Map.Entry<Integer, Cipher> e ->
- String cipherTextWithoutIVAndDelimiter = CIPHER_TEXTS[e.key][18..-1]
- String plaintext = new String(e.value.doFinal(decoder.decode(cipherTextWithoutIVAndDelimiter)), StandardCharsets.UTF_8)
- [(e.key): plaintext]
- }
- CIPHER_TEXTS.each { key, ct -> logger.expected("Cipher text for ${key} length key: ${ct}") }
-
- assert plaintexts.every { int ks, String pt -> pt == PLAINTEXT }
- }
-
- @Test
- void testShouldHandleProtectEmptyValue() throws Exception {
- final List<String> EMPTY_PLAINTEXTS = ["", " ", null]
-
- // Act
- KEY_SIZES.collectEntries { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- EMPTY_PLAINTEXTS.each { String emptyPlaintext ->
- def msg = shouldFail(IllegalArgumentException) {
- spp.protect(emptyPlaintext)
- }
- logger.expected("${msg} for keySize ${keySize} and plaintext [${emptyPlaintext}]")
-
- // Assert
- assert msg == "Cannot encrypt an empty value"
- }
- }
- }
-
- @Test
- void testShouldUnprotectValue() throws Exception {
- // Arrange
- final String PLAINTEXT = "This is a plaintext value"
-
- Map<Integer, Cipher> encryptionCiphers = KEY_SIZES.collectEntries { int keySize ->
- byte[] iv = new byte[IV_LENGTH]
- secureRandom.nextBytes(iv)
- [(keySize): getCipher(true, keySize, iv)]
- }
-
- Map<Integer, String> CIPHER_TEXTS = encryptionCiphers.collectEntries { Map.Entry<Integer, Cipher> e ->
- String iv = encoder.encodeToString(e.value.getIV())
- String cipherText = encoder.encodeToString(e.value.doFinal(PLAINTEXT.getBytes(StandardCharsets.UTF_8)))
- [(e.key): "${iv}||${cipherText}"]
- }
- CIPHER_TEXTS.each { key, ct -> logger.expected("Cipher text for ${key} length key: ${ct}") }
-
- // Act
- Map<Integer, String> plaintexts = CIPHER_TEXTS.collectEntries { int keySize, String cipherText ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- [(keySize): spp.unprotect(cipherText)]
- }
- plaintexts.each { ks, pt -> logger.info("Decrypted for ${ks} length key: ${pt}") }
-
- // Assert
- assert plaintexts.every { int ks, String pt -> pt == PLAINTEXT }
- }
-
- /**
- * Tests inputs where the entire String is empty/blank space/{@code null}.
- *
- * @throws Exception
- */
- @Test
- void testShouldHandleUnprotectEmptyValue() throws Exception {
- // Arrange
- final List<String> EMPTY_CIPHER_TEXTS = ["", " ", null]
-
- // Act
- KEY_SIZES.each { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- EMPTY_CIPHER_TEXTS.each { String emptyCipherText ->
- def msg = shouldFail(IllegalArgumentException) {
- spp.unprotect(emptyCipherText)
- }
- logger.expected("${msg} for keySize ${keySize} and cipher text [${emptyCipherText}]")
-
- // Assert
- assert msg == "Cannot decrypt a cipher text shorter than ${AESSensitivePropertyProvider.minCipherTextLength} chars".toString()
- }
- }
- }
-
- @Test
- void testShouldUnprotectValueWithWhitespace() throws Exception {
- // Arrange
- final String PLAINTEXT = "This is a plaintext value"
-
- Map<Integer, Cipher> encryptionCiphers = KEY_SIZES.collectEntries { int keySize ->
- byte[] iv = new byte[IV_LENGTH]
- secureRandom.nextBytes(iv)
- [(keySize): getCipher(true, keySize, iv)]
- }
-
- Map<Integer, String> CIPHER_TEXTS = encryptionCiphers.collectEntries { Map.Entry<Integer, Cipher> e ->
- String iv = encoder.encodeToString(e.value.getIV())
- String cipherText = encoder.encodeToString(e.value.doFinal(PLAINTEXT.getBytes(StandardCharsets.UTF_8)))
- [(e.key): "${iv}||${cipherText}"]
- }
- CIPHER_TEXTS.each { key, ct -> logger.expected("Cipher text for ${key} length key: ${ct}") }
-
- // Act
- Map<Integer, String> plaintexts = CIPHER_TEXTS.collectEntries { int keySize, String cipherText ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- [(keySize): spp.unprotect("\t" + cipherText + "\n")]
- }
- plaintexts.each { ks, pt -> logger.info("Decrypted for ${ks} length key: ${pt}") }
-
- // Assert
- assert plaintexts.every { int ks, String pt -> pt == PLAINTEXT }
- }
-
- @Test
- void testShouldHandleUnprotectMalformedValue() throws Exception {
- // Arrange
- final String PLAINTEXT = "This is a plaintext value"
-
- // Act
- KEY_SIZES.each { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- String cipherText = spp.protect(PLAINTEXT)
- // Swap two characters in the cipher text
- final String MALFORMED_CIPHER_TEXT = manipulateString(cipherText, 25, 28)
- logger.info("Manipulated ${cipherText} to\n${MALFORMED_CIPHER_TEXT.padLeft(163)}")
-
- def msg = shouldFail(SensitivePropertyProtectionException) {
- spp.unprotect(MALFORMED_CIPHER_TEXT)
- }
- logger.expected("${msg} for keySize ${keySize} and cipher text [${MALFORMED_CIPHER_TEXT}]")
-
- // Assert
- assert msg == "Error decrypting a protected value"
- }
- }
-
- @Test
- void testShouldHandleUnprotectMissingIV() throws Exception {
- // Arrange
- final String PLAINTEXT = "This is a plaintext value"
-
- // Act
- KEY_SIZES.each { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- String cipherText = spp.protect(PLAINTEXT)
-
- // Remove the IV from the "complete" cipher text
- final String MISSING_IV_CIPHER_TEXT = cipherText[18..-1]
- logger.info("Manipulated ${cipherText} to\n${MISSING_IV_CIPHER_TEXT.padLeft(172)}")
-
- def msg = shouldFail(IllegalArgumentException) {
- spp.unprotect(MISSING_IV_CIPHER_TEXT)
- }
- logger.expected("${msg} for keySize ${keySize} and cipher text [${MISSING_IV_CIPHER_TEXT}]")
-
- // Remove the IV from the "complete" cipher text but keep the delimiter
- final String MISSING_IV_CIPHER_TEXT_WITH_DELIMITER = cipherText[16..-1]
- logger.info("Manipulated ${cipherText} to\n${MISSING_IV_CIPHER_TEXT_WITH_DELIMITER.padLeft(172)}")
-
- def msgWithDelimiter = shouldFail(IllegalArgumentException) {
- spp.unprotect(MISSING_IV_CIPHER_TEXT_WITH_DELIMITER)
- }
- logger.expected("${msgWithDelimiter} for keySize ${keySize} and cipher text [${MISSING_IV_CIPHER_TEXT_WITH_DELIMITER}]")
-
- // Assert
- assert msg == "The cipher text does not contain the delimiter || -- it should be of the form Base64(IV) || Base64(cipherText)"
-
- // Assert
- assert msgWithDelimiter == "The IV (0 bytes) must be at least 12 bytes"
- }
- }
-
- /**
- * Tests inputs which have a valid IV and delimiter but no "cipher text".
- *
- * @throws Exception
- */
- @Test
- void testShouldHandleUnprotectEmptyCipherText() throws Exception {
- // Arrange
- final String IV_AND_DELIMITER = "${encoder.encodeToString("Bad IV value".getBytes(StandardCharsets.UTF_8))}||"
- logger.info("IV and delimiter: ${IV_AND_DELIMITER}")
-
- final List<String> EMPTY_CIPHER_TEXTS = ["", " ", "\n"].collect { "${IV_AND_DELIMITER}${it}" }
-
- // Act
- KEY_SIZES.each { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- EMPTY_CIPHER_TEXTS.each { String emptyCipherText ->
- def msg = shouldFail(IllegalArgumentException) {
- spp.unprotect(emptyCipherText)
- }
- logger.expected("${msg} for keySize ${keySize} and cipher text [${emptyCipherText}]")
-
- // Assert
- assert msg == "Cannot decrypt a cipher text shorter than ${AESSensitivePropertyProvider.minCipherTextLength} chars".toString()
- }
- }
- }
-
- @Test
- void testShouldHandleUnprotectMalformedIV() throws Exception {
- // Arrange
- final String PLAINTEXT = "This is a plaintext value"
-
- // Act
- KEY_SIZES.each { int keySize ->
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(Hex.decode(getKeyOfSize(keySize)))
- logger.info("Initialized ${spp.name} with key size ${keySize}")
- String cipherText = spp.protect(PLAINTEXT)
- // Swap two characters in the IV
- final String MALFORMED_IV_CIPHER_TEXT = manipulateString(cipherText, 8, 11)
- logger.info("Manipulated ${cipherText} to\n${MALFORMED_IV_CIPHER_TEXT.padLeft(163)}")
-
- def msg = shouldFail(SensitivePropertyProtectionException) {
- spp.unprotect(MALFORMED_IV_CIPHER_TEXT)
- }
- logger.expected("${msg} for keySize ${keySize} and cipher text [${MALFORMED_IV_CIPHER_TEXT}]")
-
- // Assert
- assert msg == "Error decrypting a protected value"
- }
- }
-
- @Test
- void testShouldGetIdentifierKeyWithDifferentMaxKeyLengths() throws Exception {
- // Arrange
- def keys = getAvailableKeySizes().collectEntries { int keySize ->
- [(keySize): getKeyOfSize(keySize)]
- }
- logger.info("Keys: ${keys}")
-
- // Act
- keys.each { int size, String key ->
- String identifierKey = new AESSensitivePropertyProvider(key).getIdentifierKey()
- logger.info("Identifier key: ${identifierKey} for size ${size}")
-
- // Assert
- assert identifierKey =~ /aes\/gcm\/${size}/
- }
- }
-
- @Test
- void testShouldNotAllowEmptyKey() throws Exception {
- // Arrange
- final String INVALID_KEY = ""
-
- // Act
- def msg = shouldFail(SensitivePropertyProtectionException) {
- AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(INVALID_KEY)
- }
-
- // Assert
- assert msg == "The key cannot be empty"
- }
-
- @Test
- void testShouldNotAllowIncorrectlySizedKey() throws Exception {
- // Arrange
- final String INVALID_KEY = "Z" * 31
-
- // Act
- def msg = shouldFail(SensitivePropertyProtectionException) {
- AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(INVALID_KEY)
- }
-
- // Assert
- assert msg == "The key must be a valid hexadecimal key"
- }
-
- @Test
- void testShouldNotAllowInvalidKey() throws Exception {
- // Arrange
- final String INVALID_KEY = "Z" * 32
-
- // Act
- def msg = shouldFail(SensitivePropertyProtectionException) {
- AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(INVALID_KEY)
- }
-
- // Assert
- assert msg == "The key must be a valid hexadecimal key"
- }
-
- /**
- * This test is to ensure internal consistency and allow for encrypting value for various property files
- */
- @Test
- void testShouldEncryptArbitraryValues() {
- // Arrange
- def values = ["thisIsABadPassword", "thisIsABadSensitiveKeyPassword", "thisIsABadKeystorePassword", "thisIsABadKeyPassword", "thisIsABadTruststorePassword", "This is an encrypted banner message", "nififtw!"]
-
- String key = "2C576A9585DB862F5ECBEE5B4FFFCCA1" //getKeyOfSize(128)
- // key = "0" * 64
-
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(key)
-
- // Act
- def encryptedValues = values.collect { String v ->
- def encryptedValue = spp.protect(v)
- logger.info("${v} -> ${encryptedValue}")
- def (String iv, String cipherText) = encryptedValue.tokenize("||")
- logger.info("Normal Base64 encoding would be ${encoder.encodeToString(decoder.decode(iv))}||${encoder.encodeToString(decoder.decode(cipherText))}")
- encryptedValue
- }
-
- // Assert
- assert values == encryptedValues.collect { spp.unprotect(it) }
- }
-
- /**
- * This test is to ensure external compatibility in case someone encodes the encrypted value with Base64 and does not remove the padding
- */
- @Test
- void testShouldDecryptPaddedValueWith256BitKey() {
- // Arrange
- Assume.assumeTrue("JCE unlimited strength crypto policy must be installed for this test", Cipher.getMaxAllowedKeyLength("AES") > 128)
-
- final String EXPECTED_VALUE = getKeyOfSize(256) // "thisIsABadKeyPassword"
- String cipherText = "aYDkDKys1ENr3gp+||sTBPpMlIvHcOLTGZlfWct8r9RY8BuDlDkoaYmGJ/9m9af9tZIVzcnDwvYQAaIKxRGF7vI2yrY7Xd6x9GTDnWGiGiRXlaP458BBMMgfzH2O8"
- String unpaddedCipherText = cipherText.replaceAll("=", "")
-
- String key = "AAAABBBBCCCCDDDDEEEEFFFF00001111" * 2 // getKeyOfSize(256)
-
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(key)
-
- // Act
- String rawValue = spp.unprotect(cipherText)
- logger.info("Decrypted ${cipherText} to ${rawValue}")
- String rawUnpaddedValue = spp.unprotect(unpaddedCipherText)
- logger.info("Decrypted ${unpaddedCipherText} to ${rawUnpaddedValue}")
-
- // Assert
- assert rawValue == EXPECTED_VALUE
- assert rawUnpaddedValue == EXPECTED_VALUE
- }
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesGroovyTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesGroovyTest.groovy
index c7a0a4d..92480d0 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesGroovyTest.groovy
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesGroovyTest.groovy
@@ -52,8 +52,8 @@ class NiFiRegistryPropertiesGroovyTest extends GroovyTestCase {
try {
filePath = NiFiRegistryPropertiesGroovyTest.class.getResource(propertiesFilePath).toURI().getPath()
} catch (URISyntaxException ex) {
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " +
+ ex.getLocalizedMessage(), ex)
}
NiFiRegistryProperties properties = new NiFiRegistryProperties()
@@ -66,8 +66,8 @@ class NiFiRegistryPropertiesGroovyTest extends GroovyTestCase {
return properties
} catch (final Exception ex) {
logger.error("Cannot load properties file due to " + ex.getLocalizedMessage())
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " +
+ ex.getLocalizedMessage(), ex)
}
}
@@ -106,8 +106,9 @@ class NiFiRegistryPropertiesGroovyTest extends GroovyTestCase {
// Arrange
// Act
- NiFiRegistryProperties properties = new NiFiRegistryProperties()
- properties.setProperty("key", "value")
+ Properties props = new Properties()
+ props.setProperty("key", "value")
+ NiFiRegistryProperties properties = new NiFiRegistryProperties(props)
logger.info("niFiProperties has ${properties.size()} properties: ${properties.getPropertyKeys()}")
NiFiRegistryProperties emptyProperties = new NiFiRegistryProperties()
logger.info("emptyProperties has ${emptyProperties.size()} properties: ${emptyProperties.getPropertyKeys()}")
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoaderGroovyTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoaderGroovyTest.groovy
index 58c8087..5fe7eed 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoaderGroovyTest.groovy
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/NiFiRegistryPropertiesLoaderGroovyTest.groovy
@@ -17,7 +17,6 @@
package org.apache.nifi.registry.properties
import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.junit.After
import org.junit.AfterClass
import org.junit.Before
import org.junit.BeforeClass
@@ -46,7 +45,7 @@ class NiFiRegistryPropertiesLoaderGroovyTest extends GroovyTestCase {
private static final String PASSWORD_KEY_HEX_128 = "2C576A9585DB862F5ECBEE5B4FFFCCA1"
@BeforeClass
- public static void setUpOnce() throws Exception {
+ static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
@@ -55,21 +54,15 @@ class NiFiRegistryPropertiesLoaderGroovyTest extends GroovyTestCase {
}
@Before
- public void setUp() throws Exception {
- }
-
- @After
- public void tearDown() throws Exception {
- // Clear the sensitive property providers between runs
- NiFiRegistryPropertiesLoader.@sensitivePropertyProviderFactory = null
+ void setUp() throws Exception {
}
@AfterClass
- public static void tearDownOnce() {
+ static void tearDownOnce() {
}
@Test
- public void testConstructorShouldCreateNewInstance() throws Exception {
+ void testConstructorShouldCreateNewInstance() throws Exception {
// Arrange
// Act
@@ -104,31 +97,6 @@ class NiFiRegistryPropertiesLoaderGroovyTest extends GroovyTestCase {
}
@Test
- public void testShouldGetDefaultProviderKey() throws Exception {
- // Arrange
- final String expectedProviderKey = "aes/gcm/${Cipher.getMaxAllowedKeyLength("AES") > 128 ? 256 : 128}"
- logger.info("Expected provider key: ${expectedProviderKey}")
-
- // Act
- String defaultKey = NiFiRegistryPropertiesLoader.getDefaultProviderKey()
- logger.info("Default key: ${defaultKey}")
- // Assert
- assert defaultKey == expectedProviderKey
- }
-
- @Test
- public void testShouldInitializeSensitivePropertyProviderFactory() throws Exception {
- // Arrange
- NiFiRegistryPropertiesLoader propertiesLoader = new NiFiRegistryPropertiesLoader()
-
- // Act
- propertiesLoader.initializeSensitivePropertyProviderFactory()
-
- // Assert
- assert propertiesLoader.@sensitivePropertyProviderFactory
- }
-
- @Test
public void testShouldLoadUnprotectedPropertiesFromFile() throws Exception {
// Arrange
File unprotectedFile = new File("src/test/resources/conf/nifi-registry.properties")
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/ProtectedNiFiPropertiesGroovyTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/ProtectedNiFiRegistryPropertiesGroovyTest.groovy
similarity index 87%
rename from nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/ProtectedNiFiPropertiesGroovyTest.groovy
rename to nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/ProtectedNiFiRegistryPropertiesGroovyTest.groovy
index 86c7fb4..b1bb526 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/ProtectedNiFiPropertiesGroovyTest.groovy
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/ProtectedNiFiRegistryPropertiesGroovyTest.groovy
@@ -16,6 +16,12 @@
*/
package org.apache.nifi.registry.properties
+import org.apache.nifi.properties.ApplicationPropertiesProtector
+import org.apache.nifi.properties.MultipleSensitivePropertyProtectionException
+import org.apache.nifi.properties.PropertyProtectionScheme
+import org.apache.nifi.properties.SensitivePropertyProtectionException
+import org.apache.nifi.properties.SensitivePropertyProvider
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.After
import org.junit.AfterClass
@@ -32,8 +38,8 @@ import javax.crypto.Cipher
import java.security.Security
@RunWith(JUnit4.class)
-class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
- private static final Logger logger = LoggerFactory.getLogger(ProtectedNiFiPropertiesGroovyTest.class)
+class ProtectedNiFiRegistryPropertiesGroovyTest extends GroovyTestCase {
+ private static final Logger logger = LoggerFactory.getLogger(ProtectedNiFiRegistryPropertiesGroovyTest.class)
private static final String KEYSTORE_PASSWORD_KEY = NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD
private static final String KEY_PASSWORD_KEY = NiFiRegistryProperties.SECURITY_KEY_PASSWD
@@ -83,39 +89,40 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
throw new IllegalArgumentException("NiFi Registry properties file missing or unreadable")
}
- NiFiRegistryProperties properties = new NiFiRegistryProperties()
FileReader reader = new FileReader(file)
try {
- properties.load(reader)
+ final Properties props = new Properties()
+ props.load(reader)
+ NiFiRegistryProperties properties = new NiFiRegistryProperties(props)
logger.info("Loaded {} properties from {}", properties.size(), file.getAbsolutePath())
ProtectedNiFiRegistryProperties protectedNiFiProperties = new ProtectedNiFiRegistryProperties(properties)
// If it has protected keys, inject the SPP
if (protectedNiFiProperties.hasProtectedKeys()) {
- protectedNiFiProperties.addSensitivePropertyProvider(new AESSensitivePropertyProvider(keyHex))
+ protectedNiFiProperties.addSensitivePropertyProvider(StandardSensitivePropertyProviderFactory.withKey(keyHex)
+ .getProvider(PropertyProtectionScheme.AES_GCM))
}
return protectedNiFiProperties
} catch (final Exception ex) {
logger.error("Cannot load properties file due to " + ex.getLocalizedMessage())
- throw new RuntimeException("Cannot load properties file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load properties file due to " +
+ ex.getLocalizedMessage(), ex)
}
}
private static File fileForResource(String resourcePath) {
String filePath
try {
- URL resourceURL = ProtectedNiFiPropertiesGroovyTest.class.getResource(resourcePath)
+ URL resourceURL = ProtectedNiFiRegistryPropertiesGroovyTest.class.getResource(resourcePath)
if (!resourceURL) {
throw new RuntimeException("File ${resourcePath} not found in class resources, cannot load.")
}
filePath = resourceURL.toURI().getPath()
} catch (URISyntaxException ex) {
- throw new RuntimeException("Cannot load resource file due to "
- + ex.getLocalizedMessage(), ex)
+ throw new RuntimeException("Cannot load resource file due to " + ex.getLocalizedMessage(), ex)
}
File file = new File(filePath)
return file
@@ -287,16 +294,18 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
boolean isSensitive = properties.isPropertySensitive(KEYSTORE_PASSWORD_KEY)
boolean isProtected = properties.isPropertyProtected(KEYSTORE_PASSWORD_KEY)
- // While the value is "protected", the scheme is not registered, so treat it as raw
+ // While the value is "protected", the scheme is not registered
logger.info("The property is ${isSensitive ? "sensitive" : "not sensitive"} and ${isProtected ? "protected" : "not protected"}")
// Act
- NiFiRegistryProperties unprotectedProperties = properties.getUnprotectedProperties()
- String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
- logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ def msg = shouldFail(IllegalStateException) {
+ NiFiRegistryProperties unprotectedProperties = properties.getUnprotectedProperties()
+ String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
+ logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ }
// Assert
- assert retrievedKeystorePassword == expectedKeystorePassword
+ assert msg == "No provider available for nifi.registry.security.keyPasswd"
assert isSensitive
assert isProtected
}
@@ -340,7 +349,8 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
loadFromResourceFile("/conf/nifi-registry.with_sensitive_props_protected_aes_multiple_malformed.properties", KEY_HEX_128)
// Iterate over the protected keys and track the ones that fail to decrypt
- SensitivePropertyProvider spp = new AESSensitivePropertyProvider(KEY_HEX_128)
+ SensitivePropertyProvider spp = StandardSensitivePropertyProviderFactory.withKey(KEY_HEX_128)
+ .getProvider(PropertyProtectionScheme.AES_GCM)
Set<String> malformedKeys = properties.getProtectedPropertyKeys()
.findAll { String key, String scheme -> scheme == spp.identifierKey }
.keySet()
@@ -384,19 +394,21 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Read raw value from properties: ${expectedKeystorePassword}")
// Overwrite the internal cache
- properties.localProviderCache = [:]
+ properties.getSensitivePropertyProviders().clear()
boolean isSensitive = properties.isPropertySensitive(KEYSTORE_PASSWORD_KEY)
boolean isProtected = properties.isPropertyProtected(KEYSTORE_PASSWORD_KEY)
logger.info("The property is ${isSensitive ? "sensitive" : "not sensitive"} and ${isProtected ? "protected" : "not protected"}")
// Act
- NiFiRegistryProperties unprotectedProperties = properties.getUnprotectedProperties()
- String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
- logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ def msg = shouldFail(IllegalStateException) {
+ NiFiRegistryProperties unprotectedProperties = properties.getUnprotectedProperties()
+ String retrievedKeystorePassword = unprotectedProperties.getProperty(KEYSTORE_PASSWORD_KEY)
+ logger.info("${KEYSTORE_PASSWORD_KEY}: ${retrievedKeystorePassword}")
+ }
// Assert
- assert retrievedKeystorePassword == expectedKeystorePassword
+ assert msg == "No provider available for nifi.registry.security.keyPasswd"
assert isSensitive
assert isProtected
}
@@ -455,13 +467,17 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Protected property keys: ${properties.getProtectedPropertyKeys().keySet()}")
// Act
- double percentProtected = properties.getPercentOfSensitivePropertiesProtected()
+ double percentProtected = getPercentOfSensitivePropertiesProtected(properties)
logger.info("${percentProtected}% (${properties.getProtectedPropertyKeys().size()} of ${properties.getPopulatedSensitivePropertyKeys().size()}) protected")
// Assert
assert percentProtected == 0.0
}
+ private static double getPercentOfSensitivePropertiesProtected(final ProtectedNiFiRegistryProperties properties) {
+ return (int) Math.round(properties.getProtectedPropertyKeys().size() / ((double) properties.getPopulatedSensitivePropertyKeys().size()) * 100);
+ }
+
@Test
void testShouldGetPercentageOfSensitivePropertiesProtected_75() throws Exception {
// Arrange
@@ -472,7 +488,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Protected property keys: ${properties.getProtectedPropertyKeys().keySet()}")
// Act
- double percentProtected = properties.getPercentOfSensitivePropertiesProtected()
+ double percentProtected = getPercentOfSensitivePropertiesProtected(properties)
logger.info("${percentProtected}% (${properties.getProtectedPropertyKeys().size()} of ${properties.getPopulatedSensitivePropertyKeys().size()}) protected")
// Assert
@@ -489,7 +505,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Protected property keys: ${properties.getProtectedPropertyKeys().keySet()}")
// Act
- double percentProtected = properties.getPercentOfSensitivePropertiesProtected()
+ double percentProtected = getPercentOfSensitivePropertiesProtected(properties)
logger.info("${percentProtected}% (${properties.getProtectedPropertyKeys().size()} of ${properties.getPopulatedSensitivePropertyKeys().size()}) protected")
// Assert
@@ -500,13 +516,13 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
void testInstanceWithNoProtectedPropertiesShouldNotLoadSPP() throws Exception {
// Arrange
ProtectedNiFiRegistryProperties properties = loadFromResourceFile("/conf/nifi-registry.properties")
- assert properties.@localProviderCache?.isEmpty()
+ assert properties.getSensitivePropertyProviders().isEmpty()
logger.info("Has protected properties: ${properties.hasProtectedKeys()}")
assert !properties.hasProtectedKeys()
// Act
- Map localCache = properties.@localProviderCache
+ Map localCache = properties.getSensitivePropertyProviders()
logger.info("Internal cache ${localCache} has ${localCache.size()} providers loaded")
// Assert
@@ -537,10 +553,10 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
void testShouldNotAddNullSensitivePropertyProvider() throws Exception {
// Arrange
ProtectedNiFiRegistryProperties properties = new ProtectedNiFiRegistryProperties()
- assert properties.@localProviderCache?.isEmpty()
+ assert properties.getSensitivePropertyProviders().isEmpty()
// Act
- def msg = shouldFail(IllegalArgumentException) {
+ def msg = shouldFail(NullPointerException) {
properties.addSensitivePropertyProvider(null)
}
logger.info(msg)
@@ -589,7 +605,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
ProtectedNiFiRegistryProperties protectedNiFiProperties = loadFromResourceFile("/conf/nifi-registry.properties")
logger.info("Loaded ${protectedNiFiProperties.size()} properties from conf/nifi.properties")
- int hashCode = protectedNiFiProperties.internalNiFiProperties.hashCode()
+ int hashCode = protectedNiFiProperties.getApplicationProperties().hashCode()
logger.info("Hash code of internal instance: ${hashCode}")
// Act
@@ -599,7 +615,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Assert
assert unprotectedNiFiProperties.size() == protectedNiFiProperties.size()
assert unprotectedNiFiProperties.getPropertyKeys().every {
- !unprotectedNiFiProperties.getProperty(it).endsWith(".protected")
+ !unprotectedNiFiProperties.getProperty(it).endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)
}
logger.info("Hash code from returned unprotected instance: ${unprotectedNiFiProperties.hashCode()}")
assert unprotectedNiFiProperties.hashCode() == hashCode
@@ -614,7 +630,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
int protectedPropertyCount = protectedNiFiProperties.getProtectedPropertyKeys().size()
int protectionSchemeCount = protectedNiFiProperties
.getPropertyKeysIncludingProtectionSchemes()
- .findAll { it.endsWith(".protected") }
+ .findAll { it.endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX) }
.size()
int expectedUnprotectedPropertyCount = protectedNiFiProperties.size()
@@ -630,7 +646,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Expected unprotected property count: ${expectedUnprotectedPropertyCount}")
- int hashCode = protectedNiFiProperties.internalNiFiProperties.hashCode()
+ int hashCode = protectedNiFiProperties.getApplicationProperties().hashCode()
logger.info("Hash code of internal instance: ${hashCode}")
// Act
@@ -640,7 +656,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Assert
assert unprotectedNiFiProperties.size() == expectedUnprotectedPropertyCount
assert unprotectedNiFiProperties.getPropertyKeys().every {
- !unprotectedNiFiProperties.getProperty(it).endsWith(".protected")
+ !unprotectedNiFiProperties.getProperty(it).endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)
}
logger.info("Hash code from returned unprotected instance: ${unprotectedNiFiProperties.hashCode()}")
assert unprotectedNiFiProperties.hashCode() != hashCode
@@ -656,7 +672,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
int protectedPropertyCount = protectedNiFiProperties.getProtectedPropertyKeys().size()
int protectionSchemeCount = protectedNiFiProperties
.getPropertyKeysIncludingProtectionSchemes()
- .findAll { it.endsWith(".protected") }
+ .findAll { it.endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX) }
.size()
int expectedUnprotectedPropertyCount = protectedNiFiProperties.size()
@@ -672,7 +688,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
logger.info("Expected unprotected property count: ${expectedUnprotectedPropertyCount}")
- int hashCode = protectedNiFiProperties.internalNiFiProperties.hashCode()
+ int hashCode = protectedNiFiProperties.getApplicationProperties().hashCode()
logger.info("Hash code of internal instance: ${hashCode}")
// Act
@@ -682,7 +698,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Assert
assert unprotectedNiFiProperties.size() == expectedUnprotectedPropertyCount
assert unprotectedNiFiProperties.getPropertyKeys().every {
- !unprotectedNiFiProperties.getProperty(it).endsWith(".protected")
+ !unprotectedNiFiProperties.getProperty(it).endsWith(ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX)
}
logger.info("Hash code from returned unprotected instance: ${unprotectedNiFiProperties.hashCode()}")
assert unprotectedNiFiProperties.hashCode() != hashCode
@@ -691,14 +707,15 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
@Test
void testShouldCalculateSize() {
// Arrange
- NiFiRegistryProperties rawProperties = [key: "protectedValue", "key.protected": "scheme", "key2": "value2"] as NiFiRegistryProperties
+ Properties props = [key: "protectedValue", "key.protected": "scheme", "key2": "value2"] as Properties
+ NiFiRegistryProperties rawProperties = new NiFiRegistryProperties(props)
ProtectedNiFiRegistryProperties protectedNiFiProperties = new ProtectedNiFiRegistryProperties(rawProperties)
- logger.info("Raw properties (${rawProperties.size()}): ${rawProperties.keySet().join(", ")}")
+ logger.info("Raw properties (${rawProperties.size()}): ${rawProperties.getPropertyKeys().join(", ")}")
// Act
int protectedSize = protectedNiFiProperties.size()
logger.info("Protected properties (${protectedNiFiProperties.size()}): " +
- "${protectedNiFiProperties.getPropertyKeysExcludingProtectionSchemes().join(", ")}")
+ "${protectedNiFiProperties.getPropertyKeys().join(", ")}")
// Assert
assert protectedSize == rawProperties.size() - 1
@@ -707,25 +724,27 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
@Test
void testGetPropertyKeysShouldMatchSize() {
// Arrange
- NiFiRegistryProperties rawProperties = [key: "protectedValue", "key.protected": "scheme", "key2": "value2"] as NiFiRegistryProperties
+ Properties props = [key: "protectedValue", "key.protected": "scheme", "key2": "value2"] as Properties
+ NiFiRegistryProperties rawProperties = new NiFiRegistryProperties(props)
ProtectedNiFiRegistryProperties protectedNiFiProperties = new ProtectedNiFiRegistryProperties(rawProperties)
- logger.info("Raw properties (${rawProperties.size()}): ${rawProperties.keySet().join(", ")}")
+ logger.info("Raw properties (${rawProperties.size()}): ${rawProperties.getPropertyKeys().join(", ")}")
// Act
- def filteredKeys = protectedNiFiProperties.getPropertyKeysExcludingProtectionSchemes()
+ def filteredKeys = protectedNiFiProperties.getPropertyKeys()
logger.info("Protected properties (${protectedNiFiProperties.size()}): ${filteredKeys.join(", ")}")
// Assert
assert protectedNiFiProperties.size() == rawProperties.size() - 1
- assert filteredKeys == rawProperties.keySet() - "key.protected"
+ assert filteredKeys == rawProperties.getPropertyKeys() - "key.protected"
}
@Test
void testShouldGetPropertyKeysIncludingProtectionSchemes() {
// Arrange
- NiFiRegistryProperties rawProperties = [key: "protectedValue", "key.protected": "scheme", "key2": "value2"] as NiFiRegistryProperties
+ Properties props = [key: "protectedValue", "key.protected": "scheme", "key2": "value2"] as Properties
+ NiFiRegistryProperties rawProperties = new NiFiRegistryProperties(props)
ProtectedNiFiRegistryProperties protectedNiFiProperties = new ProtectedNiFiRegistryProperties(rawProperties)
- logger.info("Raw properties (${rawProperties.size()}): ${rawProperties.keySet().join(", ")}")
+ logger.info("Raw properties (${rawProperties.size()}): ${rawProperties.getPropertyKeys().join(", ")}")
// Act
def allKeys = protectedNiFiProperties.getPropertyKeysIncludingProtectionSchemes()
@@ -733,7 +752,7 @@ class ProtectedNiFiPropertiesGroovyTest extends GroovyTestCase {
// Assert
assert allKeys.size() == rawProperties.size()
- assert allKeys == rawProperties.keySet()
+ assert allKeys == rawProperties.getPropertyKeys()
}
}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/security/crypto/CryptoKeyLoaderGroovyTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapUtilsGroovyTest.groovy
similarity index 81%
rename from nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/security/crypto/CryptoKeyLoaderGroovyTest.groovy
rename to nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapUtilsGroovyTest.groovy
index 6d8623e..465a122 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/security/crypto/CryptoKeyLoaderGroovyTest.groovy
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/test/groovy/org/apache/nifi/registry/properties/util/NiFiRegistryBootstrapUtilsGroovyTest.groovy
@@ -14,10 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.nifi.security.crypto
+package org.apache.nifi.registry.properties.util
import org.apache.commons.lang3.SystemUtils
-import org.apache.nifi.registry.security.crypto.CryptoKeyLoader
import org.apache.nifi.registry.security.crypto.CryptoKeyProvider
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Assume
@@ -33,9 +32,9 @@ import java.nio.file.attribute.PosixFilePermission
import java.security.Security
@RunWith(JUnit4.class)
-class CryptoKeyLoaderGroovyTest extends GroovyTestCase {
+class NiFiRegistryBootstrapUtilsGroovyTest extends GroovyTestCase {
- private static final Logger logger = LoggerFactory.getLogger(CryptoKeyLoaderGroovyTest.class)
+ private static final Logger logger = LoggerFactory.getLogger(NiFiRegistryBootstrapUtilsGroovyTest.class)
private static final String KEY_HEX_128 = "0123456789ABCDEFFEDCBA9876543210"
private static final String KEY_HEX_256 = KEY_HEX_128 * 2
@@ -57,7 +56,7 @@ class CryptoKeyLoaderGroovyTest extends GroovyTestCase {
final String expectedKey = KEY_HEX_256
// Act
- String key = CryptoKeyLoader.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.conf")
+ String key = NiFiRegistryBootstrapUtils.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.conf")
// Assert
assert key == expectedKey
@@ -68,7 +67,7 @@ class CryptoKeyLoaderGroovyTest extends GroovyTestCase {
// Arrange
// Act
- String key = CryptoKeyLoader.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.with_missing_key_line.conf")
+ String key = NiFiRegistryBootstrapUtils.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.with_missing_key_line.conf")
// Assert
assert key == CryptoKeyProvider.EMPTY_KEY
@@ -79,7 +78,7 @@ class CryptoKeyLoaderGroovyTest extends GroovyTestCase {
// Arrange
// Act
- String key = CryptoKeyLoader.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.with_missing_key.conf")
+ String key = NiFiRegistryBootstrapUtils.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.with_missing_key.conf")
// Assert
assert key == CryptoKeyProvider.EMPTY_KEY
@@ -91,7 +90,7 @@ class CryptoKeyLoaderGroovyTest extends GroovyTestCase {
// Act
def msg = shouldFail(IOException) {
- CryptoKeyLoader.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.missing.conf")
+ NiFiRegistryBootstrapUtils.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.missing.conf")
}
logger.info(msg)
@@ -110,7 +109,7 @@ class CryptoKeyLoaderGroovyTest extends GroovyTestCase {
// Act
def msg = shouldFail(IOException) {
- CryptoKeyLoader.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.unreadable_file_permissions.conf")
+ NiFiRegistryBootstrapUtils.extractKeyFromBootstrapFile("src/test/resources/conf/bootstrap.unreadable_file_permissions.conf")
}
logger.info(msg)
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-runtime/src/main/java/org/apache/nifi/registry/NiFiRegistry.java b/nifi-registry/nifi-registry-core/nifi-registry-runtime/src/main/java/org/apache/nifi/registry/NiFiRegistry.java
index 455ab9d..0123e39 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-runtime/src/main/java/org/apache/nifi/registry/NiFiRegistry.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-runtime/src/main/java/org/apache/nifi/registry/NiFiRegistry.java
@@ -16,10 +16,10 @@
*/
package org.apache.nifi.registry;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
import org.apache.nifi.registry.jetty.JettyServer;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
import org.apache.nifi.registry.properties.NiFiRegistryPropertiesLoader;
-import org.apache.nifi.registry.properties.SensitivePropertyProtectionException;
import org.apache.nifi.registry.security.crypto.BootstrapFileCryptoKeyProvider;
import org.apache.nifi.registry.security.crypto.CryptoKeyProvider;
import org.apache.nifi.registry.security.crypto.MissingCryptoKeyException;
@@ -42,13 +42,6 @@ public class NiFiRegistry {
public static final String BOOTSTRAP_PORT_PROPERTY = "nifi.registry.bootstrap.listen.port";
- public static final String NIFI_REGISTRY_PROPERTIES_FILE_PATH_PROPERTY = "nifi.registry.properties.file.path";
- public static final String NIFI_REGISTRY_BOOTSTRAP_FILE_PATH_PROPERTY = "nifi.registry.bootstrap.config.file.path";
- public static final String NIFI_REGISTRY_BOOTSTRAP_DOCS_DIR_PROPERTY = "nifi.registry.bootstrap.config.docs.dir";
-
- public static final String RELATIVE_BOOTSTRAP_FILE_LOCATION = "conf/bootstrap.conf";
- public static final String RELATIVE_PROPERTIES_FILE_LOCATION = "conf/nifi-registry.properties";
- public static final String RELATIVE_DOCS_LOCATION = "docs";
private final JettyServer server;
private final BootstrapListener bootstrapListener;
@@ -106,7 +99,8 @@ public class NiFiRegistry {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
- final String docsDir = System.getProperty(NIFI_REGISTRY_BOOTSTRAP_DOCS_DIR_PROPERTY, RELATIVE_DOCS_LOCATION);
+ final String docsDir = System.getProperty(NiFiRegistryProperties.NIFI_REGISTRY_BOOTSTRAP_DOCS_DIR_PROPERTY,
+ NiFiRegistryProperties.RELATIVE_DOCS_LOCATION);
final long startTime = System.nanoTime();
server = new JettyServer(properties, masterKeyProvider, docsDir);
@@ -168,7 +162,8 @@ public class NiFiRegistry {
}
public static CryptoKeyProvider getMasterKeyProvider() {
- final String bootstrapConfigFilePath = System.getProperty(NIFI_REGISTRY_BOOTSTRAP_FILE_PATH_PROPERTY, RELATIVE_BOOTSTRAP_FILE_LOCATION);
+ final String bootstrapConfigFilePath = System.getProperty(NiFiRegistryProperties.NIFI_REGISTRY_BOOTSTRAP_FILE_PATH_PROPERTY,
+ NiFiRegistryProperties.RELATIVE_BOOTSTRAP_FILE_LOCATION);
CryptoKeyProvider masterKeyProvider = new BootstrapFileCryptoKeyProvider(bootstrapConfigFilePath);
LOGGER.info("Read property protection key from {}", bootstrapConfigFilePath);
return masterKeyProvider;
@@ -186,7 +181,8 @@ public class NiFiRegistry {
try {
try {
// Load properties using key. If properties are protected and key missing, throw RuntimeException
- final String nifiRegistryPropertiesFilePath = System.getProperty(NIFI_REGISTRY_PROPERTIES_FILE_PATH_PROPERTY, RELATIVE_PROPERTIES_FILE_LOCATION);
+ final String nifiRegistryPropertiesFilePath = System.getProperty(NiFiRegistryProperties.NIFI_REGISTRY_PROPERTIES_FILE_PATH_PROPERTY,
+ NiFiRegistryProperties.RELATIVE_PROPERTIES_FILE_LOCATION);
final NiFiRegistryProperties properties = NiFiRegistryPropertiesLoader.withKey(key).load(nifiRegistryPropertiesFilePath);
LOGGER.info("Loaded {} properties", properties.size());
return properties;
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
index 0fe0538..7a30d37 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
@@ -40,6 +40,7 @@ import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -176,17 +177,17 @@ public abstract class IntegrationTestBase {
* @param propertiesFilePath The location of the properties file
* @return A NiFIRegistryProperties instance based on the properties file contents
*/
- static NiFiRegistryProperties loadNiFiRegistryProperties(String propertiesFilePath) {
- NiFiRegistryProperties properties = new NiFiRegistryProperties();
+ static NiFiRegistryProperties loadNiFiRegistryProperties(final String propertiesFilePath) {
+ final Properties props = new Properties();
try (final FileReader reader = new FileReader(propertiesFilePath)) {
- properties.load(reader);
+ props.load(reader);
+ return new NiFiRegistryProperties(props);
} catch (final IOException ioe) {
throw new RuntimeException("Unable to load properties: " + ioe, ioe);
}
- return properties;
}
- private static Client createClientFromConfig(NiFiRegistryClientConfig registryClientConfig) {
+ private static Client createClientFromConfig(final NiFiRegistryClientConfig registryClientConfig) {
final ClientConfig clientConfig = new ClientConfig();
clientConfig.register(jacksonJaxbJsonProvider());
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
index f6d1248..2bbefc3 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
@@ -17,6 +17,9 @@
package org.apache.nifi.registry.web.api;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.properties.PropertyProtectionScheme;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory;
import org.apache.nifi.registry.SecureLdapTestApiApplication;
import org.apache.nifi.registry.authorization.AccessPolicy;
import org.apache.nifi.registry.authorization.AccessPolicySummary;
@@ -33,9 +36,7 @@ import org.apache.nifi.registry.client.UserClient;
import org.apache.nifi.registry.client.impl.JerseyNiFiRegistryClient;
import org.apache.nifi.registry.client.impl.request.BearerTokenRequestConfig;
import org.apache.nifi.registry.extension.ExtensionManager;
-import org.apache.nifi.registry.properties.AESSensitivePropertyProvider;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
-import org.apache.nifi.registry.properties.SensitivePropertyProvider;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.apache.nifi.registry.security.authorization.Authorizer;
import org.apache.nifi.registry.security.authorization.AuthorizerFactory;
@@ -140,7 +141,8 @@ public class SecureLdapIT extends IntegrationTestBase {
@Primary
@Bean
public static SensitivePropertyProvider sensitivePropertyProvider() throws Exception {
- return new AESSensitivePropertyProvider(getNiFiRegistryMasterKeyProvider().getKey());
+ return StandardSensitivePropertyProviderFactory.withKey(getNiFiRegistryMasterKeyProvider().getKey())
+ .getProvider(PropertyProtectionScheme.AES_GCM);
}
private static CryptoKeyProvider getNiFiRegistryMasterKeyProvider() {
diff --git a/nifi-registry/nifi-registry-toolkit/nifi-registry-toolkit-persistence/src/main/java/org/apache/nifi/registry/toolkit/persistence/FlowPersistenceProviderMigrator.java b/nifi-registry/nifi-registry-toolkit/nifi-registry-toolkit-persistence/src/main/java/org/apache/nifi/registry/toolkit/persistence/FlowPersistenceProviderMigrator.java
index a510281..266d248 100644
--- a/nifi-registry/nifi-registry-toolkit/nifi-registry-toolkit-persistence/src/main/java/org/apache/nifi/registry/toolkit/persistence/FlowPersistenceProviderMigrator.java
+++ b/nifi-registry/nifi-registry-toolkit/nifi-registry-toolkit-persistence/src/main/java/org/apache/nifi/registry/toolkit/persistence/FlowPersistenceProviderMigrator.java
@@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
+import java.util.Properties;
public class FlowPersistenceProviderMigrator {
private static final Logger log = LoggerFactory.getLogger(FlowPersistenceProviderMigrator.class);
@@ -91,12 +92,13 @@ public class FlowPersistenceProviderMigrator {
new FlowPersistenceProviderMigrator().doMigrate(fromMetadataService, fromPersistenceProvider, toPersistenceProvider);
}
- private static NiFiRegistryProperties createToProperties(CommandLine commandLine, NiFiRegistryProperties fromProperties) {
- NiFiRegistryProperties toProperties = new NiFiRegistryProperties();
- for (String propertyKey : fromProperties.getPropertyKeys()) {
- toProperties.setProperty(propertyKey, fromProperties.getProperty(propertyKey));
+ private static NiFiRegistryProperties createToProperties(final CommandLine commandLine, final NiFiRegistryProperties fromProperties) {
+ final Properties props = new Properties();
+ for (final String propertyKey : fromProperties.getPropertyKeys()) {
+ props.setProperty(propertyKey, fromProperties.getProperty(propertyKey));
}
- toProperties.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, commandLine.getOptionValue('t'));
+ props.setProperty(NiFiRegistryProperties.PROVIDERS_CONFIGURATION_FILE, commandLine.getOptionValue('t'));
+ final NiFiRegistryProperties toProperties = new NiFiRegistryProperties(props);
return toProperties;
}
diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy
index de5aeb4..f9a2f9f 100644
--- a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy
+++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy
@@ -28,6 +28,7 @@ import org.apache.nifi.toolkit.admin.client.ClientFactory
import org.apache.nifi.toolkit.admin.client.NiFiClientFactory
import org.apache.nifi.toolkit.admin.client.NiFiClientUtil
import org.apache.nifi.toolkit.admin.util.AdminUtil
+import org.apache.nifi.util.NiFiBootstrapUtils
import org.apache.nifi.util.NiFiProperties
import org.apache.nifi.util.StringUtils
import org.apache.nifi.web.api.dto.NodeDTO
@@ -271,7 +272,7 @@ public class NodeManagerTool extends AbstractAdminTool {
String nifiConfDir = AdminUtil.getRelativeDirectory(bootstrapProperties.getProperty("conf.dir"), bootstrapConf.getCanonicalFile().getParentFile().getParentFile().getCanonicalPath())
String nifiLibDir = AdminUtil.getRelativeDirectory(bootstrapProperties.getProperty("lib.dir"), bootstrapConf.getCanonicalFile().getParentFile().getParentFile().getCanonicalPath())
String nifiPropertiesFileName = nifiConfDir + File.separator +"nifi.properties"
- final String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile(bootstrapConfFileName)
+ final String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile(bootstrapConfFileName)
final NiFiProperties niFiProperties = NiFiPropertiesLoader.withKey(key).load(nifiPropertiesFileName)
final String operation = commandLine.getOptionValue(OPERATION)
diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy
index df538e8..136bcd8 100644
--- a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy
+++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy
@@ -29,6 +29,7 @@ import org.apache.nifi.toolkit.admin.client.ClientFactory
import org.apache.nifi.toolkit.admin.client.NiFiClientFactory
import org.apache.nifi.toolkit.admin.client.NiFiClientUtil
import org.apache.nifi.toolkit.admin.util.AdminUtil
+import org.apache.nifi.util.NiFiBootstrapUtils
import org.apache.nifi.util.NiFiProperties
import org.apache.nifi.web.api.dto.BulletinDTO
import org.apache.nifi.web.api.entity.BulletinEntity
@@ -88,7 +89,7 @@ public class NotificationTool extends AbstractAdminTool {
logger.info("Loading nifi properties for host information")
}
- final String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile(bootstrapConfFile)
+ final String key = NiFiBootstrapUtils.extractKeyFromBootstrapFile(bootstrapConfFile)
final NiFiProperties niFiProperties = NiFiPropertiesLoader.withKey(key).load(nifiPropertiesFile)
final Client client = clientFactory.getClient(niFiProperties,nifiInstallDir)
final String url = NiFiClientUtil.getUrl(niFiProperties,NOTIFICATION_ENDPOINT)
diff --git a/nifi-toolkit/nifi-toolkit-admin/src/test/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactorySpec.groovy b/nifi-toolkit/nifi-toolkit-admin/src/test/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactorySpec.groovy
index ddb445f..e469980 100644
--- a/nifi-toolkit/nifi-toolkit-admin/src/test/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactorySpec.groovy
+++ b/nifi-toolkit/nifi-toolkit-admin/src/test/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactorySpec.groovy
@@ -23,6 +23,7 @@ import org.apache.nifi.properties.NiFiPropertiesLoader
import org.apache.nifi.security.util.CertificateUtils
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandaloneCommandLine
+import org.apache.nifi.util.NiFiBootstrapUtils
import org.apache.nifi.util.NiFiProperties
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder
@@ -90,7 +91,7 @@ class NiFiClientFactorySpec extends Specification {
def bootstrapConfFile = "src/test/resources/notify/conf/bootstrap.conf"
def nifiPropertiesFile = "src/test/resources/notify/conf/nifi-secured.properties"
- def key = NiFiPropertiesLoader.extractKeyFromBootstrapFile(bootstrapConfFile)
+ def key = NiFiBootstrapUtils.extractKeyFromBootstrapFile(bootstrapConfFile)
def NiFiProperties niFiProperties = NiFiPropertiesLoader.withKey(key).load(nifiPropertiesFile)
def clientFactory = new NiFiClientFactory()
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/pom.xml b/nifi-toolkit/nifi-toolkit-encrypt-config/pom.xml
index 319e2d9..606664a 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/pom.xml
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/pom.xml
@@ -35,6 +35,11 @@
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
+ <groupId>org.apache.nifi.registry</groupId>
+ <artifactId>nifi-registry-properties</artifactId>
+ <version>1.14.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties-loader</artifactId>
<version>1.14.0-SNAPSHOT</version>
@@ -168,4 +173,4 @@
</plugin>
</plugins>
</build>
-</project>
\ No newline at end of file
+</project>
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
index f6f4453..9f48d6c 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
@@ -31,9 +31,10 @@ import org.apache.nifi.encrypt.PropertyEncryptor
import org.apache.nifi.encrypt.PropertyEncryptorFactory
import org.apache.nifi.flow.encryptor.FlowEncryptor
import org.apache.nifi.flow.encryptor.StandardFlowEncryptor
-import org.apache.nifi.security.kms.CryptoUtils
+import org.apache.nifi.registry.properties.util.NiFiRegistryBootstrapUtils
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException
import org.apache.nifi.toolkit.tls.commandLine.ExitCode
+import org.apache.nifi.util.NiFiBootstrapUtils
import org.apache.nifi.util.NiFiProperties
import org.apache.nifi.util.console.TextDevice
import org.apache.nifi.util.console.TextDevices
@@ -46,16 +47,17 @@ import org.xml.sax.SAXException
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import java.nio.charset.StandardCharsets
+import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.security.KeyException
import java.security.Security
+import java.util.function.Supplier
import java.util.regex.Matcher
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import java.util.zip.ZipException
-import java.nio.file.Files
class ConfigEncryptionTool {
private static final Logger logger = LoggerFactory.getLogger(ConfigEncryptionTool.class)
@@ -70,6 +72,10 @@ class ConfigEncryptionTool {
public static flowXmlPath
public String outputFlowXmlPath
+ static final PropertyProtectionScheme DEFAULT_PROTECTION_SCHEME = PropertyProtectionScheme.AES_GCM
+
+ private PropertyProtectionScheme protectionScheme = DEFAULT_PROTECTION_SCHEME
+ private PropertyProtectionScheme migrationProtectionScheme = DEFAULT_PROTECTION_SCHEME
private String keyHex
private String migrationKeyHex
private String password
@@ -77,6 +83,7 @@ class ConfigEncryptionTool {
// This is the raw value used in nifi.sensitive.props.key
private String flowPropertiesPassword
+ private String existingFlowPropertiesPassword
private String newFlowAlgorithm
private String newFlowProvider
@@ -109,9 +116,11 @@ class ConfigEncryptionTool {
private static final String FLOW_XML_ARG = "flowXml"
private static final String OUTPUT_FLOW_XML_ARG = "outputFlowXml"
private static final String KEY_ARG = "key"
+ private static final String PROTECTION_SCHEME_ARG = "protectionScheme"
private static final String PASSWORD_ARG = "password"
private static final String KEY_MIGRATION_ARG = "oldKey"
private static final String PASSWORD_MIGRATION_ARG = "oldPassword"
+ private static final String PROTECTION_SCHEME_MIGRATION_ARG = "oldProtectionScheme"
private static final String USE_KEY_ARG = "useRawKey"
private static final String MIGRATION_ARG = "migrate"
private static final String PROPS_KEY_ARG = "propsKey"
@@ -120,6 +129,9 @@ class ConfigEncryptionTool {
private static final String NEW_FLOW_PROVIDER_ARG = "newFlowProvider"
private static final String TRANSLATE_CLI_ARG = "translateCli"
+ private static final String PROTECTION_SCHEME_DESC = String.format("Selects the protection scheme for encrypted properties. " +
+ "Valid values are: [%s] (default is %s)", PropertyProtectionScheme.values().join(", "), DEFAULT_PROTECTION_SCHEME.name())
+
// Static holder to avoid re-generating the options object multiple times in an invocation
private static Options staticOptions
@@ -230,13 +242,15 @@ class ConfigEncryptionTool {
options.addOption(Option.builder("u").longOpt(OUTPUT_AUTHORIZERS_ARG).hasArg(true).argName("file").desc("The destination authorizers.xml file containing protected config values (will not modify input authorizers.xml)").build())
options.addOption(Option.builder("f").longOpt(FLOW_XML_ARG).hasArg(true).argName("file").desc("The flow.xml.gz file currently protected with old password (will be overwritten unless -g is specified)").build())
options.addOption(Option.builder("g").longOpt(OUTPUT_FLOW_XML_ARG).hasArg(true).argName("file").desc("The destination flow.xml.gz file containing protected config values (will not modify input flow.xml.gz)").build())
- options.addOption(Option.builder("b").longOpt(BOOTSTRAP_CONF_ARG).hasArg(true).argName("file").desc("The bootstrap.conf file to persist root key").build())
+ options.addOption(Option.builder("b").longOpt(BOOTSTRAP_CONF_ARG).hasArg(true).argName("file").desc("The bootstrap.conf file to persist root key and to optionally provide any configuration for the protection scheme.").build())
+ options.addOption(Option.builder("S").longOpt(PROTECTION_SCHEME_ARG).hasArg(true).argName("protectionScheme").desc(PROTECTION_SCHEME_DESC).build())
options.addOption(Option.builder("k").longOpt(KEY_ARG).hasArg(true).argName("keyhex").desc("The raw hexadecimal key to use to encrypt the sensitive properties").build())
options.addOption(Option.builder("e").longOpt(KEY_MIGRATION_ARG).hasArg(true).argName("keyhex").desc("The old raw hexadecimal key to use during key migration").build())
+ options.addOption(Option.builder("H").longOpt(PROTECTION_SCHEME_MIGRATION_ARG).hasArg(true).argName("protectionScheme").desc("The old protection scheme to use during encryption migration (see --protectionScheme for possible values). Default is " + DEFAULT_PROTECTION_SCHEME.name()).build())
options.addOption(Option.builder("p").longOpt(PASSWORD_ARG).hasArg(true).argName("password").desc("The password from which to derive the key to use to encrypt the sensitive properties").build())
options.addOption(Option.builder("w").longOpt(PASSWORD_MIGRATION_ARG).hasArg(true).argName("password").desc("The old password from which to derive the key during migration").build())
options.addOption(Option.builder("r").longOpt(USE_KEY_ARG).hasArg(false).desc("If provided, the secure console will prompt for the raw key value in hexadecimal form").build())
- options.addOption(Option.builder("m").longOpt(MIGRATION_ARG).hasArg(false).desc("If provided, the nifi.properties and/or login-identity-providers.xml sensitive properties will be re-encrypted with a new key").build())
+ options.addOption(Option.builder("m").longOpt(MIGRATION_ARG).hasArg(false).desc("If provided, the nifi.properties and/or login-identity-providers.xml sensitive properties will be re-encrypted with the new scheme").build())
options.addOption(Option.builder("x").longOpt(DO_NOT_ENCRYPT_NIFI_PROPERTIES_ARG).hasArg(false).desc("If provided, the properties in flow.xml.gz will be re-encrypted with a new key but the nifi.properties and/or login-identity-providers.xml files will not be modified").build())
options.addOption(Option.builder("s").longOpt(PROPS_KEY_ARG).hasArg(true).argName("password|keyhex").desc("The password or key to use to encrypt the sensitive processor properties in flow.xml.gz").build())
options.addOption(Option.builder("A").longOpt(NEW_FLOW_ALGORITHM_ARG).hasArg(true).argName("algorithm").desc("The algorithm to use to encrypt the sensitive processor properties in flow.xml.gz").build())
@@ -312,6 +326,10 @@ class ConfigEncryptionTool {
}
}
+ if (commandLine.hasOption(PROTECTION_SCHEME_ARG)) {
+ protectionScheme = PropertyProtectionScheme.valueOf(commandLine.getOptionValue(PROTECTION_SCHEME_ARG))
+ }
+
// If translating nifi.properties to CLI format, none of the remaining parsing is necessary
if (translatingCli) {
@@ -410,17 +428,23 @@ class ConfigEncryptionTool {
if (isVerbose) {
logger.info("Key migration mode activated")
}
- if (commandLine.hasOption(PASSWORD_MIGRATION_ARG)) {
- usingPasswordMigration = true
- if (commandLine.hasOption(KEY_MIGRATION_ARG)) {
- printUsageAndThrow("Only one of '-w'/'--${PASSWORD_MIGRATION_ARG}' and '-e'/'--${KEY_MIGRATION_ARG}' can be used", ExitCode.INVALID_ARGS)
+ if (commandLine.hasOption(PROTECTION_SCHEME_MIGRATION_ARG)) {
+ migrationProtectionScheme = PropertyProtectionScheme.valueOf(commandLine.getOptionValue(PROTECTION_SCHEME_MIGRATION_ARG))
+ }
+
+ if (migrationProtectionScheme.requiresSecretKey()) {
+ if (commandLine.hasOption(PASSWORD_MIGRATION_ARG)) {
+ usingPasswordMigration = true
+ if (commandLine.hasOption(KEY_MIGRATION_ARG)) {
+ printUsageAndThrow("Only one of '-w'/'--${PASSWORD_MIGRATION_ARG}' and '-e'/'--${KEY_MIGRATION_ARG}' can be used", ExitCode.INVALID_ARGS)
+ } else {
+ migrationPassword = commandLine.getOptionValue(PASSWORD_MIGRATION_ARG)
+ }
} else {
- migrationPassword = commandLine.getOptionValue(PASSWORD_MIGRATION_ARG)
+ migrationKeyHex = commandLine.getOptionValue(KEY_MIGRATION_ARG)
+ // Use the "migration password" value if the migration key hex is absent
+ usingPasswordMigration = !migrationKeyHex
}
- } else {
- migrationKeyHex = commandLine.getOptionValue(KEY_MIGRATION_ARG)
- // Use the "migration password" value if the migration key hex is absent
- usingPasswordMigration = !migrationKeyHex
}
} else {
if (commandLine.hasOption(PASSWORD_MIGRATION_ARG) || commandLine.hasOption(KEY_MIGRATION_ARG)) {
@@ -428,23 +452,25 @@ class ConfigEncryptionTool {
}
}
- if (commandLine.hasOption(PASSWORD_ARG)) {
- usingPassword = true
- if (commandLine.hasOption(KEY_ARG)) {
- printUsageAndThrow("Only one of '-p'/'--${PASSWORD_ARG}' and '-k'/'--${KEY_ARG}' can be used", ExitCode.INVALID_ARGS)
+ if (protectionScheme.requiresSecretKey()) {
+ if (commandLine.hasOption(PASSWORD_ARG)) {
+ usingPassword = true
+ if (commandLine.hasOption(KEY_ARG)) {
+ printUsageAndThrow("Only one of '-p'/'--${PASSWORD_ARG}' and '-k'/'--${KEY_ARG}' can be used", ExitCode.INVALID_ARGS)
+ } else {
+ password = commandLine.getOptionValue(PASSWORD_ARG)
+ }
} else {
- password = commandLine.getOptionValue(PASSWORD_ARG)
+ keyHex = commandLine.getOptionValue(KEY_ARG)
+ usingPassword = !keyHex
}
- } else {
- keyHex = commandLine.getOptionValue(KEY_ARG)
- usingPassword = !keyHex
- }
- if (commandLine.hasOption(USE_KEY_ARG)) {
- if (keyHex || password) {
- logger.warn("If the key or password is provided in the arguments, '-r'/'--${USE_KEY_ARG}' is ignored")
- } else {
- usingPassword = false
+ if (commandLine.hasOption(USE_KEY_ARG)) {
+ if (keyHex || password) {
+ logger.warn("If the key or password is provided in the arguments, '-r'/'--${USE_KEY_ARG}' is ignored")
+ } else {
+ usingPassword = false
+ }
}
}
@@ -555,8 +581,10 @@ class ConfigEncryptionTool {
private static String parseKey(String rawKey) throws KeyException {
String hexKey = rawKey.replaceAll("[^0-9a-fA-F]", "")
def validKeyLengths = getValidKeyLengths()
+ List<Integer> validHexCharLengths = validKeyLengths.collect {it / 4 }
if (!validKeyLengths.contains(hexKey.size() * 4)) {
- throw new KeyException("The key (${hexKey.size()} hex chars) must be of length ${validKeyLengths} bits (${validKeyLengths.collect { it / 4 }} hex characters)")
+ throw new KeyException("The key (${hexKey.size()} hex chars) must be of length ${validKeyLengths} " +
+ "bits (${validHexCharLengths} hex characters)")
}
hexKey.toUpperCase()
}
@@ -570,6 +598,10 @@ class ConfigEncryptionTool {
Cipher.getMaxAllowedKeyLength("AES") > 128 ? [128, 192, 256] : [128]
}
+ private NiFiPropertiesLoader getNiFiPropertiesLoader(final String keyHex) {
+ return protectionScheme.requiresSecretKey() ? NiFiPropertiesLoader.withKey(keyHex) : new NiFiPropertiesLoader()
+ }
+
/**
* Loads the {@link NiFiProperties} instance from the provided file path (restoring the original value of the System property {@code nifi.properties.file.path} after loading this instance).
*
@@ -581,7 +613,7 @@ class ConfigEncryptionTool {
if (niFiPropertiesPath && (niFiPropertiesFile = new File(niFiPropertiesPath)).exists()) {
NiFiProperties properties
try {
- properties = NiFiPropertiesLoader.withKey(existingKeyHex).load(niFiPropertiesFile)
+ properties = getNiFiPropertiesLoader(existingKeyHex).load(niFiPropertiesFile)
logger.info("Loaded NiFiProperties instance with ${properties.size()} properties")
return properties
} catch (RuntimeException e) {
@@ -725,14 +757,18 @@ class ConfigEncryptionTool {
Paths.get(originalOutputFlowXmlPath).resolveSibling(migratedFileName)
}
+ private SensitivePropertyProviderFactory getSensitivePropertyProviderFactory(final String keyHex) {
+ StandardSensitivePropertyProviderFactory.withKeyAndBootstrapSupplier(keyHex, getBootstrapSupplier(bootstrapConfPath))
+ }
+
String decryptLoginIdentityProviders(String encryptedXml, String existingKeyHex = keyHex) {
- AESSensitivePropertyProvider sensitivePropertyProvider = new AESSensitivePropertyProvider(existingKeyHex)
+ final SensitivePropertyProviderFactory providerFactory = getSensitivePropertyProviderFactory(existingKeyHex)
try {
def doc = getXmlSlurper().parseText(encryptedXml)
// Find the provider element by class even if it has been renamed
def passwords = doc.provider.find { it.'class' as String == LDAP_PROVIDER_CLASS }.property.findAll {
- it.@name =~ "Password" && it.@encryption =~ "aes/gcm/\\d{3}"
+ it.@name =~ "Password" && it.@encryption != ""
}
if (passwords.isEmpty()) {
@@ -743,8 +779,10 @@ class ConfigEncryptionTool {
}
passwords.each { password ->
+ final SensitivePropertyProvider sensitivePropertyProvider = providerFactory
+ .getProvider(PropertyProtectionScheme.fromIdentifier((String) password.@encryption))
if (isVerbose) {
- logger.info("Attempting to decrypt ${password.text()}")
+ logger.info("Attempting to decrypt ${password.text()} using protection scheme ${password.@encryption}")
}
String decryptedValue = sensitivePropertyProvider.unprotect(password.text().trim())
password.replaceNode {
@@ -762,7 +800,7 @@ class ConfigEncryptionTool {
}
String decryptAuthorizers(String encryptedXml, String existingKeyHex = keyHex) {
- AESSensitivePropertyProvider sensitivePropertyProvider = new AESSensitivePropertyProvider(existingKeyHex)
+ final SensitivePropertyProviderFactory providerFactory = getSensitivePropertyProviderFactory(existingKeyHex)
try {
def filename = "authorizers.xml"
@@ -771,7 +809,7 @@ class ConfigEncryptionTool {
def passwords = doc.userGroupProvider.find {
it.'class' as String == LDAP_USER_GROUP_PROVIDER_CLASS
}.property.findAll {
- it.@name =~ "Password" && it.@encryption =~ "aes/gcm/\\d{3}"
+ it.@name =~ "Password" && it.@encryption != ""
}
if (passwords.isEmpty()) {
@@ -784,8 +822,10 @@ class ConfigEncryptionTool {
passwords.each { password ->
// TODO: Capture the raw password, and only display it in the log if the decrypted value is different (to avoid possibly printing an incorrectly provided plaintext password)
if (isVerbose) {
- logger.info("Attempting to decrypt ${password.text()}")
+ logger.info("Attempting to decrypt ${password.text()} using protection scheme ${password.@encryption}")
}
+ final SensitivePropertyProvider sensitivePropertyProvider = providerFactory
+ .getProvider(PropertyProtectionScheme.fromIdentifier((String) password.@encryption))
String decryptedValue = sensitivePropertyProvider.unprotect(password.text().trim())
password.replaceNode {
property(name: password.@name, encryption: "none", decryptedValue)
@@ -803,8 +843,8 @@ class ConfigEncryptionTool {
}
}
- String encryptLoginIdentityProviders(String plainXml, String newKeyHex = keyHex) {
- AESSensitivePropertyProvider sensitivePropertyProvider = new AESSensitivePropertyProvider(newKeyHex)
+ String encryptLoginIdentityProviders(final String plainXml, final String newKeyHex = keyHex, final PropertyProtectionScheme newProtectionScheme = protectionScheme) {
+ final SensitivePropertyProviderFactory providerFactory = getSensitivePropertyProviderFactory(newKeyHex)
// TODO: Switch to XmlParser & XmlNodePrinter to maintain "empty" element structure
try {
@@ -822,10 +862,11 @@ class ConfigEncryptionTool {
}
return plainXml
}
+ final SensitivePropertyProvider sensitivePropertyProvider = providerFactory.getProvider(newProtectionScheme)
passwords.each { password ->
if (isVerbose) {
- logger.info("Attempting to encrypt ${password.name()}")
+ logger.info("Attempting to encrypt ${password.name()} using protection scheme ${sensitivePropertyProvider.identifierKey}")
}
String encryptedValue = sensitivePropertyProvider.protect(password.text().trim())
password.replaceNode {
@@ -845,8 +886,8 @@ class ConfigEncryptionTool {
}
}
- String encryptAuthorizers(String plainXml, String newKeyHex = keyHex) {
- AESSensitivePropertyProvider sensitivePropertyProvider = new AESSensitivePropertyProvider(newKeyHex)
+ String encryptAuthorizers(final String plainXml, final String newKeyHex = keyHex, final PropertyProtectionScheme newProtectionScheme = protectionScheme) {
+ final SensitivePropertyProviderFactory providerFactory = getSensitivePropertyProviderFactory(newKeyHex)
// TODO: Switch to XmlParser & XmlNodePrinter to maintain "empty" element structure
try {
@@ -865,10 +906,11 @@ class ConfigEncryptionTool {
}
return plainXml
}
+ final SensitivePropertyProvider sensitivePropertyProvider = providerFactory.getProvider(newProtectionScheme)
passwords.each { password ->
if (isVerbose) {
- logger.info("Attempting to encrypt ${password.name()}")
+ logger.info("Attempting to encrypt ${password.name()} using protection scheme ${sensitivePropertyProvider.identifierKey}")
}
String encryptedValue = sensitivePropertyProvider.protect(password.text().trim())
password.replaceNode {
@@ -912,7 +954,8 @@ class ConfigEncryptionTool {
// Holder for encrypted properties and protection schemes
Properties encryptedProperties = new Properties()
- AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(keyHex)
+ final SensitivePropertyProviderFactory sensitivePropertyProviderFactory = getSensitivePropertyProviderFactory(keyHex)
+ final SensitivePropertyProvider spp = sensitivePropertyProviderFactory.getProvider(protectionScheme)
protectedWrapper.addSensitivePropertyProvider(spp)
List<String> keysToSkip = []
@@ -929,7 +972,7 @@ class ConfigEncryptionTool {
logger.info("Protected ${key} with ${spp.getIdentifierKey()} -> \t${protectedValue}")
// Add the protection key ("x.y.z.protected" -> "aes/gcm/{128,256}")
- String protectionKey = protectedWrapper.getProtectionKey(key)
+ String protectionKey = ApplicationPropertiesProtector.getProtectionKey(key)
encryptedProperties.setProperty(protectionKey, spp.getIdentifierKey())
logger.info("Updated protection key ${protectionKey}")
@@ -943,7 +986,7 @@ class ConfigEncryptionTool {
nonSensitiveKeys.each { String key ->
encryptedProperties.setProperty(key, plainProperties.getProperty(key))
}
- NiFiProperties mergedProperties = new StandardNiFiProperties(encryptedProperties)
+ NiFiProperties mergedProperties = new NiFiProperties(encryptedProperties)
logger.info("Final result: ${mergedProperties.size()} keys including ${ProtectedNiFiProperties.countProtectedProperties(mergedProperties)} protected keys")
mergedProperties
@@ -1129,7 +1172,8 @@ class ConfigEncryptionTool {
// Only need to replace the keys that have been protected AND nifi.sensitive.props.key
Map<String, String> protectedKeys = protectedNiFiProperties.getProtectedPropertyKeys()
if (!protectedKeys.containsKey(NiFiProperties.SENSITIVE_PROPS_KEY)) {
- protectedKeys.put(NiFiProperties.SENSITIVE_PROPS_KEY, protectedNiFiProperties.getProperty(ProtectedNiFiProperties.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY)))
+ protectedKeys.put(NiFiProperties.SENSITIVE_PROPS_KEY, protectedNiFiProperties.getProperty(ApplicationPropertiesProtector
+ .getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY)))
}
protectedKeys.each { String key, String protectionScheme ->
@@ -1139,8 +1183,8 @@ class ConfigEncryptionTool {
}
// Get the index of the following line (or cap at max)
int p = l + 1 > lines.size() ? lines.size() : l + 1
- String protectionLine = "${protectedNiFiProperties.getProtectionKey(key)}=${protectionScheme ?: ""}"
- if (p < lines.size() && lines.get(p).startsWith("${protectedNiFiProperties.getProtectionKey(key)}=")) {
+ String protectionLine = "${ApplicationPropertiesProtector.getProtectionKey(key)}=${protectionScheme ?: ""}"
+ if (p < lines.size() && lines.get(p).startsWith("${ApplicationPropertiesProtector.getProtectionKey(key)}=")) {
lines.set(p, protectionLine)
} else {
lines.add(p, protectionLine)
@@ -1260,7 +1304,7 @@ class ConfigEncryptionTool {
boolean niFiPropertiesAreEncrypted() {
if (niFiPropertiesPath) {
try {
- def nfp = NiFiPropertiesLoader.withKey(keyHex).readProtectedPropertiesFromDisk(new File(niFiPropertiesPath))
+ def nfp = getNiFiPropertiesLoader(keyHex).readProtectedPropertiesFromDisk(new File(niFiPropertiesPath))
return nfp.hasProtectedKeys()
} catch (SensitivePropertyProtectionException | IOException e) {
return true
@@ -1299,7 +1343,7 @@ class ConfigEncryptionTool {
if (tool.translatingCli) {
if (tool.bootstrapConfPath) {
// Check to see if bootstrap.conf has a root key
- tool.keyHex = CryptoUtils.extractKeyFromBootstrapFile(tool.bootstrapConfPath)
+ tool.keyHex = NiFiBootstrapUtils.extractKeyFromBootstrapFile(tool.bootstrapConfPath)
}
if (!tool.keyHex) {
@@ -1319,7 +1363,7 @@ class ConfigEncryptionTool {
if (!tool.ignorePropertiesFiles || (tool.handlingFlowXml && existingNiFiPropertiesAreEncrypted)) {
// If we are handling the flow.xml.gz and nifi.properties is already encrypted, try getting the key from bootstrap.conf rather than the console
if (tool.ignorePropertiesFiles) {
- tool.keyHex = CryptoUtils.extractKeyFromBootstrapFile(tool.bootstrapConfPath)
+ tool.keyHex = NiFiBootstrapUtils.extractKeyFromBootstrapFile(tool.bootstrapConfPath)
} else {
tool.keyHex = tool.getKey()
}
@@ -1338,7 +1382,7 @@ class ConfigEncryptionTool {
tool.printUsageAndThrow(e.getMessage(), ExitCode.INVALID_ARGS)
}
- if (tool.migration) {
+ if (tool.migration && tool.migrationProtectionScheme.requiresSecretKey()) {
String migrationKeyHex = tool.getMigrationKey()
if (!migrationKeyHex) {
@@ -1397,6 +1441,9 @@ class ConfigEncryptionTool {
}
if (tool.handlingNiFiProperties) {
+ // If the flow password was not set in nifi.properties, use the hard-coded default
+ tool.existingFlowPropertiesPassword = tool.getExistingFlowPassword()
+
tool.niFiProperties = tool.encryptSensitiveProperties(tool.niFiProperties)
}
} catch (CommandLineParseException e) {
@@ -1444,8 +1491,7 @@ class ConfigEncryptionTool {
}
void handleFlowXml(boolean existingNiFiPropertiesAreEncrypted = false) {
- // If the flow password was not set in nifi.properties, use the hard-coded default
- String existingFlowPassword = getExistingFlowPassword()
+ String existingFlowPassword = existingFlowPropertiesPassword ?: getExistingFlowPassword()
// If the new password was not provided in the arguments, read from the console. If that is empty, use the same value (essentially a copy no-op)
String newFlowPassword = flowPropertiesPassword ?: getFlowPassword()
@@ -1481,20 +1527,22 @@ class ConfigEncryptionTool {
rawProperties.put(k, nfp.getProperty(k))
}
- // If the tool is not going to encrypt NiFiProperties and the existing file is already encrypted, encrypt and update the new sensitive props key
- if (!handlingNiFiProperties && existingNiFiPropertiesAreEncrypted) {
- AESSensitivePropertyProvider spp = new AESSensitivePropertyProvider(keyHex)
+ // If the tool is supposed to encrypt NiFiProperties or the existing file is already encrypted, encrypt and update the new sensitive props key
+ if (handlingNiFiProperties || existingNiFiPropertiesAreEncrypted) {
+ final SensitivePropertyProviderFactory sensitivePropertyProviderFactory = getSensitivePropertyProviderFactory(keyHex)
+ SensitivePropertyProvider spp = sensitivePropertyProviderFactory.getProvider(protectionScheme)
String encryptedSPK = spp.protect(newFlowPassword)
rawProperties.put(NiFiProperties.SENSITIVE_PROPS_KEY, encryptedSPK)
// Manually update the protection scheme or it will be lost
- rawProperties.put(ProtectedNiFiProperties.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY), spp.getIdentifierKey())
+ rawProperties.put(ApplicationPropertiesProtector.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY), spp.getIdentifierKey())
if (isVerbose) {
logger.info("Tool is not configured to encrypt nifi.properties, but the existing nifi.properties is encrypted and flow.xml.gz was migrated, so manually persisting the new encrypted value to nifi.properties")
}
} else {
rawProperties.put(NiFiProperties.SENSITIVE_PROPS_KEY, newFlowPassword)
+ rawProperties.put(ApplicationPropertiesProtector.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY), "")
}
- niFiProperties = new StandardNiFiProperties(rawProperties)
+ niFiProperties = new NiFiProperties(rawProperties)
}
}
@@ -1513,6 +1561,19 @@ class ConfigEncryptionTool {
cliOutput.join("\n")
}
+ static Supplier<BootstrapProperties> getBootstrapSupplier(final String bootstrapConfPath) {
+ new Supplier<BootstrapProperties>() {
+ @Override
+ BootstrapProperties get() {
+ try {
+ NiFiRegistryBootstrapUtils.loadBootstrapProperties(bootstrapConfPath)
+ } catch (final IOException e) {
+ throw new SensitivePropertyProtectionException(e.getCause(), e)
+ }
+ }
+ }
+ }
+
static String determineBaseUrl(NiFiProperties niFiProperties) {
String protocol = niFiProperties.isHTTPSConfigured() ? "https" : "http"
String host = niFiProperties.isHTTPSConfigured() ? niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_HOST) : niFiProperties.getProperty(NiFiProperties.WEB_HTTP_HOST)
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/DecryptMode.groovy b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/DecryptMode.groovy
index b2b901b..52289c7 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/DecryptMode.groovy
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/DecryptMode.groovy
@@ -19,8 +19,10 @@ package org.apache.nifi.toolkit.encryptconfig
import groovy.cli.commons.CliBuilder
import groovy.cli.commons.OptionAccessor
import org.apache.commons.cli.HelpFormatter
-import org.apache.nifi.properties.AESSensitivePropertyProvider
+import org.apache.nifi.properties.ConfigEncryptionTool
+import org.apache.nifi.properties.PropertyProtectionScheme
import org.apache.nifi.properties.SensitivePropertyProvider
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory
import org.apache.nifi.toolkit.encryptconfig.util.BootstrapUtil
import org.apache.nifi.toolkit.encryptconfig.util.PropertiesEncryptor
import org.apache.nifi.toolkit.encryptconfig.util.ToolUtilities
@@ -208,6 +210,7 @@ class DecryptMode implements ToolMode {
OptionAccessor rawOptions
Configuration.KeySource keySource
+ PropertyProtectionScheme protectionScheme = ConfigEncryptionTool.DEFAULT_PROTECTION_SCHEME
String key
SensitivePropertyProvider decryptionProvider
String inputBootstrapPath
@@ -228,11 +231,17 @@ class DecryptMode implements ToolMode {
validateOptions()
determineInputFileFromRemainingArgs()
- determineKey()
- if (!key) {
- throw new RuntimeException("Failed to configure tool, could not determine key.")
+ determineProtectionScheme()
+ determineBootstrapProperties()
+ if (protectionScheme.requiresSecretKey()) {
+ determineKey()
+ if (!key) {
+ throw new RuntimeException("Failed to configure tool, could not determine key.")
+ }
}
- decryptionProvider = new AESSensitivePropertyProvider(key)
+ decryptionProvider = StandardSensitivePropertyProviderFactory
+ .withKeyAndBootstrapSupplier(key, ConfigEncryptionTool.getBootstrapSupplier(inputBootstrapPath))
+ .getProvider(protectionScheme)
if (rawOptions.t) {
fileType = FileType.valueOf(rawOptions.t)
@@ -244,6 +253,12 @@ class DecryptMode implements ToolMode {
}
}
+ private void determineBootstrapProperties() {
+ if (rawOptions.b) {
+ inputBootstrapPath = rawOptions.b
+ }
+ }
+
private void validateOptions() {
String validationFailedMessage = null
@@ -268,6 +283,13 @@ class DecryptMode implements ToolMode {
this.inputFilePath = remainingArgs[0]
}
+ private void determineProtectionScheme() {
+
+ if (rawOptions.S) {
+ protectionScheme = PropertyProtectionScheme.valueOf(rawOptions.S)
+ }
+ }
+
private void determineKey() {
boolean usingPassword = false
@@ -302,7 +324,6 @@ class DecryptMode implements ToolMode {
}
key = ToolUtilities.determineKey(TextDevices.defaultTextDevice(), keyHex, password, usingPassword)
} else if (usingBootstrapKey) {
- inputBootstrapPath = rawOptions.b
logger.debug("Looking in bootstrap conf file ${inputBootstrapPath} for root key for decryption.")
// first, try to treat the bootstrap file as a NiFi bootstrap.conf
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryDecryptMode.groovy b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryDecryptMode.groovy
index f30b011..919e2b0 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryDecryptMode.groovy
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryDecryptMode.groovy
@@ -17,7 +17,9 @@
package org.apache.nifi.toolkit.encryptconfig
import groovy.cli.commons.CliBuilder
-import org.apache.nifi.properties.AESSensitivePropertyProvider
+import org.apache.nifi.properties.ConfigEncryptionTool
+import org.apache.nifi.properties.PropertyProtectionScheme
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory
import org.apache.nifi.toolkit.encryptconfig.util.BootstrapUtil
import org.apache.nifi.toolkit.encryptconfig.util.ToolUtilities
import org.slf4j.Logger
@@ -72,6 +74,10 @@ class NiFiRegistryDecryptMode extends DecryptMode {
config.inputFilePath = options.r
config.fileType = FileType.properties // disables auto-detection, which is still experimental
+ if (options.S) {
+ config.protectionScheme = PropertyProtectionScheme.valueOf((String) options.S)
+ }
+
// one of [-p, -k, -b]
String keyHex = null
String password = null
@@ -110,7 +116,9 @@ class NiFiRegistryDecryptMode extends DecryptMode {
}
}
- config.decryptionProvider = new AESSensitivePropertyProvider(config.key)
+ config.decryptionProvider = StandardSensitivePropertyProviderFactory
+ .withKeyAndBootstrapSupplier(config.key, ConfigEncryptionTool.getBootstrapSupplier(config.inputBootstrapPath))
+ .getProvider(config.protectionScheme)
run(config)
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryMode.groovy b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryMode.groovy
index 4a43c21..3b5bbce 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryMode.groovy
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/NiFiRegistryMode.groovy
@@ -20,8 +20,10 @@ import groovy.cli.commons.CliBuilder
import groovy.cli.commons.OptionAccessor
import org.apache.commons.cli.HelpFormatter
import org.apache.commons.cli.Options
-import org.apache.nifi.properties.AESSensitivePropertyProvider
+import org.apache.nifi.properties.ConfigEncryptionTool
+import org.apache.nifi.properties.PropertyProtectionScheme
import org.apache.nifi.properties.SensitivePropertyProvider
+import org.apache.nifi.properties.StandardSensitivePropertyProviderFactory
import org.apache.nifi.toolkit.encryptconfig.util.BootstrapUtil
import org.apache.nifi.toolkit.encryptconfig.util.NiFiRegistryAuthorizersXmlEncryptor
import org.apache.nifi.toolkit.encryptconfig.util.NiFiRegistryIdentityProvidersXmlEncryptor
@@ -186,6 +188,10 @@ class NiFiRegistryMode implements ToolMode {
argName: 'keyhex',
optionalArg: true,
'Protect the files using a raw hexadecimal key. If an argument is not provided to this flag, interactive mode will be triggered to prompt the user to enter the key.')
+ cli.S(longOpt: 'protectionScheme',
+ args: 1,
+ argName: 'protectionScheme',
+ "Selects the protection scheme for encrypted properties. Valid values are: [${PropertyProtectionScheme.values().join(", ")}] (default is ${ConfigEncryptionTool.DEFAULT_PROTECTION_SCHEME.name()})")
// Options for the old password or key, if running the tool to migrate keys
cli._(longOpt: 'oldPassword',
@@ -196,12 +202,16 @@ class NiFiRegistryMode implements ToolMode {
args: 1,
argName: 'keyhex',
'If the input files are already protected using a key, this specifies the raw hexadecimal key so that the files can be unprotected before re-protecting.')
+ cli.H(longOpt: 'oldProtectionScheme',
+ args: 1,
+ argName: 'protectionScheme',
+ "The old protection scheme to use during encryption migration (see --protectionScheme for possible values). Default is ${ConfigEncryptionTool.DEFAULT_PROTECTION_SCHEME.name()}.")
// Options for output bootstrap.conf file
cli.b(longOpt: 'bootstrapConf',
args: 1,
argName: 'file',
- 'The bootstrap.conf file containing no root key or an existing root key. If a new password or key is specified (using -p or -k) and no output bootstrap.conf file is specified, then this file will be overwritten to persist the new master key.')
+ 'The bootstrap.conf file containing no root key or an existing root key, and any other protection scheme configuration properties. If a new password or key is specified (using -p or -k) and no output bootstrap.conf file is specified, then this file will be overwritten to persist the new master key.')
cli.B(longOpt: 'outputBootstrapConf',
args: 1,
argName: 'file',
@@ -249,7 +259,9 @@ class NiFiRegistryMode implements ToolMode {
boolean usingPassword
boolean usingBootstrapKey
+ PropertyProtectionScheme protectionScheme
String encryptionKey
+ PropertyProtectionScheme oldProtectionScheme
String decryptionKey
SensitivePropertyProvider encryptionProvider
@@ -285,32 +297,43 @@ class NiFiRegistryMode implements ToolMode {
// Set input bootstrap.conf path
inputBootstrapPath = rawOptions.b
- // Determine key for encryption (required)
- determineEncryptionKey()
- if (!encryptionKey) {
- throw new RuntimeException("Failed to configure tool, could not determine encryption key. Must provide -p, -k, or -b. If using -b, bootstrap.conf argument must already contain root key.")
- }
- encryptionProvider = new AESSensitivePropertyProvider(encryptionKey)
-
+ determineOldProtectionScheme()
// Determine key for decryption (if migrating)
determineDecryptionKey()
if (!decryptionKey) {
logger.debug("No decryption key specified via options, so if any input files require decryption prior to re-encryption (i.e., migration), this tool will fail.")
}
- decryptionProvider = decryptionKey ? new AESSensitivePropertyProvider(decryptionKey) : null
-
- writingKeyToBootstrap = (usingPassword || usingRawKeyHex || rawOptions.B)
- if (writingKeyToBootstrap) {
- outputBootstrapPath = rawOptions.B ?: inputBootstrapPath
- }
handlingNiFiRegistryProperties = rawOptions.r
if (handlingNiFiRegistryProperties) {
inputNiFiRegistryPropertiesPath = rawOptions.r
outputNiFiRegistryPropertiesPath = rawOptions.R ?: inputNiFiRegistryPropertiesPath
+ }
+
+ determineProtectionScheme()
+
+ // Determine key for encryption (required)
+ determineEncryptionKey()
+ if (!encryptionKey) {
+ throw new RuntimeException("Failed to configure tool, could not determine encryption key. Must provide -p, -k, or -b. If using -b, bootstrap.conf argument must already contain root key.")
+ }
+ encryptionProvider = StandardSensitivePropertyProviderFactory
+ .withKeyAndBootstrapSupplier(encryptionKey, ConfigEncryptionTool.getBootstrapSupplier(inputBootstrapPath))
+ .getProvider(oldProtectionScheme)
+
+ decryptionProvider = decryptionKey ? StandardSensitivePropertyProviderFactory
+ .withKeyAndBootstrapSupplier(decryptionKey, ConfigEncryptionTool.getBootstrapSupplier(inputBootstrapPath))
+ .getProvider(protectionScheme) : null
+
+ if (handlingNiFiRegistryProperties) {
propertiesEncryptor = new NiFiRegistryPropertiesEncryptor(encryptionProvider, decryptionProvider)
}
+ writingKeyToBootstrap = (usingPassword || usingRawKeyHex || rawOptions.B)
+ if (writingKeyToBootstrap) {
+ outputBootstrapPath = rawOptions.B ?: inputBootstrapPath
+ }
+
handlingIdentityProviders = rawOptions.i
if (handlingIdentityProviders) {
inputIdentityProvidersPath = rawOptions.i
@@ -327,6 +350,23 @@ class NiFiRegistryMode implements ToolMode {
}
+ private void determineProtectionScheme() {
+
+ if (rawOptions.S) {
+ protectionScheme = PropertyProtectionScheme.valueOf(rawOptions.S)
+ } else {
+ protectionScheme = ConfigEncryptionTool.DEFAULT_PROTECTION_SCHEME
+ }
+ }
+ private void determineOldProtectionScheme() {
+
+ if (rawOptions.H) {
+ oldProtectionScheme = PropertyProtectionScheme.valueOf(rawOptions.H)
+ } else {
+ oldProtectionScheme = ConfigEncryptionTool.DEFAULT_PROTECTION_SCHEME
+ }
+ }
+
private void validateOptions() {
String validationFailedMessage = null
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/util/NiFiRegistryAuthorizersXmlEncryptor.groovy b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/util/NiFiRegistryAuthorizersXmlEncryptor.groovy
index 102ad9e..30639a8 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/util/NiFiRegistryAuthorizersXmlEncryptor.groovy
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/util/NiFiRegistryAuthorizersXmlEncryptor.groovy
@@ -42,7 +42,7 @@ class NiFiRegistryAuthorizersXmlEncryptor extends XmlEncryptor {
* .*?</userGroupProvider> -> find everything as needed up until and including occurrence of '</userGroupProvider>'
*/
- NiFiRegistryAuthorizersXmlEncryptor(SensitivePropertyProvider encryptionProvider, SensitivePropertyProvider decryptionProvider) {
+ NiFiRegistryAuthorizersXmlEncryptor(final SensitivePropertyProvider encryptionProvider, final SensitivePropertyProvider decryptionProvider) {
super(encryptionProvider, decryptionProvider)
}
diff --git a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
index acb118b..57be63b 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
@@ -19,11 +19,10 @@ package org.apache.nifi.properties
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.CommandLineParser
import org.apache.commons.cli.DefaultParser
+import org.apache.commons.io.IOUtils
import org.apache.commons.lang3.SystemUtils
import org.apache.log4j.AppenderSkeleton
import org.apache.log4j.spi.LoggingEvent
-import org.apache.nifi.encrypt.PropertyEncryptor
-import org.apache.nifi.encrypt.PropertyEncryptorFactory
import org.apache.nifi.security.util.EncryptionMethod
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException
import org.apache.nifi.util.NiFiProperties
@@ -34,7 +33,6 @@ import org.junit.After
import org.junit.AfterClass
import org.junit.Assume
import org.junit.BeforeClass
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.contrib.java.lang.system.Assertion
@@ -50,20 +48,13 @@ import org.xmlunit.diff.DefaultNodeMatcher
import org.xmlunit.diff.Diff
import org.xmlunit.diff.ElementSelectors
-import javax.crypto.BadPaddingException
import javax.crypto.Cipher
-import javax.crypto.SecretKey
-import javax.crypto.SecretKeyFactory
-import javax.crypto.spec.PBEKeySpec
-import javax.crypto.spec.PBEParameterSpec
+import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.attribute.PosixFilePermission
import java.security.KeyException
import java.security.Security
-import org.apache.commons.io.IOUtils
-import java.nio.charset.StandardCharsets
-
@RunWith(JUnit4.class)
class ConfigEncryptionToolTest extends GroovyTestCase {
private static final Logger logger = LoggerFactory.getLogger(ConfigEncryptionToolTest.class)
@@ -82,6 +73,8 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
public static final String KEY_HEX = isUnlimitedStrengthCryptoAvailable() ? KEY_HEX_256 : KEY_HEX_128
private static final String PASSWORD = "thisIsABadPassword"
private static final String ANOTHER_PASSWORD = "thisIsAnotherBadPassword"
+
+ private static final SensitivePropertyProviderFactory DEFAULT_PROVIDER_FACTORY = StandardSensitivePropertyProviderFactory.withKey(KEY_HEX)
// From ConfigEncryptionTool.deriveKeyFromPassword("thisIsABadPassword")
... 481 lines suppressed ...