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/21 19:40:28 UTC
[nifi] branch main updated: NIFI-8683 support Expression Language
for the Truststore/Keystore properties of SSLContextService
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 02b4e33 NIFI-8683 support Expression Language for the Truststore/Keystore properties of SSLContextService
02b4e33 is described below
commit 02b4e33aa6cbcba4e3dea706aa9b20e8b501b06f
Author: Chris Sampson <ch...@gmail.com>
AuthorDate: Fri Jun 11 12:13:24 2021 +0100
NIFI-8683 support Expression Language for the Truststore/Keystore properties of SSLContextService
This closes #5147
Signed-off-by: David Handermann <ex...@apache.org>
---
.../apache/nifi/ssl/StandardSSLContextService.java | 33 +++-
.../org/apache/nifi/ssl/SSLContextServiceTest.java | 172 ++++++++++-----------
2 files changed, 112 insertions(+), 93 deletions(-)
diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java b/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
index af304ea..d4d2f6c 100644
--- a/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
+++ b/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
@@ -27,8 +27,10 @@ import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType;
+import org.apache.nifi.context.PropertyContext;
import org.apache.nifi.controller.AbstractControllerService;
import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.reporting.InitializationException;
@@ -49,6 +51,7 @@ import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -63,6 +66,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
public static final PropertyDescriptor TRUSTSTORE = new PropertyDescriptor.Builder()
.name("Truststore Filename")
.description("The fully-qualified filename of the Truststore")
+ .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.defaultValue(null)
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
.sensitive(false)
@@ -85,6 +89,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
public static final PropertyDescriptor KEYSTORE = new PropertyDescriptor.Builder()
.name("Keystore Filename")
.description("The fully-qualified filename of the Keystore")
+ .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.defaultValue(null)
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
.sensitive(false)
@@ -149,8 +154,10 @@ public class StandardSSLContextService extends AbstractControllerService impleme
configContext = context;
final Collection<ValidationResult> results = new ArrayList<>();
- results.addAll(validateStore(context.getProperties(), KeystoreValidationGroup.KEYSTORE));
- results.addAll(validateStore(context.getProperties(), KeystoreValidationGroup.TRUSTSTORE));
+
+ final Map<PropertyDescriptor, String> properties = evaluateProperties(context);
+ results.addAll(validateStore(properties, KeystoreValidationGroup.KEYSTORE));
+ results.addAll(validateStore(properties, KeystoreValidationGroup.TRUSTSTORE));
if (!results.isEmpty()) {
final StringBuilder sb = new StringBuilder(this + " is not valid due to:");
@@ -185,14 +192,26 @@ public class StandardSSLContextService extends AbstractControllerService impleme
}
}
- results.addAll(validateStore(validationContext.getProperties(), KeystoreValidationGroup.KEYSTORE));
- results.addAll(validateStore(validationContext.getProperties(), KeystoreValidationGroup.TRUSTSTORE));
+ final Map<PropertyDescriptor, String> properties = evaluateProperties(validationContext);
+ results.addAll(validateStore(properties, KeystoreValidationGroup.KEYSTORE));
+ results.addAll(validateStore(properties, KeystoreValidationGroup.TRUSTSTORE));
isValidated = results.isEmpty();
return results;
}
+ private Map<PropertyDescriptor, String> evaluateProperties(final PropertyContext context) {
+ final Map<PropertyDescriptor, String> evaluatedProperties = new HashMap<>(getSupportedPropertyDescriptors().size(), 1);
+ for (final PropertyDescriptor pd : getSupportedPropertyDescriptors()) {
+ final PropertyValue pv = pd.isExpressionLanguageSupported()
+ ? context.getProperty(pd).evaluateAttributeExpressions()
+ : context.getProperty(pd);
+ evaluatedProperties.put(pd, pv.isSet() ? pv.getValue() : null);
+ }
+ return evaluatedProperties;
+ }
+
private void resetValidationCache() {
validationCacheCount = 0;
isValidated = false;
@@ -289,7 +308,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
@Override
public String getTrustStoreFile() {
- return configContext.getProperty(TRUSTSTORE).getValue();
+ return configContext.getProperty(TRUSTSTORE).evaluateAttributeExpressions().getValue();
}
@Override
@@ -310,7 +329,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
@Override
public String getKeyStoreFile() {
- return configContext.getProperty(KEYSTORE).getValue();
+ return configContext.getProperty(KEYSTORE).evaluateAttributeExpressions().getValue();
}
@Override
@@ -582,6 +601,6 @@ public class StandardSSLContextService extends AbstractControllerService impleme
allowableValues.add(new AllowableValue(supportedProtocol, supportedProtocol, description));
}
- return allowableValues.toArray(new AllowableValue[allowableValues.size()]);
+ return allowableValues.toArray(new AllowableValue[0]);
}
}
diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/test/java/org/apache/nifi/ssl/SSLContextServiceTest.java b/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/test/java/org/apache/nifi/ssl/SSLContextServiceTest.java
index 0bd7a87..0437706 100644
--- a/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/test/java/org/apache/nifi/ssl/SSLContextServiceTest.java
+++ b/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/test/java/org/apache/nifi/ssl/SSLContextServiceTest.java
@@ -137,6 +137,29 @@ public class SSLContextServiceTest {
}
@Test
+ public void testGoodWithEL() throws InitializationException {
+ final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+ SSLContextService service = new StandardSSLContextService();
+ runner.addControllerService("test-good1", service);
+ runner.setVariable("keystore", KEYSTORE_PATH);
+ runner.setVariable("truststore", TRUSTSTORE_PATH);
+ runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "${keystore}");
+ runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
+ runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), "${truststore}");
+ runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
+ runner.enableControllerService(service);
+
+ runner.setProperty("SSL Context Svc ID", "test-good1");
+ runner.assertValid(service);
+ service = (SSLContextService) runner.getProcessContext().getControllerServiceLookup().getControllerService("test-good1");
+ Assert.assertNotNull(service);
+ SSLContextService sslService = service;
+ sslService.createContext();
+ }
+
+ @Test
public void testWithChanges() throws InitializationException {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
SSLContextService service = new StandardSSLContextService();
@@ -180,7 +203,7 @@ public class SSLContextServiceTest {
Files.copy(originalTruststore.toPath(), tmpTruststore.toPath(), StandardCopyOption.REPLACE_EXISTING);
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
- SSLContextService service = new StandardSSLContextService();
+ StandardSSLContextService service = new StandardSSLContextService();
final String serviceIdentifier = "test-should-expire";
runner.addControllerService(serviceIdentifier, service);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tmpKeystore.getAbsolutePath());
@@ -194,8 +217,6 @@ public class SSLContextServiceTest {
runner.setProperty("SSL Context Svc ID", serviceIdentifier);
runner.assertValid(service);
- final StandardSSLContextService sslContextService = (StandardSSLContextService) service;
-
// Act
boolean isDeleted = tmpKeystore.delete();
assert isDeleted;
@@ -208,68 +229,57 @@ public class SSLContextServiceTest {
final ValidationContext validationContext = new MockValidationContext(processContext, null, null);
// Even though the keystore file is no longer present, because no property changed, the cached result is still valid
- Collection<ValidationResult> validationResults = sslContextService.customValidate(validationContext);
+ Collection<ValidationResult> validationResults = service.customValidate(validationContext);
assertTrue("validation results is not empty", validationResults.isEmpty());
logger.info("(1) StandardSSLContextService#customValidate() returned true even though the keystore file is no longer available");
// Assert
// Have to exhaust the cached result by checking n-1 more times
- for (int i = 2; i < sslContextService.getValidationCacheExpiration(); i++) {
- validationResults = sslContextService.customValidate(validationContext);
+ for (int i = 2; i < service.getValidationCacheExpiration(); i++) {
+ validationResults = service.customValidate(validationContext);
assertTrue("validation results is not empty", validationResults.isEmpty());
logger.info("(" + i + ") StandardSSLContextService#customValidate() returned true even though the keystore file is no longer available");
}
- validationResults = sslContextService.customValidate(validationContext);
+ validationResults = service.customValidate(validationContext);
assertFalse("validation results is empty", validationResults.isEmpty());
- logger.info("(" + sslContextService.getValidationCacheExpiration() + ") StandardSSLContextService#customValidate() returned false because the cache expired");
+ logger.info("(" + service.getValidationCacheExpiration() + ") StandardSSLContextService#customValidate() returned false because the cache expired");
}
@Test
- public void testGoodTrustOnly() {
- try {
- TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
- SSLContextService service = new StandardSSLContextService();
- HashMap<String, String> properties = new HashMap<>();
- properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
- properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
- properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
- runner.addControllerService("test-good2", service, properties);
- runner.enableControllerService(service);
-
- runner.setProperty("SSL Context Svc ID", "test-good2");
- runner.assertValid();
- Assert.assertNotNull(service);
- assertTrue(service instanceof StandardSSLContextService);
- service.createContext();
- } catch (InitializationException e) {
- }
+ public void testGoodTrustOnly() throws InitializationException {
+ TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+ SSLContextService service = new StandardSSLContextService();
+ HashMap<String, String> properties = new HashMap<>();
+ properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
+ properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
+ runner.addControllerService("test-good2", service, properties);
+ runner.enableControllerService(service);
+
+ runner.setProperty("SSL Context Svc ID", "test-good2");
+ runner.assertValid();
+ Assert.assertNotNull(service);
+ service.createContext();
}
@Test
@Deprecated
- public void testGoodKeyOnly() {
- try {
- TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
- SSLContextService service = new StandardSSLContextService();
- HashMap<String, String> properties = new HashMap<>();
- properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
- properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
- properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
- runner.addControllerService("test-good3", service, properties);
- runner.enableControllerService(service);
-
- runner.setProperty("SSL Context Svc ID", "test-good3");
- runner.assertValid();
- Assert.assertNotNull(service);
- assertTrue(service instanceof StandardSSLContextService);
- SSLContextService sslService = service;
- sslService.createContext();
- } catch (Exception e) {
- System.out.println(e);
- Assert.fail("Should not have thrown a exception " + e.getMessage());
- }
+ public void testGoodKeyOnly() throws Exception {
+ TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+ SSLContextService service = new StandardSSLContextService();
+ HashMap<String, String> properties = new HashMap<>();
+ properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
+ properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
+ runner.addControllerService("test-good3", service, properties);
+ runner.enableControllerService(service);
+
+ runner.setProperty("SSL Context Svc ID", "test-good3");
+ runner.assertValid();
+ Assert.assertNotNull(service);
+ service.createContext();
}
/**
@@ -278,29 +288,24 @@ public class SSLContextServiceTest {
* set on individual keys will fail this test.
*/
@Test
- public void testDifferentKeyPassword() {
- try {
- final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
- final SSLContextService service = new StandardSSLContextService();
- final Map<String, String> properties = new HashMap<>();
- properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_WITH_KEY_PASSWORD_PATH);
- properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
- properties.put(StandardSSLContextService.KEY_PASSWORD.getName(), "keypassword");
- properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
- properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
- properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
- properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
- runner.addControllerService("test-diff-keys", service, properties);
- runner.enableControllerService(service);
-
- runner.setProperty("SSL Context Svc ID", "test-diff-keys");
- runner.assertValid();
- Assert.assertNotNull(service);
- service.createContext();
- } catch (Exception e) {
- System.out.println(e);
- Assert.fail("Should not have thrown a exception " + e.getMessage());
- }
+ public void testDifferentKeyPassword() throws Exception {
+ final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+ final SSLContextService service = new StandardSSLContextService();
+ final Map<String, String> properties = new HashMap<>();
+ properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_WITH_KEY_PASSWORD_PATH);
+ properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ properties.put(StandardSSLContextService.KEY_PASSWORD.getName(), "keypassword");
+ properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
+ properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
+ properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
+ runner.addControllerService("test-diff-keys", service, properties);
+ runner.enableControllerService(service);
+
+ runner.setProperty("SSL Context Svc ID", "test-diff-keys");
+ runner.assertValid();
+ Assert.assertNotNull(service);
+ service.createContext();
}
/**
@@ -309,21 +314,16 @@ public class SSLContextServiceTest {
* set on individual keys will fail this test.
*/
@Test
- public void testDifferentKeyPasswordWithoutSpecifyingKeyPassword() {
- try {
- final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
- final SSLContextService service = new StandardSSLContextService();
- final Map<String, String> properties = new HashMap<>();
- properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_WITH_KEY_PASSWORD_PATH);
- properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
- properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
- runner.addControllerService("test-diff-keys", service, properties);
-
- // Assert the service is not valid due to an internal "cannot recover key" because the key password is missing
- runner.assertNotValid(service);
- } catch (Exception e) {
- System.out.println(e);
- Assert.fail("Should not have thrown a exception " + e.getMessage());
- }
+ public void testDifferentKeyPasswordWithoutSpecifyingKeyPassword() throws Exception {
+ final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+ final SSLContextService service = new StandardSSLContextService();
+ final Map<String, String> properties = new HashMap<>();
+ properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_WITH_KEY_PASSWORD_PATH);
+ properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
+ properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
+ runner.addControllerService("test-diff-keys", service, properties);
+
+ // Assert the service is not valid due to an internal "cannot recover key" because the key password is missing
+ runner.assertNotValid(service);
}
}