nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From joew...@apache.org
Subject nifi git commit: NIFI-4655: This closes #2347. Fixed NPE in the InvokeHTTP processor caused when an SSLContextService was assigned without keystore properties
Date Mon, 18 Dec 2017 14:12:07 GMT
Repository: nifi
Updated Branches:
  refs/heads/master 9217e2fc6 -> f50a07ec8


NIFI-4655: This closes #2347. Fixed NPE in the InvokeHTTP processor caused when an SSLContextService
was assigned without keystore properties

- Added check for keystore properties and only initialized keystore when necessary.
- Added TestInvokeHttpTwoWaySSL test class to test with two-way SSL
- Modified TestInvokeHttpSSL to test with one-way SSL

Signed-off-by: joewitt <joewitt@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/f50a07ec
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/f50a07ec
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/f50a07ec

Branch: refs/heads/master
Commit: f50a07ec888094cb610e5e12f6b52599143c42ea
Parents: 9217e2f
Author: Josh Anderton <josh.anderton@gmail.com>
Authored: Sun Dec 17 20:01:34 2017 -0600
Committer: joewitt <joewitt@apache.org>
Committed: Mon Dec 18 09:11:53 2017 -0500

----------------------------------------------------------------------
 .../nifi/processors/standard/InvokeHTTP.java    | 69 ++++++++++++--------
 .../processors/standard/TestInvokeHttpSSL.java  | 41 ++++++++++--
 .../standard/TestInvokeHttpTwoWaySSL.java       | 51 +++++++++++++++
 3 files changed, 128 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/f50a07ec/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java
index 65a28d5..300e9cb 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java
@@ -62,6 +62,7 @@ import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 
 import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSession;
@@ -253,14 +254,14 @@ public final class InvokeHTTP extends AbstractProcessor {
             .build();
 
     public static final PropertyDescriptor PROP_CONTENT_TYPE = new PropertyDescriptor.Builder()
-        .name("Content-Type")
-        .description("The Content-Type to specify for when content is being transmitted through
a PUT, POST or PATCH. "
-            + "In the case of an empty value after evaluating an expression language expression,
Content-Type defaults to " + DEFAULT_CONTENT_TYPE)
-        .required(true)
-        .expressionLanguageSupported(true)
-        .defaultValue("${" + CoreAttributes.MIME_TYPE.key() + "}")
-        .addValidator(StandardValidators.createAttributeExpressionLanguageValidator(AttributeExpression.ResultType.STRING))
-        .build();
+            .name("Content-Type")
+            .description("The Content-Type to specify for when content is being transmitted
through a PUT, POST or PATCH. "
+                    + "In the case of an empty value after evaluating an expression language
expression, Content-Type defaults to " + DEFAULT_CONTENT_TYPE)
+            .required(true)
+            .expressionLanguageSupported(true)
+            .defaultValue("${" + CoreAttributes.MIME_TYPE.key() + "}")
+            .addValidator(StandardValidators.createAttributeExpressionLanguageValidator(AttributeExpression.ResultType.STRING))
+            .build();
 
     public static final PropertyDescriptor PROP_SEND_BODY = new PropertyDescriptor.Builder()
             .name("send-message-body")
@@ -567,31 +568,42 @@ public final class InvokeHTTP extends AbstractProcessor {
      */
     private void setSslSocketFactory(OkHttpClient.Builder okHttpClientBuilder, SSLContextService
sslService, SSLContext sslContext)
             throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException,
UnrecoverableKeyException, KeyManagementException {
-        final String keystoreLocation = sslService.getKeyStoreFile();
-        final String keystorePass = sslService.getKeyStorePassword();
-        final String keystoreType = sslService.getKeyStoreType();
 
-        // prepare the keystore
-        final KeyStore keyStore = KeyStore.getInstance(keystoreType);
+        final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
+        // initialize the KeyManager array to null and we will overwrite later if a keystore
is loaded
+        KeyManager[] keyManagers = null;
 
-        try (FileInputStream keyStoreStream = new FileInputStream(keystoreLocation)) {
-            keyStore.load(keyStoreStream, keystorePass.toCharArray());
-        }
+        // we will only initialize the keystore if properties have been supplied by the SSLContextService
+        if (sslService.isKeyStoreConfigured()) {
+            final String keystoreLocation = sslService.getKeyStoreFile();
+            final String keystorePass = sslService.getKeyStorePassword();
+            final String keystoreType = sslService.getKeyStoreType();
 
-        final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
-        keyManagerFactory.init(keyStore, keystorePass.toCharArray());
+            // prepare the keystore
+            final KeyStore keyStore = KeyStore.getInstance(keystoreType);
 
-        // load truststore
-        final String truststoreLocation = sslService.getTrustStoreFile();
-        final String truststorePass = sslService.getTrustStorePassword();
-        final String truststoreType = sslService.getTrustStoreType();
+            try (FileInputStream keyStoreStream = new FileInputStream(keystoreLocation))
{
+                keyStore.load(keyStoreStream, keystorePass.toCharArray());
+            }
 
-        KeyStore truststore = KeyStore.getInstance(truststoreType);
-        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
-        truststore.load(new FileInputStream(truststoreLocation), truststorePass.toCharArray());
-        trustManagerFactory.init(truststore);
+            keyManagerFactory.init(keyStore, keystorePass.toCharArray());
+            keyManagers = keyManagerFactory.getKeyManagers();
+        }
+
+        // we will only initialize the truststure if properties have been supplied by the
SSLContextService
+        if (sslService.isTrustStoreConfigured()) {
+            // load truststore
+            final String truststoreLocation = sslService.getTrustStoreFile();
+            final String truststorePass = sslService.getTrustStorePassword();
+            final String truststoreType = sslService.getTrustStoreType();
+
+            KeyStore truststore = KeyStore.getInstance(truststoreType);
+            truststore.load(new FileInputStream(truststoreLocation), truststorePass.toCharArray());
+            trustManagerFactory.init(truststore);
+        }
 
-        /*
+         /*
             TrustManagerFactory.getTrustManagers returns a trust manager for each type of
trust material. Since we are getting a trust manager factory that uses "X509"
             as it's trust management algorithm, we are able to grab the first (and thus the
most preferred) and use it as our x509 Trust Manager
 
@@ -605,7 +617,8 @@ public final class InvokeHTTP extends AbstractProcessor {
             throw new IllegalStateException("List of trust managers is null");
         }
 
-        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
null);
+        // if keystore properties were not supplied, the keyManagers array will be null
+        sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), null);
 
         final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
         okHttpClientBuilder.sslSocketFactory(sslSocketFactory, x509TrustManager);

http://git-wip-us.apache.org/repos/asf/nifi/blob/f50a07ec/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpSSL.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpSSL.java
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpSSL.java
index 655492f..7f0bcbb 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpSSL.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpSSL.java
@@ -29,9 +29,15 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
+/**
+ * Executes the same tests as TestInvokeHttp but with one-way SSL enabled.  The Jetty server
created for these tests
+ * will not require client certificates and the client will not use keystore properties in
the SSLContextService.
+ */
 public class TestInvokeHttpSSL extends TestInvokeHttpCommon {
 
-    private static Map<String, String> sslProperties;
+    protected static Map<String, String> sslProperties;
+    protected static Map<String, String> serverSslProperties;
+
 
     @BeforeClass
     public static void beforeClass() throws Exception {
@@ -41,7 +47,8 @@ public class TestInvokeHttpSSL extends TestInvokeHttpCommon {
 
         // create the SSL properties, which basically store keystore / trustore information
         // this is used by the StandardSSLContextService and the Jetty Server
-        sslProperties = createSslProperties();
+        serverSslProperties = createServerSslProperties(false);
+        sslProperties = createSslProperties(false);
 
         // create a Jetty server on a random port
         server = createServer();
@@ -81,15 +88,39 @@ public class TestInvokeHttpSSL extends TestInvokeHttpCommon {
         runner.shutdown();
     }
 
-    private static TestServer createServer() throws IOException {
-        return new TestServer(sslProperties);
+    protected static TestServer createServer() throws IOException {
+        return new TestServer(serverSslProperties);
     }
 
-    private static Map<String, String> createSslProperties() {
+    protected static Map<String, String> createServerSslProperties(boolean clientAuth)
{
         final Map<String, String> map = new HashMap<>();
+        // if requesting client auth then we must also provide a truststore
+        if (clientAuth) {
+            map.put(TestServer.NEED_CLIENT_AUTH, Boolean.toString(true));
+            map.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/localhost-ts.jks");
+            map.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "localtest");
+            map.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
+        } else {
+            map.put(TestServer.NEED_CLIENT_AUTH, Boolean.toString(false));
+        }
+        // keystore is always required for the server SSL properties
         map.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/localhost-ks.jks");
         map.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "localtest");
         map.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
+
+        return map;
+    }
+
+
+    protected static Map<String, String> createSslProperties(boolean clientAuth) {
+        final Map<String, String> map = new HashMap<>();
+        // if requesting client auth then we must provide a keystore
+        if (clientAuth) {
+            map.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/localhost-ks.jks");
+            map.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "localtest");
+            map.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
+        }
+        // truststore is always required for the client SSL properties
         map.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/localhost-ts.jks");
         map.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "localtest");
         map.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");

http://git-wip-us.apache.org/repos/asf/nifi/blob/f50a07ec/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpTwoWaySSL.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpTwoWaySSL.java
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpTwoWaySSL.java
new file mode 100644
index 0000000..cfd96e0
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpTwoWaySSL.java
@@ -0,0 +1,51 @@
+/*
+ * 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.processors.standard;
+
+import org.junit.BeforeClass;
+
+/**
+ * This is probably overkill but in keeping with the same pattern as the TestInvokeHttp and
TestInvokeHttpSSL class,
+ * we will execute the same tests using two-way SSL. The Jetty server created for these tests
will require client
+ * certificates and the client will utilize keystore properties in the SSLContextService.
+ */
+public class TestInvokeHttpTwoWaySSL extends TestInvokeHttpSSL {
+
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        // useful for verbose logging output
+        // don't commit this with this property enabled, or any 'mvn test' will be really
verbose
+        // System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard", "debug");
+
+        // create the SSL properties, which basically store keystore / trustore information
+        // this is used by the StandardSSLContextService and the Jetty Server
+        serverSslProperties = createServerSslProperties(true);
+        sslProperties = createSslProperties(true);
+
+        // create a Jetty server on a random port
+        server = createServer();
+        server.startServer();
+
+        // Allow time for the server to start
+        Thread.sleep(500);
+        // this is the base url with the random port
+        url = server.getSecureUrl();
+    }
+
+}


Mime
View raw message