This is an automated email from the ASF dual-hosted git repository.
pvillard31 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 8ac3c9a8465 NIFI-16207 Switched CaptureChangeMySQL to
SSLContextProvider (#11551)
8ac3c9a8465 is described below
commit 8ac3c9a84659a7f55a640f9866c979ea33fa4554
Author: David Handermann <[email protected]>
AuthorDate: Sat Aug 15 07:20:04 2026 -0500
NIFI-16207 Switched CaptureChangeMySQL to SSLContextProvider (#11551)
- Required MySQL Connector/J 8.1.0 as the minimum version
- Added DelegatingSSLContextProvider for configuring supplied SSLContext
from SSLContextProvider
---
.../nifi-cdc-mysql-processors/pom.xml | 15 +-
.../cdc/mysql/processors/CaptureChangeMySQL.java | 71 +++++++--
.../ssl/DelegatingSSLContextProvider.java | 58 +++++++
.../processors/ssl/DelegatingSSLContextSpi.java | 77 +++++++++
.../cdc/mysql/processors/ssl/SecurityProperty.java | 32 +---
.../ssl/StandardConnectionPropertiesProvider.java | 63 +-------
.../mysql/processors/CaptureChangeMySQLTest.java | 48 ++++--
.../DelegatingSSLContextProviderMySQLDriverIT.java | 175 +++++++++++++++++++++
.../StandardConnectionPropertiesProviderTest.java | 131 ++-------------
9 files changed, 435 insertions(+), 235 deletions(-)
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/pom.xml
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/pom.xml
index 2d686772204..ac6fe48abbb 100644
---
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/pom.xml
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/pom.xml
@@ -29,10 +29,6 @@ language governing permissions and limitations under the
License. -->
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-distributed-cache-client-service-api</artifactId>
</dependency>
- <dependency>
- <groupId>org.apache.nifi</groupId>
- <artifactId>nifi-security-utils-api</artifactId>
- </dependency>
<dependency>
<groupId>io.debezium</groupId>
<artifactId>mysql-binlog-connector-java</artifactId>
@@ -62,5 +58,16 @@ language governing permissions and limitations under the
License. -->
<version>2.12.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-mysql</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.mysql</groupId>
+ <artifactId>mysql-connector-j</artifactId>
+ <version>26.7.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQL.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQL.java
index 6aa92467aed..82db4a52577 100644
---
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQL.java
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQL.java
@@ -61,6 +61,8 @@ import
org.apache.nifi.cdc.mysql.event.handler.UpdateEventHandler;
import org.apache.nifi.cdc.mysql.event.io.AbstractBinlogEventWriter;
import org.apache.nifi.cdc.mysql.processors.ssl.BinaryLogSSLSocketFactory;
import org.apache.nifi.cdc.mysql.processors.ssl.ConnectionPropertiesProvider;
+import org.apache.nifi.cdc.mysql.processors.ssl.DelegatingSSLContextProvider;
+import org.apache.nifi.cdc.mysql.processors.ssl.SecurityProperty;
import
org.apache.nifi.cdc.mysql.processors.ssl.StandardConnectionPropertiesProvider;
import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.PropertyDescriptor;
@@ -84,11 +86,11 @@ import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.security.util.TlsConfiguration;
-import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.ssl.SSLContextProvider;
import java.io.IOException;
import java.net.InetSocketAddress;
+import java.security.Security;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
@@ -109,6 +111,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
@@ -155,6 +158,12 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
private static final int DEFAULT_MYSQL_PORT = 3306;
+ // MySQL Connector/J 8.x driver class. Connections require Connector/J
8.1.0 or later when an SSLContextProvider is configured
+ private static final String MYSQL_DRIVER_CLASS_NAME =
"com.mysql.cj.jdbc.Driver";
+
+ // Legacy MySQL Connector/J 5.1.x driver class replaced by
MYSQL_DRIVER_CLASS_NAME during property migration
+ private static final String LEGACY_MYSQL_DRIVER_CLASS_NAME =
"com.mysql.jdbc.Driver";
+
// A regular expression matching multiline comments, used when parsing DDL
statements
private static final Pattern MULTI_COMMENT_PATTERN =
Pattern.compile("/\\*.*?\\*/", Pattern.DOTALL);
@@ -233,7 +242,7 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
public static final PropertyDescriptor DRIVER_NAME = new
PropertyDescriptor.Builder()
.name("MySQL Driver Class Name")
.description("The class name of the MySQL database driver class")
- .defaultValue("com.mysql.jdbc.Driver")
+ .defaultValue(MYSQL_DRIVER_CLASS_NAME)
.required(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
@@ -241,8 +250,10 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
public static final PropertyDescriptor DRIVER_LOCATION = new
PropertyDescriptor.Builder()
.name("MySQL Driver Locations")
- .description("Comma-separated list of files/folders and/or URLs
containing the MySQL driver JAR and its dependencies (if any). "
- + "For example
'/var/tmp/mysql-connector-java-5.1.38-bin.jar'")
+ .description("""
+ Comma-separated list of files/folders and/or URLs
containing the MySQL driver JAR and its dependencies (if any). \
+ MySQL Connector/J 8.1.0 or later is required when an SSL
Context Provider is configured. \
+ For example '/var/tmp/mysql-connector-j-8.4.0.jar'""")
.required(false)
.identifiesExternalResource(ResourceCardinality.MULTIPLE,
ResourceType.FILE, ResourceType.DIRECTORY, ResourceType.URL)
.expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
@@ -404,9 +415,9 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new
PropertyDescriptor.Builder()
.name("SSL Context Service")
- .description("SSL Context Service supporting encrypted socket
communication")
+ .description("SSL Context Provider supporting encrypted socket
communication")
.required(false)
- .identifiesControllerService(SSLContextService.class)
+ .identifiesControllerService(SSLContextProvider.class)
.dependsOn(SSL_MODE,
SSL_MODE_PREFERRED,
SSL_MODE_REQUIRED,
@@ -487,6 +498,9 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
config.renameProperty("capture-change-mysql-max-wait-time",
CONNECT_TIMEOUT.getName());
config.renameProperty("capture-change-mysql-hosts", HOSTS.getName());
config.renameProperty("capture-change-mysql-driver-class",
DRIVER_NAME.getName());
+ config.getRawPropertyValue(DRIVER_NAME.getName())
+ .filter(LEGACY_MYSQL_DRIVER_CLASS_NAME::equals)
+ .ifPresent(legacyDriverClassName ->
config.setProperty(DRIVER_NAME.getName(), MYSQL_DRIVER_CLASS_NAME));
List.of("capture-change-mysql-driver-locations", "MySQL Driver
Location(s)").forEach(
oldNameProperty -> config.renameProperty(oldNameProperty,
DRIVER_LOCATION.getName()));
config.renameProperty("capture-change-mysql-username",
USERNAME.getName());
@@ -632,7 +646,7 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
}
final SSLMode sslMode =
SSLMode.valueOf(context.getProperty(SSL_MODE).getValue());
- final SSLContextService sslContextService = sslMode ==
SSLMode.DISABLED ? null :
context.getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class);
+ final SSLContextProvider sslContextProvider = sslMode ==
SSLMode.DISABLED ? null :
context.getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextProvider.class);
// Save off MySQL cluster and JDBC driver information, will be used to
connect for event enrichment as well as for the binlog connector
try {
@@ -653,7 +667,7 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
Long serverId =
context.getProperty(SERVER_ID).evaluateAttributeExpressions().asLong();
- connect(hosts, username, password, serverId, driverLocation,
driverName, connectTimeout, sslContextService, sslMode);
+ connect(hosts, username, password, serverId, driverLocation,
driverName, connectTimeout, sslContextProvider, sslMode);
} catch (IOException | IllegalStateException e) {
if (eventListener != null) {
eventListener.stop();
@@ -754,12 +768,13 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
protected void connect(List<InetSocketAddress> hosts, String username,
String password, Long serverId,
String driverLocation, String driverName, long
connectTimeout,
- final SSLContextService sslContextService, final
SSLMode sslMode) throws IOException {
+ final SSLContextProvider sslContextProvider, final
SSLMode sslMode) throws IOException {
int connectionAttempts = 0;
final int numHosts = hosts.size();
InetSocketAddress connectedHost = null;
Exception lastConnectException = new Exception("Unknown connection
error");
+ final SSLContext sslContext = sslContextProvider == null ? null :
sslContextProvider.createContext();
try {
// Ensure driverLocation and driverName are correct before
establishing binlog connection
@@ -804,8 +819,7 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
}
binlogClient.setSSLMode(sslMode);
- if (sslContextService != null) {
- final SSLContext sslContext =
sslContextService.createContext();
+ if (sslContext != null) {
final BinaryLogSSLSocketFactory sslSocketFactory = new
BinaryLogSSLSocketFactory(sslContext.getSocketFactory());
binlogClient.setSslSocketFactory(sslSocketFactory);
}
@@ -838,10 +852,9 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
throw new IOException("Could not connect binlog client to any of
the specified hosts due to: " + lastConnectException.getMessage(),
lastConnectException);
}
- final TlsConfiguration tlsConfiguration = sslContextService == null ?
null : sslContextService.createTlsConfiguration();
- final ConnectionPropertiesProvider connectionPropertiesProvider = new
StandardConnectionPropertiesProvider(sslMode, tlsConfiguration);
+ final ConnectionPropertiesProvider connectionPropertiesProvider = new
StandardConnectionPropertiesProvider(sslMode);
final Map<String, String> jdbcConnectionProperties =
connectionPropertiesProvider.getConnectionProperties();
- jdbcConnectionHolder = new JDBCConnectionHolder(connectedHost,
username, password, jdbcConnectionProperties, connectTimeout);
+ jdbcConnectionHolder = new JDBCConnectionHolder(connectedHost,
username, password, jdbcConnectionProperties, connectTimeout, sslContext,
getIdentifier());
try {
// Ensure connection can be created.
getJdbcConnection();
@@ -1253,10 +1266,14 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
private final String connectionUrl;
private final Properties connectionProps = new Properties();
private final long connectionTimeoutMillis;
+ private final SSLContext sslContext;
+ private final String sslContextProviderNamePrefix;
+ private final AtomicLong sslContextProviderNameCounter = new
AtomicLong();
private Connection connection;
- private JDBCConnectionHolder(InetSocketAddress host, String username,
String password, Map<String, String> customProperties, long
connectionTimeoutMillis) {
+ private JDBCConnectionHolder(InetSocketAddress host, String username,
String password, Map<String, String> customProperties, long
connectionTimeoutMillis,
+ final SSLContext sslContext, final String
componentId) {
this.connectionUrl = "jdbc:mysql://" + host.getHostString() + ":"
+ host.getPort();
connectionProps.putAll(customProperties);
if (username != null) {
@@ -1267,6 +1284,8 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
}
this.connectionTimeoutMillis = connectionTimeoutMillis;
+ this.sslContext = sslContext;
+ this.sslContextProviderNamePrefix =
CaptureChangeMySQL.this.getClass().getSimpleName() + "-" + componentId;
}
private Connection getConnection() throws SQLException {
@@ -1279,10 +1298,28 @@ public class CaptureChangeMySQL extends
AbstractSessionFactoryProcessor {
close();
getLogger().trace("Creating a new JDBC connection.");
- connection = DriverManager.getConnection(connectionUrl,
connectionProps);
+ connection = createConnection();
return connection;
}
+ private Connection createConnection() throws SQLException {
+ if (sslContext == null) {
+ return DriverManager.getConnection(connectionUrl,
connectionProps);
+ }
+
+ // Register a uniquely named security provider that exposes the
configured SSLContext and direct MySQL Connector/J to it
+ // for the duration of the connection attempt. Registration is
scoped to the connection attempt so that the JVM-wide
+ // security provider registry does not retain a reference to this
instance ClassLoader after the connection is established.
+ final String sslContextProviderName = sslContextProviderNamePrefix
+ "-" + sslContextProviderNameCounter.incrementAndGet();
+
connectionProps.setProperty(SecurityProperty.SSL_CONTEXT_PROVIDER.getProperty(),
sslContextProviderName);
+ Security.addProvider(new
DelegatingSSLContextProvider(sslContextProviderName, sslContext));
+ try {
+ return DriverManager.getConnection(connectionUrl,
connectionProps);
+ } finally {
+ Security.removeProvider(sslContextProviderName);
+ }
+ }
+
private void close() {
if (connection != null) {
try {
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextProvider.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextProvider.java
new file mode 100644
index 00000000000..bb16ada35f7
--- /dev/null
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextProvider.java
@@ -0,0 +1,58 @@
+/*
+ * 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.cdc.mysql.processors.ssl;
+
+import java.security.Provider;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.SSLContext;
+
+/**
+ * Java Security Provider that exposes a single pre-initialized {@link
SSLContext} under the standard TLS algorithm.
+ * MySQL Connector/J can be directed to this provider using the {@code
sslContextProvider} connection property, which
+ * allows an in-memory SSLContext to be used for JDBC connections without
configuring keystore or truststore files.
+ * Instances are expected to be registered under a unique name for the
duration of a connection attempt and removed
+ * afterward to avoid retaining references in the JVM-wide security provider
registry.
+ */
+public final class DelegatingSSLContextProvider extends Provider {
+
+ private static final String PROVIDER_VERSION = "1.0";
+
+ private static final String SERVICE_TYPE = "SSLContext";
+
+ private static final String TLS_ALGORITHM = "TLS";
+
+ public DelegatingSSLContextProvider(final String name, final SSLContext
sslContext) {
+ super(name, PROVIDER_VERSION, "Delegates the TLS SSLContext to a
pre-initialized instance");
+ putService(new DelegatingService(this, sslContext));
+ }
+
+ private static final class DelegatingService extends Service {
+
+ private final SSLContext sslContext;
+
+ private DelegatingService(final Provider provider, final SSLContext
sslContext) {
+ super(provider, SERVICE_TYPE, TLS_ALGORITHM,
DelegatingSSLContextSpi.class.getName(), List.of(), Map.of());
+ this.sslContext = sslContext;
+ }
+
+ @Override
+ public Object newInstance(final Object constructorParameter) {
+ return new DelegatingSSLContextSpi(sslContext);
+ }
+ }
+}
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextSpi.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextSpi.java
new file mode 100644
index 00000000000..c7d358182d7
--- /dev/null
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextSpi.java
@@ -0,0 +1,77 @@
+/*
+ * 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.cdc.mysql.processors.ssl;
+
+import java.security.SecureRandom;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLContextSpi;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+/**
+ * SSLContextSpi implementation that delegates all operations to a
pre-initialized SSLContext. Initialization
+ * parameters supplied by the caller are ignored because the delegated
SSLContext already contains the configured
+ * key and trust material. This allows MySQL Connector/J to obtain a fully
configured SSLContext from a
+ * {@link DelegatingSSLContextProvider} rather than building one from keystore
and truststore files.
+ */
+final class DelegatingSSLContextSpi extends SSLContextSpi {
+
+ private final SSLContext sslContext;
+
+ DelegatingSSLContextSpi(final SSLContext sslContext) {
+ this.sslContext = sslContext;
+ }
+
+ @Override
+ protected void engineInit(final KeyManager[] keyManagers, final
TrustManager[] trustManagers, final SecureRandom secureRandom) {
+ // The delegated SSLContext is already initialized, so the key and
trust managers provided by the caller are ignored
+ }
+
+ @Override
+ protected SSLSocketFactory engineGetSocketFactory() {
+ return sslContext.getSocketFactory();
+ }
+
+ @Override
+ protected SSLServerSocketFactory engineGetServerSocketFactory() {
+ return sslContext.getServerSocketFactory();
+ }
+
+ @Override
+ protected SSLEngine engineCreateSSLEngine() {
+ return sslContext.createSSLEngine();
+ }
+
+ @Override
+ protected SSLEngine engineCreateSSLEngine(final String host, final int
port) {
+ return sslContext.createSSLEngine(host, port);
+ }
+
+ @Override
+ protected SSLSessionContext engineGetServerSessionContext() {
+ return sslContext.getServerSessionContext();
+ }
+
+ @Override
+ protected SSLSessionContext engineGetClientSessionContext() {
+ return sslContext.getClientSessionContext();
+ }
+}
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/SecurityProperty.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/SecurityProperty.java
index 82d8027c1d9..460c48c9bed 100644
---
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/SecurityProperty.java
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/SecurityProperty.java
@@ -20,35 +20,11 @@ package org.apache.nifi.cdc.mysql.processors.ssl;
* MySQL Connector/J Security Properties
*/
public enum SecurityProperty {
- /** Deprecated alias for tlsVersions */
- ENABLED_TLS_PROTOCOLS("enabledTLSProtocols"),
+ /** Preferred SSL Mode selection introduced in MySQL Connector/J 8.0.13 */
+ SSL_MODE("sslMode"),
- /** Added in MySQL 5.1.0 */
- TRUST_CERTIFICATE_KEY_STORE_URL("trustsCertificateKeyStoreUrl"),
-
- /** Added in MySQL 5.1.0 and defaults to JKS */
- TRUST_CERTIFICATE_KEY_STORE_TYPE("trustCertificateKeyStoreType"),
-
- /** Added in MySQL 5.1.0 */
- TRUST_CERTIFICATE_KEY_STORE_PASSWORD("trustCertificateKeyStorePassword"),
-
- /** Added in MySQL 5.1.0 */
- CLIENT_CERTIFICATE_KEY_STORE_URL("clientCertificateKeyStoreUrl"),
-
- /** Added in MySQL 5.1.0 and defaults to JKS */
- CLIENT_CERTIFICATE_KEY_STORE_TYPE("clientCertificateKeyStoreType"),
-
- /** Added in MySQL 5.1.0 */
- CLIENT_CERTIFICATE_KEY_STORE_PASSWORD("clientCertificateKeyStorePassword"),
-
- /** Deprecated in favor of sslMode and evaluated when useSSL is enabled */
- REQUIRE_SSL("requireSSL"),
-
- /** Deprecated in favor of sslMode and defaults to true in 8.0.13 and
later */
- USE_SSL("useSSL"),
-
- /** Deprecated in favor of sslMode and defaults to false in 8.0.13 and
later */
- VERIFY_SERVER_CERTIFICATE("verifyServerCertificate");
+ /** Name of a registered java.security.Provider supplying the SSLContext
introduced in MySQL Connector/J 8.1.0 */
+ SSL_CONTEXT_PROVIDER("sslContextProvider");
private final String property;
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProvider.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProvider.java
index 4a3ea797a88..fce5d690217 100644
---
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProvider.java
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/main/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProvider.java
@@ -17,8 +17,6 @@
package org.apache.nifi.cdc.mysql.processors.ssl;
import com.github.shyiko.mysql.binlog.network.SSLMode;
-import org.apache.nifi.security.util.TlsConfiguration;
-import org.apache.nifi.security.util.TlsPlatform;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -28,77 +26,22 @@ import java.util.Objects;
* Standard implementation of Connection Properties Provider
*/
public class StandardConnectionPropertiesProvider implements
ConnectionPropertiesProvider {
- private static final String COMMA_SEPARATOR = ",";
private final SSLMode sslMode;
- private final TlsConfiguration tlsConfiguration;
-
- public StandardConnectionPropertiesProvider(
- final SSLMode sslMode,
- final TlsConfiguration tlsConfiguration
- ) {
+ public StandardConnectionPropertiesProvider(final SSLMode sslMode) {
this.sslMode = Objects.requireNonNull(sslMode, "SSL Mode required");
- this.tlsConfiguration = tlsConfiguration;
}
/**
- * Get Connection Properties based on SSL Mode and TLS Configuration
+ * Get Connection Properties based on the configured SSL Mode
*
* @return JDBC Connection Properties
*/
@Override
public Map<String, String> getConnectionProperties() {
final Map<String, String> properties = new LinkedHashMap<>();
-
- if (SSLMode.DISABLED == sslMode) {
- properties.put(SecurityProperty.USE_SSL.getProperty(),
Boolean.FALSE.toString());
- } else {
- // Enable TLS negotiation for all modes
- properties.put(SecurityProperty.USE_SSL.getProperty(),
Boolean.TRUE.toString());
-
- if (SSLMode.PREFERRED == sslMode) {
- properties.put(SecurityProperty.REQUIRE_SSL.getProperty(),
Boolean.FALSE.toString());
- } else {
- // Modes other than preferred require SSL
- properties.put(SecurityProperty.REQUIRE_SSL.getProperty(),
Boolean.TRUE.toString());
- }
-
- if (SSLMode.VERIFY_IDENTITY == sslMode) {
-
properties.put(SecurityProperty.VERIFY_SERVER_CERTIFICATE.getProperty(),
Boolean.TRUE.toString());
- }
-
- if (tlsConfiguration == null) {
- // Set preferred protocols based on Java platform configuration
- final String protocols = String.join(COMMA_SEPARATOR,
TlsPlatform.getPreferredProtocols());
-
properties.put(SecurityProperty.ENABLED_TLS_PROTOCOLS.getProperty(), protocols);
- } else {
- final Map<String, String> certificateProperties =
getCertificateProperties();
- properties.putAll(certificateProperties);
- }
- }
-
- return properties;
- }
-
- private Map<String, String> getCertificateProperties() {
- final Map<String, String> properties = new LinkedHashMap<>();
-
- final String protocols = String.join(COMMA_SEPARATOR,
tlsConfiguration.getEnabledProtocols());
- properties.put(SecurityProperty.ENABLED_TLS_PROTOCOLS.getProperty(),
protocols);
-
- if (tlsConfiguration.isKeystorePopulated()) {
-
properties.put(SecurityProperty.CLIENT_CERTIFICATE_KEY_STORE_URL.getProperty(),
tlsConfiguration.getKeystorePath());
-
properties.put(SecurityProperty.CLIENT_CERTIFICATE_KEY_STORE_TYPE.getProperty(),
tlsConfiguration.getKeystoreType().getType());
-
properties.put(SecurityProperty.CLIENT_CERTIFICATE_KEY_STORE_PASSWORD.getProperty(),
tlsConfiguration.getKeystorePassword());
- }
-
- if (tlsConfiguration.isTruststorePopulated()) {
-
properties.put(SecurityProperty.TRUST_CERTIFICATE_KEY_STORE_URL.getProperty(),
tlsConfiguration.getTruststorePath());
-
properties.put(SecurityProperty.TRUST_CERTIFICATE_KEY_STORE_TYPE.getProperty(),
tlsConfiguration.getTruststoreType().getType());
-
properties.put(SecurityProperty.TRUST_CERTIFICATE_KEY_STORE_PASSWORD.getProperty(),
tlsConfiguration.getTruststorePassword());
- }
-
+ properties.put(SecurityProperty.SSL_MODE.getProperty(),
sslMode.toString());
return properties;
}
}
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQLTest.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQLTest.java
index a245243a0c2..ed7e68a249f 100644
---
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQLTest.java
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/CaptureChangeMySQLTest.java
@@ -47,8 +47,9 @@ import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.provenance.ProvenanceEventType;
import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.ssl.SSLContextProvider;
import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.MockPropertyConfiguration;
import org.apache.nifi.util.PropertyMigrationResult;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
@@ -126,6 +127,9 @@ public class CaptureChangeMySQLTest {
private static final String FOUR = "4";
private static final String TEN = "10";
private static final ObjectMapper MAPPER = new ObjectMapper();
+ private static final String LEGACY_DRIVER_CLASS_NAME =
"com.mysql.jdbc.Driver";
+ private static final String CURRENT_DRIVER_CLASS_NAME =
"com.mysql.cj.jdbc.Driver";
+ private static final String CUSTOM_DRIVER_CLASS_NAME =
"com.example.CustomDriver";
private MockCaptureChangeMySQL processor;
private TestRunner testRunner;
@@ -153,32 +157,32 @@ public class CaptureChangeMySQLTest {
}
@Test
- public void testSslModeRequiredSslContextServiceConfigured(@Mock
SSLContextService sslContextService) throws InitializationException {
+ public void testSslModeRequiredSslContextServiceConfigured(@Mock
SSLContextProvider sslContextProvider) throws InitializationException {
testRunner.setProperty(CaptureChangeMySQL.HOSTS,
LOCAL_HOST_DEFAULT_PORT);
testRunner.setProperty(CaptureChangeMySQL.SSL_MODE,
SSLMode.REQUIRED.toString());
- String identifier = SSLContextService.class.getName();
- when(sslContextService.getIdentifier()).thenReturn(identifier);
- testRunner.addControllerService(identifier, sslContextService);
- testRunner.enableControllerService(sslContextService);
+ String identifier = SSLContextProvider.class.getName();
+ when(sslContextProvider.getIdentifier()).thenReturn(identifier);
+ testRunner.addControllerService(identifier, sslContextProvider);
+ testRunner.enableControllerService(sslContextProvider);
testRunner.setProperty(CaptureChangeMySQL.SSL_CONTEXT_SERVICE,
identifier);
testRunner.assertValid();
}
@Test
- public void testSslModeRequiredSslContextServiceConnected(@Mock
SSLContextService sslContextService) throws NoSuchAlgorithmException,
InitializationException {
+ public void testSslModeRequiredSslContextServiceConnected(@Mock
SSLContextProvider sslContextProvider) throws NoSuchAlgorithmException,
InitializationException {
testRunner.setProperty(CaptureChangeMySQL.HOSTS,
LOCAL_HOST_DEFAULT_PORT);
SSLMode sslMode = SSLMode.REQUIRED;
testRunner.setProperty(CaptureChangeMySQL.SSL_MODE,
sslMode.toString());
SSLContext sslContext = SSLContext.getDefault();
- String identifier = SSLContextService.class.getName();
- when(sslContextService.getIdentifier()).thenReturn(identifier);
- doReturn(sslContext).when(sslContextService).createContext();
+ String identifier = SSLContextProvider.class.getName();
+ when(sslContextProvider.getIdentifier()).thenReturn(identifier);
+ doReturn(sslContext).when(sslContextProvider).createContext();
- testRunner.addControllerService(identifier, sslContextService);
- testRunner.enableControllerService(sslContextService);
+ testRunner.addControllerService(identifier, sslContextProvider);
+ testRunner.enableControllerService(sslContextProvider);
testRunner.setProperty(CaptureChangeMySQL.SSL_CONTEXT_SERVICE,
identifier);
testRunner.assertValid();
@@ -1350,6 +1354,26 @@ public class CaptureChangeMySQLTest {
assertEquals(expectedRemoved,
propertyMigrationResult.getPropertiesRemoved());
}
+ @Test
+ void testMigrationReplacesLegacyDriverClassName() {
+ final MockPropertyConfiguration config = new MockPropertyConfiguration(
+ Map.of(CaptureChangeMySQL.DRIVER_NAME.getName(),
LEGACY_DRIVER_CLASS_NAME));
+
+ processor.migrateProperties(config);
+
+ assertEquals(CURRENT_DRIVER_CLASS_NAME,
config.getRawPropertyValue(CaptureChangeMySQL.DRIVER_NAME.getName()).orElse(null));
+ }
+
+ @Test
+ void testMigrationRetainsCustomDriverClassName() {
+ final MockPropertyConfiguration config = new MockPropertyConfiguration(
+ Map.of(CaptureChangeMySQL.DRIVER_NAME.getName(),
CUSTOM_DRIVER_CLASS_NAME));
+
+ processor.migrateProperties(config);
+
+ assertEquals(CUSTOM_DRIVER_CLASS_NAME,
config.getRawPropertyValue(CaptureChangeMySQL.DRIVER_NAME.getName()).orElse(null));
+ }
+
/********************************
* Mock and helper classes below
********************************/
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextProviderMySQLDriverIT.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextProviderMySQLDriverIT.java
new file mode 100644
index 00000000000..efae9e3f854
--- /dev/null
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/DelegatingSSLContextProviderMySQLDriverIT.java
@@ -0,0 +1,175 @@
+/*
+ * 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.cdc.mysql.processors.ssl;
+
+import com.github.shyiko.mysql.binlog.network.SSLMode;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.mysql.MySQLContainer;
+import org.testcontainers.utility.DockerImageName;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DelegatingSSLContextProviderMySQLDriverIT {
+
+ private static final String MYSQL_IMAGE = "mysql:8.4";
+
+ private static final String SSL_CONTEXT_PROTOCOL = "TLS";
+
+ private static final String MYSQL_CA_PATH = "/var/lib/mysql/ca.pem";
+
+ private static final String CERTIFICATE_TYPE = "X.509";
+
+ private static final String SSL_CIPHER_STATUS_QUERY = "SHOW SESSION STATUS
LIKE 'Ssl_cipher'";
+
+ private static MySQLContainer mysql;
+
+ @BeforeAll
+ static void startContainer() {
+ mysql = new MySQLContainer(DockerImageName.parse(MYSQL_IMAGE));
+ mysql.start();
+ }
+
+ @AfterAll
+ static void stopContainer() {
+ if (mysql != null) {
+ mysql.stop();
+ }
+ }
+
+ @Test
+ void testRequiredModeUsesInjectedSslContext() throws Exception {
+ final AtomicBoolean trustManagerInvoked = new AtomicBoolean(false);
+ final SSLContext sslContext =
SSLContext.getInstance(SSL_CONTEXT_PROTOCOL);
+ sslContext.init(null, new TrustManager[]{new
RecordingTrustManager(trustManagerInvoked)}, new SecureRandom());
+
+ try (Connection connection = connect(SSLMode.REQUIRED, sslContext)) {
+ assertTlsActive(connection);
+ }
+
+ assertTrue(trustManagerInvoked.get());
+ }
+
+ @Test
+ void testVerifyCaSucceedsWhenInjectedContextTrustsServerAuthority() throws
Exception {
+ final SSLContext sslContext = buildContextTrusting(readServerCa());
+
+ try (Connection connection = connect(SSLMode.VERIFY_CA, sslContext)) {
+ assertTlsActive(connection);
+ }
+ }
+
+ @Test
+ void testVerifyCaFailsWhenInjectedContextDoesNotTrustServerAuthority()
throws Exception {
+ final SSLContext sslContext = buildContextTrusting(null);
+
+ assertThrows(SQLException.class, () -> connect(SSLMode.VERIFY_CA,
sslContext));
+ }
+
+ private Connection connect(final SSLMode sslMode, final SSLContext
sslContext) throws SQLException {
+ final Map<String, String> sslProperties = new
StandardConnectionPropertiesProvider(sslMode).getConnectionProperties();
+
+ final Properties properties = new Properties();
+ properties.putAll(sslProperties);
+ properties.setProperty("user", mysql.getUsername());
+ properties.setProperty("password", mysql.getPassword());
+
+ final String providerName =
DelegatingSSLContextProvider.class.getSimpleName() + UUID.randomUUID();
+
properties.setProperty(SecurityProperty.SSL_CONTEXT_PROVIDER.getProperty(),
providerName);
+ final Provider provider = new
DelegatingSSLContextProvider(providerName, sslContext);
+ Security.addProvider(provider);
+ try {
+ return DriverManager.getConnection(mysql.getJdbcUrl(), properties);
+ } finally {
+ Security.removeProvider(providerName);
+ }
+ }
+
+ private void assertTlsActive(final Connection connection) throws
SQLException {
+ try (Statement statement = connection.createStatement();
+ ResultSet resultSet =
statement.executeQuery(SSL_CIPHER_STATUS_QUERY)) {
+ assertTrue(resultSet.next());
+ final String cipher = resultSet.getString(2);
+ assertNotNull(cipher);
+ assertFalse(cipher.isEmpty(), "Connection is not using TLS");
+ }
+ }
+
+ private X509Certificate readServerCa() throws Exception {
+ final byte[] caBytes = mysql.copyFileFromContainer(MYSQL_CA_PATH,
InputStream::readAllBytes);
+ final CertificateFactory certificateFactory =
CertificateFactory.getInstance(CERTIFICATE_TYPE);
+ return (X509Certificate) certificateFactory.generateCertificate(new
ByteArrayInputStream(caBytes));
+ }
+
+ private SSLContext buildContextTrusting(final X509Certificate
trustedCertificate) throws Exception {
+ final KeyStore trustStore =
KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(null, null);
+ if (trustedCertificate != null) {
+ trustStore.setCertificateEntry("mysql-ca", trustedCertificate);
+ }
+
+ final TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+
+ final SSLContext sslContext =
SSLContext.getInstance(SSL_CONTEXT_PROTOCOL);
+ sslContext.init(null, trustManagerFactory.getTrustManagers(), new
SecureRandom());
+ return sslContext;
+ }
+
+ private record RecordingTrustManager(AtomicBoolean invoked) implements
X509TrustManager {
+
+ @Override
+ public void checkClientTrusted(final X509Certificate[] chain, final
String authType) {
+ }
+
+ @Override
+ public void checkServerTrusted(final X509Certificate[] chain, final
String authType) {
+ invoked.set(true);
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ }
+}
diff --git
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProviderTest.java
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProviderTest.java
index 3784067ce28..d189ecb31fa 100644
---
a/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProviderTest.java
+++
b/nifi-extension-bundles/nifi-cdc/nifi-cdc-mysql-bundle/nifi-cdc-mysql-processors/src/test/java/org/apache/nifi/cdc/mysql/processors/ssl/StandardConnectionPropertiesProviderTest.java
@@ -17,150 +17,53 @@
package org.apache.nifi.cdc.mysql.processors.ssl;
import com.github.shyiko.mysql.binlog.network.SSLMode;
-import org.apache.nifi.security.util.KeystoreType;
-import org.apache.nifi.security.util.TlsConfiguration;
-import org.apache.nifi.security.util.TlsPlatform;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.mockito.Mockito.when;
-@ExtendWith(MockitoExtension.class)
-public class StandardConnectionPropertiesProviderTest {
- private static final String KEY_STORE_PATH = "keystore.p12";
+class StandardConnectionPropertiesProviderTest {
- private static final KeystoreType KEY_STORE_TYPE = KeystoreType.PKCS12;
+ private static final String SSL_MODE_PROPERTY =
SecurityProperty.SSL_MODE.getProperty();
- private static final String KEY_STORE_PASSWORD = String.class.getName();
-
- private static final String TRUST_STORE_PATH = "cacerts";
-
- private static final KeystoreType TRUST_STORE_TYPE = KeystoreType.PKCS12;
-
- private static final String TRUST_STORE_PASSWORD = Integer.class.getName();
-
- @Mock
- TlsConfiguration tlsConfiguration;
+ private static final String SSL_CONTEXT_PROVIDER_PROPERTY =
SecurityProperty.SSL_CONTEXT_PROVIDER.getProperty();
@Test
void testGetConnectionPropertiesSslModeDisabled() {
- final StandardConnectionPropertiesProvider provider = new
StandardConnectionPropertiesProvider(SSLMode.DISABLED, null);
-
- final Map<String, String> properties =
provider.getConnectionProperties();
-
- assertNotNull(properties);
-
- final String useSsl =
properties.get(SecurityProperty.USE_SSL.getProperty());
- assertEquals(Boolean.FALSE.toString(), useSsl);
+ assertSslModeMapped(SSLMode.DISABLED);
}
@Test
void testGetConnectionPropertiesSslModePreferred() {
- final StandardConnectionPropertiesProvider provider = new
StandardConnectionPropertiesProvider(SSLMode.PREFERRED, null);
-
- final Map<String, String> properties =
provider.getConnectionProperties();
-
- assertNotNull(properties);
-
- final String useSsl =
properties.get(SecurityProperty.USE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), useSsl);
-
- final String requireSsl =
properties.get(SecurityProperty.REQUIRE_SSL.getProperty());
- assertEquals(Boolean.FALSE.toString(), requireSsl);
-
- final String protocols =
properties.get(SecurityProperty.ENABLED_TLS_PROTOCOLS.getProperty());
- assertNotNull(protocols);
+ assertSslModeMapped(SSLMode.PREFERRED);
}
@Test
void testGetConnectionPropertiesSslModeRequired() {
- final StandardConnectionPropertiesProvider provider = new
StandardConnectionPropertiesProvider(SSLMode.REQUIRED, null);
-
- final Map<String, String> properties =
provider.getConnectionProperties();
-
- assertNotNull(properties);
-
- final String useSsl =
properties.get(SecurityProperty.USE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), useSsl);
-
- final String requireSsl =
properties.get(SecurityProperty.REQUIRE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), requireSsl);
-
- final String protocols =
properties.get(SecurityProperty.ENABLED_TLS_PROTOCOLS.getProperty());
- assertNotNull(protocols);
+ assertSslModeMapped(SSLMode.REQUIRED);
}
@Test
- void testGetConnectionPropertiesSslModeVerifyIdentity() {
- final StandardConnectionPropertiesProvider provider = new
StandardConnectionPropertiesProvider(SSLMode.VERIFY_IDENTITY, null);
-
- final Map<String, String> properties =
provider.getConnectionProperties();
-
- assertNotNull(properties);
-
- final String useSsl =
properties.get(SecurityProperty.USE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), useSsl);
-
- final String requireSsl =
properties.get(SecurityProperty.REQUIRE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), requireSsl);
-
- final String protocols =
properties.get(SecurityProperty.ENABLED_TLS_PROTOCOLS.getProperty());
- assertNotNull(protocols);
-
- final String verifyServerCertificate =
properties.get(SecurityProperty.VERIFY_SERVER_CERTIFICATE.getProperty());
- assertEquals(Boolean.TRUE.toString(), verifyServerCertificate);
+ void testGetConnectionPropertiesSslModeVerifyCa() {
+ assertSslModeMapped(SSLMode.VERIFY_CA);
}
@Test
- void testGetConnectionPropertiesSslModeRequiredTlsConfiguration() {
- final String latestProtocol = TlsPlatform.getLatestProtocol();
- when(tlsConfiguration.getEnabledProtocols()).thenReturn(new
String[]{latestProtocol});
- when(tlsConfiguration.isKeystorePopulated()).thenReturn(true);
- when(tlsConfiguration.getKeystorePath()).thenReturn(KEY_STORE_PATH);
- when(tlsConfiguration.getKeystoreType()).thenReturn(KEY_STORE_TYPE);
-
when(tlsConfiguration.getKeystorePassword()).thenReturn(KEY_STORE_PASSWORD);
- when(tlsConfiguration.isTruststorePopulated()).thenReturn(true);
-
when(tlsConfiguration.getTruststorePath()).thenReturn(TRUST_STORE_PATH);
-
when(tlsConfiguration.getTruststoreType()).thenReturn(TRUST_STORE_TYPE);
-
when(tlsConfiguration.getTruststorePassword()).thenReturn(TRUST_STORE_PASSWORD);
+ void testGetConnectionPropertiesSslModeVerifyIdentity() {
+ assertSslModeMapped(SSLMode.VERIFY_IDENTITY);
+ }
- final StandardConnectionPropertiesProvider provider = new
StandardConnectionPropertiesProvider(SSLMode.REQUIRED, tlsConfiguration);
+ private void assertSslModeMapped(final SSLMode sslMode) {
+ final StandardConnectionPropertiesProvider provider = new
StandardConnectionPropertiesProvider(sslMode);
final Map<String, String> properties =
provider.getConnectionProperties();
assertNotNull(properties);
-
- final String useSsl =
properties.get(SecurityProperty.USE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), useSsl);
-
- final String requireSsl =
properties.get(SecurityProperty.REQUIRE_SSL.getProperty());
- assertEquals(Boolean.TRUE.toString(), requireSsl);
-
- final String protocols =
properties.get(SecurityProperty.ENABLED_TLS_PROTOCOLS.getProperty());
- assertEquals(latestProtocol, protocols);
-
- final String clientCertificateUrl =
properties.get(SecurityProperty.CLIENT_CERTIFICATE_KEY_STORE_URL.getProperty());
- assertEquals(KEY_STORE_PATH, clientCertificateUrl);
-
- final String clientCertificateType =
properties.get(SecurityProperty.CLIENT_CERTIFICATE_KEY_STORE_TYPE.getProperty());
- assertEquals(KEY_STORE_TYPE.getType(), clientCertificateType);
-
- final String clientCertificatePassword =
properties.get(SecurityProperty.CLIENT_CERTIFICATE_KEY_STORE_PASSWORD.getProperty());
- assertEquals(KEY_STORE_PASSWORD, clientCertificatePassword);
-
- final String trustCertificateUrl =
properties.get(SecurityProperty.TRUST_CERTIFICATE_KEY_STORE_URL.getProperty());
- assertEquals(TRUST_STORE_PATH, trustCertificateUrl);
-
- final String trustCertificateType =
properties.get(SecurityProperty.TRUST_CERTIFICATE_KEY_STORE_TYPE.getProperty());
- assertEquals(TRUST_STORE_TYPE.getType(), trustCertificateType);
-
- final String trustCertificatePassword =
properties.get(SecurityProperty.TRUST_CERTIFICATE_KEY_STORE_PASSWORD.getProperty());
- assertEquals(TRUST_STORE_PASSWORD, trustCertificatePassword);
+ assertEquals(sslMode.toString(), properties.get(SSL_MODE_PROPERTY));
+ assertFalse(properties.containsKey(SSL_CONTEXT_PROVIDER_PROPERTY),
+ "The SSLContext provider name is registered per connection
attempt and must not be produced by the properties provider");
}
}