Skip to content

Commit 1d9da3c

Browse files
committed
updating based on PR comments
version update adds support for mongo-oidc
1 parent 36631d0 commit 1d9da3c

11 files changed

Lines changed: 720 additions & 76 deletions

build.gradle

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ plugins {
44
id "com.github.johnrengelman.shadow" version "7.0.0"
55
}
66

7-
version '1.20'
7+
version '1.30'
88

99
repositories {
1010
mavenCentral()
1111
}
1212

1313
dependencies {
1414
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
15-
implementation "org.mongodb:mongodb-driver-sync:4.11.1"
15+
implementation "org.mongodb:mongodb-driver-sync:5.6.1"
1616
implementation group: 'org.jetbrains', name: 'annotations', version: '15.0'
1717
implementation group: 'org.apache.commons', name: 'commons-text', version: '1.10.0'
1818
implementation group: 'org.graalvm.js', name: 'js', version: '22.3.1'
1919
implementation files('libs/JMongosh-0.9.jar')
20+
implementation group: 'com.nimbusds', name: 'oauth2-oidc-sdk', version: '11.+'
2021
testImplementation group: 'junit', name: 'junit', version: '4.13.1'
2122
testImplementation group: 'commons-io', name: 'commons-io', version: '2.7'
2223
}
@@ -29,4 +30,10 @@ test {
2930
shadowJar {
3031
archiveFileName = "mongo-jdbc-standalone-${version}.jar"
3132
mergeServiceFiles()
33+
34+
relocate('org', 'shadow.org') {
35+
exclude 'org.ow2.asm:.*'
36+
exclude 'net.minidev:.*'
37+
exclude 'org.javassist:.*'
38+
}
3239
}

src/main/java/com/dbschema/MongoJdbcDriver.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package com.dbschema;
22

33
import com.dbschema.mongo.DriverPropertyInfoHelper;
4+
import com.dbschema.mongo.MongoClientWrapper;
45
import com.dbschema.mongo.MongoConnection;
6+
import com.dbschema.mongo.MongoService;
57
import com.dbschema.mongo.mongosh.LazyShellHolder;
68
import com.dbschema.mongo.mongosh.PrecalculatingShellHolder;
79
import com.dbschema.mongo.mongosh.ShellHolder;
10+
import com.dbschema.mongo.oidc.OidcCallback;
11+
import com.mongodb.MongoCredential;
812
import org.graalvm.polyglot.Engine;
913
import org.jetbrains.annotations.NotNull;
1014
import org.jetbrains.annotations.Nullable;
1115

1216
import java.sql.*;
17+
import java.util.Optional;
1318
import java.util.Properties;
1419
import java.util.concurrent.ExecutorService;
1520
import java.util.logging.Logger;
@@ -32,6 +37,7 @@ public class MongoJdbcDriver implements Driver {
3237
private @Nullable ExecutorService executorService;
3338
private @Nullable Engine sharedEngine;
3439
private @NotNull ShellHolder shellHolder;
40+
private MongoConnection mongoConnection;
3541

3642
static {
3743
try {
@@ -91,7 +97,16 @@ public Connection connect(String url, Properties info) throws SQLException {
9197
synchronized (this) {
9298
ShellHolder shellHolder = this.shellHolder;
9399
this.shellHolder = createShellHolder();
94-
return new MongoConnection(url, info, username, password, fetchDocumentsForMeta, shellHolder);
100+
MongoCredential.OidcCallbackContext existingResult = Optional.ofNullable(this.mongoConnection)
101+
.map(MongoConnection::getService)
102+
.map(MongoService::getClient)
103+
.map(MongoClientWrapper::getOidcCallback)
104+
.map(OidcCallback::getCallbackContext)
105+
.orElse(null);
106+
107+
this.mongoConnection = new MongoConnection(url, info, username, password, fetchDocumentsForMeta, shellHolder, existingResult);
108+
109+
return this.mongoConnection;
95110
}
96111
}
97112

src/main/java/com/dbschema/mongo/DriverPropertyInfoHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
public class DriverPropertyInfoHelper {
77
public static final String AUTH_MECHANISM = "authMechanism";
8-
public static final String[] AUTH_MECHANISM_CHOICES = new String[]{"GSSAPI", "MONGODB-AWS", "MONGODB-X509", "PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256"};
8+
public static final String[] AUTH_MECHANISM_CHOICES = new String[]{"GSSAPI", "MONGODB-AWS", "MONGODB-X509", "PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256", "MONGODB-OIDC"};
99
public static final String AUTH_SOURCE = "authSource";
1010
public static final String AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN";
1111
public static final String SERVICE_NAME = "SERVICE_NAME";

src/main/java/com/dbschema/mongo/MongoClientWrapper.java

Lines changed: 100 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.dbschema.mongo;
22

3+
import com.dbschema.mongo.oidc.OidcCallback;
34
import com.mongodb.ConnectionString;
45
import com.mongodb.MongoClientSettings;
6+
import com.mongodb.MongoCredential;
7+
import com.mongodb.ServerApi;
8+
import com.mongodb.ServerApiVersion;
59
import com.mongodb.client.MongoClient;
610
import com.mongodb.client.MongoClients;
711
import com.mongodb.client.MongoDatabase;
@@ -26,79 +30,103 @@ public class MongoClientWrapper implements AutoCloseable {
2630
private boolean isClosed = false;
2731
private final MongoClient mongoClient;
2832
public final String databaseNameFromUrl;
33+
public final OidcCallback oidcCallback;
2934

30-
public MongoClientWrapper(@NotNull String uri, @NotNull Properties prop, @Nullable String username, @Nullable String password) throws SQLException {
35+
public MongoClientWrapper(@NotNull String uri, @NotNull Properties prop, @Nullable String username, @Nullable String password, @Nullable MongoCredential.OidcCallbackContext callbackContext) throws SQLException {
36+
this.oidcCallback = new OidcCallback(callbackContext);
3137
try {
32-
boolean automaticEncoding = ENCODE_CREDENTIALS_DEFAULT;
33-
if (prop.getProperty(ENCODE_CREDENTIALS) != null) {
34-
automaticEncoding = Boolean.parseBoolean(prop.getProperty(ENCODE_CREDENTIALS));
35-
}
38+
boolean automaticEncoding = ENCODE_CREDENTIALS_DEFAULT;
39+
if (prop.getProperty(ENCODE_CREDENTIALS) != null) {
40+
automaticEncoding = Boolean.parseBoolean(prop.getProperty(ENCODE_CREDENTIALS));
41+
}
3642

37-
uri = insertCredentials(uri, username, password, automaticEncoding);
38-
uri = insertAuthMechanism(uri, prop.getProperty(AUTH_MECHANISM));
39-
uri = insertAuthSource(uri, prop.getProperty(AUTH_SOURCE));
40-
uri = insertAuthProperty(uri, AWS_SESSION_TOKEN, prop.getProperty(AWS_SESSION_TOKEN));
41-
uri = insertAuthProperty(uri, SERVICE_NAME, prop.getProperty(SERVICE_NAME));
42-
uri = insertAuthProperty(uri, SERVICE_REALM, prop.getProperty(SERVICE_REALM));
43-
String canonicalizeHostName = prop.getProperty(CANONICALIZE_HOST_NAME);
44-
if (Boolean.TRUE.toString().equalsIgnoreCase(canonicalizeHostName) || Boolean.FALSE.toString().equalsIgnoreCase(canonicalizeHostName)) {
45-
uri = insertAuthProperty(uri, CANONICALIZE_HOST_NAME, canonicalizeHostName);
46-
}
47-
else if (canonicalizeHostName != null) {
48-
System.err.println("Unknown " + CANONICALIZE_HOST_NAME + " value. Must be true or false.");
49-
}
50-
uri = insertRetryWrites(uri, prop.getProperty(RETRY_WRITES));
43+
uri = insertCredentials(uri, username, password, automaticEncoding);
44+
uri = insertAuthMechanism(uri, prop.getProperty(AUTH_MECHANISM));
45+
uri = insertAuthSource(uri, prop.getProperty(AUTH_SOURCE));
46+
uri = insertAuthProperty(uri, AWS_SESSION_TOKEN, prop.getProperty(AWS_SESSION_TOKEN));
47+
uri = insertAuthProperty(uri, SERVICE_NAME, prop.getProperty(SERVICE_NAME));
48+
uri = insertAuthProperty(uri, SERVICE_REALM, prop.getProperty(SERVICE_REALM));
49+
String canonicalizeHostName = prop.getProperty(CANONICALIZE_HOST_NAME);
50+
if (Boolean.TRUE.toString().equalsIgnoreCase(canonicalizeHostName) || Boolean.FALSE.toString().equalsIgnoreCase(canonicalizeHostName)) {
51+
uri = insertAuthProperty(uri, CANONICALIZE_HOST_NAME, canonicalizeHostName);
52+
}
53+
else if (canonicalizeHostName != null) {
54+
System.err.println("Unknown " + CANONICALIZE_HOST_NAME + " value. Must be true or false.");
55+
}
56+
uri = insertRetryWrites(uri, prop.getProperty(RETRY_WRITES));
57+
58+
59+
// Construct a ServerApi instance using the ServerApi.builder() method
60+
ServerApi serverApi = ServerApi.builder()
61+
.version(ServerApiVersion.V1)
62+
.build();
5163

5264
ConnectionString connectionString = new ConnectionString(uri);
53-
databaseNameFromUrl = connectionString.getDatabase();
54-
int maxPoolSize = getMaxPoolSize(prop);
55-
MongoClientSettings.Builder builder = MongoClientSettings.builder()
56-
.applyConnectionString(connectionString)
57-
.applyToConnectionPoolSettings(b -> b.maxSize(maxPoolSize));
58-
String application = prop.getProperty(APPLICATION_NAME);
59-
if (!isNullOrEmpty(application)) {
60-
builder.applicationName(application);
61-
}
62-
if ("true".equals(prop.getProperty("ssl"))) {
63-
boolean allowInvalidCertificates = uri.contains("tlsAllowInvalidCertificates=true") || uri.contains("sslAllowInvalidCertificates=true")
64-
|| isTrue(prop.getProperty(ALLOW_INVALID_CERTIFICATES, Boolean.toString(ALLOW_INVALID_CERTIFICATES_DEFAULT)));
65-
builder.applyToSslSettings(s -> {
66-
s.enabled(true);
67-
boolean allowInvalidHostnames = isTrue(prop.getProperty(ALLOW_INVALID_HOSTNAMES, Boolean.toString(ALLOW_INVALID_HOSTNAMES_DEFAULT)));
68-
if (allowInvalidHostnames) s.invalidHostNameAllowed(true);
69-
if (allowInvalidCertificates) {
70-
String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
71-
String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword", "");
72-
String keyStoreUrl = System.getProperty("javax.net.ssl.keyStore", "");
73-
// check keyStoreUrl
74-
if (!isNullOrEmpty(keyStoreUrl)) {
75-
try {
76-
new URL(keyStoreUrl);
77-
} catch (MalformedURLException e) {
78-
keyStoreUrl = "file:" + keyStoreUrl;
79-
}
80-
}
81-
try {
82-
s.context(getTrustEverybodySSLContext(keyStoreUrl, keyStoreType, keyStorePassword));
83-
}
84-
catch (SSLUtil.SSLParamsException e) {
85-
throw new RuntimeException(e);
86-
}
87-
}
88-
});
89-
}
90-
if (connectionString.getUuidRepresentation() == null) {
91-
String uuidRepresentation = prop.getProperty(UUID_REPRESENTATION, UUID_REPRESENTATION_DEFAULT);
92-
builder.uuidRepresentation(createUuidRepresentation(uuidRepresentation));
93-
}
94-
if (connectionString.getServerSelectionTimeout() == null) {
95-
int timeout = Integer.parseInt(prop.getProperty(SERVER_SELECTION_TIMEOUT, SERVER_SELECTION_TIMEOUT_DEFAULT));
96-
builder.applyToClusterSettings(b -> b.serverSelectionTimeout(timeout, TimeUnit.MILLISECONDS));
97-
}
98-
if (connectionString.getConnectTimeout() == null) {
99-
int timeout = Integer.parseInt(prop.getProperty(CONNECT_TIMEOUT, CONNECT_TIMEOUT_DEFAULT));
100-
builder.applyToSocketSettings(b -> b.connectTimeout(timeout, TimeUnit.MILLISECONDS));
101-
}
65+
66+
MongoCredential credential;
67+
68+
credential =
69+
MongoCredential.createOidcCredential(
70+
connectionString.getUsername())
71+
.withMechanismProperty(
72+
MongoCredential.OIDC_HUMAN_CALLBACK_KEY, oidcCallback);
73+
74+
75+
databaseNameFromUrl = connectionString.getDatabase();
76+
MongoClientSettings.Builder builder = MongoClientSettings.builder()
77+
.applyConnectionString(connectionString)
78+
.serverApi(serverApi)
79+
.credential(credential)
80+
.uuidRepresentation(createUuidRepresentation(prop.getProperty(UUID_REPRESENTATION, UUID_REPRESENTATION_DEFAULT)))
81+
.applyToConnectionPoolSettings(b -> b.maxSize(getMaxPoolSize(prop)))
82+
83+
.applyToSocketSettings(b -> b.connectTimeout(Integer.parseInt(prop.getProperty(CONNECT_TIMEOUT, CONNECT_TIMEOUT_DEFAULT)), TimeUnit.MILLISECONDS));
84+
85+
String application = prop.getProperty(APPLICATION_NAME);
86+
if (!isNullOrEmpty(application)) {
87+
builder.applicationName(application);
88+
}
89+
if ("true".equals(prop.getProperty("ssl"))) {
90+
boolean allowInvalidCertificates = uri.contains("tlsAllowInvalidCertificates=true") || uri.contains("sslAllowInvalidCertificates=true")
91+
|| isTrue(prop.getProperty(ALLOW_INVALID_CERTIFICATES, Boolean.toString(ALLOW_INVALID_CERTIFICATES_DEFAULT)));
92+
builder.applyToSslSettings(s -> {
93+
s.enabled(true);
94+
boolean allowInvalidHostnames = isTrue(prop.getProperty(ALLOW_INVALID_HOSTNAMES, Boolean.toString(ALLOW_INVALID_HOSTNAMES_DEFAULT)));
95+
if (allowInvalidHostnames) s.invalidHostNameAllowed(true);
96+
if (allowInvalidCertificates) {
97+
String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
98+
String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword", "");
99+
String keyStoreUrl = System.getProperty("javax.net.ssl.keyStore", "");
100+
// check keyStoreUrl
101+
if (!isNullOrEmpty(keyStoreUrl)) {
102+
try {
103+
new URL(keyStoreUrl);
104+
} catch (MalformedURLException e) {
105+
keyStoreUrl = "file:" + keyStoreUrl;
106+
}
107+
}
108+
try {
109+
s.context(getTrustEverybodySSLContext(keyStoreUrl, keyStoreType, keyStorePassword));
110+
}
111+
catch (SSLUtil.SSLParamsException e) {
112+
throw new RuntimeException(e);
113+
}
114+
}
115+
});
116+
}
117+
if (connectionString.getUuidRepresentation() == null) {
118+
String uuidRepresentation = prop.getProperty(UUID_REPRESENTATION, UUID_REPRESENTATION_DEFAULT);
119+
builder.uuidRepresentation(createUuidRepresentation(uuidRepresentation));
120+
}
121+
if (connectionString.getServerSelectionTimeout() == null) {
122+
int timeout = Integer.parseInt(prop.getProperty(SERVER_SELECTION_TIMEOUT, SERVER_SELECTION_TIMEOUT_DEFAULT));
123+
builder.applyToClusterSettings(b -> b.serverSelectionTimeout(timeout, TimeUnit.MILLISECONDS));
124+
}
125+
if (connectionString.getConnectTimeout() == null) {
126+
int timeout = Integer.parseInt(prop.getProperty(CONNECT_TIMEOUT, CONNECT_TIMEOUT_DEFAULT));
127+
builder.applyToSocketSettings(b -> b.connectTimeout(timeout, TimeUnit.MILLISECONDS));
128+
}
129+
102130
this.mongoClient = MongoClients.create(builder.build());
103131
}
104132
catch (Exception e) {
@@ -160,6 +188,10 @@ public MongoDatabase getDatabase(String databaseName) throws SQLAlreadyClosedExc
160188
return mongoClient.getDatabase(databaseName);
161189
}
162190

191+
public OidcCallback getOidcCallback() {
192+
return this.oidcCallback;
193+
}
194+
163195
@NotNull
164196
public MongoClient getMongoClient() {
165197
return mongoClient;

src/main/java/com/dbschema/mongo/MongoConnection.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.dbschema.mongo.mongosh.MongoshScriptEngine;
44
import com.dbschema.mongo.mongosh.PrecalculatingShellHolder;
55
import com.dbschema.mongo.mongosh.ShellHolder;
6+
import com.mongodb.MongoCredential;
67
import org.jetbrains.annotations.NotNull;
78
import org.jetbrains.annotations.Nullable;
89

@@ -17,14 +18,17 @@ public class MongoConnection implements Connection {
1718
private String schema;
1819
private boolean isClosed = false;
1920
private boolean isReadOnly = false;
21+
private MongoCredential.OidcCallbackContext oidcCallbackContext;
2022

2123
public MongoConnection(@NotNull String url,
2224
@NotNull Properties info,
2325
@Nullable String username,
2426
@Nullable String password,
2527
int fetchDocumentsForMeta,
26-
@NotNull ShellHolder shellHolder) throws SQLException {
27-
this.service = new MongoService(url, info, username, password, fetchDocumentsForMeta);
28+
@NotNull ShellHolder shellHolder,
29+
@Nullable MongoCredential.OidcCallbackContext callbackContext) throws SQLException {
30+
this.oidcCallbackContext = callbackContext;
31+
this.service = new MongoService(url, info, username, password, fetchDocumentsForMeta, this.oidcCallbackContext);
2832
this.scriptEngine = new MongoshScriptEngine(this, shellHolder);
2933
try {
3034
setSchema(service.getDatabaseNameFromUrl());

src/main/java/com/dbschema/mongo/MongoService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.dbschema.mongo;
22

33
import com.dbschema.mongo.schema.MetaCollection;
4+
import com.mongodb.MongoCredential;
45
import com.mongodb.MongoSecurityException;
56
import com.mongodb.client.MongoClient;
67
import com.mongodb.client.MongoCollection;
@@ -26,10 +27,10 @@ public class MongoService implements AutoCloseable {
2627

2728

2829
public MongoService(@NotNull String uri, @NotNull Properties prop, @Nullable String username,
29-
@Nullable String password, int fetchDocumentsForMeta) throws SQLException {
30+
@Nullable String password, int fetchDocumentsForMeta, @Nullable MongoCredential.OidcCallbackContext callbackContext) throws SQLException {
3031
this.uri = uri;
3132
this.fetchDocumentsForMeta = fetchDocumentsForMeta;
32-
client = new MongoClientWrapper(uri, prop, username, password);
33+
client = new MongoClientWrapper(uri, prop, username, password, callbackContext);
3334
}
3435

3536
public MongoClientWrapper getClient() {

0 commit comments

Comments
 (0)