NIFI-9228 Refactored tests to use generated KeyStores

- Refactored multiple tests using KeyStoreUtils
- Removed static KeyStore and TrustStore files

Signed-off-by: Matthew Burgess <mattyb149@apache.org>

This closes #5401
This commit is contained in:
exceptionfactory 2021-09-20 22:23:08 -05:00 committed by Matthew Burgess
parent 2e998839eb
commit ba775d28de
No known key found for this signature in database
GPG Key ID: 05D3DEB8126DAD24
74 changed files with 395 additions and 2525 deletions

View File

@ -96,11 +96,6 @@
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>

View File

@ -22,6 +22,7 @@ import static org.apache.nifi.remote.protocol.http.HttpHeaders.LOCATION_URI_INTE
import static org.apache.nifi.remote.protocol.http.HttpHeaders.LOCATION_URI_INTENT_VALUE;
import static org.apache.nifi.remote.protocol.http.HttpHeaders.PROTOCOL_VERSION;
import static org.apache.nifi.remote.protocol.http.HttpHeaders.SERVER_SIDE_TRANSACTION_TTL;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@ -31,12 +32,14 @@ import static org.junit.Assume.assumeFalse;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@ -65,6 +68,7 @@ import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol;
import org.apache.nifi.remote.protocol.http.HttpHeaders;
import org.apache.nifi.remote.protocol.http.HttpProxy;
import org.apache.nifi.remote.util.StandardDataPacket;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.stream.io.StreamUtils;
import org.apache.nifi.web.api.dto.ControllerDTO;
@ -116,6 +120,8 @@ public class TestHttpClient {
private static Set<PeerDTO> peersSecure;
private static String serverChecksum;
private static TlsConfiguration tlsConfiguration;
public static class SiteInfoServlet extends HttpServlet {
@Override
@ -181,7 +187,7 @@ public class TestHttpClient {
public static class PortTransactionsServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
final int reqProtocolVersion = getReqProtocolVersion(req);
@ -201,7 +207,7 @@ public class TestHttpClient {
public static class PortTransactionsAccessDeniedServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
respondWithText(resp, "Unable to perform the desired action" +
" due to insufficient permissions. Contact the system administrator.", 403);
@ -213,7 +219,7 @@ public class TestHttpClient {
public static class InputPortTransactionServlet extends HttpServlet {
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
final int reqProtocolVersion = getReqProtocolVersion(req);
final TransactionResultEntity entity = new TransactionResultEntity();
@ -226,7 +232,7 @@ public class TestHttpClient {
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
final int reqProtocolVersion = getReqProtocolVersion(req);
@ -244,7 +250,7 @@ public class TestHttpClient {
public static class OutputPortTransactionServlet extends HttpServlet {
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
final int reqProtocolVersion = getReqProtocolVersion(req);
final TransactionResultEntity entity = new TransactionResultEntity();
@ -257,7 +263,7 @@ public class TestHttpClient {
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
final int reqProtocolVersion = getReqProtocolVersion(req);
@ -332,7 +338,7 @@ public class TestHttpClient {
public static class FlowFilesTimeoutAfterDataExchangeServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
final int reqProtocolVersion = getReqProtocolVersion(req);
@ -398,7 +404,7 @@ public class TestHttpClient {
private static int getReqProtocolVersion(HttpServletRequest req) {
final String reqProtocolVersionStr = req.getHeader(PROTOCOL_VERSION);
assertTrue(!isEmpty(reqProtocolVersionStr));
assertFalse(isEmpty(reqProtocolVersionStr));
return Integer.parseInt(reqProtocolVersionStr);
}
@ -454,11 +460,12 @@ public class TestHttpClient {
wrongPathContextHandler.insertHandler(wrongPathServletHandler);
final SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("src/test/resources/certs/keystore.jks");
sslContextFactory.setKeyStorePassword("passwordpassword");
sslContextFactory.setKeyStoreType("JKS");
setTlsConfiguration();
sslContextFactory.setKeyStorePath(tlsConfiguration.getKeystorePath());
sslContextFactory.setKeyStorePassword(tlsConfiguration.getKeystorePassword());
sslContextFactory.setKeyStoreType(tlsConfiguration.getKeystoreType().getType());
sslContextFactory.setProtocol(TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion());
sslContextFactory.setExcludeProtocols("TLS", "TLSv1", "TLSv1.1");
httpConnector = new ServerConnector(server);
@ -597,10 +604,6 @@ public class TestHttpClient {
@Before
public void before() throws Exception {
System.setProperty("org.slf4j.simpleLogger.log.org.apache.nifi.remote", "TRACE");
System.setProperty("org.slf4j.simpleLogger.log.org.apache.nifi.remote.protocol.http.HttpClientTransaction", "DEBUG");
testCaseFinished = new CountDownLatch(1);
final PeerDTO peer = new PeerDTO();
@ -693,13 +696,12 @@ public class TestHttpClient {
return new SiteToSiteClient.Builder().transportProtocol(SiteToSiteTransportProtocol.HTTP)
.url("https://localhost:" + sslConnector.getLocalPort() + "/nifi")
.timeout(3, TimeUnit.MINUTES)
.keystoreFilename("src/test/resources/certs/keystore.jks")
.keystorePass("passwordpassword")
.keystoreType(KeystoreType.JKS)
.truststoreFilename("src/test/resources/certs/truststore.jks")
.truststorePass("passwordpassword")
.truststoreType(KeystoreType.JKS)
;
.keystoreFilename(tlsConfiguration.getKeystorePath())
.keystorePass(tlsConfiguration.getKeystorePassword())
.keystoreType(KeystoreType.valueOf(tlsConfiguration.getKeystoreType().getType()))
.truststoreFilename(tlsConfiguration.getTruststorePath())
.truststorePass(tlsConfiguration.getTruststorePassword())
.truststoreType(KeystoreType.valueOf(tlsConfiguration.getTruststoreType().getType()));
}
private static void consumeDataPacket(DataPacket packet) throws IOException {
@ -1400,4 +1402,9 @@ public class TestHttpClient {
}
}
private static void setTlsConfiguration() throws GeneralSecurityException, IOException {
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
}
}

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-4r [%t] %-5p %c{3} - %m%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>./target/log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.nifi" level="INFO"/>
<logger name="org.apache.nifi.remote.client" level="INFO"/>
<logger name="org.apache.nifi.remote.client.PeerSelectorTest" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -1,318 +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.elasticsearch.integration
import org.apache.nifi.elasticsearch.DeleteOperationResponse
import org.apache.nifi.elasticsearch.ElasticSearchClientService
import org.apache.nifi.elasticsearch.ElasticSearchClientServiceImpl
import org.apache.nifi.elasticsearch.IndexOperationRequest
import org.apache.nifi.elasticsearch.IndexOperationResponse
import org.apache.nifi.elasticsearch.SearchResponse
import org.apache.nifi.security.util.KeystoreType
import org.apache.nifi.ssl.StandardSSLContextService
import org.apache.nifi.util.StringUtils
import org.apache.nifi.util.TestRunner
import org.apache.nifi.util.TestRunners
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import static groovy.json.JsonOutput.prettyPrint
import static groovy.json.JsonOutput.toJson
class ElasticSearch5ClientService_IT {
private TestRunner runner
private ElasticSearchClientServiceImpl service
static String INDEX = "messages"
static String TYPE = StringUtils.isNotBlank(System.getProperty("type_name")) ? System.getProperty("type_name") : null;
@Before
void before() throws Exception {
runner = TestRunners.newTestRunner(TestControllerServiceProcessor.class)
service = new ElasticSearchClientServiceImpl()
runner.addControllerService("Client Service", service)
runner.setProperty(service, ElasticSearchClientService.HTTP_HOSTS, "http://localhost:9400")
runner.setProperty(service, ElasticSearchClientService.CONNECT_TIMEOUT, "10000")
runner.setProperty(service, ElasticSearchClientService.SOCKET_TIMEOUT, "60000")
runner.setProperty(service, ElasticSearchClientService.RETRY_TIMEOUT, "60000")
runner.setProperty(service, ElasticSearchClientService.SUPPRESS_NULLS, ElasticSearchClientService.ALWAYS_SUPPRESS.getValue())
try {
runner.enableControllerService(service)
} catch (Exception ex) {
ex.printStackTrace()
throw ex
}
}
@After
void after() throws Exception {
service.onDisabled()
}
@Test
void testBasicSearch() throws Exception {
String query = prettyPrint(toJson([
size: 10,
query: [
match_all: [:]
],
aggs: [
term_counts: [
terms: [
field: "msg",
size: 5
]
]
]
]))
SearchResponse response = service.search(query, "messages", TYPE)
Assert.assertNotNull("Response was null", response)
Assert.assertEquals("Wrong count", 15, response.numberOfHits)
Assert.assertFalse("Timed out", response.isTimedOut())
Assert.assertNotNull("Hits was null", response.getHits())
Assert.assertEquals("Wrong number of hits", 10, response.hits.size())
Assert.assertNotNull("Aggregations are missing", response.aggregations)
Assert.assertEquals("Aggregation count is wrong", 1, response.aggregations.size())
Map termCounts = response.aggregations.get("term_counts")
Assert.assertNotNull("Term counts was missing", termCounts)
def buckets = termCounts.get("buckets")
Assert.assertNotNull("Buckets branch was empty", buckets)
def expected = [
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5
]
buckets.each { aggRes ->
def key = aggRes["key"]
def docCount = aggRes["doc_count"]
Assert.assertEquals("${key} did not match.", expected[key], docCount)
}
}
@Test
void testDeleteByQuery() throws Exception {
String query = prettyPrint(toJson([
query: [
match: [
msg: "five"
]
]
]))
DeleteOperationResponse response = service.deleteByQuery(query, INDEX, TYPE)
Assert.assertNotNull(response)
Assert.assertTrue(response.getTook() > 0)
}
@Test
void testDeleteById() throws Exception {
final String ID = "1"
DeleteOperationResponse response = service.deleteById(INDEX, TYPE, ID)
Assert.assertNotNull(response)
Assert.assertTrue(response.getTook() > 0)
def doc = service.get(INDEX, TYPE, ID)
Assert.assertNull(doc)
doc = service.get(INDEX, TYPE, "2")
Assert.assertNotNull(doc)
}
@Test
void testGet() throws IOException {
Map old
System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + "TYPE: " + TYPE + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
1.upto(15) { index ->
String id = String.valueOf(index)
def doc = service.get(INDEX, TYPE, id)
Assert.assertNotNull("Doc was null", doc)
Assert.assertNotNull("${doc.toString()}\t${doc.keySet().toString()}", doc.get("msg"))
old = doc
}
}
@Test
void testSSL() {
def sslContext = new StandardSSLContextService()
runner.setProperty(TestControllerServiceProcessor.CLIENT_SERVICE, "Client Service")
runner.disableControllerService(service)
runner.addControllerService("sslContext", sslContext)
runner.setProperty(sslContext, StandardSSLContextService.TRUSTSTORE, "src/test/resources/truststore.jks")
runner.setProperty(sslContext, StandardSSLContextService.TRUSTSTORE_PASSWORD, "2DZ5i7yvbG2GA3Ld4yiAsH62QDqAjWt4ToCU0yHajwM")
runner.setProperty(sslContext, StandardSSLContextService.TRUSTSTORE_TYPE, KeystoreType.JKS.getType())
runner.setProperty(service, ElasticSearchClientService.PROP_SSL_CONTEXT_SERVICE, "sslContext")
runner.enableControllerService(sslContext)
runner.enableControllerService(service)
runner.assertValid()
runner.disableControllerService(service)
runner.disableControllerService(sslContext)
runner.setProperty(sslContext, StandardSSLContextService.KEYSTORE, "src/test/resources/keystore.jks")
runner.setProperty(sslContext, StandardSSLContextService.KEYSTORE_PASSWORD, "pben4DTOUhLDI8mZiCHNX1dGEAWrpGnSYX38FTvmaeU")
runner.setProperty(sslContext, StandardSSLContextService.KEYSTORE_TYPE, KeystoreType.JKS.getType())
runner.enableControllerService(sslContext)
runner.enableControllerService(service)
runner.assertValid()
}
@Test
void testNullSuppression() {
Map<String, Object> doc = new HashMap<String, Object>(){{
put("msg", "test")
put("is_null", null)
put("is_empty", "")
put("is_blank", " ")
put("empty_nested", Collections.emptyMap())
put("empty_array", Collections.emptyList())
}}
// index with nulls
suppressNulls(false)
IndexOperationResponse response = service.bulk([new IndexOperationRequest("nulls", TYPE, "1", doc, IndexOperationRequest.Operation.Index)])
Assert.assertNotNull(response)
Assert.assertTrue(response.getTook() > 0)
Thread.sleep(2000)
Map<String, Object> result = service.get("nulls", TYPE, "1")
Assert.assertEquals(doc, result)
// suppress nulls
suppressNulls(true)
response = service.bulk([new IndexOperationRequest("nulls", TYPE, "2", doc, IndexOperationRequest.Operation.Index)])
Assert.assertNotNull(response)
Assert.assertTrue(response.getTook() > 0)
Thread.sleep(2000)
result = service.get("nulls", TYPE, "2")
Assert.assertTrue("Non-nulls (present): " + result.toString(), result.keySet().containsAll(["msg", "is_blank"]))
Assert.assertFalse("is_null (should be omitted): " + result.toString(), result.keySet().contains("is_null"))
Assert.assertFalse("is_empty (should be omitted): " + result.toString(), result.keySet().contains("is_empty"))
Assert.assertFalse("empty_nested (should be omitted): " + result.toString(), result.keySet().contains("empty_nested"))
Assert.assertFalse("empty_array (should be omitted): " + result.toString(), result.keySet().contains("empty_array"))
}
private void suppressNulls(final boolean suppressNulls) {
runner.setProperty(TestControllerServiceProcessor.CLIENT_SERVICE, "Client Service")
runner.disableControllerService(service)
runner.setProperty(service, ElasticSearchClientService.SUPPRESS_NULLS, suppressNulls ? ElasticSearchClientService.ALWAYS_SUPPRESS.getValue() : ElasticSearchClientService.NEVER_SUPPRESS.getValue())
runner.enableControllerService(service)
runner.assertValid()
}
@Test
void testBulkAddTwoIndexes() throws Exception {
List<IndexOperationRequest> payload = new ArrayList<>()
for (int x = 0; x < 20; x++) {
String index = x % 2 == 0 ? "bulk_a": "bulk_b"
payload.add(new IndexOperationRequest(index, TYPE, String.valueOf(x), new HashMap<String, Object>(){{
put("msg", "test")
}}, IndexOperationRequest.Operation.Index))
}
for (int x = 0; x < 5; x++) {
payload.add(new IndexOperationRequest("bulk_c", TYPE, String.valueOf(x), new HashMap<String, Object>(){{
put("msg", "test")
}}, IndexOperationRequest.Operation.Index))
}
IndexOperationResponse response = service.bulk(payload)
Assert.assertNotNull(response)
Assert.assertTrue(response.getTook() > 0)
Thread.sleep(2000)
/*
* Now, check to ensure that both indexes got populated appropriately.
*/
String query = "{ \"query\": { \"match_all\": {}}}"
Long indexA = service.count(query, "bulk_a", TYPE)
Long indexB = service.count(query, "bulk_b", TYPE)
Long indexC = service.count(query, "bulk_c", TYPE)
Assert.assertNotNull(indexA)
Assert.assertNotNull(indexB)
Assert.assertNotNull(indexC)
Assert.assertEquals(indexA, indexB)
Assert.assertEquals(10, indexA.intValue())
Assert.assertEquals(10, indexB.intValue())
Assert.assertEquals(5, indexC.intValue())
Long total = service.count(query, "bulk_*", TYPE)
Assert.assertNotNull(total)
Assert.assertEquals(25, total.intValue())
}
@Test
void testUpdateAndUpsert() {
final String TEST_ID = "update-test"
Map<String, Object> doc = new HashMap<>()
doc.put("msg", "Buongiorno, mondo")
service.add(new IndexOperationRequest(INDEX, TYPE, TEST_ID, doc, IndexOperationRequest.Operation.Index))
Map<String, Object> result = service.get(INDEX, TYPE, TEST_ID)
Assert.assertEquals("Not the same", doc, result)
Map<String, Object> updates = new HashMap<>()
updates.put("from", "john.smith")
Map<String, Object> merged = new HashMap<>()
merged.putAll(updates)
merged.putAll(doc)
IndexOperationRequest request = new IndexOperationRequest(INDEX, TYPE, TEST_ID, updates, IndexOperationRequest.Operation.Update)
service.add(request)
result = service.get(INDEX, TYPE, TEST_ID)
Assert.assertTrue(result.containsKey("from"))
Assert.assertTrue(result.containsKey("msg"))
Assert.assertEquals("Not the same after update.", merged, result)
final String UPSERTED_ID = "upsert-ftw"
Map<String, Object> upsertItems = new HashMap<>()
upsertItems.put("upsert_1", "hello")
upsertItems.put("upsert_2", 1)
upsertItems.put("upsert_3", true)
request = new IndexOperationRequest(INDEX, TYPE, UPSERTED_ID, upsertItems, IndexOperationRequest.Operation.Upsert)
service.add(request)
result = service.get(INDEX, TYPE, UPSERTED_ID)
Assert.assertEquals(upsertItems, result)
List<IndexOperationRequest> deletes = new ArrayList<>()
deletes.add(new IndexOperationRequest(INDEX, TYPE, TEST_ID, null, IndexOperationRequest.Operation.Delete))
deletes.add(new IndexOperationRequest(INDEX, TYPE, UPSERTED_ID, null, IndexOperationRequest.Operation.Delete))
service.bulk(deletes)
Assert.assertNull(service.get(INDEX, TYPE, TEST_ID))
Assert.assertNull(service.get(INDEX, TYPE, UPSERTED_ID))
}
@Test
void testGetBulkResponsesWithErrors() {
def ops = [
new IndexOperationRequest(INDEX, TYPE, "1", [ "msg": "one", intField: 1], IndexOperationRequest.Operation.Index), // OK
new IndexOperationRequest(INDEX, TYPE, "2", [ "msg": "two", intField: 1], IndexOperationRequest.Operation.Create), // already exists
new IndexOperationRequest(INDEX, TYPE, "1", [ "msg": "one", intField: "notaninteger"], IndexOperationRequest.Operation.Index) // can't parse int field
]
def response = service.bulk(ops)
assert response.hasErrors()
assert response.items.findAll {
def key = it.keySet().stream().findFirst().get()
it[key].containsKey("error")
}.size() == 2
}
}

View File

@ -104,11 +104,6 @@
<artifactId>poi-scratchpad</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ssl-context-service</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-mock</artifactId>

View File

@ -19,7 +19,12 @@ package org.apache.nifi.processors.email;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
@ -27,20 +32,57 @@ import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.net.ssl.SSLContext;
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.KeyStoreUtils;
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.RestrictedSSLContextService;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestListenSMTP {
private static final String SSL_SERVICE_IDENTIFIER = "ssl-context";
private static TlsConfiguration tlsConfiguration;
private static SSLContextService sslContextService;
private static final int MESSAGES = 2;
@BeforeClass
public static void setTlsConfiguration() throws IOException, GeneralSecurityException {
final TlsConfiguration testTlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(testTlsConfiguration.getKeystorePath()).deleteOnExit();
new File(testTlsConfiguration.getTruststorePath()).deleteOnExit();
tlsConfiguration = new StandardTlsConfiguration(
testTlsConfiguration.getKeystorePath(),
testTlsConfiguration.getKeystorePassword(),
testTlsConfiguration.getKeyPassword(),
testTlsConfiguration.getKeystoreType(),
testTlsConfiguration.getTruststorePath(),
testTlsConfiguration.getTruststorePassword(),
testTlsConfiguration.getTruststoreType(),
TlsConfiguration.TLS_1_2_PROTOCOL
);
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration);
sslContextService = mock(RestrictedSSLContextService.class);
when(sslContextService.getIdentifier()).thenReturn(SSL_SERVICE_IDENTIFIER);
when(sslContextService.createContext()).thenReturn(sslContext);
when(sslContextService.createTlsConfiguration()).thenReturn(tlsConfiguration);
}
@Test
public void testListenSMTP() throws Exception {
final int port = NetworkUtils.availablePort();
@ -50,13 +92,12 @@ public class TestListenSMTP {
assertPortListening(port);
final Session session = getSession(port);
final int numMessages = 5;
for (int i = 0; i < numMessages; i++) {
for (int i = 0; i < MESSAGES; i++) {
sendMessage(session, i);
}
runner.shutdown();
runner.assertAllFlowFilesTransferred(ListenSMTP.REL_SUCCESS, numMessages);
runner.assertAllFlowFilesTransferred(ListenSMTP.REL_SUCCESS, MESSAGES);
}
@Test
@ -64,23 +105,21 @@ public class TestListenSMTP {
final int port = NetworkUtils.availablePort();
final TestRunner runner = newTestRunner(port);
final String tlsProtocol = TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion();
configureSslContextService(runner, tlsProtocol);
configureSslContextService(runner);
runner.setProperty(ListenSMTP.SSL_CONTEXT_SERVICE, SSL_SERVICE_IDENTIFIER);
runner.setProperty(ListenSMTP.CLIENT_AUTH, ClientAuth.NONE.name());
runner.assertValid();
runner.run(1, false);
assertPortListening(port);
final Session session = getSessionTls(port, tlsProtocol);
final Session session = getSessionTls(port, tlsConfiguration.getProtocol());
final int numMessages = 5;
for (int i = 0; i < numMessages; i++) {
for (int i = 0; i < MESSAGES; i++) {
sendMessage(session, i);
}
runner.shutdown();
runner.assertAllFlowFilesTransferred(ListenSMTP.REL_SUCCESS, numMessages);
runner.assertAllFlowFilesTransferred(ListenSMTP.REL_SUCCESS, MESSAGES);
}
@Test
@ -88,7 +127,7 @@ public class TestListenSMTP {
final int port = NetworkUtils.availablePort();
final TestRunner runner = newTestRunner(port);
configureSslContextService(runner, TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion());
configureSslContextService(runner);
runner.setProperty(ListenSMTP.SSL_CONTEXT_SERVICE, SSL_SERVICE_IDENTIFIER);
runner.setProperty(ListenSMTP.CLIENT_AUTH, ClientAuth.NONE.name());
runner.assertValid();
@ -105,7 +144,7 @@ public class TestListenSMTP {
}
@Test
public void testListenSMTPwithTooLargeMessage() throws Exception {
public void testListenSMTPwithTooLargeMessage() {
final int port = NetworkUtils.availablePort();
final TestRunner runner = newTestRunner(port);
runner.setProperty(ListenSMTP.SMTP_MAXIMUM_MSG_SIZE, "10 B");
@ -172,16 +211,8 @@ public class TestListenSMTP {
Transport.send(email);
}
private void configureSslContextService(final TestRunner runner, final String tlsProtocol) throws InitializationException {
final SSLContextService sslContextService = new StandardRestrictedSSLContextService();
private void configureSslContextService(final TestRunner runner) throws InitializationException {
runner.addControllerService(SSL_SERVICE_IDENTIFIER, 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, tlsProtocol);
runner.enableControllerService(sslContextService);
}
}

View File

@ -18,33 +18,27 @@
package org.apache.nifi.cluster.coordination.http.replication.okhttp
import org.apache.nifi.security.util.KeyStoreUtils
import org.apache.nifi.security.util.TlsConfiguration
import org.apache.nifi.util.NiFiProperties
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@RunWith(JUnit4.class)
class OkHttpReplicationClientTest extends GroovyTestCase {
private static final Logger logger = LoggerFactory.getLogger(OkHttpReplicationClientTest.class)
private static TlsConfiguration tlsConfiguration
@BeforeClass
static void setUpOnce() throws Exception {
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore()
new File(tlsConfiguration.keystorePath).deleteOnExit()
new File(tlsConfiguration.truststorePath).deleteOnExit()
}
private static NiFiProperties mockNiFiProperties() {
[getClusterNodeConnectionTimeout: { -> "10 ms" },
getClusterNodeReadTimeout : { -> "10 ms" },
getProperty : { String prop ->
logger.mock("Requested getProperty(${prop}) -> \"\"")
""
}] as NiFiProperties
return NiFiProperties.createBasicNiFiProperties(null)
}
@Test
@ -52,7 +46,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Arrange
def headers = ["Content-Length": "123", "Other-Header": "arbitrary value"]
String method = "DELETE"
logger.info("Original headers: ${headers}")
NiFiProperties mockProperties = mockNiFiProperties()
@ -60,7 +53,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
client.checkContentLengthHeader(method, headers)
logger.info("Checked headers: ${headers}")
// Assert
assert headers.size() == 2
@ -72,7 +64,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Arrange
def headers = ["Content-Length": "123", "Other-Header": "arbitrary value"]
String method = "delete"
logger.info("Original headers: ${headers}")
NiFiProperties mockProperties = mockNiFiProperties()
@ -80,7 +71,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
client.checkContentLengthHeader(method, headers)
logger.info("Checked headers: ${headers}")
// Assert
assert headers.size() == 2
@ -100,11 +90,7 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
zeroOrNullContentLengths.each { String contentLength ->
def headers = ["Content-Length": contentLength, "Other-Header": "arbitrary value"]
logger.info("Original headers: ${headers}")
logger.info("Trying method ${method}")
client.checkContentLengthHeader(method, headers)
logger.info("Checked headers: ${headers}")
// Assert
assert headers.size() == 2
@ -116,7 +102,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
void testShouldNotReplaceNonZeroContentLengthHeaderOnOtherMethod() {
// Arrange
def headers = ["Content-Length": "123", "Other-Header": "arbitrary value"]
logger.info("Original headers: ${headers}")
NiFiProperties mockProperties = mockNiFiProperties()
@ -126,9 +111,7 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
nonDeleteMethods.each { String method ->
logger.info("Trying method ${method}")
client.checkContentLengthHeader(method, headers)
logger.info("Checked headers: ${headers}")
// Assert
assert headers.size() == 2
@ -140,12 +123,12 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
void testShouldUseKeystorePasswordIfKeyPasswordIsBlank() {
// Arrange
Map propsMap = [
(NiFiProperties.SECURITY_TRUSTSTORE) : "./src/test/resources/conf/truststore.jks",
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_KEYSTORE) : "./src/test/resources/conf/keystore.jks",
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : "passwordpassword",
(NiFiProperties.SECURITY_TRUSTSTORE) : tlsConfiguration.truststorePath,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : tlsConfiguration.truststoreType.type,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): tlsConfiguration.truststorePassword,
(NiFiProperties.SECURITY_KEYSTORE) : tlsConfiguration.keystorePath,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : tlsConfiguration.keystoreType.type,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : tlsConfiguration.keystorePassword,
(NiFiProperties.SECURITY_KEY_PASSWD) : "",
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
@ -154,7 +137,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
logger.info("Created secure HTTPS client with TLS configured: ${client.isTLSConfigured()}")
// Assert
assert client.isTLSConfigured()
@ -164,12 +146,12 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
void testShouldUseKeystorePasswordIfKeyPasswordIsNull() {
// Arrange
Map flowfileEncryptionProps = [
(NiFiProperties.SECURITY_TRUSTSTORE) : "./src/test/resources/conf/truststore.jks",
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_KEYSTORE) : "./src/test/resources/conf/keystore.jks",
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : "passwordpassword",
(NiFiProperties.SECURITY_TRUSTSTORE) : tlsConfiguration.truststorePath,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : tlsConfiguration.truststoreType.type,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): tlsConfiguration.truststorePassword,
(NiFiProperties.SECURITY_KEYSTORE) : tlsConfiguration.keystorePath,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : tlsConfiguration.keystoreType.type,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : tlsConfiguration.keystorePassword,
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
]
@ -177,7 +159,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
logger.info("Created secure HTTPS client with TLS configured: ${client.isTLSConfigured()}")
// Assert
assert client.isTLSConfigured()
@ -187,13 +168,13 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
void testShouldFailIfKeyPasswordIsSetButKeystorePasswordIsBlank() {
// Arrange
Map propsMap = [
(NiFiProperties.SECURITY_TRUSTSTORE) : "./src/test/resources/conf/truststore.jks",
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_KEYSTORE) : "./src/test/resources/conf/keystore.jks",
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE) : tlsConfiguration.truststorePath,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : tlsConfiguration.truststoreType.type,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): tlsConfiguration.truststorePassword,
(NiFiProperties.SECURITY_KEYSTORE) : tlsConfiguration.keystorePath,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : tlsConfiguration.keystoreType.type,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : tlsConfiguration.keystorePassword,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : "",
(NiFiProperties.SECURITY_KEY_PASSWD) : "passwordpassword",
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
(NiFiProperties.WEB_HTTPS_PORT) : "51552",
]
@ -201,7 +182,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
logger.info("Created (invalid) secure HTTPS client with TLS configured: ${client.isTLSConfigured()}")
// Assert
assert !client.isTLSConfigured()
@ -211,11 +191,11 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
void testShouldFailIfKeyPasswordAndKeystorePasswordAreBlank() {
// Arrange
Map propsMap = [
(NiFiProperties.SECURITY_TRUSTSTORE) : "./src/test/resources/conf/truststore.jks",
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_KEYSTORE) : "./src/test/resources/conf/keystore.jks",
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE) : tlsConfiguration.truststorePath,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : tlsConfiguration.truststoreType.type,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): tlsConfiguration.truststorePassword,
(NiFiProperties.SECURITY_KEYSTORE) : tlsConfiguration.keystorePath,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : tlsConfiguration.keystoreType.type,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : "",
(NiFiProperties.SECURITY_KEY_PASSWD) : "",
(NiFiProperties.WEB_HTTPS_HOST) : "localhost",
@ -225,7 +205,6 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
logger.info("Created (invalid) secure HTTPS client with TLS configured: ${client.isTLSConfigured()}")
// Assert
assert !client.isTLSConfigured()
@ -238,13 +217,12 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
(NiFiProperties.WEB_HTTPS_PORT): "51552",]
Map tlsPropsMap = [
(NiFiProperties.SECURITY_KEYSTORE) : "./src/test/resources/conf/keystore.jks",
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : "passwordpassword",
(NiFiProperties.SECURITY_KEY_PASSWD) : "",
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE) : "./src/test/resources/conf/truststore.jks",
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : "JKS",
(NiFiProperties.SECURITY_TRUSTSTORE) : tlsConfiguration.truststorePath,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : tlsConfiguration.truststoreType.type,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): tlsConfiguration.truststorePassword,
(NiFiProperties.SECURITY_KEYSTORE) : tlsConfiguration.keystorePath,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : tlsConfiguration.keystoreType.type,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : tlsConfiguration.keystorePassword
] + propsMap
@ -258,14 +236,8 @@ class OkHttpReplicationClientTest extends GroovyTestCase {
// Act
OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties)
logger.info("Created plaintext HTTP client with TLS configured: ${client.isTLSConfigured()}")
OkHttpReplicationClient invalidTlsClient = new OkHttpReplicationClient(mockInvalidTLSNiFiProperties)
logger.info("Created (invalid) secure HTTPS client with TLS configured: ${invalidTlsClient.isTLSConfigured()}")
OkHttpReplicationClient tlsClient = new OkHttpReplicationClient(mockTLSNiFiProperties)
logger.info("Created secure HTTPS client with TLS configured: ${tlsClient.isTLSConfigured()}")
// Assert
assert !client.isTLSConfigured()

View File

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<configuration scan="true" scanPeriod="30 seconds">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-4r [%t] %-5p %c - %m%n</pattern>
</encoder>
</appender>
<!-- valid logging levels: TRACE, DEBUG, INFO, WARN, ERROR -->
<logger name="org.apache.nifi" level="INFO"/>
<logger name="org.apache.nifi.security.util" level="DEBUG"/>
<logger name="org.apache.nifi.cluster.coordination.http.replication.okhttp" level="DEBUG"/>
<logger name="org.apache.nifi.engine.FlowEngine" level="OFF" />
<logger name="org.apache.nifi.cluster.coordination.node" level="DEBUG" />
<logger name="org.apache.curator.framework.recipes.leader.LeaderSelector" level="OFF" />
<logger name="org.apache.zookeeper.ClientCnxn" level="ERROR" />
<logger name="org.apache.curator.framework.imps.CuratorFrameworkImpl" level="OFF" />
<!-- Logger for managing logging statements for nifi clusters. -->
<logger name="org.apache.nifi.cluster" level="INFO"/>
<!--
Logger for logging HTTP requests received by the web server. Setting
log level to 'debug' activates HTTP request logging.
-->
<logger name="org.apache.nifi.server.JettyServer" level="INFO"/>
<!-- Logger for managing logging statements for jetty -->
<logger name="org.mortbay" level="INFO"/>
<logger name="org.apache.nifi.processors.standard" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -275,8 +275,6 @@
<exclude>src/test/resources/old-swap-file.swap</exclude>
<exclude>src/test/resources/xxe_template.xml</exclude>
<exclude>src/test/resources/swap/444-old-swap-file.swap</exclude>
<exclude>src/test/resources/ZooKeeperStateServerConfigurationsTest/keystore.jks</exclude>
<exclude>src/test/resources/ZooKeeperStateServerConfigurationsTest/truststore.jks</exclude>
</excludes>
</configuration>
</plugin>

View File

@ -19,38 +19,21 @@ package org.apache.nifi.controller.queue.clustered.server
import org.apache.nifi.events.EventReporter
import org.apache.nifi.reporting.Severity
import org.apache.nifi.security.util.KeyStoreUtils
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.TlsPlatform
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.After
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLPeerUnverifiedException
import javax.net.ssl.SSLServerSocket
import java.security.Security
@RunWith(JUnit4.class)
class ConnectionLoadBalanceServerTest extends GroovyTestCase {
private static final Logger logger = LoggerFactory.getLogger(ConnectionLoadBalanceServerTest.class)
private static final String KEYSTORE_PATH = "src/test/resources/localhost-ks.jks"
private static final String KEYSTORE_PASSWORD = "OI7kMpWzzVNVx/JGhTL/0uO4+PWpGJ46uZ/pfepbkwI"
private static final KeystoreType KEYSTORE_TYPE = KeystoreType.JKS
private static final String TRUSTSTORE_PATH = "src/test/resources/localhost-ts.jks"
private static final String TRUSTSTORE_PASSWORD = "wAOR0nQJ2EXvOP0JZ2EaqA/n7W69ILS4sWAHghmIWCc"
private static final KeystoreType TRUSTSTORE_TYPE = KeystoreType.JKS
private static final String HOSTNAME = "localhost"
private static final int PORT = 54321
private static final int NUM_THREADS = 1
@ -63,13 +46,9 @@ class ConnectionLoadBalanceServerTest extends GroovyTestCase {
@BeforeClass
static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
tlsConfiguration = new StandardTlsConfiguration(KEYSTORE_PATH, KEYSTORE_PASSWORD, KEYSTORE_TYPE, TRUSTSTORE_PATH, TRUSTSTORE_PASSWORD, TRUSTSTORE_TYPE)
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore()
new File(tlsConfiguration.keystorePath).deleteOnExit()
new File(tlsConfiguration.truststorePath).deleteOnExit()
sslContext = SslContextFactory.createSslContext(tlsConfiguration)
}
@ -87,9 +66,7 @@ class ConnectionLoadBalanceServerTest extends GroovyTestCase {
@Test
void testRequestPeerListShouldUseTLS() {
// Arrange
logger.info("Creating SSL Context from TLS Configuration: ${tlsConfiguration}")
SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration)
logger.info("Created SSL Context: ${KeyStoreUtils.sslContextToString(sslContext)}")
def mockLBP = [
receiveFlowFiles: { Socket s, InputStream i, OutputStream o -> null }
@ -132,7 +109,7 @@ class ConnectionLoadBalanceServerTest extends GroovyTestCase {
receiveFlowFiles: { Socket s, InputStream i, OutputStream o -> null }
] as LoadBalanceProtocol
EventReporter mockER = [
reportEvent: { Severity s, String c, String m -> logger.mock("${s}: ${c} | ${m}") }
reportEvent: { Severity s, String c, String m -> }
] as EventReporter
def output = [debug: 0, error: 0]
@ -142,12 +119,8 @@ class ConnectionLoadBalanceServerTest extends GroovyTestCase {
// Override the threshold to 100 ms
communicateAction.EXCEPTION_THRESHOLD_MILLIS = 100
long listenerStart = System.currentTimeMillis()
// Act
CONNECTION_ATTEMPTS.times { int i ->
long now = System.currentTimeMillis()
logger.debug("Attempting connection ${i + 1} at ${now} [${now - listenerStart}]")
boolean printedError = communicateAction.handleTlsError(peerDescription, e)
if (printedError) {
output.error++
@ -156,17 +129,11 @@ class ConnectionLoadBalanceServerTest extends GroovyTestCase {
}
sleep(10)
}
logger.info("After ${CONNECTION_ATTEMPTS} attempts, debug: ${output.debug}, error: ${output.error}")
// Assert
logger.info("output.debug (${output.debug}) > output.error (${output.error}): ${output.debug > output.error}")
// Only enforce if the test completed in a reasonable amount of time (i.e. external delays did not influence the timing)
long testStopMillis = System.currentTimeMillis()
long testDurationMillis = testStopMillis - testStartMillis
if (testDurationMillis > MAX_TEST_DURATION_MILLIS) {
logger.warn("The test took ${testDurationMillis} ms, which is longer than the max duration ${MAX_TEST_DURATION_MILLIS} ms, so the timing may be suspect and the assertion will not be enforced")
} else {
if (testDurationMillis <= MAX_TEST_DURATION_MILLIS) {
assert output.debug > output.error
}

View File

@ -56,11 +56,9 @@ import org.apache.nifi.controller.repository.claim.ResourceClaimManager;
import org.apache.nifi.controller.repository.claim.StandardResourceClaimManager;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.provenance.ProvenanceRepository;
import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.security.util.KeyStoreUtils;
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.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@ -70,9 +68,11 @@ import org.mockito.stubbing.Answer;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@ -141,7 +141,7 @@ public class LoadBalancedQueueIT {
private final AtomicReference<LoadBalanceCompression> compressionReference = new AtomicReference<>();
@Before
public void setup() throws IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, TlsException {
public void setup() throws IOException, GeneralSecurityException {
compressionReference.set(LoadBalanceCompression.DO_NOT_COMPRESS);
nodeIdentifiers = new HashSet<>();
@ -190,13 +190,9 @@ public class LoadBalancedQueueIT {
clientRepoRecords = Collections.synchronizedList(new ArrayList<>());
clientFlowFileRepo = createFlowFileRepository(clientRepoRecords);
final String keystore = "src/test/resources/localhost-ks.jks";
final String keystorePass = "OI7kMpWzzVNVx/JGhTL/0uO4+PWpGJ46uZ/pfepbkwI";
final String keyPass = keystorePass;
final String truststore = "src/test/resources/localhost-ts.jks";
final String truststorePass = "wAOR0nQJ2EXvOP0JZ2EaqA/n7W69ILS4sWAHghmIWCc";
TlsConfiguration tlsConfiguration = new StandardTlsConfiguration(keystore, keystorePass, keyPass, KeystoreType.JKS,
truststore, truststorePass, KeystoreType.JKS, TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion());
TlsConfiguration tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
sslContext = SslContextFactory.createSslContext(tlsConfiguration);
}

View File

@ -28,11 +28,14 @@ import org.apache.nifi.controller.state.providers.AbstractTestStateProvider;
import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.mock.MockComponentLogger;
import org.apache.nifi.parameter.ParameterLookup;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
import org.apache.zookeeper.server.ServerCnxnFactory;
import org.apache.zookeeper.server.ZooKeeperServer;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -44,6 +47,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@ -64,14 +68,14 @@ public class ITZooKeeperStateProvider extends AbstractTestStateProvider {
private static ServerCnxnFactory serverConnectionFactory;
private static NiFiProperties nifiProperties;
private static final String CLIENT_KEYSTORE = "src/test/resources/localhost-ks.jks";
private static final String CLIENT_TRUSTSTORE = "src/test/resources/localhost-ts.jks";
private static final String CLIENT_KEYSTORE_TYPE = "JKS";
private static final String CLIENT_TRUSTSTORE_TYPE = "JKS";
private static final String SERVER_KEYSTORE = "src/test/resources/localhost-ks.jks";
private static final String SERVER_TRUSTSTORE = "src/test/resources/localhost-ts.jks";
private static final String KEYSTORE_PASSWORD = "OI7kMpWzzVNVx/JGhTL/0uO4+PWpGJ46uZ/pfepbkwI";
private static final String TRUSTSTORE_PASSWORD = "wAOR0nQJ2EXvOP0JZ2EaqA/n7W69ILS4sWAHghmIWCc";
private static TlsConfiguration tlsConfiguration;
@BeforeClass
public static void setTlsConfiguration() throws GeneralSecurityException, IOException {
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
}
@Before
public void setup() throws Exception {
@ -86,22 +90,22 @@ public class ITZooKeeperStateProvider extends AbstractTestStateProvider {
dataDir,
tempDir,
clientPort,
Paths.get(SERVER_KEYSTORE),
KEYSTORE_PASSWORD,
Paths.get(SERVER_TRUSTSTORE),
TRUSTSTORE_PASSWORD
Paths.get(tlsConfiguration.getKeystorePath()),
tlsConfiguration.getKeystorePassword(),
Paths.get(tlsConfiguration.getTruststorePath()),
tlsConfiguration.getTruststorePassword()
);
zkServer = serverConnectionFactory.getZooKeeperServer();
// Set up state provider (client) TLS properties, normally injected through StateProviderContext annotation
nifiProperties = createSecureClientProperties(
clientPort,
Paths.get(CLIENT_KEYSTORE),
CLIENT_KEYSTORE_TYPE,
KEYSTORE_PASSWORD,
Paths.get(CLIENT_TRUSTSTORE),
CLIENT_TRUSTSTORE_TYPE,
TRUSTSTORE_PASSWORD
Paths.get(tlsConfiguration.getKeystorePath()),
tlsConfiguration.getKeystoreType().getType(),
tlsConfiguration.getKeystorePassword(),
Paths.get(tlsConfiguration.getTruststorePath()),
tlsConfiguration.getTruststoreType().getType(),
tlsConfiguration.getTruststorePassword()
);
// Set up state provider properties
@ -138,12 +142,12 @@ public class ITZooKeeperStateProvider extends AbstractTestStateProvider {
}
propValueMap.put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, Boolean.TRUE.toString());
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE, CLIENT_KEYSTORE);
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_PASSWD, KEYSTORE_PASSWORD);
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, CLIENT_KEYSTORE_TYPE);
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE, CLIENT_TRUSTSTORE);
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_PASSWD, TRUSTSTORE_PASSWORD);
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, CLIENT_TRUSTSTORE_TYPE);
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
propValueMap.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
return propValueMap;
}

View File

@ -24,6 +24,8 @@ import org.apache.curator.utils.DefaultZookeeperFactory;
import org.apache.curator.utils.ZookeeperFactory;
import org.apache.nifi.controller.cluster.SecureClientZooKeeperFactory;
import org.apache.nifi.controller.cluster.ZooKeeperClientConfig;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
@ -32,6 +34,7 @@ import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -39,6 +42,7 @@ import org.junit.rules.ExpectedException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -50,9 +54,6 @@ import static org.junit.Assert.assertNotNull;
// Testing setting up a ZooKeeperStateServer with TLS
public class ITZooKeeperStateServerTLS {
private static final String KEY_STORE = getPath("keystore.jks");
private static final String TRUST_STORE = getPath("truststore.jks");
private static final String STORE_TYPE = "JKS";
private static final String INSECURE_ZOOKEEPER_PROPS = getPath("insecure.zookeeper.properties");
private static final String PARTIAL_ZOOKEEPER_PROPS = getPath("partial.zookeeper.properties");
private static final String SECURE_ZOOKEEPER_PROPS = getPath("secure.zookeeper.properties");
@ -60,9 +61,41 @@ public class ITZooKeeperStateServerTLS {
private static final String ZOOKEEPER_CNXN_FACTORY = "org.apache.zookeeper.server.NettyServerCnxnFactory";
private static final String QUORUM_CONNECT_STRING = "node0.apache.org:2281,node1.apache.org:2281";
private static final Map<String, String> SECURE_NIFI_PROPS = new HashMap<>();
private static final Map<String, String> SECURE_ZOOKEEPER_NIFI_PROPS = new HashMap<>();
private static TlsConfiguration tlsConfiguration;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@BeforeClass
public static void setTlsConfiguration() throws GeneralSecurityException, IOException {
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
SECURE_NIFI_PROPS.put(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, SECURE_ZOOKEEPER_PROPS);
SECURE_NIFI_PROPS.put(NiFiProperties.WEB_HTTPS_PORT, "8443");
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
SECURE_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, SECURE_ZOOKEEPER_PROPS);
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.WEB_HTTPS_PORT, "8443");
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
SECURE_ZOOKEEPER_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
}
private static final Map<String, String> INSECURE_NIFI_PROPS = new HashMap<String, String>() {{
put(ZOOKEEPER_PROPERTIES_FILE_KEY, INSECURE_ZOOKEEPER_PROPS);
put(NiFiProperties.WEB_HTTP_HOST, "localhost");
@ -70,32 +103,6 @@ public class ITZooKeeperStateServerTLS {
put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "false");
}};
private static final String TEST_PASSWORD = "passwordpassword";
private static final Map<String, String> SECURE_NIFI_PROPS = new HashMap<String, String>() {{
put(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, SECURE_ZOOKEEPER_PROPS);
put(NiFiProperties.WEB_HTTPS_PORT, "8443");
put(NiFiProperties.SECURITY_KEYSTORE, KEY_STORE);
put(NiFiProperties.SECURITY_KEYSTORE_TYPE, STORE_TYPE);
put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, TEST_PASSWORD);
put(NiFiProperties.SECURITY_TRUSTSTORE, TRUST_STORE);
put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, STORE_TYPE);
put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, TEST_PASSWORD);
put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
}};
private static final Map<String, String> SECURE_ZOOKEEPER_NIFI_PROPS = new HashMap<String, String>() {{
put(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, SECURE_ZOOKEEPER_PROPS);
put(NiFiProperties.WEB_HTTPS_PORT, "8443");
put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE, KEY_STORE);
put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_TYPE, STORE_TYPE);
put(NiFiProperties.ZOOKEEPER_SECURITY_KEYSTORE_PASSWD, TEST_PASSWORD);
put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE, TRUST_STORE);
put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_TYPE, STORE_TYPE);
put(NiFiProperties.ZOOKEEPER_SECURITY_TRUSTSTORE_PASSWD, TEST_PASSWORD);
put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
}};
private NiFiProperties niFiProps;
private static NiFiProperties clientProperties;
private QuorumPeerConfig quorumPeerConfig;
@ -245,7 +252,6 @@ public class ITZooKeeperStateServerTLS {
@Test
public void testSecureClientQuorumConnectString() throws Exception {
final int actualPort = Integer.parseInt(secureZooKeeperProps.getProperty("secureClientPort", "0"));
final String connect = "localhost:" + actualPort;
final NiFiProperties validZkClientProps = NiFiProperties.createBasicNiFiProperties(null, new HashMap<String, String>() {{
putAll(SECURE_NIFI_PROPS);
put(ZOOKEEPER_PROPERTIES_FILE_KEY, SECURE_ZOOKEEPER_PROPS);
@ -271,7 +277,6 @@ public class ITZooKeeperStateServerTLS {
// Connect to an insecure ZooKeeperStateServer with an insecure client (ensure insecure setup still works)
@Test
public void testInsecureZooKeeperWithInsecureClient() throws Exception {
final int actualPort = Integer.parseInt(insecureZooKeeperProps.getProperty("clientPort", "0"));
final String connect = "localhost:" + 2381;
final NiFiProperties validZkClientProps = NiFiProperties.createBasicNiFiProperties(null, new HashMap<String, String>() {{
putAll(INSECURE_NIFI_PROPS);
@ -281,8 +286,6 @@ public class ITZooKeeperStateServerTLS {
server = ZooKeeperStateServer.create(validZkClientProps);
assertNotNull(server);
final int serverPort = server.getQuorumPeerConfig().getClientPortAddress().getPort();
//assertEquals(actualPort, 2381);
server.start();
// Set up a ZK client
@ -318,7 +321,7 @@ public class ITZooKeeperStateServerTLS {
final String testPath = "/test";
// Expect this to fail with ConnectionLossException
final String createResult = client.create().forPath(testPath, new byte[0]);
client.create().forPath(testPath, new byte[0]);
}
// Fail to connect to a secure ZooKeeperStateServer with missing client configuration
@ -516,12 +519,12 @@ public class ITZooKeeperStateServerTLS {
// TODO: port being set needs to be based on port set in nifi.properties, should create client in the same
clientProperties = createSecureClientProperties(
port,
Paths.get(KEY_STORE),
STORE_TYPE,
TEST_PASSWORD,
Paths.get(TRUST_STORE),
STORE_TYPE,
TEST_PASSWORD
Paths.get(tlsConfiguration.getKeystorePath()),
tlsConfiguration.getKeystoreType().getType(),
tlsConfiguration.getKeystorePassword(),
Paths.get(tlsConfiguration.getTruststorePath()),
tlsConfiguration.getTruststoreType().getType(),
tlsConfiguration.getTruststorePassword()
);
final ZooKeeperClientConfig zkClientConfig =

View File

@ -17,16 +17,20 @@
package org.apache.nifi.controller.state.server;
import org.apache.commons.io.FileUtils;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
import org.apache.zookeeper.server.ServerCnxnFactory;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -35,38 +39,23 @@ import java.util.Properties;
// This class tests the behaviors involved with the ZooKeeperStateServer::create method. The servers are not started,
// and TLS connections are not used.
public class TestZooKeeperStateServerConfigurations {
private static final String KEY_STORE = getPath("keystore.jks");
private static final String TRUST_STORE = getPath("truststore.jks");
private static final String INSECURE_ZOOKEEPER_PROPS = getPath("insecure.zookeeper.properties");
private static final String SECURE_ZOOKEEPER_PROPS = getPath("secure.zookeeper.properties");
private static final String ZOOKEEPER_PROPERTIES_FILE_KEY = "nifi.state.management.embedded.zookeeper.properties";
private static final String ZOOKEEPER_CNXN_FACTORY = "org.apache.zookeeper.server.NettyServerCnxnFactory";
private static final String KEYSTORE_PASSWORD = "passwordpassword";
private static final String TRUSTSTORE_PASSWORD = "passwordpassword";
private static final String STORE_TYPE = "JKS";
private static final Map<String, String> INSECURE_PROPS = new HashMap<String, String>() {{
put(ZOOKEEPER_PROPERTIES_FILE_KEY, INSECURE_ZOOKEEPER_PROPS);
}};
private static final Map<String, String> SECURE_NIFI_PROPS = new HashMap<>();
private static final Map<String, String> INSECURE_NIFI_PROPS = new HashMap<String, String>() {{
putAll(INSECURE_PROPS);
put(NiFiProperties.WEB_HTTP_PORT, "8080");
put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "false");
}};
private static final Map<String, String> SECURE_NIFI_PROPS = new HashMap<String, String>() {{
put(ZOOKEEPER_PROPERTIES_FILE_KEY, SECURE_ZOOKEEPER_PROPS);
put(NiFiProperties.WEB_HTTPS_PORT, "8443");
put(NiFiProperties.SECURITY_KEYSTORE, KEY_STORE);
put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, KEYSTORE_PASSWORD);
put(NiFiProperties.SECURITY_KEYSTORE_TYPE, STORE_TYPE);
put(NiFiProperties.SECURITY_TRUSTSTORE, TRUST_STORE);
put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, TRUSTSTORE_PASSWORD);
put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, STORE_TYPE);
put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
}};
private NiFiProperties secureNiFiProps;
private NiFiProperties insecureNiFiProps;
private QuorumPeerConfig secureQuorumPeerConfig;
@ -74,6 +63,25 @@ public class TestZooKeeperStateServerConfigurations {
private Properties secureZooKeeperProps;
private Properties insecureZooKeeperProps;
private static TlsConfiguration tlsConfiguration;
@BeforeClass
public static void setTlsConfiguration() throws GeneralSecurityException, IOException {
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
SECURE_NIFI_PROPS.put(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, SECURE_ZOOKEEPER_PROPS);
SECURE_NIFI_PROPS.put(NiFiProperties.WEB_HTTPS_PORT, "8443");
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
SECURE_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
}
@Before
public void setupWithValidProperties() throws IOException, QuorumPeerConfig.ConfigException {

View File

@ -1,22 +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.
log4j.rootLogger=INFO,console
log4j.category.org.apache.nifi=DEBUG
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-4r [%t] %-5p %c{3} - %m%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>./target/log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.nifi" level="INFO"/>
<logger name="org.apache.nifi.controller.tasks" level="DEBUG"/>
<logger name="org.apache.nifi.controller.service" level="DEBUG"/>
<logger name="org.apache.nifi.encrypt" level="DEBUG"/>
<logger name="org.apache.nifi.security.util.crypto" level="DEBUG"/>
<logger name="org.apache.nifi.controller.repository.crypto" level="DEBUG"/>
<logger name="org.apache.nifi.security.repository" level="DEBUG"/>
<logger name="org.apache.nifi.controller.service.mock" level="ERROR"/>
<logger name="StandardProcessSession.claims" level="INFO" />
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -25,7 +25,7 @@
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>

View File

@ -16,14 +16,11 @@
*/
package org.apache.nifi.web.server
import org.apache.log4j.AppenderSkeleton
import org.apache.log4j.spi.LoggingEvent
import org.apache.nifi.bundle.Bundle
import org.apache.nifi.nar.ExtensionManagerHolder
import org.apache.nifi.nar.ExtensionMapping
import org.apache.nifi.nar.SystemBundle
import org.apache.nifi.processor.DataUnit
import org.apache.nifi.remote.io.socket.NetworkUtils
import org.apache.nifi.security.util.KeyStoreUtils
import org.apache.nifi.security.util.StandardTlsConfiguration
import org.apache.nifi.security.util.TlsConfiguration
import org.apache.nifi.security.util.TlsPlatform
@ -81,44 +78,41 @@ class JettyServerGroovyTest extends GroovyTestCase {
private static final int HTTPS_PORT = NetworkUtils.getAvailableTcpPort()
private static final String HTTPS_HOSTNAME = "localhost"
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 STORE_PASSWORD = "passwordpassword"
private static final String STORE_TYPE = "JKS"
private static final String TLS_1_3_PROTOCOL = "TLSv1.3"
private static final List<String> TLS_1_3_CIPHER_SUITES = ["TLS_AES_128_GCM_SHA256"]
private static final TlsConfiguration TLS_CONFIGURATION = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore()
// These protocol versions should not ever be supported
static private final List<String> LEGACY_TLS_PROTOCOLS = ["TLS", "TLSv1", "TLSv1.1", "SSL", "SSLv2", "SSLv2Hello", "SSLv3"]
NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT) : HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST) : HTTPS_HOSTNAME,
(NiFiProperties.SECURITY_KEYSTORE) : KEYSTORE_PATH,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : STORE_PASSWORD,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : STORE_TYPE,
(NiFiProperties.SECURITY_TRUSTSTORE) : TRUSTSTORE_PATH,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): STORE_PASSWORD,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : STORE_TYPE,
(NiFiProperties.SECURITY_KEYSTORE) : TLS_CONFIGURATION.keystorePath,
(NiFiProperties.SECURITY_KEYSTORE_PASSWD) : TLS_CONFIGURATION.keystorePassword,
(NiFiProperties.SECURITY_KEYSTORE_TYPE) : TLS_CONFIGURATION.keystoreType.type,
(NiFiProperties.SECURITY_TRUSTSTORE) : TLS_CONFIGURATION.truststorePath,
(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): TLS_CONFIGURATION.truststorePassword,
(NiFiProperties.SECURITY_TRUSTSTORE_TYPE) : TLS_CONFIGURATION.truststoreType.type,
]))
@BeforeClass
static void setUpOnce() throws Exception {
new File(TLS_CONFIGURATION.keystorePath).deleteOnExit()
new File(TLS_CONFIGURATION.truststorePath).deleteOnExit()
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
TestAppender.reset()
}
@After
void tearDown() throws Exception {
// Cleans up the EMH so it can be reinitialized when a new Jetty server starts
ExtensionManagerHolder.INSTANCE = null
TestAppender.reset()
}
@Test
@ -143,12 +137,9 @@ class JettyServerGroovyTest extends GroovyTestCase {
// Act
boolean bothConfigsPresent = JettyServer.bothHttpAndHttpsConnectorsConfigured(mockProps)
logger.info("Both configs present: ${bothConfigsPresent}")
def log = TestAppender.getLogLines()
// Assert
assert bothConfigsPresent
assert !log.isEmpty()
assert log.first() =~ "Both the HTTP and HTTPS connectors are configured in nifi.properties. Only one of these connectors should be configured. See the NiFi Admin Guide for more details"
}
@Test
@ -163,7 +154,6 @@ class JettyServerGroovyTest extends GroovyTestCase {
getSslPort : { -> null },
getProperty: { String prop ->
String value = httpMap[prop] ?: "no_value"
logger.mock("getProperty(${prop}) -> ${value}")
value
},
] as NiFiProperties
@ -177,7 +167,6 @@ class JettyServerGroovyTest extends GroovyTestCase {
getSslPort : { -> DEFAULT_HTTPS_PORT },
getProperty: { String prop ->
String value = httpsMap[prop] ?: "no_value"
logger.mock("getProperty(${prop}) -> ${value}")
value
},
] as NiFiProperties
@ -188,15 +177,10 @@ class JettyServerGroovyTest extends GroovyTestCase {
boolean bothConfigsPresentForHttps = JettyServer.bothHttpAndHttpsConnectorsConfigured(httpsProps)
logger.info("Both configs present for HTTPS properties: ${bothConfigsPresentForHttps}")
def log = TestAppender.getLogLines()
// Assert
assert !bothConfigsPresentForHttp
assert !bothConfigsPresentForHttps
// Verifies that the warning was not logged (messages are duplicated because of log4j.properties settings)
assert log.size() == 4
assert log.every { it =~ "Both configs present for HTTPS? properties: false" }
}
@Test
@ -240,80 +224,6 @@ class JettyServerGroovyTest extends GroovyTestCase {
// Assertions defined above
}
/**
* Regression test added after NiFi 1.12.0 because Jetty upgrade to 9.4.26 no longer works
* with multiple certificate keystores.
*/
@Test
void testShouldStartWithMultipleCertificatePKCS12Keystore() {
// Arrange
final String externalHostname = "localhost"
NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT): HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST): externalHostname,
(NiFiProperties.SECURITY_KEYSTORE): "src/test/resources/multiple_cert_keystore.p12",
(NiFiProperties.SECURITY_KEYSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_KEYSTORE_TYPE): "PKCS12",
(NiFiProperties.NAR_LIBRARY_DIRECTORY): "target/"
]))
JettyServer jetty = createJettyServer(httpsProps)
Server internalServer = jetty.server
List<Connector> connectors = Arrays.asList(internalServer.connectors)
// Act
jetty.start()
// Assert
assertServerConnector(connectors, externalHostname, HTTPS_PORT)
// Clean up
jetty.stop()
}
/**
* Regression test added after NiFi 1.12.0 because Jetty upgrade to 9.4.26 no longer works
* with multiple certificate keystores.
*/
@Test
void testShouldStartWithMultipleCertificateJKSKeystore() {
// Arrange
final String externalHostname = "localhost"
NiFiProperties httpsProps = new NiFiProperties(new Properties([
(NiFiProperties.WEB_HTTPS_PORT): HTTPS_PORT as String,
(NiFiProperties.WEB_HTTPS_HOST): externalHostname,
(NiFiProperties.SECURITY_KEYSTORE): "src/test/resources/multiple_cert_keystore.jks",
(NiFiProperties.SECURITY_KEYSTORE_PASSWD): "passwordpassword",
(NiFiProperties.SECURITY_KEYSTORE_TYPE): "JKS",
(NiFiProperties.NAR_LIBRARY_DIRECTORY): "target/"
]))
JettyServer jetty = createJettyServer(httpsProps)
Server internalServer = jetty.server
List<Connector> connectors = Arrays.asList(internalServer.connectors)
// Act
jetty.start()
// Assert
assertServerConnector(connectors, externalHostname, HTTPS_PORT)
// Clean up
jetty.stop()
}
private static JettyServer createJettyServer(NiFiProperties httpsProps) {
Server internalServer = new Server()
JettyServer jetty = new JettyServer(internalServer, httpsProps)
jetty.systemBundle = SystemBundle.create(httpsProps)
jetty.bundles = [] as Set<Bundle>
jetty.extensionMapping = [size: { -> 0 }] as ExtensionMapping
jetty.configureHttpsConnector(internalServer, new HttpConfiguration())
jetty
}
@Test
void testShouldConfigureHTTPSConnector() {
// Arrange
@ -523,35 +433,3 @@ class JettyServerGroovyTest extends GroovyTestCase {
assert !filterNames.contains("ContentLengthFilter")
}
}
class TestAppender extends AppenderSkeleton {
static final List<LoggingEvent> events = new ArrayList<>()
@Override
protected void append(LoggingEvent e) {
synchronized (events) {
events.add(e)
}
}
static void reset() {
synchronized (events) {
events.clear()
}
}
@Override
void close() {
}
@Override
boolean requiresLayout() {
return false
}
static List<String> getLogLines() {
synchronized (events) {
events.collect { LoggingEvent le -> le.getRenderedMessage() }
}
}
}

View File

@ -1,28 +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.
#
log4j.rootLogger=INFO,console,test
log4j.logger.org.apache.nifi.web=DEBUG,console,test
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
log4j.appender.test=org.apache.nifi.web.server.TestAppender
log4j.appender.test.layout=org.apache.log4j.PatternLayout
log4j.appender.test.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-4r [%t] %-5p %c - %m%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>./target/log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.nifi" level="INFO"/>
<logger name="org.apache.nifi.web.server" level="DEBUG"/>
<logger name="org.apache.nifi.web.util" level="DEBUG"/>
<logger name="org.apache.nifi.web.filter" level="DEBUG"/>
<logger name="org.eclipse.jetty.server" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -17,6 +17,8 @@
package org.apache.nifi.web.security.saml.impl;
import org.apache.commons.lang3.SystemUtils;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.saml.SAMLConfigurationFactory;
import org.apache.nifi.web.security.saml.SAMLService;
@ -28,12 +30,14 @@ import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -43,7 +47,6 @@ public class TestStandardSAMLService {
private SAMLConfigurationFactory samlConfigurationFactory;
private SAMLService samlService;
@BeforeClass
public static void setUpSuite() {
Assume.assumeTrue("Test only runs on *nix", !SystemUtils.IS_OS_WINDOWS);
@ -62,18 +65,22 @@ public class TestStandardSAMLService {
}
@Test
public void testSamlEnabledWithFileBasedIdpMetadata() {
public void testSamlEnabledWithFileBasedIdpMetadata() throws GeneralSecurityException, IOException {
final String spEntityId = "org:apache:nifi";
final File idpMetadataFile = new File("src/test/resources/saml/sso-circle-meta.xml");
final String baseUrl = "https://localhost:8443/nifi-api";
when(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE)).thenReturn("src/test/resources/saml/keystore.jks");
when(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD)).thenReturn("passwordpassword");
when(properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD)).thenReturn("passwordpassword");
when(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE)).thenReturn("JKS");
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE)).thenReturn("src/test/resources/saml/truststore.jks");
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD)).thenReturn("passwordpassword");
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE)).thenReturn("JKS");
final TlsConfiguration tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
when(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE)).thenReturn(tlsConfiguration.getKeystorePath());
when(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD)).thenReturn(tlsConfiguration.getKeystorePassword());
when(properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD)).thenReturn(tlsConfiguration.getKeyPassword());
when(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE)).thenReturn(tlsConfiguration.getKeystoreType().getType());
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE)).thenReturn(tlsConfiguration.getTruststorePath());
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD)).thenReturn(tlsConfiguration.getTruststorePassword());
when(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE)).thenReturn(tlsConfiguration.getTruststoreType().getType());
when(properties.getPropertyKeys()).thenReturn(new HashSet<>(Arrays.asList(
NiFiProperties.SECURITY_KEYSTORE,
NiFiProperties.SECURITY_KEYSTORE_PASSWD,
@ -110,25 +117,9 @@ public class TestStandardSAMLService {
@Test
public void testInitializeWhenSamlNotEnabled() {
when(properties.isSamlEnabled()).thenReturn(false);
// initialize the saml service
samlService.initialize();
assertFalse(samlService.isSamlEnabled());
// methods should throw IllegalStateException...
try {
samlService.initializeServiceProvider("https://localhost:8443/nifi-api");
fail("Should have thrown exception");
} catch (IllegalStateException e) {
}
try {
samlService.getServiceProviderMetadata();
fail("Should have thrown exception");
} catch (IllegalStateException e) {
}
assertThrows(IllegalStateException.class, () -> samlService.initializeServiceProvider("https://localhost:8443/nifi-api"));
assertThrows(IllegalStateException.class, () -> samlService.getServiceProviderMetadata());
}
}

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-4r [%t] %-5p %c - %m%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>./target/log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.nifi" level="INFO"/>
<logger name="org.apache.nifi.web.api" level="DEBUG"/>
<logger name="org.apache.nifi.web.server" level="DEBUG"/>
<logger name="org.apache.nifi.web.security.requests" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -22,18 +22,12 @@ import io.grpc.ManagedChannel;
import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.reporting.InitializationException;
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.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@ -43,38 +37,8 @@ import static org.mockito.Mockito.when;
public class ITListenGRPC {
private static final String HOST = "localhost";
private static final String CERT_DN = "CN=localhost, OU=NIFI";
private static final String SOURCE_SYSTEM_UUID = "FAKE_UUID";
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 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 static void useSSLContextService(final TestRunner controller, 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(InvokeGRPC.PROP_SSL_CONTEXT_SERVICE, "ssl-service");
}
@Test
public void testSuccessfulRoundTrip() throws Exception {
final int randPort = TestGRPCClient.randomPort();
@ -200,195 +164,4 @@ public class ITListenGRPC {
channel.shutdown();
}
}
@Test
public void testSecureTwoWaySSL() throws Exception {
final int randPort = TestGRPCClient.randomPort();
final Map<String, String> sslProperties = getKeystoreProperties();
sslProperties.putAll(getTruststoreProperties());
final ManagedChannel channel = TestGRPCClient.buildChannel(HOST, randPort, sslProperties);
final FlowFileServiceGrpc.FlowFileServiceBlockingStub stub = FlowFileServiceGrpc.newBlockingStub(channel);
final ListenGRPC listenGRPC = new ListenGRPC();
final TestRunner runner = TestRunners.newTestRunner(listenGRPC);
runner.setProperty(ListenGRPC.PROP_SERVICE_PORT, String.valueOf(randPort));
runner.setProperty(ListenGRPC.PROP_USE_SECURE, "true");
useSSLContextService(runner, sslProperties);
final ProcessContext processContext = runner.getProcessContext();
final ProcessSessionFactory processSessionFactory = runner.getProcessSessionFactory();
try {
// start the server. The order of the following statements shouldn't matter, because the
// startServer() method waits for a processSessionFactory to be available to it.
listenGRPC.startServer(processContext);
listenGRPC.onTrigger(processContext, processSessionFactory);
final FlowFileRequest ingestFile = FlowFileRequest.newBuilder()
.putAttributes("FOO", "BAR")
.setContent(ByteString.copyFrom("content".getBytes()))
.build();
final FlowFileReply reply = stub.send(ingestFile);
assertThat(reply.getResponseCode(), equalTo(FlowFileReply.ResponseCode.SUCCESS));
assertThat(reply.getBody(), equalTo("FlowFile successfully received."));
runner.assertTransferCount(ListenGRPC.REL_SUCCESS, 1);
final List<MockFlowFile> successFiles = runner.getFlowFilesForRelationship(ListenGRPC.REL_SUCCESS);
assertThat(successFiles.size(), equalTo(1));
final MockFlowFile mockFlowFile = successFiles.get(0);
assertThat(mockFlowFile.getAttribute("FOO"), equalTo("BAR"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_HOST), equalTo("127.0.0.1"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_USER_DN), equalTo(CERT_DN));
} finally {
// stop the server
listenGRPC.stopServer(processContext);
channel.shutdown();
}
}
@Test
public void testSecureOneWaySSL() throws Exception {
final int randPort = TestGRPCClient.randomPort();
final Map<String, String> sslProperties = getTruststoreProperties();
final ManagedChannel channel = TestGRPCClient.buildChannel(HOST, randPort, sslProperties);
final FlowFileServiceGrpc.FlowFileServiceBlockingStub stub = FlowFileServiceGrpc.newBlockingStub(channel);
final ListenGRPC listenGRPC = new ListenGRPC();
final TestRunner runner = TestRunners.newTestRunner(listenGRPC);
runner.setProperty(ListenGRPC.PROP_SERVICE_PORT, String.valueOf(randPort));
runner.setProperty(ListenGRPC.PROP_USE_SECURE, "true");
useSSLContextService(runner, getKeystoreProperties());
final ProcessContext processContext = runner.getProcessContext();
final ProcessSessionFactory processSessionFactory = runner.getProcessSessionFactory();
try {
// start the server. The order of the following statements shouldn't matter, because the
// startServer() method waits for a processSessionFactory to be available to it.
listenGRPC.startServer(processContext);
listenGRPC.onTrigger(processContext, processSessionFactory);
final FlowFileRequest ingestFile = FlowFileRequest.newBuilder()
.putAttributes("FOO", "BAR")
.setContent(ByteString.copyFrom("content".getBytes()))
.build();
final FlowFileReply reply = stub.send(ingestFile);
assertThat(reply.getResponseCode(), equalTo(FlowFileReply.ResponseCode.SUCCESS));
assertThat(reply.getBody(), equalTo("FlowFile successfully received."));
// known race condition spot: grpc reply vs flowfile transfer
Thread.sleep(10);
runner.assertTransferCount(ListenGRPC.REL_SUCCESS, 1);
final List<MockFlowFile> successFiles = runner.getFlowFilesForRelationship(ListenGRPC.REL_SUCCESS);
assertThat(successFiles.size(), equalTo(1));
final MockFlowFile mockFlowFile = successFiles.get(0);
assertThat(mockFlowFile.getAttribute("FOO"), equalTo("BAR"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_HOST), equalTo("127.0.0.1"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_USER_DN), equalTo(FlowFileIngestServiceInterceptor.DEFAULT_FOUND_SUBJECT));
} finally {
// stop the server
listenGRPC.stopServer(processContext);
channel.shutdown();
}
}
@Test(expected = io.grpc.StatusRuntimeException.class)
public void testSecureTwoWaySSLFailAuthorizedDNCheck() throws Exception {
final int randPort = TestGRPCClient.randomPort();
final Map<String, String> sslProperties = getKeystoreProperties();
sslProperties.putAll(getTruststoreProperties());
final ManagedChannel channel = TestGRPCClient.buildChannel(HOST, randPort, sslProperties);
final FlowFileServiceGrpc.FlowFileServiceBlockingStub stub = FlowFileServiceGrpc.newBlockingStub(channel);
final ListenGRPC listenGRPC = new ListenGRPC();
final TestRunner runner = TestRunners.newTestRunner(listenGRPC);
runner.setProperty(ListenGRPC.PROP_SERVICE_PORT, String.valueOf(randPort));
runner.setProperty(ListenGRPC.PROP_USE_SECURE, "true");
runner.setProperty(ListenGRPC.PROP_AUTHORIZED_DN_PATTERN, "CN=FAKE.*");
useSSLContextService(runner, sslProperties);
final ProcessContext processContext = runner.getProcessContext();
final ProcessSessionFactory processSessionFactory = runner.getProcessSessionFactory();
try {
// start the server. The order of the following statements shouldn't matter, because the
// startServer() method waits for a processSessionFactory to be available to it.
listenGRPC.startServer(processContext);
listenGRPC.onTrigger(processContext, processSessionFactory);
final FlowFileRequest ingestFile = FlowFileRequest.newBuilder()
.putAttributes("FOO", "BAR")
.setContent(ByteString.copyFrom("content".getBytes()))
.build();
final FlowFileReply reply = stub.send(ingestFile);
assertThat(reply.getResponseCode(), equalTo(FlowFileReply.ResponseCode.SUCCESS));
assertThat(reply.getBody(), equalTo("FlowFile successfully received."));
runner.assertTransferCount(ListenGRPC.REL_SUCCESS, 1);
final List<MockFlowFile> successFiles = runner.getFlowFilesForRelationship(ListenGRPC.REL_SUCCESS);
assertThat(successFiles.size(), equalTo(1));
final MockFlowFile mockFlowFile = successFiles.get(0);
assertThat(mockFlowFile.getAttribute("FOO"), equalTo("BAR"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_HOST), equalTo("127.0.0.1"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_USER_DN), equalTo(CERT_DN));
} finally {
// stop the server
listenGRPC.stopServer(processContext);
channel.shutdown();
}
}
@Test
public void testSecureTwoWaySSLPassAuthorizedDNCheck() throws Exception {
final int randPort = TestGRPCClient.randomPort();
final Map<String, String> sslProperties = getKeystoreProperties();
sslProperties.putAll(getTruststoreProperties());
final ManagedChannel channel = TestGRPCClient.buildChannel(HOST, randPort, sslProperties);
final FlowFileServiceGrpc.FlowFileServiceBlockingStub stub = FlowFileServiceGrpc.newBlockingStub(channel);
final ListenGRPC listenGRPC = new ListenGRPC();
final TestRunner runner = TestRunners.newTestRunner(listenGRPC);
runner.setProperty(ListenGRPC.PROP_SERVICE_PORT, String.valueOf(randPort));
runner.setProperty(ListenGRPC.PROP_USE_SECURE, "true");
runner.setProperty(ListenGRPC.PROP_AUTHORIZED_DN_PATTERN, "CN=localhost.*");
useSSLContextService(runner, sslProperties);
final ProcessContext processContext = runner.getProcessContext();
final ProcessSessionFactory processSessionFactory = runner.getProcessSessionFactory();
try {
// start the server. The order of the following statements shouldn't matter, because the
// startServer() method waits for a processSessionFactory to be available to it.
listenGRPC.startServer(processContext);
listenGRPC.onTrigger(processContext, processSessionFactory);
final FlowFileRequest ingestFile = FlowFileRequest.newBuilder()
.putAttributes("FOO", "BAR")
.setContent(ByteString.copyFrom("content".getBytes()))
.build();
final FlowFileReply reply = stub.send(ingestFile);
assertThat(reply.getResponseCode(), equalTo(FlowFileReply.ResponseCode.SUCCESS));
assertThat(reply.getBody(), equalTo("FlowFile successfully received."));
runner.assertTransferCount(ListenGRPC.REL_SUCCESS, 1);
final List<MockFlowFile> successFiles = runner.getFlowFilesForRelationship(ListenGRPC.REL_SUCCESS);
assertThat(successFiles.size(), equalTo(1));
final MockFlowFile mockFlowFile = successFiles.get(0);
assertThat(mockFlowFile.getAttribute("FOO"), equalTo("BAR"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_HOST), equalTo("127.0.0.1"));
assertThat(mockFlowFile.getAttribute(ListenGRPC.REMOTE_USER_DN), equalTo(CERT_DN));
} finally {
// stop the server
listenGRPC.stopServer(processContext);
channel.shutdown();
}
}
}

View File

@ -16,21 +16,14 @@
*/
package org.apache.nifi.processors.grpc;
import org.apache.nifi.reporting.InitializationException;
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.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.grpc.stub.StreamObserver;
import io.netty.handler.ssl.ClientAuth;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@ -394,121 +387,6 @@ public class TestInvokeGRPC {
}
@Test
public void testSecureTwoWaySsl() throws Exception {
final Map<String, String> sslProperties = getKeystoreProperties();
sslProperties.putAll(getTruststoreProperties());
final TestGRPCServer<DummyFlowFileService> server = new TestGRPCServer<>(DummyFlowFileService.class, sslProperties);
try {
final TestRunner runner = TestRunners.newTestRunner(InvokeGRPC.class);
runner.setProperty(InvokeGRPC.PROP_SERVICE_HOST, TestGRPCServer.HOST);
useSSLContextService(runner, sslProperties);
final int port = server.start(0);
runner.setProperty(InvokeGRPC.PROP_SERVICE_PORT, String.valueOf(port));
runner.setProperty(InvokeGRPC.PROP_USE_SECURE, "true");
final MockFlowFile mockFlowFile = new MockFlowFile(SUCCESS);
runner.enqueue(mockFlowFile);
runner.run();
runner.assertTransferCount(InvokeGRPC.REL_RESPONSE, 1);
runner.assertTransferCount(InvokeGRPC.REL_SUCCESS_REQ, 1);
runner.assertTransferCount(InvokeGRPC.REL_RETRY, 0);
runner.assertTransferCount(InvokeGRPC.REL_NO_RETRY, 0);
runner.assertTransferCount(InvokeGRPC.REL_FAILURE, 0);
final List<MockFlowFile> responseFiles = runner.getFlowFilesForRelationship(InvokeGRPC.REL_RESPONSE);
assertThat(responseFiles.size(), equalTo(1));
final MockFlowFile response = responseFiles.get(0);
response.assertAttributeEquals(InvokeGRPC.RESPONSE_CODE, String.valueOf(FlowFileReply.ResponseCode.SUCCESS));
response.assertAttributeEquals(InvokeGRPC.RESPONSE_BODY, "success");
response.assertAttributeEquals(InvokeGRPC.SERVICE_HOST, TestGRPCServer.HOST);
response.assertAttributeEquals(InvokeGRPC.SERVICE_PORT, String.valueOf(port));
final List<MockFlowFile> successFiles = runner.getFlowFilesForRelationship(InvokeGRPC.REL_SUCCESS_REQ);
assertThat(successFiles.size(), equalTo(1));
final MockFlowFile successFile = successFiles.get(0);
successFile.assertAttributeEquals(InvokeGRPC.RESPONSE_CODE, String.valueOf(FlowFileReply.ResponseCode.SUCCESS));
successFile.assertAttributeEquals(InvokeGRPC.RESPONSE_BODY, "success");
successFile.assertAttributeEquals(InvokeGRPC.SERVICE_HOST, TestGRPCServer.HOST);
successFile.assertAttributeEquals(InvokeGRPC.SERVICE_PORT, String.valueOf(port));
} finally {
server.stop();
}
}
@Test
public void testSecureOneWaySsl() throws Exception {
final Map<String, String> sslProperties = getKeystoreProperties();
sslProperties.put(TestGRPCServer.NEED_CLIENT_AUTH, ClientAuth.NONE.name());
final TestGRPCServer<DummyFlowFileService> server = new TestGRPCServer<>(DummyFlowFileService.class, sslProperties);
try {
final TestRunner runner = TestRunners.newTestRunner(InvokeGRPC.class);
runner.setProperty(InvokeGRPC.PROP_SERVICE_HOST, TestGRPCServer.HOST);
useSSLContextService(runner, getTruststoreProperties());
final int port = server.start(0);
runner.setProperty(InvokeGRPC.PROP_SERVICE_PORT, String.valueOf(port));
runner.setProperty(InvokeGRPC.PROP_USE_SECURE, "true");
final MockFlowFile mockFlowFile = new MockFlowFile(SUCCESS);
runner.enqueue(mockFlowFile);
runner.run();
runner.assertTransferCount(InvokeGRPC.REL_RESPONSE, 1);
runner.assertTransferCount(InvokeGRPC.REL_SUCCESS_REQ, 1);
runner.assertTransferCount(InvokeGRPC.REL_RETRY, 0);
runner.assertTransferCount(InvokeGRPC.REL_NO_RETRY, 0);
runner.assertTransferCount(InvokeGRPC.REL_FAILURE, 0);
final List<MockFlowFile> responseFiles = runner.getFlowFilesForRelationship(InvokeGRPC.REL_RESPONSE);
assertThat(responseFiles.size(), equalTo(1));
final MockFlowFile response = responseFiles.get(0);
response.assertAttributeEquals(InvokeGRPC.RESPONSE_CODE, String.valueOf(FlowFileReply.ResponseCode.SUCCESS));
response.assertAttributeEquals(InvokeGRPC.RESPONSE_BODY, "success");
response.assertAttributeEquals(InvokeGRPC.SERVICE_HOST, TestGRPCServer.HOST);
response.assertAttributeEquals(InvokeGRPC.SERVICE_PORT, String.valueOf(port));
final List<MockFlowFile> successFiles = runner.getFlowFilesForRelationship(InvokeGRPC.REL_SUCCESS_REQ);
assertThat(successFiles.size(), equalTo(1));
final MockFlowFile successFile = successFiles.get(0);
successFile.assertAttributeEquals(InvokeGRPC.RESPONSE_CODE, String.valueOf(FlowFileReply.ResponseCode.SUCCESS));
successFile.assertAttributeEquals(InvokeGRPC.RESPONSE_BODY, "success");
successFile.assertAttributeEquals(InvokeGRPC.SERVICE_HOST, TestGRPCServer.HOST);
successFile.assertAttributeEquals(InvokeGRPC.SERVICE_PORT, String.valueOf(port));
} finally {
server.stop();
}
}
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 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 TestRunner controller, 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(InvokeGRPC.PROP_SSL_CONTEXT_SERVICE, "ssl-service");
}
/**
* Dummy gRPC service whose responses are dictated by the IDs on the messages it receives
*/

View File

@ -54,11 +54,12 @@
<artifactId>moquette-broker</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ssl-context-service</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>

View File

@ -21,10 +21,13 @@ import io.moquette.proto.messages.PublishMessage;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processors.mqtt.common.MQTTQueueMessage;
import org.apache.nifi.processors.mqtt.common.MqttTestClient;
import org.apache.nifi.processors.mqtt.common.MqttTestUtils;
import org.apache.nifi.processors.mqtt.common.TestConsumeMqttCommon;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.eclipse.paho.client.mqttv3.IMqttClient;
@ -33,14 +36,16 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.security.GeneralSecurityException;
import java.util.concurrent.BlockingQueue;
import static org.junit.Assert.assertTrue;
@ -50,6 +55,8 @@ import static org.mockito.Mockito.when;
public class TestConsumeMQTT extends TestConsumeMqttCommon {
private static TlsConfiguration tlsConfiguration;
public MqttTestClient mqttTestClient;
public class UnitTestableConsumeMqtt extends ConsumeMQTT {
@ -65,8 +72,15 @@ public class TestConsumeMQTT extends TestConsumeMqttCommon {
}
}
@BeforeClass
public static void setTlsConfiguration() throws IOException, GeneralSecurityException {
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
}
@Before
public void init() throws IOException {
public void init() {
PUBLISH_WAIT_MS = 0;
broker = "tcp://localhost:1883";
@ -79,7 +93,7 @@ public class TestConsumeMQTT extends TestConsumeMqttCommon {
}
@Test
public void testSSLContextServiceTruststoreOnly() throws InitializationException {
public void testSslContextService() throws InitializationException, TlsException {
String brokerURI = "ssl://localhost:8883";
TestRunner runner = TestRunners.newTestRunner(ConsumeMQTT.class);
runner.setVariable("brokerURI", brokerURI);
@ -88,26 +102,25 @@ public class TestConsumeMQTT extends TestConsumeMqttCommon {
runner.setProperty(ConsumeMQTT.PROP_TOPIC_FILTER, "testTopic");
runner.setProperty(ConsumeMQTT.PROP_MAX_QUEUE_SIZE, "100");
final StandardSSLContextService sslService = new StandardSSLContextService();
Map<String, String> sslProperties = MqttTestUtils.createSslPropertiesTruststoreOnly();
runner.addControllerService("ssl-context", sslService, sslProperties);
runner.enableControllerService(sslService);
runner.setProperty(ConsumeMQTT.PROP_SSL_CONTEXT_SERVICE, "ssl-context");
final SSLContextService sslContextService = mock(SSLContextService.class);
final String identifier = SSLContextService.class.getSimpleName();
when(sslContextService.getIdentifier()).thenReturn(identifier);
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration);
when(sslContextService.createContext()).thenReturn(sslContext);
try {
ConsumeMQTT processor = (ConsumeMQTT) runner.getProcessor();
processor.onScheduled(runner.getProcessContext());
} catch (Exception e) {
e.printStackTrace();
fail("Unexpected error");
}
runner.addControllerService(identifier, sslContextService);
runner.enableControllerService(sslContextService);
runner.setProperty(ConsumeMQTT.PROP_SSL_CONTEXT_SERVICE, identifier);
ConsumeMQTT processor = (ConsumeMQTT) runner.getProcessor();
processor.onScheduled(runner.getProcessContext());
}
/**
* If the session.commit() fails, we should not remove the unprocessed message
*/
@Test
public void testMessageNotConsumedOnCommitFail() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
public void testMessageNotConsumedOnCommitFail() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException {
testRunner.run(1, false);
ConsumeMQTT processor = (ConsumeMQTT) testRunner.getProcessor();
MQTTQueueMessage mock = mock(MQTTQueueMessage.class);
@ -133,7 +146,7 @@ public class TestConsumeMQTT extends TestConsumeMqttCommon {
}
@After
public void tearDown() throws Exception {
public void tearDown() {
if (MQTT_server != null) {
MQTT_server.stopServer();
}

View File

@ -1,46 +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.mqtt.common;
import org.apache.nifi.ssl.StandardSSLContextService;
import java.util.HashMap;
import java.util.Map;
public class MqttTestUtils {
public static Map<String, String> createSslProperties() {
final Map<String, String> map = new HashMap<>();
map.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/keystore.jks");
map.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "passwordpassword");
map.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
map.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks");
map.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword");
map.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
return map;
}
public static Map<String, String> createSslPropertiesTruststoreOnly() {
final Map<String, String> map = new HashMap<>();
map.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks");
map.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword");
map.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
return map;
}
}

View File

@ -1,148 +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.mqtt.integration;
import io.moquette.BrokerConstants;
import io.moquette.proto.messages.AbstractMessage;
import io.moquette.proto.messages.PublishMessage;
import io.moquette.server.Server;
import io.moquette.server.config.IConfig;
import io.moquette.server.config.MemoryConfig;
import org.apache.nifi.processors.mqtt.ConsumeMQTT;
import org.apache.nifi.processors.mqtt.common.TestConsumeMqttCommon;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunners;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
import static org.apache.nifi.processors.mqtt.ConsumeMQTT.BROKER_ATTRIBUTE_KEY;
import static org.apache.nifi.processors.mqtt.ConsumeMQTT.IS_DUPLICATE_ATTRIBUTE_KEY;
import static org.apache.nifi.processors.mqtt.ConsumeMQTT.IS_RETAINED_ATTRIBUTE_KEY;
import static org.apache.nifi.processors.mqtt.ConsumeMQTT.QOS_ATTRIBUTE_KEY;
import static org.apache.nifi.processors.mqtt.ConsumeMQTT.TOPIC_ATTRIBUTE_KEY;
import static org.apache.nifi.processors.mqtt.common.MqttTestUtils.createSslProperties;
public class TestConsumeMqttSSL extends TestConsumeMqttCommon {
private void startServer() throws IOException {
MQTT_server = new Server();
final Properties configProps = new Properties();
configProps.put(BrokerConstants.WEB_SOCKET_PORT_PROPERTY_NAME, "1884");
configProps.put(BrokerConstants.SSL_PORT_PROPERTY_NAME, "8883");
configProps.put(BrokerConstants.JKS_PATH_PROPERTY_NAME, "src/test/resources/keystore.jks");
configProps.put(BrokerConstants.KEY_STORE_PASSWORD_PROPERTY_NAME, "passwordpassword");
configProps.put(BrokerConstants.KEY_MANAGER_PASSWORD_PROPERTY_NAME, "passwordpassword");
configProps.setProperty(PERSISTENT_STORE_PROPERTY_NAME,"./target/moquette_store.mapdb");
IConfig server_config = new MemoryConfig(configProps);
MQTT_server.startServer(server_config);
}
@Before
public void init() throws IOException, InitializationException {
startServer();
broker = "ssl://localhost:8883";
testRunner = TestRunners.newTestRunner(ConsumeMQTT.class);
testRunner.setProperty(ConsumeMQTT.PROP_BROKER_URI, broker);
testRunner.setProperty(ConsumeMQTT.PROP_CLIENTID, "TestClient");
testRunner.setProperty(ConsumeMQTT.PROP_TOPIC_FILTER, "testTopic");
testRunner.setProperty(ConsumeMQTT.PROP_MAX_QUEUE_SIZE, "100");
final StandardSSLContextService sslService = new StandardSSLContextService();
Map<String, String> sslProperties = createSslProperties();
testRunner.addControllerService("ssl-context", sslService, sslProperties);
testRunner.enableControllerService(sslService);
testRunner.setProperty(ConsumeMQTT.PROP_SSL_CONTEXT_SERVICE, "ssl-context");
}
@After
public void tearDown() throws Exception {
if (MQTT_server != null) {
MQTT_server.stopServer();
}
final File folder = new File("./target");
final File[] files = folder.listFiles( new FilenameFilter() {
@Override
public boolean accept( final File dir,
final String name ) {
return name.matches( "moquette_store.mapdb.*" );
}
} );
for ( final File file : files ) {
if ( !file.delete() ) {
System.err.println( "Can't remove " + file.getAbsolutePath() );
}
}
}
@Test
public void testRetainedQoS2() throws Exception {
testRunner.setProperty(ConsumeMQTT.PROP_QOS, "2");
testRunner.assertValid();
PublishMessage testMessage = new PublishMessage();
testMessage.setPayload(ByteBuffer.wrap("testMessage".getBytes()));
testMessage.setTopicName("testTopic");
testMessage.setDupFlag(false);
testMessage.setQos(AbstractMessage.QOSType.EXACTLY_ONCE);
testMessage.setRetainFlag(true);
internalPublish(testMessage);
ConsumeMQTT consumeMQTT = (ConsumeMQTT) testRunner.getProcessor();
consumeMQTT.onScheduled(testRunner.getProcessContext());
reconnect(consumeMQTT, testRunner.getProcessContext());
Thread.sleep(PUBLISH_WAIT_MS);
testRunner.run(1, false, false);
testRunner.assertTransferCount(ConsumeMQTT.REL_MESSAGE, 1);
List<MockFlowFile> flowFiles = testRunner.getFlowFilesForRelationship(ConsumeMQTT.REL_MESSAGE);
MockFlowFile flowFile = flowFiles.get(0);
flowFile.assertContentEquals("testMessage");
flowFile.assertAttributeEquals(BROKER_ATTRIBUTE_KEY, broker);
flowFile.assertAttributeEquals(TOPIC_ATTRIBUTE_KEY, "testTopic");
flowFile.assertAttributeEquals(QOS_ATTRIBUTE_KEY, "2");
flowFile.assertAttributeEquals(IS_DUPLICATE_ATTRIBUTE_KEY, "false");
flowFile.assertAttributeEquals(IS_RETAINED_ATTRIBUTE_KEY, "true");
}
@Override
public void internalPublish(PublishMessage publishMessage) {
MQTT_server.internalPublish(publishMessage);
}
}

View File

@ -58,7 +58,7 @@ public class TestPublishMQTT extends TestPublishMqttCommon {
}
@After
public void tearDown() throws Exception {
public void tearDown() {
if (MQTT_server != null) {
MQTT_server.stopServer();
}

View File

@ -1,98 +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.mqtt.integration;
import io.moquette.BrokerConstants;
import io.moquette.server.Server;
import io.moquette.server.config.IConfig;
import io.moquette.server.config.MemoryConfig;
import org.apache.nifi.processors.mqtt.PublishMQTT;
import org.apache.nifi.processors.mqtt.common.TestPublishMqttCommon;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.TestRunners;
import org.junit.After;
import org.junit.Before;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import static io.moquette.BrokerConstants.PERSISTENT_STORE_PROPERTY_NAME;
import static org.apache.nifi.processors.mqtt.common.MqttTestUtils.createSslProperties;
public class TestPublishMqttSSL extends TestPublishMqttCommon {
private void startServer() throws IOException {
MQTT_server = new Server();
final Properties configProps = new Properties();
configProps.put(BrokerConstants.WEB_SOCKET_PORT_PROPERTY_NAME, "1884");
configProps.put(BrokerConstants.SSL_PORT_PROPERTY_NAME, "8883");
configProps.put(BrokerConstants.JKS_PATH_PROPERTY_NAME, "src/test/resources/keystore.jks");
configProps.put(BrokerConstants.KEY_STORE_PASSWORD_PROPERTY_NAME, "passwordpassword");
configProps.put(BrokerConstants.KEY_MANAGER_PASSWORD_PROPERTY_NAME, "passwordpassword");
configProps.setProperty(PERSISTENT_STORE_PROPERTY_NAME,"./target/moquette_store.mapdb");
IConfig server_config = new MemoryConfig(configProps);
MQTT_server.startServer(server_config);
}
@Before
public void init() throws IOException, InitializationException {
startServer();
testRunner = TestRunners.newTestRunner(PublishMQTT.class);
testRunner.setProperty(PublishMQTT.PROP_BROKER_URI, "ssl://localhost:8883");
testRunner.setProperty(PublishMQTT.PROP_CLIENTID, "TestClient");
testRunner.setProperty(PublishMQTT.PROP_RETAIN, "true");
testRunner.setProperty(PublishMQTT.PROP_TOPIC, "testTopic");
final StandardSSLContextService sslService = new StandardSSLContextService();
Map<String, String> sslProperties = createSslProperties();
testRunner.addControllerService("ssl-context", sslService, sslProperties);
testRunner.enableControllerService(sslService);
testRunner.setProperty(PublishMQTT.PROP_SSL_CONTEXT_SERVICE, "ssl-context");
}
@After
public void tearDown() throws Exception {
if (MQTT_server != null) {
MQTT_server.stopServer();
}
final File folder = new File("./target");
final File[] files = folder.listFiles(new FilenameFilter() {
@Override
public boolean accept(final File dir,
final String name) {
return name.matches("moquette_store.mapdb.*");
}
});
for (final File file : files) {
if (!file.delete()) {
System.err.println("Can't remove " + file.getAbsolutePath());
}
}
}
@Override
public void verifyPublishedMessage(byte[] payload, int qos, boolean retain) {
//Cannot verify published message without subscribing and consuming it which is outside the scope of this test.
}
}

View File

@ -1,112 +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.livy;
import org.apache.nifi.controller.livy.LivySessionController;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.web.util.TestServer;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class TestExecuteSparkInteractiveSSL extends ExecuteSparkInteractiveTestBase {
private static Map<String, String> sslProperties;
private static TestServer server;
private static String url;
@BeforeClass
public static void beforeClass() throws Exception {
// useful for verbose logging output
// don't commit this with this property enabled, or any 'mvn test' will be really verbose
// System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard", "debug");
// create the SSL properties, which basically store keystore / truststore information
// this is used by the StandardSSLContextService and the Jetty Server
sslProperties = createSslProperties();
// create a Jetty server on a random port
server = createServer();
server.startServer();
// Allow time for the server to start
Thread.sleep(1000);
// this is the base url with the random port
url = server.getSecureUrl();
}
@AfterClass
public static void afterClass() throws Exception {
server.shutdownServer();
}
@Before
public void before() throws Exception {
runner = TestRunners.newTestRunner(ExecuteSparkInteractive.class);
final StandardSSLContextService sslService = new StandardSSLContextService();
runner.addControllerService("ssl-context", sslService, sslProperties);
runner.enableControllerService(sslService);
// Allow time for the controller service to fully initialize
Thread.sleep(500);
LivySessionController livyControllerService = new LivySessionController();
runner.addControllerService("livyCS", livyControllerService);
runner.setProperty(livyControllerService, LivySessionController.LIVY_HOST, url.substring(url.indexOf("://") + 3, url.lastIndexOf(":")));
runner.setProperty(livyControllerService, LivySessionController.LIVY_PORT, url.substring(url.lastIndexOf(":") + 1));
runner.setProperty(livyControllerService, LivySessionController.SSL_CONTEXT_SERVICE, "ssl-context");
runner.enableControllerService(livyControllerService);
runner.setProperty(ExecuteSparkInteractive.LIVY_CONTROLLER_SERVICE, "livyCS");
server.clearHandlers();
}
@After
public void after() {
runner.shutdown();
}
private static TestServer createServer() {
return new TestServer(sslProperties);
}
@Test
public void testSparkSession() throws Exception {
testCode(server,"print \"hello world\"");
}
private static Map<String, String> createSslProperties() {
final Map<String, String> map = new HashMap<>();
map.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/keystore.jks");
map.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "passwordpassword");
map.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
map.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/truststore.jks");
map.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "passwordpassword");
map.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
return map;
}
}

View File

@ -17,7 +17,7 @@
package org.apache.nifi.web.util.ssl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.SslContextFactory;
import org.apache.nifi.security.util.StandardTlsConfiguration;
import org.apache.nifi.security.util.TlsConfiguration;
@ -27,33 +27,43 @@ import javax.net.ssl.SSLContext;
import java.io.File;
public class SslContextUtils {
private static final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
private static final TlsConfiguration TLS_CONFIGURATION;
private static final String KEYSTORE_AND_TRUSTSTORE_PASSWORD = "passwordpassword";
private static final TlsConfiguration KEYSTORE_TLS_CONFIGURATION;
private static final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks";
private static final TlsConfiguration TRUSTSTORE_TLS_CONFIGURATION;
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
);
static {
try {
TLS_CONFIGURATION = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(TLS_CONFIGURATION.getKeystorePath()).deleteOnExit();
new File(TLS_CONFIGURATION.getTruststorePath()).deleteOnExit();
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
);
KEYSTORE_TLS_CONFIGURATION = new StandardTlsConfiguration(
TLS_CONFIGURATION.getKeystorePath(),
TLS_CONFIGURATION.getKeystorePassword(),
TLS_CONFIGURATION.getKeyPassword(),
TLS_CONFIGURATION.getKeystoreType().getType(),
TLS_CONFIGURATION.getTruststorePath(),
TLS_CONFIGURATION.getTruststorePassword(),
TLS_CONFIGURATION.getTruststoreType().getType(),
TlsConfiguration.TLS_1_2_PROTOCOL
);
TRUSTSTORE_TLS_CONFIGURATION = new StandardTlsConfiguration(
null,
null,
null,
null,
TLS_CONFIGURATION.getTruststorePath(),
TLS_CONFIGURATION.getTruststorePassword(),
TLS_CONFIGURATION.getTruststoreType().getType(),
TlsConfiguration.TLS_1_2_PROTOCOL
);
} catch (final Exception e) {
throw new IllegalStateException("Failed to create TLS configuration for testing", e);
}
}
/**
* Create SSLContext with Key Store and Trust Store configured

View File

@ -1,244 +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.ssl
import org.apache.nifi.security.util.ClientAuth
import org.apache.nifi.util.MockProcessContext
import org.apache.nifi.util.TestRunner
import org.apache.nifi.util.TestRunners
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.*
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.net.ssl.SSLContext
import java.security.Security
import static groovy.test.GroovyAssert.shouldFail
@RunWith(JUnit4.class)
class StandardSSLContextServiceTest {
private static final Logger logger = LoggerFactory.getLogger(StandardSSLContextServiceTest.class)
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 NO_PASSWORD_TRUSTSTORE_PATH = "src/test/resources/no-password-truststore.jks"
private static final String TRUSTSTORE_PATH_WITH_EL = "\${someAttribute}/truststore.jks"
private static final String KEYSTORE_PASSWORD = "passwordpassword"
private static final String TRUSTSTORE_PASSWORD = "passwordpassword"
private static final String TRUSTSTORE_NO_PASSWORD = ""
private static final String KEYSTORE_TYPE = "JKS"
private static final String TRUSTSTORE_TYPE = "JKS"
@Rule
public TemporaryFolder tmp = new TemporaryFolder(new File("src/test/resources"))
@BeforeClass
static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
}
@Before
void setUp() throws Exception {
}
@After
void tearDown() throws Exception {
}
@AfterClass
static void tearDownOnce() throws Exception {
}
@Test
void testShouldValidateSimpleFileValidatorPath() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
// Act
runner.assertValid(sslContextService)
// Assert
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext()
assert processContext.getControllerServiceProperties(sslContextService).get(StandardSSLContextService.TRUSTSTORE, "") == TRUSTSTORE_PATH
}
@Test
void testTruststoreWithNoPasswordIsValid() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, NO_PASSWORD_TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_NO_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
// Act
runner.assertValid(sslContextService)
// Assert
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext()
assert processContext.getControllerServiceProperties(sslContextService).get(StandardSSLContextService.TRUSTSTORE, "") == NO_PASSWORD_TRUSTSTORE_PATH
}
@Test
void testTruststoreWithNullPasswordIsValid() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, NO_PASSWORD_TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, null as String)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
// Act
runner.assertValid(sslContextService)
// Assert
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext()
assert processContext.getControllerServiceProperties(sslContextService).get(StandardSSLContextService.TRUSTSTORE, "") == NO_PASSWORD_TRUSTSTORE_PATH
}
@Test
void testTruststoreWithMissingPasswordIsValid() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, NO_PASSWORD_TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
// Act
runner.assertValid(sslContextService)
// Assert
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext()
assert processContext.getControllerServiceProperties(sslContextService).get(StandardSSLContextService.TRUSTSTORE, "") == NO_PASSWORD_TRUSTSTORE_PATH
}
@Test
void testShouldConnectWithPasswordlessTruststore() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, NO_PASSWORD_TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
runner.assertValid(sslContextService)
// Act
SSLContext sslContext = sslContextService.createContext();
// Assert
assert sslContext
}
@Test
void testShouldConnectWithPasswordlessTruststoreWhenKeystorePresent() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, KEYSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, KEYSTORE_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, KEYSTORE_TYPE)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, NO_PASSWORD_TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
runner.assertValid(sslContextService)
// Act
SSLContext sslContext = sslContextService.createContext();
// Assert
assert sslContext
}
@Test
void testShouldNotValidateExpressionLanguageInFileValidator() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, TRUSTSTORE_PATH_WITH_EL)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, TRUSTSTORE_PASSWORD)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
// Act
def msg = shouldFail {
runner.enableControllerService(sslContextService)
}
// Assert
runner.assertNotValid(sslContextService)
}
/**
* This test ensures that the deprecated ClientAuth enum is correctly mapped to the canonical enum.
*/
@Test
void testShouldTranslateValidDeprecatedClientAuths() {
// Arrange
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class)
String controllerServiceId = "ssl-context"
final SSLContextService sslContextService = new StandardSSLContextService()
runner.addControllerService(controllerServiceId, sslContextService)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, NO_PASSWORD_TRUSTSTORE_PATH)
runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, TRUSTSTORE_TYPE)
runner.enableControllerService(sslContextService)
runner.assertValid(sslContextService)
// Act
Map<SSLContextService.ClientAuth, SSLContext> sslContexts = SSLContextService.ClientAuth.values().collectEntries { ca ->
[ca, sslContextService.createSSLContext(ca)]
}
// Assert
assert sslContexts.size() == ClientAuth.values().size()
sslContexts.every { clientAuth, sslContext ->
assert ClientAuth.isValidClientAuthType(clientAuth.name())
assert sslContext
}
}
}

View File

@ -23,33 +23,32 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.util.KeyStoreUtils;
import org.apache.nifi.security.util.TlsConfiguration;
import org.apache.nifi.util.MockProcessContext;
import org.apache.nifi.util.MockValidationContext;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SSLContextServiceTest {
private static final Logger logger = LoggerFactory.getLogger(SSLContextServiceTest.class);
private final String KEYSTORE_PATH = "src/test/resources/keystore.jks";
private final String KEYSTORE_AND_TRUSTSTORE_PASSWORD = "passwordpassword";
private final String JKS_TYPE = "JKS";
private final String TRUSTSTORE_PATH = "src/test/resources/truststore.jks";
private static final String KEYSTORE_WITH_KEY_PASSWORD_PATH = "src/test/resources/keystore-with-key-password.jks";
private static TlsConfiguration tlsConfiguration;
@Rule
public TemporaryFolder tmp = new TemporaryFolder(new File("src/test/resources"));
@BeforeClass
public static void setTlsConfiguration() throws GeneralSecurityException, IOException {
tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore();
new File(tlsConfiguration.getKeystorePath()).deleteOnExit();
new File(tlsConfiguration.getTruststorePath()).deleteOnExit();
}
@Test
public void testShouldFailToAddControllerServiceWithNoProperties() throws InitializationException {
@ -65,8 +64,8 @@ public class SSLContextServiceTest {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final SSLContextService service = new StandardSSLContextService();
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
runner.addControllerService("test-no-keystore-type", service, properties);
runner.assertNotValid(service);
}
@ -76,10 +75,10 @@ public class SSLContextServiceTest {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final SSLContextService service = new StandardSSLContextService();
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
runner.addControllerService("test-no-truststore-password-or-type", service, properties);
runner.assertNotValid(service);
}
@ -89,12 +88,12 @@ public class SSLContextServiceTest {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final SSLContextService service = new StandardSSLContextService();
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "wrongpassword");
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "PKCS12");
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "wrongpassword");
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), String.class.getSimpleName());
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), String.class.getSimpleName());
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.addControllerService("test-wrong-passwords", service, properties);
runner.assertNotValid(service);
@ -106,11 +105,11 @@ public class SSLContextServiceTest {
final SSLContextService service = new StandardSSLContextService();
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/DOES-NOT-EXIST.jks");
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "PKCS12");
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.addControllerService("test-keystore-file-does-not-exist", service, properties);
runner.assertNotValid(service);
}
@ -120,12 +119,12 @@ public class SSLContextServiceTest {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
SSLContextService service = new StandardSSLContextService();
runner.addControllerService("test-good1", service);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.enableControllerService(service);
runner.setProperty("SSL Context Svc ID", "test-good1");
@ -141,14 +140,14 @@ public class SSLContextServiceTest {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
SSLContextService service = new StandardSSLContextService();
runner.addControllerService("test-good1", service);
runner.setVariable("keystore", KEYSTORE_PATH);
runner.setVariable("truststore", TRUSTSTORE_PATH);
runner.setVariable("keystore", tlsConfiguration.getKeystorePath());
runner.setVariable("truststore", tlsConfiguration.getTruststorePath());
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "${keystore}");
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), "${truststore}");
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.enableControllerService(service);
runner.setProperty("SSL Context Svc ID", "test-good1");
@ -164,12 +163,12 @@ public class SSLContextServiceTest {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
SSLContextService service = new StandardSSLContextService();
runner.addControllerService("test-good1", service);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.enableControllerService(service);
runner.setProperty("SSL Context Svc ID", "test-good1");
@ -179,25 +178,23 @@ public class SSLContextServiceTest {
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/DOES-NOT-EXIST.jks");
runner.assertNotValid(service);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "badpassword");
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), String.class.getSimpleName());
runner.assertNotValid(service);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
runner.enableControllerService(service);
runner.assertValid(service);
}
@Test
public void testValidationResultsCacheShouldExpire() throws InitializationException, IOException {
// Arrange
// Copy the keystore and truststore to a tmp directory so the originals are not modified
File originalKeystore = new File(KEYSTORE_PATH);
File originalTruststore = new File(TRUSTSTORE_PATH);
File originalKeystore = new File(tlsConfiguration.getKeystorePath());
File originalTruststore = new File(tlsConfiguration.getTruststorePath());
File tmpKeystore = tmp.newFile("keystore-tmp.jks");
File tmpTruststore = tmp.newFile("truststore-tmp.jks");
File tmpKeystore = File.createTempFile(getClass().getSimpleName(), ".keystore.p12");
File tmpTruststore = File.createTempFile(getClass().getSimpleName(), ".truststore.p12");
Files.copy(originalKeystore.toPath(), tmpKeystore.toPath(), StandardCopyOption.REPLACE_EXISTING);
Files.copy(originalTruststore.toPath(), tmpTruststore.toPath(), StandardCopyOption.REPLACE_EXISTING);
@ -207,11 +204,11 @@ public class SSLContextServiceTest {
final String serviceIdentifier = "test-should-expire";
runner.addControllerService(serviceIdentifier, service);
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tmpKeystore.getAbsolutePath());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tmpTruststore.getAbsolutePath());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.enableControllerService(service);
runner.setProperty("SSL Context Svc ID", serviceIdentifier);
@ -221,7 +218,6 @@ public class SSLContextServiceTest {
boolean isDeleted = tmpKeystore.delete();
assert isDeleted;
assert !tmpKeystore.exists();
logger.info("Deleted keystore file");
// Manually validate the service (expecting cached result to be returned)
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext();
@ -231,7 +227,6 @@ public class SSLContextServiceTest {
// Even though the keystore file is no longer present, because no property changed, the cached result is still valid
Collection<ValidationResult> validationResults = service.customValidate(validationContext);
assertTrue("validation results is not empty", validationResults.isEmpty());
logger.info("(1) StandardSSLContextService#customValidate() returned true even though the keystore file is no longer available");
// Assert
@ -239,12 +234,10 @@ public class SSLContextServiceTest {
for (int i = 2; i < service.getValidationCacheExpiration(); i++) {
validationResults = service.customValidate(validationContext);
assertTrue("validation results is not empty", validationResults.isEmpty());
logger.info("(" + i + ") StandardSSLContextService#customValidate() returned true even though the keystore file is no longer available");
}
validationResults = service.customValidate(validationContext);
assertFalse("validation results is empty", validationResults.isEmpty());
logger.info("(" + service.getValidationCacheExpiration() + ") StandardSSLContextService#customValidate() returned false because the cache expired");
}
@Test
@ -252,9 +245,9 @@ public class SSLContextServiceTest {
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
SSLContextService service = new StandardSSLContextService();
HashMap<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
runner.addControllerService("test-good2", service, properties);
runner.enableControllerService(service);
@ -270,9 +263,9 @@ public class SSLContextServiceTest {
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
SSLContextService service = new StandardSSLContextService();
HashMap<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_PATH);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
runner.addControllerService("test-good3", service, properties);
runner.enableControllerService(service);
@ -281,49 +274,4 @@ public class SSLContextServiceTest {
Assert.assertNotNull(service);
service.createContext();
}
/**
* This test asserts that the keystore password and key password are different. This is only
* true because they were explicitly set that way. Normal keystores that do not have passwords
* set on individual keys will fail this test.
*/
@Test
public void testDifferentKeyPassword() throws Exception {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final SSLContextService service = new StandardSSLContextService();
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_WITH_KEY_PASSWORD_PATH);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.KEY_PASSWORD.getName(), "keypassword");
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), TRUSTSTORE_PATH);
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), JKS_TYPE);
runner.addControllerService("test-diff-keys", service, properties);
runner.enableControllerService(service);
runner.setProperty("SSL Context Svc ID", "test-diff-keys");
runner.assertValid();
Assert.assertNotNull(service);
service.createContext();
}
/**
* This test asserts that the keystore password and key password are different. This is only
* true because they were explicitly set that way. Normal keystores that do not have passwords
* set on individual keys will fail this test.
*/
@Test
public void testDifferentKeyPasswordWithoutSpecifyingKeyPassword() throws Exception {
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
final SSLContextService service = new StandardSSLContextService();
final Map<String, String> properties = new HashMap<>();
properties.put(StandardSSLContextService.KEYSTORE.getName(), KEYSTORE_WITH_KEY_PASSWORD_PATH);
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEYSTORE_AND_TRUSTSTORE_PASSWORD);
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), JKS_TYPE);
runner.addControllerService("test-diff-keys", service, properties);
// Assert the service is not valid due to an internal "cannot recover key" because the key password is missing
runner.assertNotValid(service);
}
}

View File

@ -1,100 +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.websocket.example;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* This is a WebSocket client example testcase.
*/
@Ignore
public class WebSocketClientExample {
private static Logger logger = LoggerFactory.getLogger(WebSocketClientExample.class);
@Test
public void test() {
String destUri = "wss://localhost:50010/test";
final CountDownLatch replyLatch = new CountDownLatch(1);
final SslContextFactory sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setKeyStorePath("src/test/resources/certs/keystore.jks");
sslContextFactory.setKeyStorePassword("passwordpassword");
sslContextFactory.setKeyStoreType("JKS");
sslContextFactory.setTrustStorePath("src/test/resources/certs/truststore.jks");
sslContextFactory.setTrustStorePassword("passwordpassword");
sslContextFactory.setTrustStoreType("JKS");
WebSocketClient client = new WebSocketClient(sslContextFactory);
WebSocketAdapter socket = new WebSocketAdapter() {
@Override
public void onWebSocketConnect(Session session) {
super.onWebSocketConnect(session);
try {
session.getRemote().sendString("Hello, this is Jetty ws client.");
} catch (IOException e) {
logger.error("Failed to send a message due to " + e, e);
}
}
@Override
public void onWebSocketText(String message) {
logger.info("Received a reply: {}", message);
replyLatch.countDown();
}
};
try {
client.start();
URI echoUri = new URI(destUri);
ClientUpgradeRequest request = new ClientUpgradeRequest();
final Future<Session> connect = client.connect(socket, echoUri, request);
logger.info("Connecting to : {}", echoUri);
final Session session = connect.get(3, TimeUnit.SECONDS);
logger.info("Connected, session={}", session);
session.close(StatusCode.NORMAL, "Bye");
} catch (Throwable t) {
t.printStackTrace();
} finally {
try {
client.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@ -1,195 +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.websocket.example;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
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.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* This is a WebSocket server example testcase.
*/
@Ignore
public class WebSocketServerExample {
private static Logger logger = LoggerFactory.getLogger(WebSocketServerExample.class);
private static Server server;
private static ServletHandler servletHandler;
private static ServletHolder servletHolder;
private static ServerConnector httpConnector;
private static ServerConnector sslConnector;
private static final Map<Integer, WebSocketServerExample> portToController = new HashMap<>();
private Map<String, WebSocketListener> listeners = new HashMap<>();
public class SocketListener extends WebSocketAdapter {
public SocketListener() {
logger.info("New instance is created: {}", this);
}
@Override
public void onWebSocketConnect(Session session) {
logger.info("Connected, {}, {}", session.getLocalAddress(), session.getRemoteAddress());
super.onWebSocketConnect(session);
session.getUpgradeRequest().getRequestURI();
}
@Override
public void onWebSocketText(String message) {
logger.info("Received: {}", message);
final String resultMessage;
if (message.startsWith("add-servlet")) {
// Is it possible to add servlet mapping??
final String path = message.split(":")[1].trim();
servletHandler.addServletWithMapping(servletHolder, path);
resultMessage = "Deployed new servlet under: " + path;
} else {
resultMessage = "Got message: " + message;
}
try {
getSession().getRemote().sendString(resultMessage);
} catch (IOException e) {
logger.error("Failed to send a message back to remote.", e);
}
}
}
public WebSocketServerExample() {
this.listeners.put("/test", new SocketListener());
portToController.put(httpConnector.getPort(), this);
portToController.put(sslConnector.getPort(), this);
}
public static class WSServlet extends WebSocketServlet implements WebSocketCreator {
@Override
public void configure(WebSocketServletFactory webSocketServletFactory) {
webSocketServletFactory.setCreator(this);
}
@Override
public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, ServletUpgradeResponse servletUpgradeResponse) {
final WebSocketServerExample testWebSocket = portToController.get(servletUpgradeRequest.getLocalPort());
return testWebSocket.listeners.get(servletUpgradeRequest.getRequestURI().getPath());
}
}
public static class ConnectionCheckServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().println("Ok :)");
}
}
@BeforeClass
public static void setup() throws Exception {
server = new Server(0);
final ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
final ServletContextHandler contextHandler = new ServletContextHandler();
servletHandler = new ServletHandler();
contextHandler.insertHandler(servletHandler);
handlerCollection.setHandlers(new Handler[]{contextHandler});
server.setHandler(handlerCollection);
httpConnector = new ServerConnector(server);
httpConnector.setPort(50010);
final SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("src/test/resources/certs/keystore.jks");
sslContextFactory.setKeyStorePassword("passwordpassword");
sslContextFactory.setKeyStoreType("JKS");
final HttpConfiguration https = new HttpConfiguration();
https.addCustomizer(new SecureRequestCustomizer());
sslConnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory(https));
sslConnector.setPort(50011);
server.setConnectors(new Connector[]{httpConnector, sslConnector});
servletHolder = servletHandler.addServletWithMapping(WSServlet.class, "/test");
servletHolder = servletHandler.addServletWithMapping(ConnectionCheckServlet.class, "/check");
server.start();
logger.info("Starting server on port {} for HTTP, and {} for HTTPS", httpConnector.getLocalPort(), sslConnector.getLocalPort());
}
@AfterClass
public static void teardown() throws Exception {
logger.info("Stopping server.");
try {
server.stop();
} catch (Exception e) {
logger.error("Failed to stop Jetty server due to " + e, e);
}
}
@Test
public void test() throws Exception {
logger.info("Waiting for a while...");
Thread.sleep(1000_000);
}
}

View File

@ -1,68 +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.websocket.jetty;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.ssl.StandardSSLContextService;
import org.apache.nifi.websocket.WebSocketService;
import org.junit.Test;
public class ITJettyWebSocketSecureCommunication extends ITJettyWebSocketCommunication{
private final StandardSSLContextService sslContextService = new StandardSSLContextService();
private final ControllerServiceTestContext sslTestContext = new ControllerServiceTestContext(sslContextService, "SSLContextService");
public ITJettyWebSocketSecureCommunication() {
try {
sslTestContext.setCustomValue(StandardSSLContextService.KEYSTORE, "src/test/resources/certs/keystore.jks");
sslTestContext.setCustomValue(StandardSSLContextService.KEYSTORE_PASSWORD, "passwordpassword");
sslTestContext.setCustomValue(StandardSSLContextService.KEYSTORE_TYPE, "JKS");
sslTestContext.setCustomValue(StandardSSLContextService.TRUSTSTORE, "src/test/resources/certs/truststore.jks");
sslTestContext.setCustomValue(StandardSSLContextService.TRUSTSTORE_PASSWORD, "passwordpassword");
sslTestContext.setCustomValue(StandardSSLContextService.TRUSTSTORE_TYPE, "JKS");
sslContextService.initialize(sslTestContext.getInitializationContext());
sslContextService.onConfigured(sslTestContext.getConfigurationContext());
} catch (InitializationException e) {
throw new RuntimeException(e);
}
}
@Override
protected boolean isSecure() {
return true;
}
@Override
protected void customizeServer() {
serverServiceContext.getInitializationContext().addControllerService(sslContextService);
serverServiceContext.setCustomValue(WebSocketService.SSL_CONTEXT, sslContextService.getIdentifier());
}
@Override
protected void customizeClient() {
clientServiceContext.getInitializationContext().addControllerService(sslContextService);
clientServiceContext.setCustomValue(WebSocketService.SSL_CONTEXT, sslContextService.getIdentifier());
}
@Test
public void testClientServerCommunication() throws Exception {
super.testClientServerCommunication();
}
}