Skip to content

Commit f849075

Browse files
authored
Adds SSLTrustStoreProvider support to Databricks JDBC (#875)
* truststoreprovider support * Enhance SSL support and add tests - Added support for SSL client certificate authentication via the new parameter: SSLTrustStoreProvider. - Introduced a test case for SSL trust store parameters to validate default settings in the DatabricksConnectionContext. * Add test case for SSL trust store parameters in DatabricksConnectionContextTest - Introduced a new test case to validate the handling of SSL trust store parameters, including SSLTrustStore, SSLTrustStorePwd, SSLTrustStoreType, and SSLTrustStoreProvider. * modify changelog * address comments
1 parent e3d0d8d commit f849075

7 files changed

Lines changed: 72 additions & 10 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66
- Added DCO (Developer Certificate of Origin) check workflow for pull requests to ensure all commits are properly signed-off
7+
- Added support for SSL client certificate authentication via parameter: SSLTrustStoreProvider
78

89
### Updated
910
-

src/main/java/com/databricks/jdbc/api/impl/DatabricksConnectionContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,11 @@ public String getSSLTrustStoreType() {
732732
return getParameter(DatabricksJdbcUrlParams.SSL_TRUST_STORE_TYPE);
733733
}
734734

735+
@Override
736+
public String getSSLTrustStoreProvider() {
737+
return getParameter(DatabricksJdbcUrlParams.SSL_TRUST_STORE_PROVIDER);
738+
}
739+
735740
@Override
736741
public String getSSLKeyStore() {
737742
return getParameter(DatabricksJdbcUrlParams.SSL_KEY_STORE);

src/main/java/com/databricks/jdbc/api/internal/IDatabricksConnectionContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ public interface IDatabricksConnectionContext {
227227
/** Returns the SSL key store provider for the key store. */
228228
String getSSLKeyStoreProvider();
229229

230+
/** Returns the SSL trust store provider for the trust store. */
231+
String getSSLTrustStoreProvider();
232+
230233
/** Returns the maximum number of commands that can be executed in a single batch. */
231234
int getMaxBatchSize();
232235

src/main/java/com/databricks/jdbc/common/DatabricksJdbcUrlParams.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public enum DatabricksJdbcUrlParams {
8181
SSL_KEY_STORE_PASSWORD("SSLKeyStorePwd", "SSL key store password"),
8282
SSL_KEY_STORE_TYPE("SSLKeyStoreType", "SSL key store type", "JKS"),
8383
SSL_KEY_STORE_PROVIDER("SSLKeyStoreProvider", "SSL key store provider"),
84+
SSL_TRUST_STORE_PROVIDER("SSLTrustStoreProvider", "SSL trust store provider"),
8485
USE_SYSTEM_TRUST_STORE("UseSystemTrustStore", "Use system trust store for SSL", "0"),
8586
CHECK_CERTIFICATE_REVOCATION("CheckCertRevocation", "Check certificate revocation", "1"),
8687
ACCEPT_UNDETERMINED_CERTIFICATE_REVOCATION(

src/main/java/com/databricks/jdbc/dbclient/impl/common/ConfiguratorUtils.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -470,24 +470,36 @@ public static KeyStore loadTruststoreOrNull(IDatabricksConnectionContext connect
470470
}
471471

472472
String trustStoreType = connectionContext.getSSLTrustStoreType();
473+
String trustStoreProvider = connectionContext.getSSLTrustStoreProvider();
473474

474475
try (FileInputStream trustStoreStream = new FileInputStream(trustStorePath)) {
475476
LOGGER.info("Loading trust store as type: " + trustStoreType);
476-
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
477+
KeyStore trustStore;
478+
if (trustStoreProvider != null && !trustStoreProvider.isEmpty()) {
479+
LOGGER.info("Using trust store provider: " + trustStoreProvider);
480+
trustStore = KeyStore.getInstance(trustStoreType, trustStoreProvider);
481+
} else {
482+
trustStore = KeyStore.getInstance(trustStoreType);
483+
}
477484
trustStore.load(trustStoreStream, password);
478485
LOGGER.info("Successfully loaded trust store: " + trustStorePath);
479486
return trustStore;
480487
} catch (Exception e) {
481-
String errorMessage =
482-
"Failed to load trust store: "
483-
+ trustStorePath
484-
+ " with type "
485-
+ trustStoreType
486-
+ ": "
487-
+ e.getMessage();
488-
LOGGER.error(errorMessage);
488+
String errorMessage;
489+
if (trustStoreProvider != null && !trustStoreProvider.isEmpty()) {
490+
errorMessage =
491+
String.format(
492+
"Failed to load trust store: %s with type %s and provider %s: %s",
493+
trustStorePath, trustStoreType, trustStoreProvider, e.getMessage());
494+
} else {
495+
errorMessage =
496+
String.format(
497+
"Failed to load trust store: %s with type %s: %s",
498+
trustStorePath, trustStoreType, e.getMessage());
499+
}
500+
LOGGER.error(errorMessage.toString());
489501
throw new DatabricksSSLException(
490-
errorMessage, e, DatabricksDriverErrorCode.SSL_HANDSHAKE_ERROR);
502+
errorMessage.toString(), e, DatabricksDriverErrorCode.SSL_HANDSHAKE_ERROR);
491503
}
492504
}
493505

src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionContextTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,4 +592,22 @@ public void testSSLKeystoreParameters() throws DatabricksSQLException {
592592
assertEquals("PKCS12", connectionContext.getSSLKeyStoreType());
593593
assertEquals("SunJSSE", connectionContext.getSSLKeyStoreProvider());
594594
}
595+
596+
@Test
597+
public void testSSLTrustStoreParameters() throws DatabricksSQLException {
598+
// Test case 1: Default settings (all null)
599+
String validJdbcUrl = TestConstants.VALID_URL_1;
600+
Properties properties = new Properties();
601+
DatabricksConnectionContext connectionContext =
602+
(DatabricksConnectionContext) DatabricksConnectionContext.parse(validJdbcUrl, properties);
603+
assertNull(connectionContext.getSSLTrustStore());
604+
605+
// Test case 2: With truststore parameters
606+
properties.put("SSLTrustStore", "/path/to/truststore.jks");
607+
properties.put("SSLTrustStorePwd", "truststorepassword");
608+
properties.put("SSLTrustStoreType", "PKCS12");
609+
properties.put("SSLTrustStoreProvider", "SunJSSE");
610+
connectionContext =
611+
(DatabricksConnectionContext) DatabricksConnectionContext.parse(validJdbcUrl, properties);
612+
}
595613
}

src/test/java/com/databricks/jdbc/dbclient/impl/common/ConfiguratorUtilsTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,4 +577,26 @@ void testMissingKeyStorePassword() {
577577
() -> ConfiguratorUtils.loadKeystoreOrNull(mockContext),
578578
"Should throw an exception when key store password is missing");
579579
}
580+
581+
@Test
582+
void testLoadTrustStoreWithProvider() throws Exception {
583+
String providerName = "TestProvider";
584+
when(mockContext.getSSLTrustStore()).thenReturn(DUMMY_TRUST_STORE_PATH);
585+
when(mockContext.getSSLTrustStorePassword()).thenReturn(TRUST_STORE_PASSWORD);
586+
when(mockContext.getSSLTrustStoreType()).thenReturn(TRUST_STORE_TYPE);
587+
when(mockContext.getSSLTrustStoreProvider()).thenReturn(providerName);
588+
589+
try (MockedStatic<KeyStore> keyStoreStatic = mockStatic(KeyStore.class)) {
590+
KeyStore mockKeyStore = mock(KeyStore.class);
591+
keyStoreStatic
592+
.when(() -> KeyStore.getInstance(TRUST_STORE_TYPE, providerName))
593+
.thenReturn(mockKeyStore);
594+
595+
// Call the method under test
596+
ConfiguratorUtils.loadTruststoreOrNull(mockContext);
597+
598+
keyStoreStatic.verify(() -> KeyStore.getInstance(TRUST_STORE_TYPE, providerName));
599+
verify(mockKeyStore).load(any(), eq(TRUST_STORE_PASSWORD.toCharArray()));
600+
}
601+
}
580602
}

0 commit comments

Comments
 (0)