From 595ecc09d1ea13f08b3b2564c88cdaf01f3a8286 Mon Sep 17 00:00:00 2001 From: Oliver Bucaojit Date: Fri, 15 Aug 2025 15:29:51 -0700 Subject: [PATCH 1/4] SQL-2920: Add configuration properties to support GSSAPI --- .../com/mongodb/jdbc/MongoConnection.java | 75 +++++++++++++------ .../jdbc/MongoConnectionProperties.java | 18 ++++- .../java/com/mongodb/jdbc/MongoDriver.java | 25 ++++++- .../jdbc/MongoDatabaseMetaDataTest.java | 2 +- .../com/mongodb/jdbc/MongoDriverTest.java | 73 ++++++++++++++++++ src/test/java/com/mongodb/jdbc/MongoMock.java | 2 +- 6 files changed, 166 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/mongodb/jdbc/MongoConnection.java b/src/main/java/com/mongodb/jdbc/MongoConnection.java index 26dcd349..6fa96a2c 100644 --- a/src/main/java/com/mongodb/jdbc/MongoConnection.java +++ b/src/main/java/com/mongodb/jdbc/MongoConnection.java @@ -16,6 +16,7 @@ package com.mongodb.jdbc; +import static com.mongodb.AuthenticationMechanism.GSSAPI; import static com.mongodb.AuthenticationMechanism.MONGODB_OIDC; import static com.mongodb.AuthenticationMechanism.MONGODB_X509; @@ -184,28 +185,60 @@ private MongoClientSettings createMongoClientSettings( if (credential != null) { AuthenticationMechanism authMechanism = credential.getAuthenticationMechanism(); - if (authMechanism != null && authMechanism.equals(MONGODB_OIDC)) { - // Handle OIDC authentication - OidcCallback oidcCallback = new JdbcOidcCallback(this.logger); - credential = - MongoCredential.createOidcCredential( - connectionProperties.getConnectionString().getUsername()) - .withMechanismProperty( - MongoCredential.OIDC_HUMAN_CALLBACK_KEY, oidcCallback); - settingsBuilder.credential(credential); - } else if (authMechanism != null && authMechanism.equals(MONGODB_X509)) { - String pemPath = connectionProperties.getX509PemPath(); - if (pemPath == null || pemPath.isEmpty()) { - pemPath = System.getenv(MONGODB_JDBC_X509_CLIENT_CERT_PATH); - } - if (pemPath == null || pemPath.isEmpty()) { - throw new IllegalStateException( - "PEM file path is required for X.509 authentication but was not provided."); - } + if (authMechanism != null) { + if (authMechanism.equals(MONGODB_OIDC)) { + // Handle OIDC authentication + OidcCallback oidcCallback = new JdbcOidcCallback(this.logger); + credential = + MongoCredential.createOidcCredential( + connectionProperties + .getConnectionString() + .getUsername()) + .withMechanismProperty( + MongoCredential.OIDC_HUMAN_CALLBACK_KEY, oidcCallback); + settingsBuilder.credential(credential); + } else if (authMechanism.equals(GSSAPI)) { + + String jaasPath = connectionProperties.getJaasConfigPath(); + if (jaasPath != null && !jaasPath.isEmpty()) { + System.setProperty("java.security.auth.login.config", jaasPath); + logger.log(Level.INFO, "Using custom JAAS config: " + jaasPath); + } else { + String existingConfig = + System.getProperty("java.security.auth.login.config"); + if (existingConfig != null) { + logger.log( + Level.INFO, + "Using JAAS config from system property: " + existingConfig); + } else { + logger.log( + Level.INFO, + "No JAAS config specified. Relying on classpath or JVM defaults."); + } + } + + String gssNative = connectionProperties.getGssNativeMode(); + if (gssNative != null && !gssNative.isEmpty()) { + System.setProperty("sun.security.jgss.native", gssNative); + logger.log( + Level.INFO, "Set sun.security.jgss.native = " + gssNative.trim()); + } - X509Authentication x509Authentication = new X509Authentication(logger); - x509Authentication.configureX509Authentication( - settingsBuilder, pemPath, this.x509Passphrase); + settingsBuilder.credential(credential); + } else if (authMechanism.equals(MONGODB_X509)) { + String pemPath = connectionProperties.getX509PemPath(); + if (pemPath == null || pemPath.isEmpty()) { + pemPath = System.getenv(MONGODB_JDBC_X509_CLIENT_CERT_PATH); + } + if (pemPath == null || pemPath.isEmpty()) { + throw new IllegalStateException( + "PEM file path is required for X.509 authentication but was not provided."); + } + + X509Authentication x509Authentication = new X509Authentication(logger); + x509Authentication.configureX509Authentication( + settingsBuilder, pemPath, this.x509Passphrase); + } } } diff --git a/src/main/java/com/mongodb/jdbc/MongoConnectionProperties.java b/src/main/java/com/mongodb/jdbc/MongoConnectionProperties.java index aac44c51..e60202eb 100644 --- a/src/main/java/com/mongodb/jdbc/MongoConnectionProperties.java +++ b/src/main/java/com/mongodb/jdbc/MongoConnectionProperties.java @@ -28,6 +28,8 @@ public class MongoConnectionProperties { private String clientInfo; private boolean extJsonMode; private String x509PemPath; + private String jaasConfigPath; + private String gssNativeMode; public MongoConnectionProperties( ConnectionString connectionString, @@ -36,14 +38,18 @@ public MongoConnectionProperties( File logDir, String clientInfo, boolean extJsonMode, - String x509PemPath) { + String x509PemPath, + String jaasConfigPath, + String gssNativeMode) { this.connectionString = connectionString; this.database = database; this.logLevel = logLevel; this.logDir = logDir; this.clientInfo = clientInfo; this.extJsonMode = extJsonMode; - this.x509PemPath = x509PemPath; + this.x509PemPath = (x509PemPath != null) ? x509PemPath.trim() : null; + this.jaasConfigPath = (jaasConfigPath != null) ? jaasConfigPath.trim() : null; + this.gssNativeMode = gssNativeMode != null ? gssNativeMode : null; } public ConnectionString getConnectionString() { @@ -74,6 +80,14 @@ public String getX509PemPath() { return x509PemPath; } + public String getJaasConfigPath() { + return jaasConfigPath; + } + + public String getGssNativeMode() { + return gssNativeMode; + } + /* * Generate a unique key for the connection properties. This key is used to identify the connection properties in the * connection cache. Properties that do not differentiate a specific client such as the log level are not included in the key. diff --git a/src/main/java/com/mongodb/jdbc/MongoDriver.java b/src/main/java/com/mongodb/jdbc/MongoDriver.java index f39ec22f..3bfbe3ff 100644 --- a/src/main/java/com/mongodb/jdbc/MongoDriver.java +++ b/src/main/java/com/mongodb/jdbc/MongoDriver.java @@ -100,7 +100,9 @@ public enum MongoJDBCProperty { LOG_DIR("logdir"), EXT_JSON_MODE("extjsonmode"), X509_PEM_PATH("x509pempath"), - DISABLE_CLIENT_CACHE("disableclientcache"); + DISABLE_CLIENT_CACHE("disableclientcache"), + JAAS_CONFIG_PATH("jaasconfigpath"), + GSS_NATIVE_MODE("gssnativemode"); private final String propertyName; @@ -486,6 +488,19 @@ private MongoConnection createConnection( } } + String gssNativeModeVal = info.getProperty(GSS_NATIVE_MODE.getPropertyName()); + if (gssNativeModeVal != null) { + gssNativeModeVal = gssNativeModeVal.trim().toLowerCase(); + if (!"true".equals(gssNativeModeVal) && !"false".equals(gssNativeModeVal)) { + throw new SQLException( + "Invalid " + + GSS_NATIVE_MODE.getPropertyName() + + " property value: " + + gssNativeModeVal + + ". Valid values are: 'true', 'false'."); + } + } + MongoConnectionProperties mongoConnectionProperties = new MongoConnectionProperties( cs, @@ -494,7 +509,9 @@ private MongoConnection createConnection( logDir, clientInfo, extJsonMode, - info.getProperty(X509_PEM_PATH.getPropertyName())); + info.getProperty(X509_PEM_PATH.getPropertyName()), + info.getProperty(JAAS_CONFIG_PATH.getPropertyName()), + gssNativeModeVal); String disableCacheVal = info.getProperty(DISABLE_CLIENT_CACHE.getPropertyName(), "false").toLowerCase(); @@ -675,8 +692,8 @@ public static MongoConnectionConfig getConnectionSettings(String url, Properties } if (password == null && user != null) { if (result.authMechanism == null - || (!result.authMechanism.equals(MONGODB_X509) - && !result.authMechanism.equals(MONGODB_OIDC))) { + && !result.authMechanism.equals(MONGODB_OIDC) + && !result.authMechanism.equals(GSSAPI)) { // password is null, but user is not, we must prompt for the password. mandatoryConnectionProperties.add(new DriverPropertyInfo(PASSWORD, null)); } diff --git a/src/test/java/com/mongodb/jdbc/MongoDatabaseMetaDataTest.java b/src/test/java/com/mongodb/jdbc/MongoDatabaseMetaDataTest.java index e0bbea0c..9ca3cd7d 100644 --- a/src/test/java/com/mongodb/jdbc/MongoDatabaseMetaDataTest.java +++ b/src/test/java/com/mongodb/jdbc/MongoDatabaseMetaDataTest.java @@ -40,7 +40,7 @@ public class MongoDatabaseMetaDataTest { new MongoDatabaseMetaData( new MongoConnection( new MongoConnectionProperties( - uri, database, null, null, null, false, null))); + uri, database, null, null, null, false, null, null, null))); // Report exception from MongoConnection public MongoDatabaseMetaDataTest() throws Exception {} diff --git a/src/test/java/com/mongodb/jdbc/MongoDriverTest.java b/src/test/java/com/mongodb/jdbc/MongoDriverTest.java index c849e174..8455c7fe 100644 --- a/src/test/java/com/mongodb/jdbc/MongoDriverTest.java +++ b/src/test/java/com/mongodb/jdbc/MongoDriverTest.java @@ -900,4 +900,77 @@ void testNullPropKey() throws Exception { e.printStackTrace(); } } + + @Test + void testJaasConfigPathIsSetForGSSAPI() throws SQLException { + MongoDriver d = new MongoDriver(); + Properties p = new Properties(); + p.setProperty(DATABASE.getPropertyName(), "test"); + String jaasPathWithSpaces = " /path/to/mongo/jaas.conf "; + String jaasPathTrimmed = jaasPathWithSpaces.trim(); + + p.setProperty(JAAS_CONFIG_PATH.getPropertyName(), jaasPathWithSpaces); + + MongoConnection conn = + (MongoConnection) + d.getUnvalidatedConnection(userNoPWDURL + "?authMechanism=GSSAPI", p); + assertNotNull(conn); + + // Verify system property was set + assertEquals(jaasPathTrimmed, System.getProperty("java.security.auth.login.config")); + + System.clearProperty("java.security.auth.login.config"); + } + + @Test + void testGssNativeModeTrue_SetsSystemProperty() throws SQLException { + MongoDriver d = new MongoDriver(); + Properties p = new Properties(); + p.setProperty(DATABASE.getPropertyName(), "test"); + + String url = userNoPWDURL + "?authMechanism=GSSAPI"; + + p.setProperty(GSS_NATIVE_MODE.getPropertyName(), "true"); + + MongoConnection conn = (MongoConnection) d.getUnvalidatedConnection(url, p); + assertNotNull(conn); + + // Verify system property was set to true + assertEquals("true", System.getProperty("sun.security.jgss.native")); + + System.clearProperty("sun.security.jgss.native"); + } + + @Test + void testGssNativeModeFalse_SetsSystemProperty() throws SQLException { + MongoDriver d = new MongoDriver(); + Properties p = new Properties(); + p.setProperty(DATABASE.getPropertyName(), "test"); + + String url = userNoPWDURL + "?authMechanism=GSSAPI"; + p.setProperty(GSS_NATIVE_MODE.getPropertyName(), "false"); + + MongoConnection conn = (MongoConnection) d.getUnvalidatedConnection(url, p); + assertNotNull(conn); + + // Verify system property was set to false + assertEquals("false", System.getProperty("sun.security.jgss.native")); + + System.clearProperty("sun.security.jgss.native"); + } + + @Test + void testGssNativeMode_InvalidValue_ThrowsSQLException() throws SQLException { + MongoDriver d = new MongoDriver(); + Properties p = new Properties(); + p.setProperty(DATABASE.getPropertyName(), "test"); + + String url = userNoPWDURL + "?authMechanism=GSSAPI"; + p.setProperty(GSS_NATIVE_MODE.getPropertyName(), "invalid"); + + assertThrows( + SQLException.class, + () -> d.getUnvalidatedConnection(url, p), + "Invalid gssnativemode value should throw SQLException"); + } } diff --git a/src/test/java/com/mongodb/jdbc/MongoMock.java b/src/test/java/com/mongodb/jdbc/MongoMock.java index ea194417..a37da63d 100644 --- a/src/test/java/com/mongodb/jdbc/MongoMock.java +++ b/src/test/java/com/mongodb/jdbc/MongoMock.java @@ -193,7 +193,7 @@ public abstract class MongoMock { mongoConnection = new MongoConnection( new MongoConnectionProperties( - uri, database, null, null, null, false, null)); + uri, database, null, null, null, false, null, null, null)); } catch (Exception e) { // The connection initialization should not fail, but if it does, we log the error to have more info. e.printStackTrace(); From 10bd5ad4dcca292f0d31fa2dda35dd70b9233f17 Mon Sep 17 00:00:00 2001 From: Oliver Bucaojit Date: Fri, 15 Aug 2025 16:19:31 -0700 Subject: [PATCH 2/4] SQL-2920: Fix null password check --- src/main/java/com/mongodb/jdbc/MongoDriver.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mongodb/jdbc/MongoDriver.java b/src/main/java/com/mongodb/jdbc/MongoDriver.java index 3bfbe3ff..4e3dcc50 100644 --- a/src/main/java/com/mongodb/jdbc/MongoDriver.java +++ b/src/main/java/com/mongodb/jdbc/MongoDriver.java @@ -692,9 +692,11 @@ public static MongoConnectionConfig getConnectionSettings(String url, Properties } if (password == null && user != null) { if (result.authMechanism == null - && !result.authMechanism.equals(MONGODB_OIDC) - && !result.authMechanism.equals(GSSAPI)) { - // password is null, but user is not, we must prompt for the password. + || (!result.authMechanism.equals(MONGODB_X509) + && !result.authMechanism.equals(MONGODB_OIDC) + && !result.authMechanism.equals(GSSAPI))) { + // Password is null, but user is provided. If the authentication mechanism + // does not support password-less login, then the password must be prompted for. mandatoryConnectionProperties.add(new DriverPropertyInfo(PASSWORD, null)); } } From 33588a021c8b660ef0b558546dafb9c4cd4dccfb Mon Sep 17 00:00:00 2001 From: Oliver Bucaojit Date: Fri, 19 Sep 2025 12:16:03 -0700 Subject: [PATCH 3/4] Update src/main/java/com/mongodb/jdbc/MongoConnection.java Co-authored-by: Natacha Bagnard <91975317+nbagnard@users.noreply.github.com> --- src/main/java/com/mongodb/jdbc/MongoConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/mongodb/jdbc/MongoConnection.java b/src/main/java/com/mongodb/jdbc/MongoConnection.java index 6fa96a2c..684b8e85 100644 --- a/src/main/java/com/mongodb/jdbc/MongoConnection.java +++ b/src/main/java/com/mongodb/jdbc/MongoConnection.java @@ -198,7 +198,6 @@ private MongoClientSettings createMongoClientSettings( MongoCredential.OIDC_HUMAN_CALLBACK_KEY, oidcCallback); settingsBuilder.credential(credential); } else if (authMechanism.equals(GSSAPI)) { - String jaasPath = connectionProperties.getJaasConfigPath(); if (jaasPath != null && !jaasPath.isEmpty()) { System.setProperty("java.security.auth.login.config", jaasPath); From ceea88e4340ce05f5cf68bd67027f6b0cf0a568f Mon Sep 17 00:00:00 2001 From: Oliver Bucaojit Date: Fri, 19 Sep 2025 12:17:08 -0700 Subject: [PATCH 4/4] Update src/main/java/com/mongodb/jdbc/MongoDriver.java Co-authored-by: Natacha Bagnard <91975317+nbagnard@users.noreply.github.com> --- src/main/java/com/mongodb/jdbc/MongoDriver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/mongodb/jdbc/MongoDriver.java b/src/main/java/com/mongodb/jdbc/MongoDriver.java index 4e3dcc50..f64f56ed 100644 --- a/src/main/java/com/mongodb/jdbc/MongoDriver.java +++ b/src/main/java/com/mongodb/jdbc/MongoDriver.java @@ -491,7 +491,7 @@ private MongoConnection createConnection( String gssNativeModeVal = info.getProperty(GSS_NATIVE_MODE.getPropertyName()); if (gssNativeModeVal != null) { gssNativeModeVal = gssNativeModeVal.trim().toLowerCase(); - if (!"true".equals(gssNativeModeVal) && !"false".equals(gssNativeModeVal)) { + if (!"true".equalsIgnoreCase(gssNativeModeVal) && !"false".equalsIgnoreCase(gssNativeModeVal)) { throw new SQLException( "Invalid " + GSS_NATIVE_MODE.getPropertyName()