From commits-return-44801-archive-asf-public=cust-asf.ponee.io@nifi.apache.org Mon Jun 21 19:40:30 2021 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mxout1-ec2-va.apache.org (mxout1-ec2-va.apache.org [3.227.148.255]) by mx-eu-01.ponee.io (Postfix) with ESMTPS id 0644B18037A for ; Mon, 21 Jun 2021 21:40:30 +0200 (CEST) Received: from mail.apache.org (mailroute1-lw-us.apache.org [207.244.88.153]) by mxout1-ec2-va.apache.org (ASF Mail Server at mxout1-ec2-va.apache.org) with SMTP id 3835A3F4EC for ; Mon, 21 Jun 2021 19:40:29 +0000 (UTC) Received: (qmail 5857 invoked by uid 500); 21 Jun 2021 19:40:28 -0000 Mailing-List: contact commits-help@nifi.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@nifi.apache.org Delivered-To: mailing list commits@nifi.apache.org Received: (qmail 5848 invoked by uid 99); 21 Jun 2021 19:40:28 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Jun 2021 19:40:28 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id A9945821E0; Mon, 21 Jun 2021 19:40:28 +0000 (UTC) Date: Mon, 21 Jun 2021 19:40:28 +0000 To: "commits@nifi.apache.org" Subject: [nifi] branch main updated: NIFI-8683 support Expression Language for the Truststore/Keystore properties of SSLContextService MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <162430442794.28978.12449791682089880014@gitbox.apache.org> From: exceptionfactory@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: nifi X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Oldrev: 5196ba9e14266ddd9a897b04959a9c2036286219 X-Git-Newrev: 02b4e33aa6cbcba4e3dea706aa9b20e8b501b06f X-Git-Rev: 02b4e33aa6cbcba4e3dea706aa9b20e8b501b06f X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated 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 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 --- .../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 results = new ArrayList<>(); - results.addAll(validateStore(context.getProperties(), KeystoreValidationGroup.KEYSTORE)); - results.addAll(validateStore(context.getProperties(), KeystoreValidationGroup.TRUSTSTORE)); + + final Map 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 properties = evaluateProperties(validationContext); + results.addAll(validateStore(properties, KeystoreValidationGroup.KEYSTORE)); + results.addAll(validateStore(properties, KeystoreValidationGroup.TRUSTSTORE)); isValidated = results.isEmpty(); return results; } + private Map evaluateProperties(final PropertyContext context) { + final Map 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 validationResults = sslContextService.customValidate(validationContext); + Collection 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 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 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 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 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 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 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 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 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); } }