NIFI-8178 This closes #4787. Replaced StandardSSLContextService in unit tests with SslContextUtils

- Removed references to StandardSSLContextService from nifi-standard-processors
- Removed TestGetHTTPGroovy and TestPostHTTPGroovy since these are testing deprecated processors
- Optimized HandleHttpRequest, GetHTTP, PostHTTP to use SSLContextService.createContext()
NIFI-8178 Changed TestGetHTTP to ITGetHTTP since GetHTTP is deprecated
NIFI-8178 Changed TestPostHTTP to ITPostHTTP since PostHTTP is deprecated

Signed-off-by: Joe Witt <joewitt@apache.org>
This commit is contained in:
exceptionfactory 2021-01-27 16:13:39 -06:00 committed by Joe Witt
parent 8c1fe84f62
commit 11e9ff3773
No known key found for this signature in database
GPG Key ID: 9093BF854F811A1A
18 changed files with 502 additions and 1578 deletions

View File

@ -30,9 +30,10 @@ public interface TlsConfiguration {
String TLS_1_0_PROTOCOL = "TLSv1"; String TLS_1_0_PROTOCOL = "TLSv1";
String TLS_1_1_PROTOCOL = "TLSv1.1"; String TLS_1_1_PROTOCOL = "TLSv1.1";
String TLS_1_2_PROTOCOL = "TLSv1.2";
String[] LEGACY_TLS_PROTOCOL_VERSIONS = new String[]{TLS_1_0_PROTOCOL, TLS_1_1_PROTOCOL}; String[] LEGACY_TLS_PROTOCOL_VERSIONS = new String[]{TLS_1_0_PROTOCOL, TLS_1_1_PROTOCOL};
String JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION = "TLSv1.2"; String JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION = TLS_1_2_PROTOCOL;
String JAVA_11_MAX_SUPPORTED_TLS_PROTOCOL_VERSION = "TLSv1.3"; String JAVA_11_MAX_SUPPORTED_TLS_PROTOCOL_VERSION = "TLSv1.3";
String[] JAVA_8_SUPPORTED_TLS_PROTOCOL_VERSIONS = new String[]{JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION}; String[] JAVA_8_SUPPORTED_TLS_PROTOCOL_VERSIONS = new String[]{JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION};
String[] JAVA_11_SUPPORTED_TLS_PROTOCOL_VERSIONS = new String[]{JAVA_11_MAX_SUPPORTED_TLS_PROTOCOL_VERSION, JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION}; String[] JAVA_11_SUPPORTED_TLS_PROTOCOL_VERSIONS = new String[]{JAVA_11_MAX_SUPPORTED_TLS_PROTOCOL_VERSION, JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION};

View File

@ -31,12 +31,10 @@ import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager; import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.nifi.annotation.behavior.DynamicProperties; import org.apache.nifi.annotation.behavior.DynamicProperties;
import org.apache.nifi.annotation.behavior.DynamicProperty; import org.apache.nifi.annotation.behavior.DynamicProperty;
import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement;
@ -70,24 +68,14 @@ import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.standard.util.HTTPUtils; import org.apache.nifi.processors.standard.util.HTTPUtils;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.util.StopWatch; import org.apache.nifi.util.StopWatch;
import org.apache.nifi.util.Tuple; import org.apache.nifi.util.Tuple;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -326,32 +314,6 @@ public class GetHTTP extends AbstractSessionFactoryProcessor {
.build(); .build();
} }
private SSLContext createSSLContext(final SSLContextService service)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
if (StringUtils.isNotBlank(service.getTrustStoreFile())) {
final KeyStore truststore = KeyStoreUtils.getKeyStore(service.getTrustStoreType());
try (final InputStream in = new FileInputStream(new File(service.getTrustStoreFile()))) {
truststore.load(in, service.getTrustStorePassword().toCharArray());
}
sslContextBuilder.loadTrustMaterial(truststore, new TrustSelfSignedStrategy());
}
if (StringUtils.isNotBlank(service.getKeyStoreFile())) {
final KeyStore keystore = KeyStoreUtils.getKeyStore(service.getKeyStoreType());
try (final InputStream in = new FileInputStream(new File(service.getKeyStoreFile()))) {
keystore.load(in, service.getKeyStorePassword().toCharArray());
}
sslContextBuilder.loadKeyMaterial(keystore, service.getKeyStorePassword().toCharArray());
}
sslContextBuilder.useProtocol(service.getSslAlgorithm());
return sslContextBuilder.build();
}
@Override @Override
public void onTrigger(final ProcessContext context, final ProcessSessionFactory sessionFactory) throws ProcessException { public void onTrigger(final ProcessContext context, final ProcessSessionFactory sessionFactory) throws ProcessException {
final ComponentLog logger = getLogger(); final ComponentLog logger = getLogger();
@ -384,7 +346,7 @@ public class GetHTTP extends AbstractSessionFactoryProcessor {
} else { } else {
final SSLContext sslContext; final SSLContext sslContext;
try { try {
sslContext = createSSLContext(sslContextService); sslContext = sslContextService.createContext();
} catch (final Exception e) { } catch (final Exception e) {
throw new ProcessException(e); throw new ProcessException(e);
} }

View File

@ -60,6 +60,7 @@ import com.google.common.base.Optional;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import javax.net.ssl.SSLContext;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import javax.servlet.MultipartConfigElement; import javax.servlet.MultipartConfigElement;
@ -544,25 +545,14 @@ public class HandleHttpRequest extends AbstractProcessor {
return containerQueue.size(); return containerQueue.size();
} }
private SslContextFactory createSslFactory(final SSLContextService sslService, final boolean needClientAuth, final boolean wantClientAuth) { private SslContextFactory createSslFactory(final SSLContextService sslContextService, final boolean needClientAuth, final boolean wantClientAuth) {
final SslContextFactory sslFactory = new SslContextFactory.Server(); final SslContextFactory.Server sslFactory = new SslContextFactory.Server();
sslFactory.setNeedClientAuth(needClientAuth); sslFactory.setNeedClientAuth(needClientAuth);
sslFactory.setWantClientAuth(wantClientAuth); sslFactory.setWantClientAuth(wantClientAuth);
sslFactory.setProtocol(sslService.getSslAlgorithm()); final SSLContext sslContext = sslContextService.createContext();
sslFactory.setSslContext(sslContext);
if (sslService.isKeyStoreConfigured()) {
sslFactory.setKeyStorePath(sslService.getKeyStoreFile());
sslFactory.setKeyStorePassword(sslService.getKeyStorePassword());
sslFactory.setKeyStoreType(sslService.getKeyStoreType());
}
if (sslService.isTrustStoreConfigured()) {
sslFactory.setTrustStorePath(sslService.getTrustStoreFile());
sslFactory.setTrustStorePassword(sslService.getTrustStorePassword());
sslFactory.setTrustStoreType(sslService.getTrustStoreType());
}
return sslFactory; return sslFactory;
} }

View File

@ -62,7 +62,7 @@ import org.apache.nifi.util.StopWatch;
@ReadsAttribute(attribute = HTTPUtils.HTTP_LOCAL_NAME, description = "IP address/hostname of the server. Used for provenance event."), @ReadsAttribute(attribute = HTTPUtils.HTTP_LOCAL_NAME, description = "IP address/hostname of the server. Used for provenance event."),
@ReadsAttribute(attribute = HTTPUtils.HTTP_PORT, description = "Listening port of the server. Used for provenance event."), @ReadsAttribute(attribute = HTTPUtils.HTTP_PORT, description = "Listening port of the server. Used for provenance event."),
@ReadsAttribute(attribute = HTTPUtils.HTTP_SSL_CERT, description = "SSL distinguished name (if any). Used for provenance event.")}) @ReadsAttribute(attribute = HTTPUtils.HTTP_SSL_CERT, description = "SSL distinguished name (if any). Used for provenance event.")})
@SeeAlso(value = {HandleHttpRequest.class}, classNames = {"org.apache.nifi.http.StandardHttpContextMap", "org.apache.nifi.ssl.StandardSSLContextService"}) @SeeAlso(value = {HandleHttpRequest.class}, classNames = {"org.apache.nifi.http.StandardHttpContextMap", "org.apache.nifi.ssl.SSLContextService"})
public class HandleHttpResponse extends AbstractProcessor { public class HandleHttpResponse extends AbstractProcessor {
public static final PropertyDescriptor STATUS_CODE = new PropertyDescriptor.Builder() public static final PropertyDescriptor STATUS_CODE = new PropertyDescriptor.Builder()

View File

@ -61,7 +61,7 @@ import java.util.concurrent.atomic.AtomicReference;
+ "E.g.: file.txt is uploaded to /Folder1/SubFolder, then the value of the path attribute will be \"/Folder1/SubFolder/\" " + "E.g.: file.txt is uploaded to /Folder1/SubFolder, then the value of the path attribute will be \"/Folder1/SubFolder/\" "
+ "(note that it ends with a separator character).") + "(note that it ends with a separator character).")
}) })
@SeeAlso(classNames = {"org.apache.nifi.ssl.StandardRestrictedSSLContextService","org.apache.nifi.ssl.StandardSSLContextService"}) @SeeAlso(classNames = {"org.apache.nifi.ssl.RestrictedSSLContextService","org.apache.nifi.ssl.SSLContextService"})
public class ListenFTP extends AbstractSessionFactoryProcessor { public class ListenFTP extends AbstractSessionFactoryProcessor {
public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder() public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()

View File

@ -21,8 +21,6 @@ import static org.apache.nifi.processors.standard.util.HTTPUtils.PROXY_PORT;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -30,11 +28,6 @@ import java.net.InetAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.Principal; import java.security.Principal;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@ -81,7 +74,6 @@ import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentProducer; import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate; import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider;
@ -90,8 +82,6 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext; import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.apache.http.util.VersionInfo; import org.apache.http.util.VersionInfo;
import org.apache.nifi.annotation.behavior.InputRequirement; import org.apache.nifi.annotation.behavior.InputRequirement;
@ -122,7 +112,6 @@ import org.apache.nifi.processor.io.InputStreamCallback;
import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.standard.util.HTTPUtils; import org.apache.nifi.processors.standard.util.HTTPUtils;
import org.apache.nifi.security.util.CertificateUtils; import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.stream.io.GZIPOutputStream; import org.apache.nifi.stream.io.GZIPOutputStream;
import org.apache.nifi.stream.io.LeakyBucketStreamThrottler; import org.apache.nifi.stream.io.LeakyBucketStreamThrottler;
@ -389,8 +378,7 @@ public class PostHTTP extends AbstractProcessor {
} else { } else {
final SSLContext sslContext; final SSLContext sslContext;
try { try {
sslContext = createSSLContext(sslContextService); sslContext = sslContextService.createContext();
getLogger().info("PostHTTP supports protocol: " + sslContext.getProtocol());
} catch (final Exception e) { } catch (final Exception e) {
throw new ProcessException(e); throw new ProcessException(e);
} }
@ -509,38 +497,6 @@ public class PostHTTP extends AbstractProcessor {
return url.substring(0, index); return url.substring(0, index);
} }
private SSLContext createSSLContext(final SSLContextService service)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
SSLContextBuilder builder = SSLContexts.custom();
final String trustFilename = service.getTrustStoreFile();
if (trustFilename != null) {
final KeyStore truststore = KeyStoreUtils.getKeyStore(service.getTrustStoreType());
try (final InputStream in = new FileInputStream(new File(service.getTrustStoreFile()))) {
truststore.load(in, service.getTrustStorePassword().toCharArray());
}
builder = builder.loadTrustMaterial(truststore, new TrustSelfSignedStrategy());
}
final String keyFilename = service.getKeyStoreFile();
if (keyFilename != null) {
final KeyStore keystore = KeyStoreUtils.getKeyStore(service.getKeyStoreType());
try (final InputStream in = new FileInputStream(new File(service.getKeyStoreFile()))) {
keystore.load(in, service.getKeyStorePassword().toCharArray());
}
builder = builder.loadKeyMaterial(keystore, service.getKeyStorePassword().toCharArray());
final String alias = keystore.aliases().nextElement();
final Certificate cert = keystore.getCertificate(alias);
if (cert instanceof X509Certificate) {
principal = ((X509Certificate) cert).getSubjectDN();
}
}
builder = builder.setProtocol(service.getSslAlgorithm());
final SSLContext sslContext = builder.build();
return sslContext;
}
@Override @Override
public void onTrigger(final ProcessContext context, final ProcessSession session) { public void onTrigger(final ProcessContext context, final ProcessSession session) {
FlowFile firstFlowFile = session.get(); FlowFile firstFlowFile = session.get();

View File

@ -1,488 +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.processors.standard
import groovy.servlet.GroovyServlet
import groovy.test.GroovyAssert
import org.apache.nifi.ssl.SSLContextService
import org.apache.nifi.ssl.StandardSSLContextService
import org.apache.nifi.util.StandardProcessorTestRunner
import org.apache.nifi.util.TestRunner
import org.apache.nifi.util.TestRunners
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.eclipse.jetty.server.HttpConfiguration
import org.eclipse.jetty.server.HttpConnectionFactory
import org.eclipse.jetty.server.SecureRequestCustomizer
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.SslConnectionFactory
import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.util.ssl.SslContextFactory
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
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.crypto.Cipher
import javax.net.SocketFactory
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLEngine
import javax.net.ssl.SSLHandshakeException
import javax.net.ssl.SSLSocket
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import java.security.Security
@SuppressWarnings("deprecation")
@RunWith(JUnit4.class)
class TestGetHTTPGroovy extends GroovyTestCase {
private static final Logger logger = LoggerFactory.getLogger(TestGetHTTPGroovy.class)
static private final String KEYSTORE_TYPE = "JKS"
private static final String TLSv1 = "TLSv1"
private static final String TLSv1_1 = "TLSv1.1"
private static final String TLSv1_2 = "TLSv1.2"
private static final List DEFAULT_PROTOCOLS = [TLSv1, TLSv1_1, TLSv1_2]
private static final String TLSv1_1_CIPHER_SUITE = "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"
private static final SSLContext SSL_CONTEXT = SSLContext.default
private static final SSLEngine SSL_ENGINE = SSL_CONTEXT.createSSLEngine()
private static
final List DEFAULT_CIPHER_SUITES = SSL_ENGINE.supportedCipherSuites as List
private static final String DEFAULT_HOSTNAME = "localhost"
private static final int DEFAULT_TLS_PORT = 8456
private static final String HTTPS_URL = "https://${DEFAULT_HOSTNAME}:${DEFAULT_TLS_PORT}"
private static final String GET_URL = "${HTTPS_URL}/GetHandler.groovy"
private static final String MOZILLA_INTERMEDIATE_URL = "https://mozilla-intermediate.badssl.com/"
private static final String TLS_1_URL = "https://nifi.apache.org/"
private static final String TLS_1_1_URL = "https://nifi.apache.org/"
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks"
private static final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks"
private static final String CACERTS_PATH = "/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/security/cacerts"
private static final String KEYSTORE_PASSWORD = "passwordpassword"
private static final String TRUSTSTORE_PASSWORD = "passwordpassword"
private static final String CACERTS_PASSWORD = "changeit"
private static Server server
private static X509TrustManager nullTrustManager
private static HostnameVerifier nullHostnameVerifier
private static TestRunner runner
private
static Server createServer(List supportedProtocols = DEFAULT_PROTOCOLS, List supportedCipherSuites = DEFAULT_CIPHER_SUITES) {
// Create Server
Server server = new Server()
// Add some secure config
final HttpConfiguration httpsConfiguration = new HttpConfiguration()
httpsConfiguration.setSecureScheme("https")
httpsConfiguration.setSecurePort(DEFAULT_TLS_PORT)
httpsConfiguration.addCustomizer(new SecureRequestCustomizer())
// Build the TLS connector
final ServerConnector https = createConnector(server, httpsConfiguration, supportedProtocols, supportedCipherSuites)
// Add this connector
server.addConnector(https)
logger.info("Created server with supported protocols: ${supportedProtocols}")
/** Create a simple Groovlet that responds to the incoming request by reversing the string parameter
* i.e. localhost:8456/ReverseHandler.groovy?string=Happy%20birthday -> yadhtrib yppaH
*/
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS)
context.with {
contextPath = '/'
resourceBase = 'src/test/resources/TestGetHTTP'
addServlet(GroovyServlet, '*.groovy')
}
server.setHandler(context)
server
}
private
static ServerConnector createConnector(Server server, HttpConfiguration httpsConfiguration, List supportedProtocols = DEFAULT_PROTOCOLS, List supportedCipherSuites = DEFAULT_CIPHER_SUITES) {
ServerConnector https = new ServerConnector(server,
new SslConnectionFactory(createSslContextFactory(supportedProtocols, supportedCipherSuites), "http/1.1"),
new HttpConnectionFactory(httpsConfiguration))
// set host and port
https.setHost(DEFAULT_HOSTNAME)
https.setPort(DEFAULT_TLS_PORT)
https
}
private
static SslContextFactory createSslContextFactory(List supportedProtocols = DEFAULT_PROTOCOLS, List supportedCipherSuites = DEFAULT_CIPHER_SUITES) {
final SslContextFactory contextFactory = new SslContextFactory.Server()
contextFactory.needClientAuth = false
contextFactory.wantClientAuth = false
contextFactory.setKeyStorePath(KEYSTORE_PATH)
contextFactory.setKeyStoreType(KEYSTORE_TYPE)
contextFactory.setKeyStorePassword(KEYSTORE_PASSWORD)
contextFactory.setIncludeProtocols(supportedProtocols as String[])
if (supportedCipherSuites) {
contextFactory.setIncludeCipherSuites(supportedCipherSuites as String[])
}
contextFactory
}
@BeforeClass
static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
server = createServer()
runner = configureRunner()
// Print the default cipher suite list
logger.info("Default supported cipher suites: \n\t${DEFAULT_CIPHER_SUITES.join("\n\t")}")
}
private static TestRunner configureRunner() {
// Set the default trust manager for the "default" tests (the outgoing Groovy call) to ignore certificate path verification for localhost
nullTrustManager = [
checkClientTrusted: { chain, authType -> },
checkServerTrusted: { chain, authType -> },
getAcceptedIssuers: { null }
] as X509TrustManager
nullHostnameVerifier = [
verify: { String hostname, session ->
// Will always return true if the hostname is "localhost" or the Mozilla intermediate site
hostname.equalsIgnoreCase(DEFAULT_HOSTNAME) || hostname.equalsIgnoreCase(new URL(MOZILLA_INTERMEDIATE_URL).host)
}
] as HostnameVerifier
// Configure the test runner
TestRunner runner = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class)
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService("ssl-context", sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, KEYSTORE_TYPE)
runner.enableControllerService(sslContextService)
runner.setProperty(org.apache.nifi.processors.standard.GetHTTP.URL, GET_URL)
runner.setProperty(org.apache.nifi.processors.standard.GetHTTP.SSL_CONTEXT_SERVICE, "ssl-context")
runner
}
@AfterClass
static void tearDownOnce() {
}
@Before
void setUp() throws Exception {
// This must be executed before each test, or the connections will be re-used and if a TLSv1.1 connection is re-used against a server that only supports TLSv1.2, it will fail
SSLContext sc = SSLContext.getInstance(TLSv1_2)
sc.init(null, [nullTrustManager] as TrustManager[], null)
SocketFactory socketFactory = sc.getSocketFactory()
logger.info("JCE unlimited strength installed: ${Cipher.getMaxAllowedKeyLength("AES") > 128}")
logger.info("Supported client cipher suites: ${socketFactory.supportedCipherSuites}")
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory)
HttpsURLConnection.setDefaultHostnameVerifier(nullHostnameVerifier)
runner.setProperty(org.apache.nifi.processors.standard.GetHTTP.FILENAME, "mockFlowfile_${System.currentTimeMillis()}")
(runner as StandardProcessorTestRunner).clearQueue()
}
@After
void tearDown() throws Exception {
try {
server.stop()
} catch (Exception e) {
e.printStackTrace()
}
runner.clearTransferState()
runner.clearProvenanceEvents()
(runner as StandardProcessorTestRunner).clearQueue()
}
/**
* Jetty 9.4.0+ no longer supports TLSv1.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1 is available")
void testDefaultShouldSupportTLSv1() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1 only
server = createServer([TLSv1])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
/**
* Jetty 9.4.0+ no longer supports TLSv1.1 unless compatible ("vulnerable" according to Jetty documentation, see <a href="https://github.com/eclipse/jetty.project/issues/860">https://github.com/eclipse/jetty.project/issues/860</a>) cipher suites are explicitly provided.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1.1 is available")
void testDefaultShouldSupportTLSv1_1() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1.1 only
server = createServer([TLSv1_1])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
/**
* Jetty 9.4.0+ no longer supports TLSv1.1 unless compatible ("vulnerable" according to Jetty documentation, see <a href="https://github.com/eclipse/jetty.project/issues/860">https://github.com/eclipse/jetty.project/issues/860</a>) cipher suites are explicitly provided.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1 is available")
void testDefaultShouldSupportTLSv1_1WithVulnerableCipherSuites() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1.1 only but explicitly provide the legacy cipher suite
server = createServer([TLSv1_1], [TLSv1_1_CIPHER_SUITE])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
@Test
void testDefaultShouldSupportTLSv1_2() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1.2 only
server = createServer([TLSv1_2])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
@Test
void testDefaultShouldPreferTLSv1_2() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with all TLS protocols
server = createServer()
// Start server
server.start()
// Create a connection that could use TLSv1, TLSv1.1, or TLSv1.2
SSLContext sc = SSLContext.getInstance("TLS")
sc.init(null, [nullTrustManager] as TrustManager[], null)
SocketFactory socketFactory = sc.getSocketFactory()
URL formedUrl = new URL(url)
SSLSocket socket = (SSLSocket) socketFactory.createSocket(formedUrl.host, formedUrl.port)
logger.info("Enabled protocols: ${socket.enabledProtocols}")
// Act
socket.startHandshake()
String selectedProtocol = socket.getSession().protocol
logger.info("Selected protocol: ${selectedProtocol}")
// Assert
assert selectedProtocol == TLSv1_2
}
private static void enableContextServiceProtocol(TestRunner runner, String protocol, String truststorePath = TRUSTSTORE_PATH, String truststorePassword = TRUSTSTORE_PASSWORD) {
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService("ssl-context", sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, truststorePath)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, truststorePassword)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, KEYSTORE_TYPE)
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, protocol)
runner.enableControllerService(sslContextService)
def sslContext = sslContextService.createContext();
logger.info("GetHTTP supported protocols: ${sslContext.protocol}")
logger.info("GetHTTP supported cipher suites: ${sslContext.supportedSSLParameters.cipherSuites}")
}
/**
* This test connects to a server running TLSv1/1.1/1.2. It iterates over an {@link SSLContextService} with TLSv1, TLSv1.1, and TLSv1.2 support. All three context services should be able to communicate successfully.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1 is available")
void testGetHTTPShouldConnectToServerWithTLSv1() {
// Arrange
// Connect to a server that still runs TLSv1/1.1/1.2
runner.setProperty(org.apache.nifi.processors.standard.GetHTTP.URL, TLS_1_URL)
// Act
[TLSv1, TLSv1_1, TLSv1_2].each { String tlsVersion ->
logger.info("Set context service protocol to ${tlsVersion}")
enableContextServiceProtocol(runner, tlsVersion, CACERTS_PATH, CACERTS_PASSWORD)
runner.assertQueueEmpty()
logger.info("Queue size (before run): ${runner.queueSize}")
runner.run()
// Assert
logger.info("Queue size (after run): ${runner.queueSize}")
runner.assertAllFlowFilesTransferred(REL_SUCCESS, 1)
runner.clearTransferState()
logger.info("Ran successfully")
}
}
/**
* This test creates a server that supports TLSv1.1. It iterates over an {@link SSLContextService} with TLSv1, TLSv1.1, and TLSv1.2 support. The context service with TLSv1 should not be able to communicate with a server that does not support it, but TLSv1.1 and TLSv1.2 should be able to communicate successfully.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that only supports TLSv1.1 is available")
void testGetHTTPShouldConnectToServerWithTLSv1_1() {
// Arrange
final String MSG = "This is a test message"
// Configure server with TLSv1.1 only
server = createServer([TLSv1_1])
// Start server
server.start()
// Act
logger.info("Set context service protocol to ${TLSv1}")
enableContextServiceProtocol(runner, TLSv1)
runner.enqueue(MSG.getBytes())
def ae = GroovyAssert.shouldFail(AssertionError) {
runner.run()
}
logger.expected(ae.getMessage())
logger.expected("Cause: ${ae.cause?.class?.name}")
logger.expected("Original cause: ${ae.cause?.cause?.class?.name}")
// Assert
assert ae.cause.cause instanceof SSLHandshakeException
runner.clearTransferState()
logger.expected("Unable to connect")
[TLSv1_1, TLSv1_2].each { String tlsVersion ->
logger.info("Set context service protocol to ${tlsVersion}")
enableContextServiceProtocol(runner, tlsVersion)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(REL_SUCCESS)
runner.clearTransferState()
logger.info("Ran successfully")
}
}
/**
* This test creates a server that supports TLSv1.2. It iterates over an {@link SSLContextService} with TLSv1, TLSv1.1, and TLSv1.2 support. The context services with TLSv1 and TLSv1.1 should not be able to communicate with a server that does not support it, but TLSv1.2 should be able to communicate successfully.
*/
@Test
void testGetHTTPShouldConnectToServerWithTLSv1_2() {
// Arrange
final String MSG = "This is a test message"
// Configure server with TLSv1.2 only
server = createServer([TLSv1_2])
// Start server
server.start()
// Act
[TLSv1, TLSv1_1].each { String tlsVersion ->
logger.info("Set context service protocol to ${tlsVersion}")
enableContextServiceProtocol(runner, tlsVersion)
runner.enqueue(MSG.getBytes())
def ae = GroovyAssert.shouldFail(AssertionError) {
runner.run()
}
logger.expected(ae.getMessage())
logger.expected("Cause: ${ae.cause?.class?.name}")
logger.expected("Original cause: ${ae.cause?.cause?.class?.name}")
// Assert
assert ae.cause.cause instanceof SSLHandshakeException
runner.clearTransferState()
logger.expected("Unable to connect")
}
logger.info("Set context service protocol to ${TLSv1_2}")
enableContextServiceProtocol(runner, TLSv1_2)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS)
runner.clearTransferState()
logger.info("Ran successfully")
}
}

View File

@ -1,443 +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.processors.standard
import groovy.servlet.GroovyServlet
import org.apache.nifi.security.util.TlsPlatform
import org.apache.nifi.ssl.SSLContextService
import org.apache.nifi.ssl.StandardSSLContextService
import org.apache.nifi.util.TestRunner
import org.apache.nifi.util.TestRunners
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.eclipse.jetty.server.HttpConfiguration
import org.eclipse.jetty.server.HttpConnectionFactory
import org.eclipse.jetty.server.SecureRequestCustomizer
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.SslConnectionFactory
import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.util.ssl.SslContextFactory
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
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.crypto.Cipher
import javax.net.SocketFactory
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLEngine
import javax.net.ssl.SSLSocket
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import java.security.Security
@SuppressWarnings("deprecation")
@RunWith(JUnit4.class)
class TestPostHTTPGroovy extends GroovyTestCase {
private static final Logger logger = LoggerFactory.getLogger(TestPostHTTPGroovy.class)
static private final String KEYSTORE_TYPE = "JKS"
private static final String TLSv1 = "TLSv1"
private static final String TLSv1_1 = "TLSv1.1"
private static final String TLSv1_2 = "TLSv1.2"
private static final List DEFAULT_PROTOCOLS = new ArrayList(TlsPlatform.supportedProtocols)
private static final SSLContext SSL_CONTEXT = SSLContext.default
private static final SSLEngine SSL_ENGINE = SSL_CONTEXT.createSSLEngine()
private static
final List DEFAULT_CIPHER_SUITES = SSL_ENGINE.supportedCipherSuites as List
private static final String DEFAULT_HOSTNAME = "localhost"
private static final int DEFAULT_TLS_PORT = 8456
private static final String HTTPS_URL = "https://${DEFAULT_HOSTNAME}:${DEFAULT_TLS_PORT}"
private static final String POST_URL = "${HTTPS_URL}/PostHandler.groovy"
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks"
private static final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks"
private static final String KEYSTORE_PASSWORD = "passwordpassword"
private static final String TRUSTSTORE_PASSWORD = "passwordpassword"
private static Server server
private static X509TrustManager nullTrustManager
private static HostnameVerifier nullHostnameVerifier
private static TestRunner runner
private
static Server createServer(List supportedProtocols = DEFAULT_PROTOCOLS, List supportedCipherSuites = DEFAULT_CIPHER_SUITES) {
// Create Server
Server server = new Server()
// Add some secure config
final HttpConfiguration httpsConfiguration = new HttpConfiguration()
httpsConfiguration.setSecureScheme("https")
httpsConfiguration.setSecurePort(DEFAULT_TLS_PORT)
httpsConfiguration.addCustomizer(new SecureRequestCustomizer())
// Build the TLS connector
final ServerConnector https = createConnector(server, httpsConfiguration, supportedProtocols, supportedCipherSuites)
// Add this connector
server.addConnector(https)
logger.info("Created server with supported protocols: ${supportedProtocols}")
/** Create a simple Groovlet that responds to the incoming request by reversing the string parameter
* i.e. localhost:8456/ReverseHandler.groovy?string=Happy%20birthday -> yadhtrib yppaH
*/
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS)
context.with {
contextPath = '/'
resourceBase = 'src/test/resources/TestPostHTTP'
addServlet(GroovyServlet, '*.groovy')
}
server.setHandler(context)
server
}
private
static ServerConnector createConnector(Server server, HttpConfiguration httpsConfiguration, List supportedProtocols = DEFAULT_PROTOCOLS, List supportedCipherSuites = DEFAULT_CIPHER_SUITES) {
ServerConnector https = new ServerConnector(server,
new SslConnectionFactory(createSslContextFactory(supportedProtocols, supportedCipherSuites), "http/1.1"),
new HttpConnectionFactory(httpsConfiguration))
// set host and port
https.setHost(DEFAULT_HOSTNAME)
https.setPort(DEFAULT_TLS_PORT)
https
}
private
static SslContextFactory createSslContextFactory(List supportedProtocols = DEFAULT_PROTOCOLS, List supportedCipherSuites = DEFAULT_CIPHER_SUITES) {
final SslContextFactory contextFactory = new SslContextFactory.Server()
contextFactory.needClientAuth = false
contextFactory.wantClientAuth = false
contextFactory.setKeyStorePath(KEYSTORE_PATH)
contextFactory.setKeyStoreType(KEYSTORE_TYPE)
contextFactory.setKeyStorePassword(KEYSTORE_PASSWORD)
contextFactory.setIncludeProtocols(supportedProtocols as String[])
if (supportedCipherSuites) {
contextFactory.setIncludeCipherSuites(supportedCipherSuites as String[])
}
contextFactory
}
@BeforeClass
static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
server = createServer()
runner = configureRunner()
// Print the default cipher suite list
logger.info("Default supported cipher suites: \n\t${DEFAULT_CIPHER_SUITES.join("\n\t")}")
}
private static TestRunner configureRunner() {
// Set the default trust manager for the "default" tests (the outgoing Groovy call) to ignore certificate path verification for localhost
nullTrustManager = [
checkClientTrusted: { chain, authType -> },
checkServerTrusted: { chain, authType -> },
getAcceptedIssuers: { null }
] as X509TrustManager
nullHostnameVerifier = [
verify: { String hostname, session ->
// Will always return true if the hostname is "localhost"
hostname.equalsIgnoreCase(DEFAULT_HOSTNAME)
}
] as HostnameVerifier
// Configure the test runner
TestRunner runner = TestRunners.newTestRunner(org.apache.nifi.processors.standard.PostHTTP.class)
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService("ssl-context", sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, KEYSTORE_TYPE)
runner.enableControllerService(sslContextService)
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, POST_URL)
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SSL_CONTEXT_SERVICE, "ssl-context")
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false")
runner
}
@AfterClass
static void tearDownOnce() {
}
@Before
void setUp() throws Exception {
// This must be executed before each test, or the connections will be re-used and if a TLSv1.1 connection is re-used against a server that only supports TLSv1.2, it will fail
SSLContext sc = SSLContext.getInstance(TLSv1_2)
sc.init(null, [nullTrustManager] as TrustManager[], null)
SocketFactory socketFactory = sc.getSocketFactory()
logger.info("JCE unlimited strength installed: ${Cipher.getMaxAllowedKeyLength("AES") > 128}")
logger.info("Supported client cipher suites: ${socketFactory.supportedCipherSuites}")
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory)
HttpsURLConnection.setDefaultHostnameVerifier(nullHostnameVerifier)
}
@After
void tearDown() throws Exception {
try {
server.stop()
} catch (Exception e) {
e.printStackTrace()
}
runner.clearTransferState()
runner.clearProvenanceEvents()
}
/**
* Jetty 9.4.0+ no longer supports TLSv1.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1 is available")
void testDefaultShouldSupportTLSv1() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1 only
server = createServer([TLSv1])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
/**
* Jetty 9.4.0+ no longer supports TLSv1.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1.1 is available")
void testDefaultShouldSupportTLSv1_1() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1.1 only
server = createServer([TLSv1_1])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
@Test
void testDefaultShouldSupportTLSv1_2() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with TLSv1.2 only
server = createServer([TLSv1_2])
// Start server
server.start()
// Act
String response = new URL(url).text
logger.info("Response from ${HTTPS_URL}: ${response}")
// Assert
assert response == MSG.reverse()
}
@Test
void testDefaultShouldPreferHighestSupportedVersion() {
// Arrange
final String MSG = "This is a test message"
final String url = "${HTTPS_URL}/ReverseHandler.groovy?string=${URLEncoder.encode(MSG, "UTF-8")}"
// Configure server with all TLS protocols
server = createServer()
// Start server
server.start()
// Create a connection that could use TLSv1, TLSv1.1, or TLSv1.2
SSLContext sc = SSLContext.getInstance("TLS")
sc.init(null, [nullTrustManager] as TrustManager[], null)
SocketFactory socketFactory = sc.getSocketFactory()
URL formedUrl = new URL(url)
SSLSocket socket = (SSLSocket) socketFactory.createSocket(formedUrl.host, formedUrl.port)
logger.info("Enabled protocols: ${socket.enabledProtocols}")
// Act
socket.startHandshake()
String selectedProtocol = socket.getSession().protocol
logger.info("Selected protocol: ${selectedProtocol}")
// Assert
assert selectedProtocol == TlsPlatform.latestProtocol
}
private static void enableContextServiceProtocol(TestRunner runner, String protocol) {
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService("ssl-context", sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, KEYSTORE_TYPE)
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, protocol)
runner.enableControllerService(sslContextService)
def sslContext = sslContextService.createContext();
logger.info("PostHTTP supported protocols: ${sslContext.protocol}")
logger.info("PostHTTP supported cipher suites: ${sslContext.supportedSSLParameters.cipherSuites}")
}
/**
* This test creates a server that supports TLSv1. It iterates over an {@link SSLContextService} with TLSv1, TLSv1.1, and TLSv1.2 support. All three context services should be able to communicate successfully.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1 is available")
void testPostHTTPShouldConnectToServerWithTLSv1() {
// Arrange
final String MSG = "This is a test message"
// Configure server with TLSv1 only
server = createServer([TLSv1])
// Start server
server.start()
// Act
[TLSv1, TLSv1_1, TLSv1_2].each { String tlsVersion ->
logger.info("Set context service protocol to ${tlsVersion}")
enableContextServiceProtocol(runner, tlsVersion)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1)
runner.clearTransferState()
logger.info("Ran successfully")
}
}
/**
* This test creates a server that supports TLSv1.1. It iterates over an {@link SSLContextService} with TLSv1, TLSv1.1, and TLSv1.2 support. The context service with TLSv1 should not be able to communicate with a server that does not support it, but TLSv1.1 and TLSv1.2 should be able to communicate successfully.
*/
@Test
@Ignore("Ignore until embeddable test HTTPS server that supports TLSv1.1 is available")
void testPostHTTPShouldConnectToServerWithTLSv1_1() {
// Arrange
final String MSG = "This is a test message"
// Configure server with TLSv1.1 only
server = createServer([TLSv1_1])
// Start server
server.start()
// Act
logger.info("Set context service protocol to ${TLSv1}")
enableContextServiceProtocol(runner, TLSv1)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_FAILURE, 1)
runner.clearTransferState()
logger.expected("Unable to connect")
[TLSv1_1, TLSv1_2].each { String tlsVersion ->
logger.info("Set context service protocol to ${tlsVersion}")
enableContextServiceProtocol(runner, tlsVersion)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1)
runner.clearTransferState()
logger.info("Ran successfully")
}
}
/**
* This test creates a server that supports TLSv1.2. It iterates over an {@link SSLContextService} with TLSv1, TLSv1.1, and TLSv1.2 support. The context services with TLSv1 and TLSv1.1 should not be able to communicate with a server that does not support it, but TLSv1.2 should be able to communicate successfully.
*/
@Test
void testPostHTTPShouldConnectToServerWithTLSv1_2() {
// Arrange
final String MSG = "This is a test message"
// Configure server with TLSv1.2 only
server = createServer([TLSv1_2])
// Start server
server.start()
// Act
[TLSv1, TLSv1_1].each { String tlsVersion ->
logger.info("Set context service protocol to ${tlsVersion}")
enableContextServiceProtocol(runner, tlsVersion)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_FAILURE, 1)
runner.clearTransferState()
logger.expected("Unable to connect")
}
logger.info("Set context service protocol to ${TLSv1_2}")
enableContextServiceProtocol(runner, TLSv1_2)
runner.enqueue(MSG.getBytes())
runner.run()
// Assert
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1)
runner.clearTransferState()
logger.info("Ran successfully")
}
}

View File

@ -20,37 +20,52 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.HashMap; import javax.net.ssl.SSLContext;
import java.util.Map;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.components.state.Scope; import org.apache.nifi.components.state.Scope;
import org.apache.nifi.flowfile.attributes.CoreAttributes; import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.remote.io.socket.NetworkUtils;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.ClientAuth;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.TestServer; import org.apache.nifi.web.util.JettyServerUtils;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHandler;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
/** /**
* * Integration Test for deprecated GetHTTP Processor
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class TestGetHTTP { public class ITGetHTTP {
private static final String SSL_CONTEXT_IDENTIFIER = SSLContextService.class.getName();
private static final String HTTP_URL = "http://localhost:%d";
private static final String HTTPS_URL = "https://localhost:%d";
private static SSLContext keyStoreSslContext;
private static SSLContext trustStoreSslContext;
private TestRunner controller; private TestRunner controller;
@BeforeClass @BeforeClass
public static void before() { public static void configureServices() throws TlsException {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info"); System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); System.setProperty("org.slf4j.simpleLogger.showDateTime", "true");
System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard.GetHTTP", "debug"); System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard.GetHTTP", "debug");
System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard.TestGetHTTP", "debug"); System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard.TestGetHTTP", "debug");
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
} }
@Test @Test
@ -60,14 +75,15 @@ public class TestGetHTTP {
handler.addServletWithMapping(RESTServiceContentModified.class, "/*"); handler.addServletWithMapping(RESTServiceContentModified.class, "/*");
// create the service // create the service
TestServer server = new TestServer(); final int port = NetworkUtils.availablePort();
server.addHandler(handler); final Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
// this is the base url with the random port // this is the base url with the random port
String destination = server.getUrl(); String destination = String.format(HTTP_URL, port);
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -139,34 +155,32 @@ public class TestGetHTTP {
} finally { } finally {
// shutdown web service // shutdown web service
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@Test @Test
public final void testContentModifiedTwoServers() throws Exception { public final void testContentModifiedTwoServers() throws Exception {
// set up web services final int port1 = NetworkUtils.availablePort();
ServletHandler handler1 = new ServletHandler(); final Server server1 = JettyServerUtils.createServer(port1, null, null);
final ServletHandler handler1 = new ServletHandler();
handler1.addServletWithMapping(RESTServiceContentModified.class, "/*"); handler1.addServletWithMapping(RESTServiceContentModified.class, "/*");
JettyServerUtils.addHandler(server1, handler1);
ServletHandler handler2 = new ServletHandler(); final int port2 = NetworkUtils.availablePort();
final Server server2 = JettyServerUtils.createServer(port2, null, null);
final ServletHandler handler2 = new ServletHandler();
handler2.addServletWithMapping(RESTServiceContentModified.class, "/*"); handler2.addServletWithMapping(RESTServiceContentModified.class, "/*");
JettyServerUtils.addHandler(server2, handler2);
// create the services
TestServer server1 = new TestServer();
server1.addHandler(handler1);
TestServer server2 = new TestServer();
server2.addHandler(handler2);
try { try {
server1.startServer(); JettyServerUtils.startServer(server1);
server2.startServer(); JettyServerUtils.startServer(server2);
// this is the base urls with the random ports // this is the base urls with the random ports
String destination1 = server1.getUrl(); String destination1 = String.format(HTTP_URL, port1);
String destination2 = server2.getUrl(); String destination2 = String.format(HTTP_URL, port2);
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -205,8 +219,10 @@ public class TestGetHTTP {
} finally { } finally {
// shutdown web services // shutdown web services
server1.shutdownServer(); server1.stop();
server2.shutdownServer(); server1.destroy();
server2.stop();
server2.destroy();
} }
} }
@ -217,13 +233,13 @@ public class TestGetHTTP {
handler.addServletWithMapping(UserAgentTestingServlet.class, "/*"); handler.addServletWithMapping(UserAgentTestingServlet.class, "/*");
// create the service // create the service
TestServer server = new TestServer(); final int port = NetworkUtils.availablePort();
server.addHandler(handler); Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
String destination = server.getUrl();
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -241,7 +257,8 @@ public class TestGetHTTP {
// shutdown web service // shutdown web service
} finally { } finally {
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@ -252,13 +269,13 @@ public class TestGetHTTP {
handler.addServletWithMapping(UserAgentTestingServlet.class, "/*"); handler.addServletWithMapping(UserAgentTestingServlet.class, "/*");
// create the service // create the service
TestServer server = new TestServer(); final int port = NetworkUtils.availablePort();
server.addHandler(handler); Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
String destination = server.getUrl();
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -275,7 +292,8 @@ public class TestGetHTTP {
// shutdown web service // shutdown web service
} finally { } finally {
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@ -286,13 +304,13 @@ public class TestGetHTTP {
handler.addServletWithMapping(UserAgentTestingServlet.class, "/*"); handler.addServletWithMapping(UserAgentTestingServlet.class, "/*");
// create the service // create the service
TestServer server = new TestServer(); final int port = NetworkUtils.availablePort();
server.addHandler(handler); Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
String destination = server.getUrl();
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -311,7 +329,8 @@ public class TestGetHTTP {
assertTrue(fileName.matches("test_\\d\\d\\d\\d/\\d\\d/\\d\\d_\\d\\d:\\d\\d:\\d\\d")); assertTrue(fileName.matches("test_\\d\\d\\d\\d/\\d\\d/\\d\\d_\\d\\d:\\d\\d:\\d\\d"));
// shutdown web service // shutdown web service
} finally { } finally {
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@ -326,13 +345,14 @@ public class TestGetHTTP {
handler.addServletWithMapping(HttpErrorServlet.class, "/*"); handler.addServletWithMapping(HttpErrorServlet.class, "/*");
// create the service // create the service
TestServer server = new TestServer(); final int port = NetworkUtils.availablePort();
server.addHandler(handler); Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
HttpErrorServlet servlet = (HttpErrorServlet) handler.getServlets()[0].getServlet(); HttpErrorServlet servlet = (HttpErrorServlet) handler.getServlets()[0].getServlet();
String destination = server.getUrl();
this.controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); this.controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
this.controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.CONNECTION_TIMEOUT, "5 secs"); this.controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.CONNECTION_TIMEOUT, "5 secs");
@ -357,31 +377,30 @@ public class TestGetHTTP {
this.controller.assertTransferCount(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS, 0); this.controller.assertTransferCount(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS, 0);
} finally { } finally {
// shutdown web service // shutdown web service
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@Test @Test
public final void testSecure_oneWaySsl() throws Exception { public final void testTlsClientAuthenticationNone() throws Exception {
// set up web service // set up web service
final ServletHandler handler = new ServletHandler(); final ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(HelloWorldServlet.class, "/*"); handler.addServletWithMapping(HelloWorldServlet.class, "/*");
// create the service, disabling the need for client auth // create the service, disabling the need for client auth
final Map<String, String> serverSslProperties = getKeystoreProperties(); final int port = NetworkUtils.availablePort();
serverSslProperties.put(TestServer.NEED_CLIENT_AUTH, Boolean.toString(false)); final Server server = JettyServerUtils.createServer(port, keyStoreSslContext, ClientAuth.NONE);
final TestServer server = new TestServer(serverSslProperties); server.setHandler(handler);
server.addHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
final String destination = String.format(HTTPS_URL, port);
final String destination = server.getSecureUrl();
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
// Use context service with only a truststore // Use context service with only a truststore
useSSLContextService(getTruststoreProperties()); enableSslContextService(trustStoreSslContext);
controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.CONNECTION_TIMEOUT, "5 secs"); controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.CONNECTION_TIMEOUT, "5 secs");
controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.URL, destination); controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.URL, destination);
@ -393,31 +412,30 @@ public class TestGetHTTP {
final MockFlowFile mff = controller.getFlowFilesForRelationship(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS).get(0); final MockFlowFile mff = controller.getFlowFilesForRelationship(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS).get(0);
mff.assertContentEquals("Hello, World!"); mff.assertContentEquals("Hello, World!");
} finally { } finally {
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@Test @Test
public final void testSecure_twoWaySsl() throws Exception { public final void testTlsClientAuthenticationRequired() throws Exception {
// set up web service // set up web service
final ServletHandler handler = new ServletHandler(); final ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(HelloWorldServlet.class, "/*"); handler.addServletWithMapping(HelloWorldServlet.class, "/*");
// create the service, providing both truststore and keystore properties, requiring client auth (default) // create the service, providing both truststore and keystore properties, requiring client auth (default)
final Map<String, String> twoWaySslProperties = getKeystoreProperties(); final int port = NetworkUtils.availablePort();
twoWaySslProperties.putAll(getTruststoreProperties()); final Server server = JettyServerUtils.createServer(port, keyStoreSslContext, ClientAuth.REQUIRED);
final TestServer server = new TestServer(twoWaySslProperties); server.setHandler(handler);
server.addHandler(handler);
try { try {
server.startServer(); JettyServerUtils.startServer(server);
final String destination = String.format(HTTPS_URL, port);
final String destination = server.getSecureUrl();
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
// Use context service with a keystore and a truststore // Use context service with a keystore and a truststore
useSSLContextService(twoWaySslProperties); enableSslContextService(keyStoreSslContext);
controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.CONNECTION_TIMEOUT, "10 secs"); controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.CONNECTION_TIMEOUT, "10 secs");
controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.URL, destination); controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.URL, destination);
@ -429,33 +447,32 @@ public class TestGetHTTP {
final MockFlowFile mff = controller.getFlowFilesForRelationship(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS).get(0); final MockFlowFile mff = controller.getFlowFilesForRelationship(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS).get(0);
mff.assertContentEquals("Hello, World!"); mff.assertContentEquals("Hello, World!");
} finally { } finally {
server.shutdownServer(); server.stop();
server.destroy();
} }
} }
@Test @Test
public final void testCookiePolicy() throws Exception { public final void testCookiePolicy() throws Exception {
// set up web services final int port1 = NetworkUtils.availablePort();
ServletHandler handler1 = new ServletHandler(); final Server server1 = JettyServerUtils.createServer(port1, null, null);
final ServletHandler handler1 = new ServletHandler();
handler1.addServletWithMapping(CookieTestingServlet.class, "/*"); handler1.addServletWithMapping(CookieTestingServlet.class, "/*");
JettyServerUtils.addHandler(server1, handler1);
ServletHandler handler2 = new ServletHandler(); final int port2 = NetworkUtils.availablePort();
final Server server2 = JettyServerUtils.createServer(port2, null, null);
final ServletHandler handler2 = new ServletHandler();
handler2.addServletWithMapping(CookieVerificationTestingServlet.class, "/*"); handler2.addServletWithMapping(CookieVerificationTestingServlet.class, "/*");
JettyServerUtils.addHandler(server2, handler2);
// create the services
TestServer server1 = new TestServer();
server1.addHandler(handler1);
TestServer server2 = new TestServer();
server2.addHandler(handler2);
try { try {
server1.startServer(); JettyServerUtils.startServer(server1);
server2.startServer(); JettyServerUtils.startServer(server2);
// this is the base urls with the random ports // this is the base urls with the random ports
String destination1 = server1.getUrl(); String destination1 = String.format(HTTP_URL, port1);
String destination2 = server2.getUrl(); String destination2 = String.format(HTTP_URL, port2);
// set up NiFi mock controller // set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class); controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -498,38 +515,19 @@ public class TestGetHTTP {
} finally { } finally {
// shutdown web services // shutdown web services
server1.shutdownServer(); server1.stop();
server2.shutdownServer(); server1.destroy();
server2.stop();
server2.destroy();
} }
} }
private static Map<String, String> getTruststoreProperties() { private void enableSslContextService(final SSLContext configuredSslContext) throws InitializationException {
final Map<String, String> props = new HashMap<>(); final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
props.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks"); Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_IDENTIFIER);
props.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword"); Mockito.when(sslContextService.createContext()).thenReturn(configuredSslContext);
props.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS"); controller.addControllerService(SSL_CONTEXT_IDENTIFIER, sslContextService);
return props; controller.enableControllerService(sslContextService);
controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_IDENTIFIER);
} }
private static Map<String, String> getKeystoreProperties() {
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/keystore.jks");
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "passwordpassword");
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
return properties;
}
private void useSSLContextService(final Map<String, String> sslProperties) {
final SSLContextService service = new StandardSSLContextService();
try {
controller.addControllerService("ssl-service", service, sslProperties);
controller.enableControllerService(service);
} catch (InitializationException ex) {
ex.printStackTrace();
Assert.fail("Could not create SSL Context Service");
}
controller.setProperty(org.apache.nifi.processors.standard.GetHTTP.SSL_CONTEXT_SERVICE, "ssl-service");
}
} }

View File

@ -21,28 +21,34 @@ import java.nio.charset.StandardCharsets;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSessionFactory; import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
/** /**
* Tests PutSyslog sending messages to ListenSyslog to simulate a syslog server forwarding * Tests PutSyslog sending messages to ListenSyslog to simulate a syslog server forwarding
* to ListenSyslog, or PutSyslog sending to a syslog server. * to ListenSyslog, or PutSyslog sending to a syslog server.
*/ */
public class ITListenAndPutSyslog { public class ITListenAndPutSyslog {
static final Logger LOGGER = LoggerFactory.getLogger(ITListenAndPutSyslog.class); private static final String SSL_SERVICE_IDENTIFIER = SSLContextService.class.getName();
// TODO: The NiFi SSL classes don't yet support TLSv1.3, so set the CS version explicitly private static SSLContext keyStoreSslContext;
private static final String TLS_PROTOCOL_VERSION = "TLSv1.2";
static final Logger LOGGER = LoggerFactory.getLogger(ITListenAndPutSyslog.class);
private ListenSyslog listenSyslog; private ListenSyslog listenSyslog;
private TestRunner listenSyslogRunner; private TestRunner listenSyslogRunner;
@ -50,6 +56,11 @@ public class ITListenAndPutSyslog {
private PutSyslog putSyslog; private PutSyslog putSyslog;
private TestRunner putSyslogRunner; private TestRunner putSyslogRunner;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
}
@Before @Before
public void setup() { public void setup() {
this.listenSyslog = new ListenSyslog(); this.listenSyslog = new ListenSyslog();
@ -86,10 +97,10 @@ public class ITListenAndPutSyslog {
@Test @Test
public void testTLS() throws InitializationException, IOException, InterruptedException { public void testTLS() throws InitializationException, IOException, InterruptedException {
configureSSLContextService(listenSyslogRunner); configureSSLContextService(listenSyslogRunner);
listenSyslogRunner.setProperty(ListenSyslog.SSL_CONTEXT_SERVICE, "ssl-context"); listenSyslogRunner.setProperty(ListenSyslog.SSL_CONTEXT_SERVICE, SSL_SERVICE_IDENTIFIER);
configureSSLContextService(putSyslogRunner); configureSSLContextService(putSyslogRunner);
putSyslogRunner.setProperty(PutSyslog.SSL_CONTEXT_SERVICE, "ssl-context"); putSyslogRunner.setProperty(PutSyslog.SSL_CONTEXT_SERVICE, SSL_SERVICE_IDENTIFIER);
run(ListenSyslog.TCP_VALUE.getValue(), 7, 7); run(ListenSyslog.TCP_VALUE.getValue(), 7, 7);
} }
@ -97,24 +108,19 @@ public class ITListenAndPutSyslog {
@Test @Test
public void testTLSListenerNoTLSPut() throws InitializationException, IOException, InterruptedException { public void testTLSListenerNoTLSPut() throws InitializationException, IOException, InterruptedException {
configureSSLContextService(listenSyslogRunner); configureSSLContextService(listenSyslogRunner);
listenSyslogRunner.setProperty(ListenSyslog.SSL_CONTEXT_SERVICE, "ssl-context"); listenSyslogRunner.setProperty(ListenSyslog.SSL_CONTEXT_SERVICE, SSL_SERVICE_IDENTIFIER);
// send 7 but expect 0 because sender didn't use TLS // send 7 but expect 0 because sender didn't use TLS
run(ListenSyslog.TCP_VALUE.getValue(), 7, 0); run(ListenSyslog.TCP_VALUE.getValue(), 7, 0);
} }
private SSLContextService configureSSLContextService(TestRunner runner) throws InitializationException { private void configureSSLContextService(TestRunner runner) throws InitializationException {
final SSLContextService sslContextService = new StandardSSLContextService(); final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
runner.addControllerService("ssl-context", sslContextService); Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_SERVICE_IDENTIFIER);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/truststore.jks"); Mockito.when(sslContextService.createContext()).thenReturn(keyStoreSslContext);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "passwordpassword");
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS"); runner.addControllerService(SSL_SERVICE_IDENTIFIER, sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/keystore.jks");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "passwordpassword");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, TLS_PROTOCOL_VERSION);
runner.enableControllerService(sslContextService); runner.enableControllerService(sslContextService);
return sslContextService;
} }
/** /**

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -30,133 +31,115 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.flowfile.attributes.CoreAttributes; import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.remote.io.socket.NetworkUtils;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.ClientAuth;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.FlowFileUnpackagerV3; import org.apache.nifi.util.FlowFileUnpackagerV3;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.TestServer; import org.apache.nifi.web.util.JettyServerUtils;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHandler;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import javax.net.ssl.SSLContext;
/**
* Integration Test for deprecated PostHTTP Processor
*/
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class TestPostHTTP { public class ITPostHTTP {
private TestServer server; private Server server;
private TestRunner runner; private TestRunner runner;
private CaptureServlet servlet; private CaptureServlet servlet;
private final String KEYSTORE_PATH = "src/test/resources/keystore.jks"; private static final String SSL_CONTEXT_IDENTIFIER = SSLContextService.class.getName();
private final String KEYSTORE_AND_TRUSTSTORE_PASSWORD = "passwordpassword";
private final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks"; private static final String TEST_MESSAGE = String.class.getName();
private final String JKS_TYPE = "JKS";
private static SSLContext keyStoreSslContext;
private static SSLContext trustStoreSslContext;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
private static String getUrl(final SSLContext sslContext, final int port) {
final String protocol = sslContext == null ? "http" : "https";
return String.format("%s://localhost:%d", protocol, port);
}
private void setup(final SSLContext serverSslContext, final ClientAuth clientAuth) throws Exception {
runner = TestRunners.newTestRunner(org.apache.nifi.processors.standard.PostHTTP.class);
final int port = NetworkUtils.availablePort();
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, getUrl(serverSslContext, port));
private void setup(final Map<String, String> sslProperties) throws Exception {
// set up web service // set up web service
ServletHandler handler = new ServletHandler(); ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(CaptureServlet.class, "/*"); handler.addServletWithMapping(CaptureServlet.class, "/*");
// create the service final Server configuredServer = JettyServerUtils.createServer(port, serverSslContext, clientAuth);
server = new TestServer(sslProperties); configuredServer.setHandler(handler);
server.addHandler(handler); final ServerConnector connector = new ServerConnector(configuredServer);
server.startServer(); connector.setPort(port);
JettyServerUtils.startServer(configuredServer);
servlet = (CaptureServlet) handler.getServlets()[0].getServlet(); servlet = (CaptureServlet) handler.getServlets()[0].getServlet();
runner = TestRunners.newTestRunner(org.apache.nifi.processors.standard.PostHTTP.class);
} }
@After @After
public void cleanup() throws Exception { public void cleanup() throws Exception {
if (server != null) { if (server != null) {
server.shutdownServer(); server.stop();
server.destroy();
server = null; server = null;
} }
} }
@Test @Test
public void testTruststoreSSLOnly() throws Exception { public void testUnauthenticatedTls() throws Exception {
final Map<String, String> sslProps = new HashMap<>(); setup(keyStoreSslContext, ClientAuth.NONE);
sslProps.put(TestServer.NEED_CLIENT_AUTH, "false"); enableSslContextService(trustStoreSslContext);
sslProps.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
sslProps.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
setup(sslProps);
final SSLContextService sslContextService = new StandardSSLContextService(); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.addControllerService("ssl-context", sslContextService); runner.enqueue(TEST_MESSAGE);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, JKS_TYPE);
runner.enableControllerService(sslContextService);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getSecureUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SSL_CONTEXT_SERVICE, "ssl-context");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
runner.enqueue("Hello world".getBytes());
runner.run(); runner.run();
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1); runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1);
} }
@Test @Test
public void testTwoWaySSL() throws Exception { public void testMutualTls() throws Exception {
final Map<String, String> sslProps = new HashMap<>(); setup(keyStoreSslContext, ClientAuth.REQUIRED);
sslProps.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH); enableSslContextService(keyStoreSslContext);
sslProps.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
sslProps.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
sslProps.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
sslProps.put(TestServer.NEED_CLIENT_AUTH, "true");
setup(sslProps);
final SSLContextService sslContextService = new StandardSSLContextService(); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.addControllerService("ssl-context", sslContextService); runner.enqueue(TEST_MESSAGE);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, JKS_TYPE);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, KEYSTORE_PATH);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, JKS_TYPE);
runner.enableControllerService(sslContextService);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getSecureUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SSL_CONTEXT_SERVICE, "ssl-context");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
runner.enqueue("Hello world".getBytes());
runner.run(); runner.run();
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1); runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1);
} }
@Test @Test
public void testOneWaySSLWhenServerConfiguredForTwoWay() throws Exception { public void testMutualTlsClientCertificateMissing() throws Exception {
final Map<String, String> sslProps = new HashMap<>(); setup(keyStoreSslContext, ClientAuth.REQUIRED);
sslProps.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH); enableSslContextService(trustStoreSslContext);
sslProps.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
sslProps.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
sslProps.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
sslProps.put(TestServer.NEED_CLIENT_AUTH, "true");
setup(sslProps);
final SSLContextService sslContextService = new StandardSSLContextService(); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.addControllerService("ssl-context", sslContextService); runner.enqueue(TEST_MESSAGE);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/truststore.jks");
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "passwordpassword");
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, JKS_TYPE);
runner.enableControllerService(sslContextService);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getSecureUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SSL_CONTEXT_SERVICE, "ssl-context");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
runner.enqueue("Hello world".getBytes());
runner.run(); runner.run();
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_FAILURE, 1); runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_FAILURE, 1);
@ -164,14 +147,14 @@ public class TestPostHTTP {
@Test @Test
public void testSendAsFlowFile() throws Exception { public void testSendAsFlowFile() throws Exception {
setup(null); setup( null, null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true");
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
attrs.put("abc", "cba"); attrs.put("abc", "cba");
runner.enqueue("Hello".getBytes(), attrs); runner.enqueue(TEST_MESSAGE, attrs);
attrs.put("abc", "abc"); attrs.put("abc", "abc");
attrs.put("filename", "xyz.txt"); attrs.put("filename", "xyz.txt");
runner.enqueue("World".getBytes(), attrs); runner.enqueue("World".getBytes(), attrs);
@ -188,7 +171,7 @@ public class TestPostHTTP {
// unpack first flowfile received // unpack first flowfile received
Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos); Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos);
byte[] contentReceived = baos.toByteArray(); byte[] contentReceived = baos.toByteArray();
assertEquals("Hello", new String(contentReceived)); assertEquals(TEST_MESSAGE, new String(contentReceived));
assertEquals("cba", receivedAttrs.get("abc")); assertEquals("cba", receivedAttrs.get("abc"));
assertTrue(unpacker.hasMoreData()); assertTrue(unpacker.hasMoreData());
@ -204,35 +187,16 @@ public class TestPostHTTP {
} }
@Test @Test
public void testSendAsFlowFileSecure() throws Exception { public void testMutualTlsSendFlowFile() throws Exception {
final Map<String, String> sslProps = new HashMap<>(); setup(keyStoreSslContext, ClientAuth.REQUIRED);
sslProps.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH); enableSslContextService(keyStoreSslContext);
sslProps.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
sslProps.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
sslProps.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
sslProps.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
sslProps.put(TestServer.NEED_CLIENT_AUTH, "true");
setup(sslProps);
final SSLContextService sslContextService = new StandardSSLContextService();
runner.addControllerService("ssl-context", sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, JKS_TYPE);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, KEYSTORE_PATH);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, JKS_TYPE);
runner.enableControllerService(sslContextService);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getSecureUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SSL_CONTEXT_SERVICE, "ssl-context");
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
attrs.put("abc", "cba"); attrs.put("abc", "cba");
runner.enqueue("Hello".getBytes(), attrs); runner.enqueue(TEST_MESSAGE, attrs);
attrs.put("abc", "abc"); attrs.put("abc", "abc");
attrs.put("filename", "xyz.txt"); attrs.put("filename", "xyz.txt");
runner.enqueue("World".getBytes(), attrs); runner.enqueue("World".getBytes(), attrs);
@ -249,7 +213,7 @@ public class TestPostHTTP {
// unpack first flowfile received // unpack first flowfile received
Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos); Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos);
byte[] contentReceived = baos.toByteArray(); byte[] contentReceived = baos.toByteArray();
assertEquals("Hello", new String(contentReceived)); assertEquals(TEST_MESSAGE, new String(contentReceived));
assertEquals("cba", receivedAttrs.get("abc")); assertEquals("cba", receivedAttrs.get("abc"));
assertTrue(unpacker.hasMoreData()); assertTrue(unpacker.hasMoreData());
@ -265,15 +229,14 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithMimeType() throws Exception { public void testSendWithMimeType() throws Exception {
setup(null); setup(null, null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
attrs.put(CoreAttributes.MIME_TYPE.key(), suppliedMimeType); attrs.put(CoreAttributes.MIME_TYPE.key(), suppliedMimeType);
runner.enqueue("Camping is great!".getBytes(), attrs); runner.enqueue("Camping is great!".getBytes(), attrs);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.run(1); runner.run(1);
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS); runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS);
@ -285,9 +248,9 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithEmptyELExpression() throws Exception { public void testSendWithEmptyELExpression() throws Exception {
setup(null); setup( null, null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
attrs.put(CoreAttributes.MIME_TYPE.key(), ""); attrs.put(CoreAttributes.MIME_TYPE.key(), "");
@ -302,12 +265,11 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithContentTypeProperty() throws Exception { public void testSendWithContentTypeProperty() throws Exception {
setup(null); setup(null, null);
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
attrs.put(CoreAttributes.MIME_TYPE.key(), "text/csv"); attrs.put(CoreAttributes.MIME_TYPE.key(), "text/csv");
@ -322,10 +284,9 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithCompressionServerAcceptGzip() throws Exception { public void testSendWithCompressionServerAcceptGzip() throws Exception {
setup(null); setup(null, null);
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.COMPRESSION_LEVEL, "9"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.COMPRESSION_LEVEL, "9");
@ -346,10 +307,9 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithoutCompressionServerAcceptGzip() throws Exception { public void testSendWithoutCompressionServerAcceptGzip() throws Exception {
setup(null); setup(null, null);
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.COMPRESSION_LEVEL, "0"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.COMPRESSION_LEVEL, "0");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
@ -371,11 +331,14 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithCompressionServerNotAcceptGzip() throws Exception { public void testSendWithCompressionServerNotAcceptGzip() throws Exception {
setup(null); setup(null, null);
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
// Specify a property to the URL to have the CaptureServlet specify it doesn't accept gzip // Specify a property to the URL to have the CaptureServlet specify it doesn't accept gzip
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl()+"?acceptGzip=false");
final String serverUrl = runner.getProcessContext().getProperty(PostHTTP.URL).getValue();
final String url = String.format("%s?acceptGzip=false", serverUrl);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, url);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.COMPRESSION_LEVEL, "9"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.COMPRESSION_LEVEL, "9");
@ -395,12 +358,11 @@ public class TestPostHTTP {
@Test @Test
public void testSendChunked() throws Exception { public void testSendChunked() throws Exception {
setup(null); setup(null, null);
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "true"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.TRUE.toString());
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
attrs.put(CoreAttributes.MIME_TYPE.key(), "text/plain"); attrs.put(CoreAttributes.MIME_TYPE.key(), "text/plain");
@ -421,10 +383,9 @@ public class TestPostHTTP {
@Test @Test
public void testSendWithThrottler() throws Exception { public void testSendWithThrottler() throws Exception {
setup(null); setup(null, null);
final String suppliedMimeType = "text/plain"; final String suppliedMimeType = "text/plain";
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CONTENT_TYPE, suppliedMimeType);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.MAX_DATA_RATE, "10kb"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.MAX_DATA_RATE, "10kb");
@ -448,31 +409,28 @@ public class TestPostHTTP {
@Test @Test
public void testDefaultUserAgent() throws Exception { public void testDefaultUserAgent() throws Exception {
setup(null); setup(null, null);
Assert.assertTrue(runner.getProcessContext().getProperty(org.apache.nifi.processors.standard.PostHTTP.USER_AGENT).getValue().startsWith("Apache-HttpClient")); Assert.assertTrue(runner.getProcessContext().getProperty(org.apache.nifi.processors.standard.PostHTTP.USER_AGENT).getValue().startsWith("Apache-HttpClient"));
} }
@Test @Test
public void testBatchWithMultipleUrls() throws Exception { public void testBatchWithMultipleUrls() throws Exception {
CaptureServlet servletA, servletB; setup(null,null);
TestServer serverA, serverB; final CaptureServlet servletA = servlet;
final String urlA = runner.getProcessContext().getProperty(org.apache.nifi.processors.standard.PostHTTP.URL).getValue();
{ // setup test servers // set up second web service
setup(null); ServletHandler handler = new ServletHandler();
servletA = servlet; handler.addServletWithMapping(CaptureServlet.class, "/*");
serverA = server;
// set up second web service // create the second service
ServletHandler handler = new ServletHandler(); final int portB = NetworkUtils.availablePort();
handler.addServletWithMapping(CaptureServlet.class, "/*"); final String urlB = getUrl(null, portB);
final Server serverB = JettyServerUtils.createServer(portB, null, null);
serverB.setHandler(handler);
JettyServerUtils.startServer(serverB);
// create the second service final CaptureServlet servletB = (CaptureServlet) handler.getServlets()[0].getServlet();
serverB = new TestServer(null);
serverB.addHandler(handler);
serverB.startServer();
servletB = (CaptureServlet) handler.getServlets()[0].getServlet();
}
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, "${url}"); // use EL for the URL runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, "${url}"); // use EL for the URL
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true"); runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true");
@ -486,8 +444,8 @@ public class TestPostHTTP {
// enqueue 9 FlowFiles // enqueue 9 FlowFiles
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
enqueueWithURL("a" + i, serverA.getUrl()); enqueueWithURL("a" + i, urlA);
enqueueWithURL("b" + i, serverB.getUrl()); enqueueWithURL("b" + i, urlB);
expectedContentA.add("a" + i); expectedContentA.add("a" + i);
expectedContentB.add("b" + i); expectedContentB.add("b" + i);
@ -503,10 +461,10 @@ public class TestPostHTTP {
MockFlowFile mff = successFiles.get(0); MockFlowFile mff = successFiles.get(0);
final String urlAttr = mff.getAttribute("url"); final String urlAttr = mff.getAttribute("url");
if (serverA.getUrl().equals(urlAttr)) { if (urlA.equals(urlAttr)) {
checkBatch(serverA, servletA, actualContentA, (actualContentA.isEmpty() ? 5 : 4)); checkBatch(urlA, servletA, actualContentA, (actualContentA.isEmpty() ? 5 : 4));
} else if (serverB.getUrl().equals(urlAttr)) { } else if (urlB.equals(urlAttr)) {
checkBatch(serverB, servletB, actualContentB, (actualContentB.isEmpty() ? 5 : 4)); checkBatch(urlB, servletB, actualContentB, (actualContentB.isEmpty() ? 5 : 4));
} else { } else {
fail("unexpected url attribute"); fail("unexpected url attribute");
} }
@ -526,7 +484,7 @@ public class TestPostHTTP {
runner.enqueue(data.getBytes(), attrs); runner.enqueue(data.getBytes(), attrs);
} }
private void checkBatch(TestServer server, CaptureServlet servlet, Set<String> actualContent, int expectedCount) throws Exception { private void checkBatch(final String url, CaptureServlet servlet, Set<String> actualContent, int expectedCount) throws Exception {
FlowFileUnpackagerV3 unpacker = new FlowFileUnpackagerV3(); FlowFileUnpackagerV3 unpacker = new FlowFileUnpackagerV3();
Set<String> actualFFContent = new HashSet<>(); Set<String> actualFFContent = new HashSet<>();
Set<String> actualPostContent = new HashSet<>(); Set<String> actualPostContent = new HashSet<>();
@ -538,7 +496,7 @@ public class TestPostHTTP {
final List<MockFlowFile> successFlowFiles = runner.getFlowFilesForRelationship(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS); final List<MockFlowFile> successFlowFiles = runner.getFlowFilesForRelationship(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS);
for (int i = 0; i < expectedCount; i++) { for (int i = 0; i < expectedCount; i++) {
MockFlowFile mff = successFlowFiles.get(i); MockFlowFile mff = successFlowFiles.get(i);
mff.assertAttributeEquals("url", server.getUrl()); mff.assertAttributeEquals("url", url);
String content = new String(mff.toByteArray()); String content = new String(mff.toByteArray());
actualFFContent.add(content); actualFFContent.add(content);
} }
@ -549,9 +507,10 @@ public class TestPostHTTP {
ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
for (int i = 0; i < expectedCount; i++) { for (int i = 0; i < expectedCount; i++) {
Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos); Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos);
String receivedContent = new String(baos.toByteArray()); final byte[] bytesReceived = baos.toByteArray();
String receivedContent = new String(bytesReceived, StandardCharsets.UTF_8);
actualPostContent.add(receivedContent); actualPostContent.add(receivedContent);
assertEquals(server.getUrl(), receivedAttrs.get("url")); assertEquals(url, receivedAttrs.get("url"));
assertTrue(unpacker.hasMoreData() || i == (expectedCount - 1)); assertTrue(unpacker.hasMoreData() || i == (expectedCount - 1));
baos.reset(); baos.reset();
} }
@ -560,8 +519,17 @@ public class TestPostHTTP {
// confirm that the transferred and POSTed content match // confirm that the transferred and POSTed content match
assertEquals(actualFFContent, actualPostContent); assertEquals(actualFFContent, actualPostContent);
// accumulate actial content // accumulate actual content
actualContent.addAll(actualPostContent); actualContent.addAll(actualPostContent);
runner.clearTransferState(); runner.clearTransferState();
} }
private void enableSslContextService(final SSLContext configuredSslContext) throws InitializationException {
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_IDENTIFIER);
Mockito.when(sslContextService.createContext()).thenReturn(configuredSslContext);
runner.addControllerService(SSL_CONTEXT_IDENTIFIER, sslContextService);
runner.enableControllerService(sslContextService);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_IDENTIFIER);
}
} }

View File

@ -25,9 +25,7 @@ import java.io.InputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -60,72 +58,36 @@ import org.apache.nifi.http.HttpContextMap;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processors.standard.util.HTTPUtils; import org.apache.nifi.processors.standard.util.HTTPUtils;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.SslContextFactory; import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.security.util.StandardTlsConfiguration; import org.apache.nifi.ssl.RestrictedSSLContextService;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
public class ITestHandleHttpRequest { public class ITestHandleHttpRequest {
private static final String KEYSTORE = "src/test/resources/keystore.jks";
private static final String KEYSTORE_PASSWORD = "passwordpassword";
private static final String KEYSTORE_TYPE = "JKS";
private static final String TRUSTSTORE = "src/test/resources/truststore.jks";
private static final String TRUSTSTORE_PASSWORD = "passwordpassword";
private static final String TRUSTSTORE_TYPE = "JKS";
private static final String CLIENT_KEYSTORE = "src/test/resources/client-keystore.p12";
private static final String CLIENT_KEYSTORE_TYPE = "PKCS12";
private HandleHttpRequest processor; private HandleHttpRequest processor;
private TlsConfiguration clientTlsConfiguration; private static SSLContext keyStoreSslContext;
private TlsConfiguration trustOnlyTlsConfiguration;
private static Map<String, String> getTruststoreProperties() { private static SSLContext trustStoreSslContext;
final Map<String, String> props = new HashMap<>();
props.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE);
props.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUSTSTORE_PASSWORD);
props.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), TRUSTSTORE_TYPE);
return props;
}
private static Map<String, String> getServerKeystoreProperties() { @BeforeClass
final Map<String, String> properties = new HashMap<>(); public static void configureServices() throws TlsException {
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE); keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_PASSWORD); trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), KEYSTORE_TYPE);
return properties;
}
private static SSLContext useSSLContextService(final TestRunner controller, final Map<String, String> sslProperties) {
final SSLContextService service = new StandardRestrictedSSLContextService();
try {
controller.addControllerService("ssl-service", service, sslProperties);
controller.enableControllerService(service);
} catch (InitializationException ex) {
ex.printStackTrace();
Assert.fail("Could not create SSL Context Service");
}
controller.setProperty(HandleHttpRequest.SSL_CONTEXT, "ssl-service");
return service.createContext();
} }
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
clientTlsConfiguration = new StandardTlsConfiguration(CLIENT_KEYSTORE, KEYSTORE_PASSWORD, null, CLIENT_KEYSTORE_TYPE,
TRUSTSTORE, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE, TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion());
trustOnlyTlsConfiguration = new StandardTlsConfiguration(null, null, null, null,
TRUSTSTORE, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE, TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion());
} }
@After @After
@ -136,7 +98,7 @@ public class ITestHandleHttpRequest {
} }
@Test(timeout = 30000) @Test(timeout = 30000)
public void testRequestAddedToService() throws InitializationException, IOException, InterruptedException { public void testRequestAddedToService() throws InitializationException {
CountDownLatch serverReady = new CountDownLatch(1); CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1); CountDownLatch requestSent = new CountDownLatch(1);
@ -191,7 +153,7 @@ public class ITestHandleHttpRequest {
} }
@Test(timeout = 30000) @Test(timeout = 30000)
public void testMultipartFormDataRequest() throws InitializationException, IOException, InterruptedException { public void testMultipartFormDataRequest() throws InitializationException {
CountDownLatch serverReady = new CountDownLatch(1); CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1); CountDownLatch requestSent = new CountDownLatch(1);
@ -304,8 +266,7 @@ public class ITestHandleHttpRequest {
} }
@Test(timeout = 30000) @Test(timeout = 30000)
public void testMultipartFormDataRequestCaptureFormAttributes() throws InitializationException, IOException, public void testMultipartFormDataRequestCaptureFormAttributes() throws InitializationException {
InterruptedException {
CountDownLatch serverReady = new CountDownLatch(1); CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1); CountDownLatch requestSent = new CountDownLatch(1);
@ -374,7 +335,7 @@ public class ITestHandleHttpRequest {
} }
@Test(timeout = 30000) @Test(timeout = 30000)
public void testMultipartFormDataRequestFailToRegisterContext() throws InitializationException, IOException, InterruptedException { public void testMultipartFormDataRequestFailToRegisterContext() throws InitializationException, InterruptedException {
CountDownLatch serverReady = new CountDownLatch(1); CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1); CountDownLatch requestSent = new CountDownLatch(1);
CountDownLatch resultReady = new CountDownLatch(1); CountDownLatch resultReady = new CountDownLatch(1);
@ -427,7 +388,7 @@ public class ITestHandleHttpRequest {
} }
@Override @Override
public void onResponse(Call call, Response response) throws IOException { public void onResponse(Call call, Response response) {
responseCode.set(response.code()); responseCode.set(response.code());
resultReady.countDown(); resultReady.countDown();
} }
@ -473,7 +434,7 @@ public class ITestHandleHttpRequest {
@Test(timeout = 30000) @Test(timeout = 30000)
public void testFailToRegister() throws InitializationException, IOException, InterruptedException { public void testFailToRegister() throws InitializationException, InterruptedException {
CountDownLatch serverReady = new CountDownLatch(1); CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1); CountDownLatch requestSent = new CountDownLatch(1);
CountDownLatch resultReady = new CountDownLatch(1); CountDownLatch resultReady = new CountDownLatch(1);
@ -649,10 +610,13 @@ public class ITestHandleHttpRequest {
runner.enableControllerService(contextMap); runner.enableControllerService(contextMap);
runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, "http-context-map"); runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, "http-context-map");
final Map<String, String> sslProperties = getServerKeystoreProperties(); final RestrictedSSLContextService sslContextService = Mockito.mock(RestrictedSSLContextService.class);
sslProperties.putAll(getTruststoreProperties()); final String serviceIdentifier = RestrictedSSLContextService.class.getName();
sslProperties.put(StandardSSLContextService.SSL_ALGORITHM.getName(), TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion()); Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
useSSLContextService(runner, sslProperties); Mockito.when(sslContextService.createContext()).thenReturn(keyStoreSslContext);
runner.addControllerService(serviceIdentifier, sslContextService);
runner.enableControllerService(sslContextService);
runner.setProperty(HandleHttpRequest.SSL_CONTEXT, serviceIdentifier);
final Thread httpThread = new Thread(new Runnable() { final Thread httpThread = new Thread(new Runnable() {
@Override @Override
@ -667,10 +631,10 @@ public class ITestHandleHttpRequest {
SSLContext clientSslContext; SSLContext clientSslContext;
if (twoWaySsl) { if (twoWaySsl) {
// Use a client certificate, do not reuse the server's keystore // Use a client certificate, do not reuse the server's keystore
clientSslContext = SslContextFactory.createSslContext(clientTlsConfiguration); clientSslContext = keyStoreSslContext;
} else { } else {
// With one-way SSL, the client still needs a truststore // With one-way SSL, the client still needs a truststore
clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration); clientSslContext = trustStoreSslContext;
} }
connection.setSSLSocketFactory(clientSslContext.getSocketFactory()); connection.setSSLSocketFactory(clientSslContext.getSocketFactory());
connection.setDoOutput(false); connection.setDoOutput(false);

View File

@ -27,12 +27,15 @@ import java.io.PrintWriter;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import javax.net.ssl.SSLContext;
import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.processors.standard.util.TestInvokeHttpCommon; import org.apache.nifi.processors.standard.util.TestInvokeHttpCommon;
import org.apache.nifi.ssl.StandardSSLContextService; import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
@ -41,12 +44,29 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class TestInvokeHTTP extends TestInvokeHttpCommon { public class TestInvokeHTTP extends TestInvokeHttpCommon {
private static final Logger logger = LoggerFactory.getLogger(TestInvokeHTTP.class); private static final Logger logger = LoggerFactory.getLogger(TestInvokeHTTP.class);
private static final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks";
private static final String TRUSTSTORE_PASSWORD = "passwordpassword";
private static final KeystoreType TRUSTSTORE_TYPE = KeystoreType.JKS;
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
private static final String KEYSTORE_PASSWORD = "passwordpassword";
private static final KeystoreType KEYSTORE_TYPE = KeystoreType.JKS;
private static final TlsConfiguration TLS_CONFIGURATION = new StandardTlsConfiguration(
KEYSTORE_PATH,
KEYSTORE_PASSWORD,
KEYSTORE_TYPE,
TRUSTSTORE_PATH,
TRUSTSTORE_PASSWORD,
TRUSTSTORE_TYPE
);
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception { public static void beforeClass() throws Exception {
configureServer(null, null); configureServer(null, null);
@ -59,20 +79,18 @@ public class TestInvokeHTTP extends TestInvokeHttpCommon {
@Test @Test
public void testSslSetHttpRequest() throws Exception { public void testSslSetHttpRequest() throws Exception {
final String serviceIdentifier = SSLContextService.class.getName();
final Map<String, String> sslProperties = new HashMap<>(); final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
sslProperties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/keystore.jks"); Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
sslProperties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "passwordpassword"); final SSLContext sslContext = SslContextFactory.createSslContext(TLS_CONFIGURATION);
sslProperties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS"); Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
sslProperties.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks"); Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(TLS_CONFIGURATION);
sslProperties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword");
sslProperties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
runner = TestRunners.newTestRunner(InvokeHTTP.class); runner = TestRunners.newTestRunner(InvokeHTTP.class);
final StandardSSLContextService sslService = new StandardSSLContextService();
runner.addControllerService("ssl-context", sslService, sslProperties); runner.addControllerService(serviceIdentifier, sslContextService);
runner.enableControllerService(sslService); runner.enableControllerService(sslContextService);
runner.setProperty(InvokeHTTP.PROP_SSL_CONTEXT_SERVICE, "ssl-context"); runner.setProperty(InvokeHTTP.PROP_SSL_CONTEXT_SERVICE, serviceIdentifier);
addHandler(new GetOrHeadHandler()); addHandler(new GetOrHeadHandler());

View File

@ -63,7 +63,6 @@ import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException; import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.RestrictedSSLContextService; import org.apache.nifi.ssl.RestrictedSSLContextService;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
@ -314,16 +313,6 @@ public class TestListenHTTP {
testPOSTRequestsReceived(HttpServletResponse.SC_NO_CONTENT, true, true); testPOSTRequestsReceived(HttpServletResponse.SC_NO_CONTENT, true, true);
} }
@Test
public void testSecureInvalidSSLConfiguration() throws Exception {
SSLContextService sslContextService = configureInvalidProcessorSslContextService();
runner.enableControllerService(sslContextService);
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
runner.assertNotValid();
}
@Test @Test
public void testSecureServerSupportsCurrentTlsProtocolVersion() throws Exception { public void testSecureServerSupportsCurrentTlsProtocolVersion() throws Exception {
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION); configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
@ -539,22 +528,8 @@ public class TestListenHTTP {
runner.enableControllerService(sslContextService); runner.enableControllerService(sslContextService);
} }
private SSLContextService configureInvalidProcessorSslContextService() throws InitializationException {
final SSLContextService sslContextService = new StandardSSLContextService();
runner.addControllerService(SSL_CONTEXT_SERVICE_IDENTIFIER, sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, KeystoreType.JKS.getType());
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, KEYSTORE);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, KEYSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, KeystoreType.JKS.getType());
runner.setProperty(ListenHTTP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_SERVICE_IDENTIFIER); @Test
return sslContextService;
}
@Test(/*timeout=10000*/)
public void testMultipartFormDataRequest() throws Exception { public void testMultipartFormDataRequest() throws Exception {
runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort)); runner.setProperty(ListenHTTP.PORT, Integer.toString(availablePort));

View File

@ -38,11 +38,12 @@ import org.apache.nifi.processors.standard.relp.response.RELPResponse;
import org.apache.nifi.provenance.ProvenanceEventRecord; import org.apache.nifi.provenance.ProvenanceEventRecord;
import org.apache.nifi.provenance.ProvenanceEventType; import org.apache.nifi.provenance.ProvenanceEventType;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -50,9 +51,6 @@ import org.mockito.Mockito;
public class TestListenRELP { public class TestListenRELP {
// TODO: The NiFi SSL classes don't yet support TLSv1.3, so set the CS version explicitly
private static final String TLS_PROTOCOL_VERSION = "TLSv1.2";
public static final String OPEN_FRAME_DATA = "relp_version=0\nrelp_software=librelp,1.2.7,http://librelp.adiscon.com\ncommands=syslog"; public static final String OPEN_FRAME_DATA = "relp_version=0\nrelp_software=librelp,1.2.7,http://librelp.adiscon.com\ncommands=syslog";
public static final String SYSLOG_FRAME_DATA = "this is a syslog message here"; public static final String SYSLOG_FRAME_DATA = "this is a syslog message here";
@ -151,19 +149,16 @@ public class TestListenRELP {
} }
@Test @Test
public void testTLS() throws InitializationException, IOException, InterruptedException { public void testMutualTls() throws IOException, InterruptedException, TlsException, InitializationException {
final SSLContextService sslContextService = new StandardSSLContextService(); final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
runner.addControllerService("ssl-context", sslContextService); final String serviceIdentifier = SSLContextService.class.getName();
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, TLS_PROTOCOL_VERSION); Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/truststore.jks"); final SSLContext sslContext = SslContextUtils.createKeyStoreSslContext();
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "passwordpassword"); Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS"); runner.addControllerService(serviceIdentifier, sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/keystore.jks");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "passwordpassword");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
runner.enableControllerService(sslContextService); runner.enableControllerService(sslContextService);
runner.setProperty(ListenRELP.SSL_CONTEXT_SERVICE, "ssl-context"); runner.setProperty(ListenRELP.SSL_CONTEXT_SERVICE, serviceIdentifier);
final List<RELPFrame> frames = new ArrayList<>(); final List<RELPFrame> frames = new ArrayList<>();
frames.add(OPEN_FRAME); frames.add(OPEN_FRAME);
@ -174,8 +169,7 @@ public class TestListenRELP {
frames.add(SYSLOG_FRAME); frames.add(SYSLOG_FRAME);
frames.add(CLOSE_FRAME); frames.add(CLOSE_FRAME);
// three syslog frames should be transferred and three responses should be sent run(frames, 5, 5, sslContext);
run(frames, 5, 5, sslContextService);
} }
@Test @Test
@ -210,7 +204,7 @@ public class TestListenRELP {
} }
protected void run(final List<RELPFrame> frames, final int expectedTransferred, final int expectedResponses, final SSLContextService sslContextService) protected void run(final List<RELPFrame> frames, final int expectedTransferred, final int expectedResponses, final SSLContext sslContext)
throws IOException, InterruptedException { throws IOException, InterruptedException {
Socket socket = null; Socket socket = null;
@ -224,11 +218,10 @@ public class TestListenRELP {
final int realPort = proc.getDispatcherPort(); final int realPort = proc.getDispatcherPort();
// create either a regular socket or ssl socket based on context being passed in // create either a regular socket or ssl socket based on context being passed in
if (sslContextService != null) { if (sslContext == null) {
final SSLContext sslContext = sslContextService.createContext();
socket = sslContext.getSocketFactory().createSocket("localhost", realPort);
} else {
socket = new Socket("localhost", realPort); socket = new Socket("localhost", realPort);
} else {
socket = sslContext.getSocketFactory().createSocket("localhost", realPort);
} }
Thread.sleep(100); Thread.sleep(100);

View File

@ -21,56 +21,51 @@ import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSessionFactory; import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.ClientAuth; import org.apache.nifi.security.util.ClientAuth;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException; import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.RestrictedSSLContextService;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
public class TestListenTCP { public class TestListenTCP {
private static final long RESPONSE_TIMEOUT = 10000;
private static final String KEYSTORE = "src/test/resources/keystore.jks"; private static final String SSL_CONTEXT_IDENTIFIER = SSLContextService.class.getName();
private static final String KEYSTORE_PASSWORD = "passwordpassword";
private static final String KEYSTORE_TYPE = "JKS";
private static final String TRUSTSTORE = "src/test/resources/truststore.jks";
private static final String TRUSTSTORE_PASSWORD = "passwordpassword";
private static final String TRUSTSTORE_TYPE = "JKS";
private static final String CLIENT_KEYSTORE = "src/test/resources/client-keystore.p12";
private static final String CLIENT_KEYSTORE_TYPE = "PKCS12";
// TODO: The NiFi SSL classes don't yet support TLSv1.3, so set the CS version explicitly private static SSLContext keyStoreSslContext;
private static final String TLS_PROTOCOL_VERSION = "TLSv1.2";
private static TlsConfiguration clientTlsConfiguration; private static SSLContext trustStoreSslContext;
private static TlsConfiguration trustOnlyTlsConfiguration;
private ListenTCP proc; private ListenTCP proc;
private TestRunner runner; private TestRunner runner;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
@Before @Before
public void setup() { public void setup() {
proc = new ListenTCP(); proc = new ListenTCP();
runner = TestRunners.newTestRunner(proc); runner = TestRunners.newTestRunner(proc);
runner.setProperty(ListenTCP.PORT, "0"); runner.setProperty(ListenTCP.PORT, "0");
clientTlsConfiguration = new StandardTlsConfiguration(CLIENT_KEYSTORE, KEYSTORE_PASSWORD, null, CLIENT_KEYSTORE_TYPE,
TRUSTSTORE, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE, TLS_PROTOCOL_VERSION);
trustOnlyTlsConfiguration = new StandardTlsConfiguration(null, null, null, null,
TRUSTSTORE, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE, TLS_PROTOCOL_VERSION);
} }
@Test @Test
@ -78,7 +73,7 @@ public class TestListenTCP {
runner.setProperty(ListenTCP.PORT, "1"); runner.setProperty(ListenTCP.PORT, "1");
runner.assertValid(); runner.assertValid();
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
runner.setProperty(ListenTCP.CLIENT_AUTH, ""); runner.setProperty(ListenTCP.CLIENT_AUTH, "");
runner.assertNotValid(); runner.assertNotValid();
@ -126,11 +121,11 @@ public class TestListenTCP {
} }
@Test @Test
public void testTLSClientAuthRequiredAndClientCertProvided() throws InitializationException, IOException, InterruptedException, public void testTLSClientAuthRequiredAndClientCertProvided() throws IOException, InterruptedException,
TlsException { InitializationException {
runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.REQUIRED.name()); runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.REQUIRED.name());
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
final List<String> messages = new ArrayList<>(); final List<String> messages = new ArrayList<>();
messages.add("This is message 1\n"); messages.add("This is message 1\n");
@ -140,9 +135,7 @@ public class TestListenTCP {
messages.add("This is message 5\n"); messages.add("This is message 5\n");
// Make an SSLContext with a key and trust store to send the test messages // Make an SSLContext with a key and trust store to send the test messages
final SSLContext clientSslContext = SslContextFactory.createSslContext(clientTlsConfiguration); runTCP(messages, messages.size(), keyStoreSslContext);
runTCP(messages, messages.size(), clientSslContext);
List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS); List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS);
for (int i = 0; i < mockFlowFiles.size(); i++) { for (int i = 0; i < mockFlowFiles.size(); i++) {
@ -151,10 +144,9 @@ public class TestListenTCP {
} }
@Test @Test
public void testTLSClientAuthRequiredAndClientCertNotProvided() throws InitializationException, TlsException { public void testTLSClientAuthRequiredAndClientCertNotProvided() throws InitializationException {
runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.REQUIRED.name()); runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.REQUIRED.name());
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
final List<String> messages = new ArrayList<>(); final List<String> messages = new ArrayList<>();
messages.add("This is message 1\n"); messages.add("This is message 1\n");
@ -164,21 +156,15 @@ public class TestListenTCP {
messages.add("This is message 5\n"); messages.add("This is message 5\n");
// Make an SSLContext that only has the trust store, this should not work since the processor has client auth REQUIRED // Make an SSLContext that only has the trust store, this should not work since the processor has client auth REQUIRED
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration); Assert.assertThrows(SSLException.class, () ->
runTCP(messages, messages.size(), trustStoreSslContext)
try { );
runTCP(messages, messages.size(), clientSslContext);
Assert.fail("Should have thrown exception");
} catch (Exception e) {
}
} }
@Test @Test
public void testTLSClientAuthNoneAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException, TlsException { public void testTLSClientAuthNoneAndClientCertNotProvided() throws IOException, InterruptedException, InitializationException {
runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.NONE.name()); runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.NONE.name());
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
final List<String> messages = new ArrayList<>(); final List<String> messages = new ArrayList<>();
messages.add("This is message 1\n"); messages.add("This is message 1\n");
@ -188,9 +174,7 @@ public class TestListenTCP {
messages.add("This is message 5\n"); messages.add("This is message 5\n");
// Make an SSLContext that only has the trust store, this should not work since the processor has client auth REQUIRED // Make an SSLContext that only has the trust store, this should not work since the processor has client auth REQUIRED
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration); runTCP(messages, messages.size(), trustStoreSslContext);
runTCP(messages, messages.size(), clientSslContext);
List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS); List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS);
for (int i = 0; i < mockFlowFiles.size(); i++) { for (int i = 0; i < mockFlowFiles.size(); i++) {
@ -212,10 +196,11 @@ public class TestListenTCP {
final int realPort = proc.getDispatcherPort(); final int realPort = proc.getDispatcherPort();
// create either a regular socket or ssl socket based on context being passed in // create either a regular socket or ssl socket based on context being passed in
if (sslContext != null) { if (sslContext == null) {
socket = sslContext.getSocketFactory().createSocket("localhost", realPort);
} else {
socket = new Socket("localhost", realPort); socket = new Socket("localhost", realPort);
} else {
final SocketFactory socketFactory = sslContext.getSocketFactory();
socket = socketFactory.createSocket("localhost", realPort);
} }
Thread.sleep(100); Thread.sleep(100);
@ -226,14 +211,12 @@ public class TestListenTCP {
} }
socket.getOutputStream().flush(); socket.getOutputStream().flush();
long responseTimeout = 10000;
// this first loop waits until the internal queue of the processor has the expected // this first loop waits until the internal queue of the processor has the expected
// number of messages ready before proceeding, we want to guarantee they are all there // number of messages ready before proceeding, we want to guarantee they are all there
// before onTrigger gets a chance to run // before onTrigger gets a chance to run
long startTimeQueueSizeCheck = System.currentTimeMillis(); long startTimeQueueSizeCheck = System.currentTimeMillis();
while (proc.getQueueSize() < messages.size() while (proc.getQueueSize() < messages.size()
&& (System.currentTimeMillis() - startTimeQueueSizeCheck < responseTimeout)) { && (System.currentTimeMillis() - startTimeQueueSizeCheck < RESPONSE_TIMEOUT)) {
Thread.sleep(100); Thread.sleep(100);
} }
@ -243,7 +226,7 @@ public class TestListenTCP {
// call onTrigger until we processed all the frames, or a certain amount of time passes // call onTrigger until we processed all the frames, or a certain amount of time passes
int numTransferred = 0; int numTransferred = 0;
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
while (numTransferred < expectedTransferred && (System.currentTimeMillis() - startTime < responseTimeout)) { while (numTransferred < expectedTransferred && (System.currentTimeMillis() - startTime < RESPONSE_TIMEOUT)) {
proc.onTrigger(context, processSessionFactory); proc.onTrigger(context, processSessionFactory);
numTransferred = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS).size(); numTransferred = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS).size();
Thread.sleep(100); Thread.sleep(100);
@ -258,20 +241,12 @@ public class TestListenTCP {
} }
} }
private SSLContextService configureProcessorSslContextService() throws InitializationException { private void enableSslContextService(final SSLContext sslContext) throws InitializationException {
final SSLContextService sslContextService = new StandardRestrictedSSLContextService(); final RestrictedSSLContextService sslContextService = Mockito.mock(RestrictedSSLContextService.class);
runner.addControllerService("ssl-context", sslContextService); Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_IDENTIFIER);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, KEYSTORE); Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, KEYSTORE_PASSWORD); runner.addControllerService(SSL_CONTEXT_IDENTIFIER, sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, KEYSTORE_TYPE);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE);
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, TLS_PROTOCOL_VERSION);
runner.enableControllerService(sslContextService); runner.enableControllerService(sslContextService);
runner.setProperty(ListenTCP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_IDENTIFIER);
runner.setProperty(ListenTCP.SSL_CONTEXT_SERVICE, "ssl-context");
return sslContextService;
} }
} }

View File

@ -31,43 +31,27 @@ import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.schema.access.SchemaAccessUtils; import org.apache.nifi.schema.access.SchemaAccessUtils;
import org.apache.nifi.security.util.ClientAuth; import org.apache.nifi.security.util.ClientAuth;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException; import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.serialization.RecordReaderFactory; import org.apache.nifi.serialization.RecordReaderFactory;
import org.apache.nifi.serialization.RecordSetWriterFactory; import org.apache.nifi.serialization.RecordSetWriterFactory;
import org.apache.nifi.serialization.record.MockRecordWriter; import org.apache.nifi.serialization.record.MockRecordWriter;
import org.apache.nifi.ssl.RestrictedSSLContextService;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class TestListenTCPRecord { public class TestListenTCPRecord {
static final Logger LOGGER = LoggerFactory.getLogger(TestListenTCPRecord.class); static final Logger LOGGER = LoggerFactory.getLogger(TestListenTCPRecord.class);
private static final String KEYSTORE = "src/test/resources/keystore.jks";
private static final String KEYSTORE_PASSWORD = "passwordpassword";
private static final String KEYSTORE_TYPE = "JKS";
private static final String TRUSTSTORE = "src/test/resources/truststore.jks";
private static final String TRUSTSTORE_PASSWORD = "passwordpassword";
private static final String TRUSTSTORE_TYPE = "JKS";
private static final String CLIENT_KEYSTORE = "src/test/resources/client-keystore.p12";
private static final String CLIENT_KEYSTORE_TYPE = "PKCS12";
// TODO: The NiFi SSL classes don't yet support TLSv1.3, so set the CS version explicitly
private static final String TLS_PROTOCOL_VERSION = "TLSv1.2";
private static TlsConfiguration clientTlsConfiguration;
private static TlsConfiguration trustOnlyTlsConfiguration;
static final String SCHEMA_TEXT = "{\n" + static final String SCHEMA_TEXT = "{\n" +
" \"name\": \"syslogRecord\",\n" + " \"name\": \"syslogRecord\",\n" +
" \"namespace\": \"nifi\",\n" + " \"namespace\": \"nifi\",\n" +
@ -91,9 +75,21 @@ public class TestListenTCPRecord {
DATA = Collections.unmodifiableList(data); DATA = Collections.unmodifiableList(data);
} }
private static final String SSL_CONTEXT_IDENTIFIER = SSLContextService.class.getName();
private static SSLContext keyStoreSslContext;
private static SSLContext trustStoreSslContext;
private ListenTCPRecord proc; private ListenTCPRecord proc;
private TestRunner runner; private TestRunner runner;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
@Before @Before
public void setup() throws InitializationException { public void setup() throws InitializationException {
proc = new ListenTCPRecord(); proc = new ListenTCPRecord();
@ -115,10 +111,6 @@ public class TestListenTCPRecord {
runner.setProperty(ListenTCPRecord.RECORD_READER, readerId); runner.setProperty(ListenTCPRecord.RECORD_READER, readerId);
runner.setProperty(ListenTCPRecord.RECORD_WRITER, writerId); runner.setProperty(ListenTCPRecord.RECORD_WRITER, writerId);
clientTlsConfiguration = new StandardTlsConfiguration(CLIENT_KEYSTORE, KEYSTORE_PASSWORD, null, CLIENT_KEYSTORE_TYPE,
TRUSTSTORE, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE, TLS_PROTOCOL_VERSION);
trustOnlyTlsConfiguration = new StandardTlsConfiguration(null, null, null, null,
TRUSTSTORE, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE, TLS_PROTOCOL_VERSION);
} }
@Test @Test
@ -126,7 +118,7 @@ public class TestListenTCPRecord {
runner.setProperty(ListenTCPRecord.PORT, "1"); runner.setProperty(ListenTCPRecord.PORT, "1");
runner.assertValid(); runner.assertValid();
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ""); runner.setProperty(ListenTCPRecord.CLIENT_AUTH, "");
runner.assertNotValid(); runner.assertNotValid();
@ -171,15 +163,11 @@ public class TestListenTCPRecord {
} }
@Test @Test
public void testTLSClientAuthRequiredAndClientCertProvided() throws InitializationException, IOException, InterruptedException, TlsException { public void testTLSClientAuthRequiredAndClientCertProvided() throws InitializationException, IOException, InterruptedException {
runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ClientAuth.REQUIRED.name()); runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ClientAuth.REQUIRED.name());
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
// Make an SSLContext with a key and trust store to send the test messages runTCP(DATA, 1, keyStoreSslContext);
final SSLContext clientSslContext = SslContextFactory.createSslContext(clientTlsConfiguration);
runTCP(DATA, 1, clientSslContext);
final List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCPRecord.REL_SUCCESS); final List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCPRecord.REL_SUCCESS);
Assert.assertEquals(1, mockFlowFiles.size()); Assert.assertEquals(1, mockFlowFiles.size());
@ -192,28 +180,21 @@ public class TestListenTCPRecord {
} }
@Test @Test
public void testTLSClientAuthRequiredAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException, TlsException { public void testTLSClientAuthRequiredAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException {
runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ClientAuth.REQUIRED.name()); runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ClientAuth.REQUIRED.name());
runner.setProperty(ListenTCPRecord.READ_TIMEOUT, "5 seconds"); runner.setProperty(ListenTCPRecord.READ_TIMEOUT, "5 seconds");
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
// Make an SSLContext that only has the trust store, this should not work since the processor has client auth REQUIRED runTCP(DATA, 0, trustStoreSslContext);
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
runTCP(DATA, 0, clientSslContext);
} }
@Test @Test
public void testTLSClientAuthNoneAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException, TlsException { public void testTLSClientAuthNoneAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException {
runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ClientAuth.NONE.name()); runner.setProperty(ListenTCPRecord.CLIENT_AUTH, ClientAuth.NONE.name());
configureProcessorSslContextService(); enableSslContextService(keyStoreSslContext);
// Make an SSLContext that only has the trust store, this should work since the processor has client auth NONE runTCP(DATA, 1, trustStoreSslContext);
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
runTCP(DATA, 1, clientSslContext);
final List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCPRecord.REL_SUCCESS); final List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCPRecord.REL_SUCCESS);
Assert.assertEquals(1, mockFlowFiles.size()); Assert.assertEquals(1, mockFlowFiles.size());
@ -262,21 +243,6 @@ public class TestListenTCPRecord {
} }
} }
private SSLContextService configureProcessorSslContextService() throws InitializationException {
final SSLContextService sslContextService = new StandardRestrictedSSLContextService();
runner.addControllerService("ssl-context", sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/truststore.jks");
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "passwordpassword");
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/keystore.jks");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "passwordpassword");
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
runner.enableControllerService(sslContextService);
runner.setProperty(ListenTCPRecord.SSL_CONTEXT_SERVICE, "ssl-context");
return sslContextService;
}
private static class SocketSender implements Runnable, Closeable { private static class SocketSender implements Runnable, Closeable {
private final int port; private final int port;
@ -324,4 +290,12 @@ public class TestListenTCPRecord {
} }
} }
private void enableSslContextService(final SSLContext sslContext) throws InitializationException {
final RestrictedSSLContextService sslContextService = Mockito.mock(RestrictedSSLContextService.class);
Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_IDENTIFIER);
Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
runner.addControllerService(SSL_CONTEXT_IDENTIFIER, sslContextService);
runner.enableControllerService(sslContextService);
runner.setProperty(ListenTCPRecord.SSL_CONTEXT_SERVICE, SSL_CONTEXT_IDENTIFIER);
}
} }

View File

@ -0,0 +1,75 @@
/*
* 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.web.util.ssl;
import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import javax.net.ssl.SSLContext;
public class SslContextUtils {
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
private static final String KEYSTORE_AND_TRUSTSTORE_PASSWORD = "passwordpassword";
private static final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks";
private static final TlsConfiguration KEYSTORE_TLS_CONFIGURATION = new StandardTlsConfiguration(
KEYSTORE_PATH,
KEYSTORE_AND_TRUSTSTORE_PASSWORD,
KEYSTORE_AND_TRUSTSTORE_PASSWORD,
KeystoreType.JKS,
TRUSTSTORE_PATH,
KEYSTORE_AND_TRUSTSTORE_PASSWORD,
KeystoreType.JKS,
TlsConfiguration.TLS_1_2_PROTOCOL
);
private static final TlsConfiguration TRUSTSTORE_TLS_CONFIGURATION = new StandardTlsConfiguration(
null,
null,
null,
null,
TRUSTSTORE_PATH,
KEYSTORE_AND_TRUSTSTORE_PASSWORD,
KeystoreType.JKS,
TlsConfiguration.TLS_1_2_PROTOCOL
);
/**
* Create SSLContext with Key Store and Trust Store configured
*
* @return SSLContext configured with Key Store and Trust Store
* @throws TlsException Thrown on SslContextFactory.createSslContext()
*/
public static SSLContext createKeyStoreSslContext() throws TlsException {
return SslContextFactory.createSslContext(KEYSTORE_TLS_CONFIGURATION);
}
/**
* Create SSLContext with Trust Store configured
*
* @return SSLContext configured with Trust Store
* @throws TlsException Thrown on SslContextFactory.createSslContext()
*/
public static SSLContext createTrustStoreSslContext() throws TlsException {
return SslContextFactory.createSslContext(TRUSTSTORE_TLS_CONFIGURATION);
}
}