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_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 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_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};

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.PlainConnectionSocketFactory;
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.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
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.DynamicProperty;
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.util.StandardValidators;
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.util.StopWatch;
import org.apache.nifi.util.Tuple;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
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.Collection;
import java.util.Collections;
@ -326,32 +314,6 @@ public class GetHTTP extends AbstractSessionFactoryProcessor {
.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
public void onTrigger(final ProcessContext context, final ProcessSessionFactory sessionFactory) throws ProcessException {
final ComponentLog logger = getLogger();
@ -384,7 +346,7 @@ public class GetHTTP extends AbstractSessionFactoryProcessor {
} else {
final SSLContext sslContext;
try {
sslContext = createSSLContext(sslContextService);
sslContext = sslContextService.createContext();
} catch (final Exception 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.collect.ImmutableList;
import javax.net.ssl.SSLContext;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.MultipartConfigElement;
@ -544,25 +545,14 @@ public class HandleHttpRequest extends AbstractProcessor {
return containerQueue.size();
}
private SslContextFactory createSslFactory(final SSLContextService sslService, final boolean needClientAuth, final boolean wantClientAuth) {
final SslContextFactory sslFactory = new SslContextFactory.Server();
private SslContextFactory createSslFactory(final SSLContextService sslContextService, final boolean needClientAuth, final boolean wantClientAuth) {
final SslContextFactory.Server sslFactory = new SslContextFactory.Server();
sslFactory.setNeedClientAuth(needClientAuth);
sslFactory.setWantClientAuth(wantClientAuth);
sslFactory.setProtocol(sslService.getSslAlgorithm());
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());
}
final SSLContext sslContext = sslContextService.createContext();
sslFactory.setSslContext(sslContext);
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_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.")})
@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 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/\" "
+ "(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 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.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -30,11 +28,6 @@ import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
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.CertificateException;
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.PlainConnectionSocketFactory;
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.EntityTemplate;
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.protocol.HttpContext;
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.VersionInfo;
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.processors.standard.util.HTTPUtils;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.stream.io.GZIPOutputStream;
import org.apache.nifi.stream.io.LeakyBucketStreamThrottler;
@ -389,8 +378,7 @@ public class PostHTTP extends AbstractProcessor {
} else {
final SSLContext sslContext;
try {
sslContext = createSSLContext(sslContextService);
getLogger().info("PostHTTP supports protocol: " + sslContext.getProtocol());
sslContext = sslContextService.createContext();
} catch (final Exception e) {
throw new ProcessException(e);
}
@ -509,38 +497,6 @@ public class PostHTTP extends AbstractProcessor {
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
public void onTrigger(final ProcessContext context, final ProcessSession session) {
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 java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.components.state.Scope;
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.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
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.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
/**
*
* Integration Test for deprecated GetHTTP Processor
*/
@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;
@BeforeClass
public static void before() {
public static void configureServices() throws TlsException {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
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.TestGetHTTP", "debug");
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
@Test
@ -60,14 +75,15 @@ public class TestGetHTTP {
handler.addServletWithMapping(RESTServiceContentModified.class, "/*");
// create the service
TestServer server = new TestServer();
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
final Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try {
server.startServer();
JettyServerUtils.startServer(server);
// 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
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -139,34 +155,32 @@ public class TestGetHTTP {
} finally {
// shutdown web service
server.shutdownServer();
server.stop();
server.destroy();
}
}
@Test
public final void testContentModifiedTwoServers() throws Exception {
// set up web services
ServletHandler handler1 = new ServletHandler();
final int port1 = NetworkUtils.availablePort();
final Server server1 = JettyServerUtils.createServer(port1, null, null);
final ServletHandler handler1 = new ServletHandler();
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, "/*");
// create the services
TestServer server1 = new TestServer();
server1.addHandler(handler1);
TestServer server2 = new TestServer();
server2.addHandler(handler2);
JettyServerUtils.addHandler(server2, handler2);
try {
server1.startServer();
server2.startServer();
JettyServerUtils.startServer(server1);
JettyServerUtils.startServer(server2);
// this is the base urls with the random ports
String destination1 = server1.getUrl();
String destination2 = server2.getUrl();
String destination1 = String.format(HTTP_URL, port1);
String destination2 = String.format(HTTP_URL, port2);
// set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -205,8 +219,10 @@ public class TestGetHTTP {
} finally {
// shutdown web services
server1.shutdownServer();
server2.shutdownServer();
server1.stop();
server1.destroy();
server2.stop();
server2.destroy();
}
}
@ -217,13 +233,13 @@ public class TestGetHTTP {
handler.addServletWithMapping(UserAgentTestingServlet.class, "/*");
// create the service
TestServer server = new TestServer();
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try {
server.startServer();
String destination = server.getUrl();
JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
// set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -241,7 +257,8 @@ public class TestGetHTTP {
// shutdown web service
} finally {
server.shutdownServer();
server.stop();
server.destroy();
}
}
@ -252,13 +269,13 @@ public class TestGetHTTP {
handler.addServletWithMapping(UserAgentTestingServlet.class, "/*");
// create the service
TestServer server = new TestServer();
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try {
server.startServer();
String destination = server.getUrl();
JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
// set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -275,7 +292,8 @@ public class TestGetHTTP {
// shutdown web service
} finally {
server.shutdownServer();
server.stop();
server.destroy();
}
}
@ -286,13 +304,13 @@ public class TestGetHTTP {
handler.addServletWithMapping(UserAgentTestingServlet.class, "/*");
// create the service
TestServer server = new TestServer();
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try {
server.startServer();
String destination = server.getUrl();
JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
// set up NiFi mock controller
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"));
// shutdown web service
} finally {
server.shutdownServer();
server.stop();
server.destroy();
}
}
@ -326,13 +345,14 @@ public class TestGetHTTP {
handler.addServletWithMapping(HttpErrorServlet.class, "/*");
// create the service
TestServer server = new TestServer();
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
Server server = JettyServerUtils.createServer(port, null, null);
server.setHandler(handler);
try {
server.startServer();
JettyServerUtils.startServer(server);
final String destination = String.format(HTTP_URL, port);
HttpErrorServlet servlet = (HttpErrorServlet) handler.getServlets()[0].getServlet();
String destination = server.getUrl();
this.controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
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);
} finally {
// shutdown web service
server.shutdownServer();
server.stop();
server.destroy();
}
}
@Test
public final void testSecure_oneWaySsl() throws Exception {
public final void testTlsClientAuthenticationNone() throws Exception {
// set up web service
final ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(HelloWorldServlet.class, "/*");
// create the service, disabling the need for client auth
final Map<String, String> serverSslProperties = getKeystoreProperties();
serverSslProperties.put(TestServer.NEED_CLIENT_AUTH, Boolean.toString(false));
final TestServer server = new TestServer(serverSslProperties);
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
final Server server = JettyServerUtils.createServer(port, keyStoreSslContext, ClientAuth.NONE);
server.setHandler(handler);
try {
server.startServer();
final String destination = server.getSecureUrl();
JettyServerUtils.startServer(server);
final String destination = String.format(HTTPS_URL, port);
// set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
// 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.URL, destination);
@ -393,31 +412,30 @@ public class TestGetHTTP {
final MockFlowFile mff = controller.getFlowFilesForRelationship(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS).get(0);
mff.assertContentEquals("Hello, World!");
} finally {
server.shutdownServer();
server.stop();
server.destroy();
}
}
@Test
public final void testSecure_twoWaySsl() throws Exception {
public final void testTlsClientAuthenticationRequired() throws Exception {
// set up web service
final ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(HelloWorldServlet.class, "/*");
// create the service, providing both truststore and keystore properties, requiring client auth (default)
final Map<String, String> twoWaySslProperties = getKeystoreProperties();
twoWaySslProperties.putAll(getTruststoreProperties());
final TestServer server = new TestServer(twoWaySslProperties);
server.addHandler(handler);
final int port = NetworkUtils.availablePort();
final Server server = JettyServerUtils.createServer(port, keyStoreSslContext, ClientAuth.REQUIRED);
server.setHandler(handler);
try {
server.startServer();
final String destination = server.getSecureUrl();
JettyServerUtils.startServer(server);
final String destination = String.format(HTTPS_URL, port);
// set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
// 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.URL, destination);
@ -429,33 +447,32 @@ public class TestGetHTTP {
final MockFlowFile mff = controller.getFlowFilesForRelationship(org.apache.nifi.processors.standard.GetHTTP.REL_SUCCESS).get(0);
mff.assertContentEquals("Hello, World!");
} finally {
server.shutdownServer();
server.stop();
server.destroy();
}
}
@Test
public final void testCookiePolicy() throws Exception {
// set up web services
ServletHandler handler1 = new ServletHandler();
final int port1 = NetworkUtils.availablePort();
final Server server1 = JettyServerUtils.createServer(port1, null, null);
final ServletHandler handler1 = new ServletHandler();
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, "/*");
// create the services
TestServer server1 = new TestServer();
server1.addHandler(handler1);
TestServer server2 = new TestServer();
server2.addHandler(handler2);
JettyServerUtils.addHandler(server2, handler2);
try {
server1.startServer();
server2.startServer();
JettyServerUtils.startServer(server1);
JettyServerUtils.startServer(server2);
// this is the base urls with the random ports
String destination1 = server1.getUrl();
String destination2 = server2.getUrl();
String destination1 = String.format(HTTP_URL, port1);
String destination2 = String.format(HTTP_URL, port2);
// set up NiFi mock controller
controller = TestRunners.newTestRunner(org.apache.nifi.processors.standard.GetHTTP.class);
@ -498,38 +515,19 @@ public class TestGetHTTP {
} finally {
// shutdown web services
server1.shutdownServer();
server2.shutdownServer();
server1.stop();
server1.destroy();
server2.stop();
server2.destroy();
}
}
private static Map<String, String> getTruststoreProperties() {
final Map<String, String> props = new HashMap<>();
props.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks");
props.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword");
props.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
return props;
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);
controller.addControllerService(SSL_CONTEXT_IDENTIFIER, sslContextService);
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.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
/**
* Tests PutSyslog sending messages to ListenSyslog to simulate a syslog server forwarding
* to ListenSyslog, or PutSyslog sending to a syslog server.
*/
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 final String TLS_PROTOCOL_VERSION = "TLSv1.2";
private static SSLContext keyStoreSslContext;
static final Logger LOGGER = LoggerFactory.getLogger(ITListenAndPutSyslog.class);
private ListenSyslog listenSyslog;
private TestRunner listenSyslogRunner;
@ -50,6 +56,11 @@ public class ITListenAndPutSyslog {
private PutSyslog putSyslog;
private TestRunner putSyslogRunner;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
}
@Before
public void setup() {
this.listenSyslog = new ListenSyslog();
@ -86,10 +97,10 @@ public class ITListenAndPutSyslog {
@Test
public void testTLS() throws InitializationException, IOException, InterruptedException {
configureSSLContextService(listenSyslogRunner);
listenSyslogRunner.setProperty(ListenSyslog.SSL_CONTEXT_SERVICE, "ssl-context");
listenSyslogRunner.setProperty(ListenSyslog.SSL_CONTEXT_SERVICE, SSL_SERVICE_IDENTIFIER);
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);
}
@ -97,24 +108,19 @@ public class ITListenAndPutSyslog {
@Test
public void testTLSListenerNoTLSPut() throws InitializationException, IOException, InterruptedException {
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
run(ListenSyslog.TCP_VALUE.getValue(), 7, 0);
}
private SSLContextService configureSSLContextService(TestRunner runner) throws InitializationException {
final SSLContextService sslContextService = new StandardSSLContextService();
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.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, TLS_PROTOCOL_VERSION);
private void configureSSLContextService(TestRunner runner) throws InitializationException {
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_SERVICE_IDENTIFIER);
Mockito.when(sslContextService.createContext()).thenReturn(keyStoreSslContext);
runner.addControllerService(SSL_SERVICE_IDENTIFIER, 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.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -30,133 +31,115 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
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.StandardSSLContextService;
import org.apache.nifi.util.FlowFileUnpackagerV3;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
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.junit.After;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import javax.net.ssl.SSLContext;
/**
* Integration Test for deprecated PostHTTP Processor
*/
@SuppressWarnings("deprecation")
public class TestPostHTTP {
private TestServer server;
public class ITPostHTTP {
private Server server;
private TestRunner runner;
private CaptureServlet servlet;
private final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
private final String KEYSTORE_AND_TRUSTSTORE_PASSWORD = "passwordpassword";
private final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks";
private final String JKS_TYPE = "JKS";
private static final String SSL_CONTEXT_IDENTIFIER = SSLContextService.class.getName();
private static final String TEST_MESSAGE = String.class.getName();
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
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(CaptureServlet.class, "/*");
// create the service
server = new TestServer(sslProperties);
server.addHandler(handler);
server.startServer();
final Server configuredServer = JettyServerUtils.createServer(port, serverSslContext, clientAuth);
configuredServer.setHandler(handler);
final ServerConnector connector = new ServerConnector(configuredServer);
connector.setPort(port);
JettyServerUtils.startServer(configuredServer);
servlet = (CaptureServlet) handler.getServlets()[0].getServlet();
runner = TestRunners.newTestRunner(org.apache.nifi.processors.standard.PostHTTP.class);
}
@After
public void cleanup() throws Exception {
if (server != null) {
server.shutdownServer();
server.stop();
server.destroy();
server = null;
}
}
@Test
public void testTruststoreSSLOnly() throws Exception {
final Map<String, String> sslProps = new HashMap<>();
sslProps.put(TestServer.NEED_CLIENT_AUTH, "false");
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);
public void testUnauthenticatedTls() throws Exception {
setup(keyStoreSslContext, ClientAuth.NONE);
enableSslContextService(trustStoreSslContext);
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.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.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.enqueue(TEST_MESSAGE);
runner.run();
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1);
}
@Test
public void testTwoWaySSL() throws Exception {
final Map<String, String> sslProps = new HashMap<>();
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);
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);
public void testMutualTls() throws Exception {
setup(keyStoreSslContext, ClientAuth.REQUIRED);
enableSslContextService(keyStoreSslContext);
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.SSL_CONTEXT_SERVICE, "ssl-context");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
runner.enqueue("Hello world".getBytes());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.enqueue(TEST_MESSAGE);
runner.run();
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS, 1);
}
@Test
public void testOneWaySSLWhenServerConfiguredForTwoWay() throws Exception {
final Map<String, String> sslProps = new HashMap<>();
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);
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);
public void testMutualTlsClientCertificateMissing() throws Exception {
setup(keyStoreSslContext, ClientAuth.REQUIRED);
enableSslContextService(trustStoreSslContext);
final SSLContextService sslContextService = new StandardSSLContextService();
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_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.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
runner.enqueue(TEST_MESSAGE);
runner.run();
runner.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_FAILURE, 1);
@ -164,14 +147,14 @@ public class TestPostHTTP {
@Test
public void testSendAsFlowFile() throws Exception {
setup(null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
setup( null, null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.SEND_AS_FLOWFILE, "true");
final Map<String, String> attrs = new HashMap<>();
attrs.put("abc", "cba");
runner.enqueue("Hello".getBytes(), attrs);
runner.enqueue(TEST_MESSAGE, attrs);
attrs.put("abc", "abc");
attrs.put("filename", "xyz.txt");
runner.enqueue("World".getBytes(), attrs);
@ -188,7 +171,7 @@ public class TestPostHTTP {
// unpack first flowfile received
Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos);
byte[] contentReceived = baos.toByteArray();
assertEquals("Hello", new String(contentReceived));
assertEquals(TEST_MESSAGE, new String(contentReceived));
assertEquals("cba", receivedAttrs.get("abc"));
assertTrue(unpacker.hasMoreData());
@ -204,35 +187,16 @@ public class TestPostHTTP {
}
@Test
public void testSendAsFlowFileSecure() throws Exception {
final Map<String, String> sslProps = new HashMap<>();
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);
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);
public void testMutualTlsSendFlowFile() throws Exception {
setup(keyStoreSslContext, ClientAuth.REQUIRED);
enableSslContextService(keyStoreSslContext);
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.SSL_CONTEXT_SERVICE, "ssl-context");
final Map<String, String> attrs = new HashMap<>();
attrs.put("abc", "cba");
runner.enqueue("Hello".getBytes(), attrs);
runner.enqueue(TEST_MESSAGE, attrs);
attrs.put("abc", "abc");
attrs.put("filename", "xyz.txt");
runner.enqueue("World".getBytes(), attrs);
@ -249,7 +213,7 @@ public class TestPostHTTP {
// unpack first flowfile received
Map<String, String> receivedAttrs = unpacker.unpackageFlowFile(bais, baos);
byte[] contentReceived = baos.toByteArray();
assertEquals("Hello", new String(contentReceived));
assertEquals(TEST_MESSAGE, new String(contentReceived));
assertEquals("cba", receivedAttrs.get("abc"));
assertTrue(unpacker.hasMoreData());
@ -265,15 +229,14 @@ public class TestPostHTTP {
@Test
public void testSendWithMimeType() throws Exception {
setup(null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
setup(null, null);
final Map<String, String> attrs = new HashMap<>();
final String suppliedMimeType = "text/plain";
attrs.put(CoreAttributes.MIME_TYPE.key(), suppliedMimeType);
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.assertAllFlowFilesTransferred(org.apache.nifi.processors.standard.PostHTTP.REL_SUCCESS);
@ -285,9 +248,9 @@ public class TestPostHTTP {
@Test
public void testSendWithEmptyELExpression() throws Exception {
setup(null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.URL, server.getUrl());
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
setup( null, null);
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
final Map<String, String> attrs = new HashMap<>();
attrs.put(CoreAttributes.MIME_TYPE.key(), "");
@ -302,12 +265,11 @@ public class TestPostHTTP {
@Test
public void testSendWithContentTypeProperty() throws Exception {
setup(null);
setup(null, null);
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.CHUNKED_ENCODING, "false");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.FALSE.toString());
final Map<String, String> attrs = new HashMap<>();
attrs.put(CoreAttributes.MIME_TYPE.key(), "text/csv");
@ -322,10 +284,9 @@ public class TestPostHTTP {
@Test
public void testSendWithCompressionServerAcceptGzip() throws Exception {
setup(null);
setup(null, null);
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.COMPRESSION_LEVEL, "9");
@ -346,10 +307,9 @@ public class TestPostHTTP {
@Test
public void testSendWithoutCompressionServerAcceptGzip() throws Exception {
setup(null);
setup(null, null);
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.COMPRESSION_LEVEL, "0");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, "false");
@ -371,11 +331,14 @@ public class TestPostHTTP {
@Test
public void testSendWithCompressionServerNotAcceptGzip() throws Exception {
setup(null);
setup(null, null);
final String suppliedMimeType = "text/plain";
// 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.COMPRESSION_LEVEL, "9");
@ -395,12 +358,11 @@ public class TestPostHTTP {
@Test
public void testSendChunked() throws Exception {
setup(null);
setup(null, null);
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.CHUNKED_ENCODING, "true");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.CHUNKED_ENCODING, Boolean.TRUE.toString());
final Map<String, String> attrs = new HashMap<>();
attrs.put(CoreAttributes.MIME_TYPE.key(), "text/plain");
@ -421,10 +383,9 @@ public class TestPostHTTP {
@Test
public void testSendWithThrottler() throws Exception {
setup(null);
setup(null, null);
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.CHUNKED_ENCODING, "false");
runner.setProperty(org.apache.nifi.processors.standard.PostHTTP.MAX_DATA_RATE, "10kb");
@ -448,31 +409,28 @@ public class TestPostHTTP {
@Test
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"));
}
@Test
public void testBatchWithMultipleUrls() throws Exception {
CaptureServlet servletA, servletB;
TestServer serverA, serverB;
setup(null,null);
final CaptureServlet servletA = servlet;
final String urlA = runner.getProcessContext().getProperty(org.apache.nifi.processors.standard.PostHTTP.URL).getValue();
{ // setup test servers
setup(null);
servletA = servlet;
serverA = server;
// set up second web service
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(CaptureServlet.class, "/*");
// set up second web service
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(CaptureServlet.class, "/*");
// create the second service
final int portB = NetworkUtils.availablePort();
final String urlB = getUrl(null, portB);
final Server serverB = JettyServerUtils.createServer(portB, null, null);
serverB.setHandler(handler);
JettyServerUtils.startServer(serverB);
// create the second service
serverB = new TestServer(null);
serverB.addHandler(handler);
serverB.startServer();
servletB = (CaptureServlet) handler.getServlets()[0].getServlet();
}
final CaptureServlet 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.SEND_AS_FLOWFILE, "true");
@ -486,8 +444,8 @@ public class TestPostHTTP {
// enqueue 9 FlowFiles
for (int i = 0; i < 9; i++) {
enqueueWithURL("a" + i, serverA.getUrl());
enqueueWithURL("b" + i, serverB.getUrl());
enqueueWithURL("a" + i, urlA);
enqueueWithURL("b" + i, urlB);
expectedContentA.add("a" + i);
expectedContentB.add("b" + i);
@ -503,10 +461,10 @@ public class TestPostHTTP {
MockFlowFile mff = successFiles.get(0);
final String urlAttr = mff.getAttribute("url");
if (serverA.getUrl().equals(urlAttr)) {
checkBatch(serverA, servletA, actualContentA, (actualContentA.isEmpty() ? 5 : 4));
} else if (serverB.getUrl().equals(urlAttr)) {
checkBatch(serverB, servletB, actualContentB, (actualContentB.isEmpty() ? 5 : 4));
if (urlA.equals(urlAttr)) {
checkBatch(urlA, servletA, actualContentA, (actualContentA.isEmpty() ? 5 : 4));
} else if (urlB.equals(urlAttr)) {
checkBatch(urlB, servletB, actualContentB, (actualContentB.isEmpty() ? 5 : 4));
} else {
fail("unexpected url attribute");
}
@ -526,7 +484,7 @@ public class TestPostHTTP {
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();
Set<String> actualFFContent = 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);
for (int i = 0; i < expectedCount; i++) {
MockFlowFile mff = successFlowFiles.get(i);
mff.assertAttributeEquals("url", server.getUrl());
mff.assertAttributeEquals("url", url);
String content = new String(mff.toByteArray());
actualFFContent.add(content);
}
@ -549,9 +507,10 @@ public class TestPostHTTP {
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
for (int i = 0; i < expectedCount; i++) {
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);
assertEquals(server.getUrl(), receivedAttrs.get("url"));
assertEquals(url, receivedAttrs.get("url"));
assertTrue(unpacker.hasMoreData() || i == (expectedCount - 1));
baos.reset();
}
@ -560,8 +519,17 @@ public class TestPostHTTP {
// confirm that the transferred and POSTed content match
assertEquals(actualFFContent, actualPostContent);
// accumulate actial content
// accumulate actual content
actualContent.addAll(actualPostContent);
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.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
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.processors.standard.util.HTTPUtils;
import org.apache.nifi.reporting.InitializationException;
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.ssl.StandardRestrictedSSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.RestrictedSSLContextService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
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 TlsConfiguration clientTlsConfiguration;
private TlsConfiguration trustOnlyTlsConfiguration;
private static SSLContext keyStoreSslContext;
private static Map<String, String> getTruststoreProperties() {
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 SSLContext trustStoreSslContext;
private static Map<String, String> getServerKeystoreProperties() {
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_PASSWORD);
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();
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
@Before
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
@ -136,7 +98,7 @@ public class ITestHandleHttpRequest {
}
@Test(timeout = 30000)
public void testRequestAddedToService() throws InitializationException, IOException, InterruptedException {
public void testRequestAddedToService() throws InitializationException {
CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1);
@ -191,7 +153,7 @@ public class ITestHandleHttpRequest {
}
@Test(timeout = 30000)
public void testMultipartFormDataRequest() throws InitializationException, IOException, InterruptedException {
public void testMultipartFormDataRequest() throws InitializationException {
CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1);
@ -304,8 +266,7 @@ public class ITestHandleHttpRequest {
}
@Test(timeout = 30000)
public void testMultipartFormDataRequestCaptureFormAttributes() throws InitializationException, IOException,
InterruptedException {
public void testMultipartFormDataRequestCaptureFormAttributes() throws InitializationException {
CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1);
@ -374,7 +335,7 @@ public class ITestHandleHttpRequest {
}
@Test(timeout = 30000)
public void testMultipartFormDataRequestFailToRegisterContext() throws InitializationException, IOException, InterruptedException {
public void testMultipartFormDataRequestFailToRegisterContext() throws InitializationException, InterruptedException {
CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1);
CountDownLatch resultReady = new CountDownLatch(1);
@ -427,7 +388,7 @@ public class ITestHandleHttpRequest {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
public void onResponse(Call call, Response response) {
responseCode.set(response.code());
resultReady.countDown();
}
@ -473,7 +434,7 @@ public class ITestHandleHttpRequest {
@Test(timeout = 30000)
public void testFailToRegister() throws InitializationException, IOException, InterruptedException {
public void testFailToRegister() throws InitializationException, InterruptedException {
CountDownLatch serverReady = new CountDownLatch(1);
CountDownLatch requestSent = new CountDownLatch(1);
CountDownLatch resultReady = new CountDownLatch(1);
@ -649,10 +610,13 @@ public class ITestHandleHttpRequest {
runner.enableControllerService(contextMap);
runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, "http-context-map");
final Map<String, String> sslProperties = getServerKeystoreProperties();
sslProperties.putAll(getTruststoreProperties());
sslProperties.put(StandardSSLContextService.SSL_ALGORITHM.getName(), TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion());
useSSLContextService(runner, sslProperties);
final RestrictedSSLContextService sslContextService = Mockito.mock(RestrictedSSLContextService.class);
final String serviceIdentifier = RestrictedSSLContextService.class.getName();
Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
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() {
@Override
@ -667,10 +631,10 @@ public class ITestHandleHttpRequest {
SSLContext clientSslContext;
if (twoWaySsl) {
// Use a client certificate, do not reuse the server's keystore
clientSslContext = SslContextFactory.createSslContext(clientTlsConfiguration);
clientSslContext = keyStoreSslContext;
} else {
// With one-way SSL, the client still needs a truststore
clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
clientSslContext = trustStoreSslContext;
}
connection.setSSLSocketFactory(clientSslContext.getSocketFactory());
connection.setDoOutput(false);

View File

@ -27,12 +27,15 @@ import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.TestRunners;
import org.eclipse.jetty.server.Request;
@ -41,12 +44,29 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestInvokeHTTP extends TestInvokeHttpCommon {
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
public static void beforeClass() throws Exception {
configureServer(null, null);
@ -59,20 +79,18 @@ public class TestInvokeHTTP extends TestInvokeHttpCommon {
@Test
public void testSslSetHttpRequest() throws Exception {
final Map<String, String> sslProperties = new HashMap<>();
sslProperties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/keystore.jks");
sslProperties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "passwordpassword");
sslProperties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
sslProperties.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks");
sslProperties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword");
sslProperties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
final String serviceIdentifier = SSLContextService.class.getName();
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
final SSLContext sslContext = SslContextFactory.createSslContext(TLS_CONFIGURATION);
Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(TLS_CONFIGURATION);
runner = TestRunners.newTestRunner(InvokeHTTP.class);
final StandardSSLContextService sslService = new StandardSSLContextService();
runner.addControllerService("ssl-context", sslService, sslProperties);
runner.enableControllerService(sslService);
runner.setProperty(InvokeHTTP.PROP_SSL_CONTEXT_SERVICE, "ssl-context");
runner.addControllerService(serviceIdentifier, sslContextService);
runner.enableControllerService(sslContextService);
runner.setProperty(InvokeHTTP.PROP_SSL_CONTEXT_SERVICE, serviceIdentifier);
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.ssl.RestrictedSSLContextService;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
@ -314,16 +313,6 @@ public class TestListenHTTP {
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
public void testSecureServerSupportsCurrentTlsProtocolVersion() throws Exception {
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, SERVER_NO_TRUSTSTORE_CONFIGURATION);
@ -539,22 +528,8 @@ public class TestListenHTTP {
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);
return sslContextService;
}
@Test(/*timeout=10000*/)
@Test
public void testMultipartFormDataRequest() throws Exception {
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.ProvenanceEventType;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -50,9 +51,6 @@ import org.mockito.Mockito;
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 SYSLOG_FRAME_DATA = "this is a syslog message here";
@ -151,19 +149,16 @@ public class TestListenRELP {
}
@Test
public void testTLS() throws InitializationException, IOException, InterruptedException {
final SSLContextService sslContextService = new StandardSSLContextService();
runner.addControllerService("ssl-context", sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.SSL_ALGORITHM, TLS_PROTOCOL_VERSION);
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");
public void testMutualTls() throws IOException, InterruptedException, TlsException, InitializationException {
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
final String serviceIdentifier = SSLContextService.class.getName();
Mockito.when(sslContextService.getIdentifier()).thenReturn(serviceIdentifier);
final SSLContext sslContext = SslContextUtils.createKeyStoreSslContext();
Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
runner.addControllerService(serviceIdentifier, 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<>();
frames.add(OPEN_FRAME);
@ -174,8 +169,7 @@ public class TestListenRELP {
frames.add(SYSLOG_FRAME);
frames.add(CLOSE_FRAME);
// three syslog frames should be transferred and three responses should be sent
run(frames, 5, 5, sslContextService);
run(frames, 5, 5, sslContext);
}
@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 {
Socket socket = null;
@ -224,11 +218,10 @@ public class TestListenRELP {
final int realPort = proc.getDispatcherPort();
// create either a regular socket or ssl socket based on context being passed in
if (sslContextService != null) {
final SSLContext sslContext = sslContextService.createContext();
socket = sslContext.getSocketFactory().createSocket("localhost", realPort);
} else {
if (sslContext == null) {
socket = new Socket("localhost", realPort);
} else {
socket = sslContext.getSocketFactory().createSocket("localhost", realPort);
}
Thread.sleep(100);

View File

@ -21,56 +21,51 @@ import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import org.apache.commons.io.IOUtils;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException;
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.ssl.RestrictedSSLContextService;
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.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
public class TestListenTCP {
private static final long RESPONSE_TIMEOUT = 10000;
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 static final String SSL_CONTEXT_IDENTIFIER = SSLContextService.class.getName();
// 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 SSLContext keyStoreSslContext;
private static TlsConfiguration clientTlsConfiguration;
private static TlsConfiguration trustOnlyTlsConfiguration;
private static SSLContext trustStoreSslContext;
private ListenTCP proc;
private TestRunner runner;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
@Before
public void setup() {
proc = new ListenTCP();
runner = TestRunners.newTestRunner(proc);
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
@ -78,7 +73,7 @@ public class TestListenTCP {
runner.setProperty(ListenTCP.PORT, "1");
runner.assertValid();
configureProcessorSslContextService();
enableSslContextService(keyStoreSslContext);
runner.setProperty(ListenTCP.CLIENT_AUTH, "");
runner.assertNotValid();
@ -126,11 +121,11 @@ public class TestListenTCP {
}
@Test
public void testTLSClientAuthRequiredAndClientCertProvided() throws InitializationException, IOException, InterruptedException,
TlsException {
public void testTLSClientAuthRequiredAndClientCertProvided() throws IOException, InterruptedException,
InitializationException {
runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.REQUIRED.name());
configureProcessorSslContextService();
enableSslContextService(keyStoreSslContext);
final List<String> messages = new ArrayList<>();
messages.add("This is message 1\n");
@ -140,9 +135,7 @@ public class TestListenTCP {
messages.add("This is message 5\n");
// Make an SSLContext with a key and trust store to send the test messages
final SSLContext clientSslContext = SslContextFactory.createSslContext(clientTlsConfiguration);
runTCP(messages, messages.size(), clientSslContext);
runTCP(messages, messages.size(), keyStoreSslContext);
List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS);
for (int i = 0; i < mockFlowFiles.size(); i++) {
@ -151,10 +144,9 @@ public class TestListenTCP {
}
@Test
public void testTLSClientAuthRequiredAndClientCertNotProvided() throws InitializationException, TlsException {
public void testTLSClientAuthRequiredAndClientCertNotProvided() throws InitializationException {
runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.REQUIRED.name());
configureProcessorSslContextService();
enableSslContextService(keyStoreSslContext);
final List<String> messages = new ArrayList<>();
messages.add("This is message 1\n");
@ -164,21 +156,15 @@ public class TestListenTCP {
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
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
try {
runTCP(messages, messages.size(), clientSslContext);
Assert.fail("Should have thrown exception");
} catch (Exception e) {
}
Assert.assertThrows(SSLException.class, () ->
runTCP(messages, messages.size(), trustStoreSslContext)
);
}
@Test
public void testTLSClientAuthNoneAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException, TlsException {
public void testTLSClientAuthNoneAndClientCertNotProvided() throws IOException, InterruptedException, InitializationException {
runner.setProperty(ListenTCP.CLIENT_AUTH, ClientAuth.NONE.name());
configureProcessorSslContextService();
enableSslContextService(keyStoreSslContext);
final List<String> messages = new ArrayList<>();
messages.add("This is message 1\n");
@ -188,9 +174,7 @@ public class TestListenTCP {
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
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
runTCP(messages, messages.size(), clientSslContext);
runTCP(messages, messages.size(), trustStoreSslContext);
List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS);
for (int i = 0; i < mockFlowFiles.size(); i++) {
@ -212,10 +196,11 @@ public class TestListenTCP {
final int realPort = proc.getDispatcherPort();
// create either a regular socket or ssl socket based on context being passed in
if (sslContext != null) {
socket = sslContext.getSocketFactory().createSocket("localhost", realPort);
} else {
if (sslContext == null) {
socket = new Socket("localhost", realPort);
} else {
final SocketFactory socketFactory = sslContext.getSocketFactory();
socket = socketFactory.createSocket("localhost", realPort);
}
Thread.sleep(100);
@ -226,14 +211,12 @@ public class TestListenTCP {
}
socket.getOutputStream().flush();
long responseTimeout = 10000;
// 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
// before onTrigger gets a chance to run
long startTimeQueueSizeCheck = System.currentTimeMillis();
while (proc.getQueueSize() < messages.size()
&& (System.currentTimeMillis() - startTimeQueueSizeCheck < responseTimeout)) {
&& (System.currentTimeMillis() - startTimeQueueSizeCheck < RESPONSE_TIMEOUT)) {
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
int numTransferred = 0;
long startTime = System.currentTimeMillis();
while (numTransferred < expectedTransferred && (System.currentTimeMillis() - startTime < responseTimeout)) {
while (numTransferred < expectedTransferred && (System.currentTimeMillis() - startTime < RESPONSE_TIMEOUT)) {
proc.onTrigger(context, processSessionFactory);
numTransferred = runner.getFlowFilesForRelationship(ListenTCP.REL_SUCCESS).size();
Thread.sleep(100);
@ -258,20 +241,12 @@ public class TestListenTCP {
}
}
private SSLContextService configureProcessorSslContextService() throws InitializationException {
final SSLContextService sslContextService = new StandardRestrictedSSLContextService();
runner.addControllerService("ssl-context", sslContextService);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, KEYSTORE);
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, KEYSTORE_PASSWORD);
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);
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(ListenTCP.SSL_CONTEXT_SERVICE, "ssl-context");
return sslContextService;
runner.setProperty(ListenTCP.SSL_CONTEXT_SERVICE, SSL_CONTEXT_IDENTIFIER);
}
}

View File

@ -31,43 +31,27 @@ import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.schema.access.SchemaAccessUtils;
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.serialization.RecordReaderFactory;
import org.apache.nifi.serialization.RecordSetWriterFactory;
import org.apache.nifi.serialization.record.MockRecordWriter;
import org.apache.nifi.ssl.RestrictedSSLContextService;
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.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.ssl.SslContextUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestListenTCPRecord {
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" +
" \"name\": \"syslogRecord\",\n" +
" \"namespace\": \"nifi\",\n" +
@ -91,9 +75,21 @@ public class TestListenTCPRecord {
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 TestRunner runner;
@BeforeClass
public static void configureServices() throws TlsException {
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
}
@Before
public void setup() throws InitializationException {
proc = new ListenTCPRecord();
@ -115,10 +111,6 @@ public class TestListenTCPRecord {
runner.setProperty(ListenTCPRecord.RECORD_READER, readerId);
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
@ -126,7 +118,7 @@ public class TestListenTCPRecord {
runner.setProperty(ListenTCPRecord.PORT, "1");
runner.assertValid();
configureProcessorSslContextService();
enableSslContextService(keyStoreSslContext);
runner.setProperty(ListenTCPRecord.CLIENT_AUTH, "");
runner.assertNotValid();
@ -171,15 +163,11 @@ public class TestListenTCPRecord {
}
@Test
public void testTLSClientAuthRequiredAndClientCertProvided() throws InitializationException, IOException, InterruptedException, TlsException {
public void testTLSClientAuthRequiredAndClientCertProvided() throws InitializationException, IOException, InterruptedException {
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
final SSLContext clientSslContext = SslContextFactory.createSslContext(clientTlsConfiguration);
runTCP(DATA, 1, clientSslContext);
runTCP(DATA, 1, keyStoreSslContext);
final List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCPRecord.REL_SUCCESS);
Assert.assertEquals(1, mockFlowFiles.size());
@ -192,28 +180,21 @@ public class TestListenTCPRecord {
}
@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.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
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
runTCP(DATA, 0, clientSslContext);
runTCP(DATA, 0, trustStoreSslContext);
}
@Test
public void testTLSClientAuthNoneAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException, TlsException {
public void testTLSClientAuthNoneAndClientCertNotProvided() throws InitializationException, IOException, InterruptedException {
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
final SSLContext clientSslContext = SslContextFactory.createSslContext(trustOnlyTlsConfiguration);
runTCP(DATA, 1, clientSslContext);
runTCP(DATA, 1, trustStoreSslContext);
final List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(ListenTCPRecord.REL_SUCCESS);
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 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);
}
}