From 4975873de18960bf19461ea710229a25ad4f0647 Mon Sep 17 00:00:00 2001 From: Rob Rudin Date: Wed, 25 Jan 2023 10:08:54 -0500 Subject: [PATCH] Builder now inits SSL context when protocol and trust manager exist Ran into this while testing, where I was providing both a protocol and a "trust all" trust manager, but the builder failed to create a client. In this scenario, the builder is able to initialize the SSLContext itself since it has a trust manager from the user, so it now does so. --- .../impl/DatabaseClientPropertySource.java | 29 +++++++++++++------ .../test/DatabaseClientBuilderTest.java | 11 +++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java index fdf02d54c..04b1aebb1 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java @@ -21,6 +21,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; +import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.LinkedHashMap; import java.util.Map; @@ -109,10 +110,12 @@ private DatabaseClientFactory.SecurityContext newSecurityContext() { } securityContext = newSecurityContext(type); - SSLContext sslContext = determineSSLContext(); + X509TrustManager trustManager = determineTrustManager(); + SSLContext sslContext = determineSSLContext(trustManager); if (sslContext != null) { - securityContext.withSSLContext(sslContext, determineTrustManager()); + securityContext.withSSLContext(sslContext, trustManager); } + securityContext.withSSLHostnameVerifier(determineHostnameVerifier()); return securityContext; } @@ -180,10 +183,10 @@ private DatabaseClientFactory.SecurityContext newSAMLAuthContext() { ); } - private SSLContext determineSSLContext() { - Object sslContext = propertySource.apply(PREFIX + "sslContext"); - if (sslContext instanceof SSLContext) { - return (SSLContext) sslContext; + private SSLContext determineSSLContext(X509TrustManager trustManager) { + SSLContext sslContext = (SSLContext) propertySource.apply(PREFIX + "sslContext"); + if (sslContext != null) { + return sslContext; } String protocol = (String) propertySource.apply(PREFIX + "sslProtocol"); if (protocol != null) { @@ -195,13 +198,21 @@ private SSLContext determineSSLContext() { } } try { - // Note that if only a protocol is specified, and not a TrustManager, an attempt will later be made - // to use the JVM's default TrustManager - return SSLContext.getInstance(protocol); + sslContext = SSLContext.getInstance(protocol); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Unable to get SSLContext instance with protocol: " + protocol + "; cause: " + e.getMessage(), e); } + // Note that if only a protocol is specified, and not a TrustManager, an attempt will later be made + // to use the JVM's default TrustManager + if (trustManager != null) { + try { + sslContext.init(null, new X509TrustManager[]{trustManager}, null); + } catch (KeyManagementException e) { + throw new RuntimeException("Unable to initialize SSLContext; protocol: " + protocol + "; cause: " + e.getMessage(), e); + } + } + return sslContext; } return null; } diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java index ae5061615..5efac7a6f 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java @@ -167,6 +167,11 @@ void sslProtocol() { assertNotNull(bean.getSecurityContext().getSSLContext()); assertNull(bean.getSecurityContext().getTrustManager()); assertNull(bean.getSecurityContext().getSSLHostnameVerifier()); + + assertThrows(IllegalStateException.class, () -> bean.getSecurityContext().getSSLContext().getSocketFactory(), + "If an SSL protocol is provided with no trust manager, the builder is expected to create an instance of " + + "SSLContext but not to initialize it. Later on - via OkHttpUtil - the Java Client will attempt to " + + "initialize the SSLContext before using it by using the JVM's default trust manager."); } @Test @@ -190,6 +195,12 @@ void sslProtocolAndTrustManager() { assertNotNull(bean.getSecurityContext().getTrustManager()); assertEquals(Common.TRUST_ALL_MANAGER, bean.getSecurityContext().getTrustManager()); assertNull(bean.getSecurityContext().getSSLHostnameVerifier()); + + assertNotNull(bean.getSecurityContext().getSSLContext().getSocketFactory(), + "Since a protocol was provided with a trust manager, the builder is expected to initialize the " + + "SSLContext created via the protocol using the given trust manager. This is primarily intended to " + + "support a use case of providing a custom trust manager (often a 'trust all' one in a development or " + + "test environment) without forcing the user to initialize an SSLContext themselves."); } @Test