Skip to content

Commit d856d84

Browse files
committed
Implement OIDC map value splitting
1 parent bc30a2f commit d856d84

File tree

3 files changed

+117
-6
lines changed

3 files changed

+117
-6
lines changed

driver-core/src/main/com/mongodb/ConnectionString.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.net.URLDecoder;
3939
import java.nio.charset.StandardCharsets;
4040
import java.util.ArrayList;
41+
import java.util.Arrays;
4142
import java.util.Collections;
4243
import java.util.HashMap;
4344
import java.util.HashSet;
@@ -923,6 +924,9 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
923924
}
924925
String key = mechanismPropertyKeyValue[0].trim().toLowerCase();
925926
String value = mechanismPropertyKeyValue[1].trim();
927+
if (!decodeWholeOptionValue(mechanism)) {
928+
value = urldecode(value);
929+
}
926930
if (MECHANISM_KEYS_DISALLOWED_IN_CONNECTION_STRING.contains(key)) {
927931
throw new IllegalArgumentException(format("The connection string contains disallowed mechanism properties. "
928932
+ "'%s' must be set on the credential programmatically.", key));
@@ -938,6 +942,13 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
938942
return credential;
939943
}
940944

945+
private static boolean decodeWholeOptionValue(final AuthenticationMechanism mechanism) {
946+
return !AuthenticationMechanism.MONGODB_OIDC.equals(mechanism);
947+
}
948+
private static boolean decodeWholeOptionValue(final List<String> options) {
949+
return !options.contains("authMechanism=" + AuthenticationMechanism.MONGODB_OIDC.getMechanismName());
950+
}
951+
941952
private MongoCredential createMongoCredentialWithMechanism(final AuthenticationMechanism mechanism, final String userName,
942953
@Nullable final char[] password,
943954
@Nullable final String authSource,
@@ -1018,12 +1029,14 @@ private String getLastValue(final Map<String, List<String>> optionsMap, final St
10181029

10191030
private Map<String, List<String>> parseOptions(final String optionsPart) {
10201031
Map<String, List<String>> optionsMap = new HashMap<>();
1021-
if (optionsPart.length() == 0) {
1032+
if (optionsPart.isEmpty()) {
10221033
return optionsMap;
10231034
}
10241035

1025-
for (final String part : optionsPart.split("&|;")) {
1026-
if (part.length() == 0) {
1036+
List<String> options = Arrays.asList(optionsPart.split("&|;"));
1037+
boolean decodeWholeOptionValue = decodeWholeOptionValue(options);
1038+
for (final String part : options) {
1039+
if (part.isEmpty()) {
10271040
continue;
10281041
}
10291042
int idx = part.indexOf("=");
@@ -1034,7 +1047,10 @@ private Map<String, List<String>> parseOptions(final String optionsPart) {
10341047
if (valueList == null) {
10351048
valueList = new ArrayList<>(1);
10361049
}
1037-
valueList.add(urldecode(value));
1050+
if (decodeWholeOptionValue) {
1051+
value = urldecode(value);
1052+
}
1053+
valueList.add(value);
10381054
optionsMap.put(key, valueList);
10391055
} else {
10401056
throw new IllegalArgumentException(format("The connection string contains an invalid option '%s'. "

driver-core/src/test/resources/auth/legacy/connection-string.json

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,13 +474,13 @@
474474
}
475475
},
476476
{
477-
"description": "should throw an exception if supplied a password (MONGODB-OIDC)",
477+
"description": "should throw an exception if username and password is specified for test environment (MONGODB-OIDC)",
478478
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test",
479479
"valid": false,
480480
"credential": null
481481
},
482482
{
483-
"description": "should throw an exception if username is specified for test (MONGODB-OIDC)",
483+
"description": "should throw an exception if username is specified for test environment (MONGODB-OIDC)",
484484
"uri": "mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&ENVIRONMENT:test",
485485
"valid": false,
486486
"credential": null
@@ -503,6 +503,12 @@
503503
"valid": false,
504504
"credential": null
505505
},
506+
{
507+
"description": "should throw an exception if neither provider nor callbacks specified (MONGODB-OIDC)",
508+
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC",
509+
"valid": false,
510+
"credential": null
511+
},
506512
{
507513
"description": "should recognise the mechanism with azure provider (MONGODB-OIDC)",
508514
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo",
@@ -533,6 +539,66 @@
533539
}
534540
}
535541
},
542+
{
543+
"description": "should accept a url-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
544+
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster",
545+
"valid": true,
546+
"credential": {
547+
"username": "user",
548+
"password": null,
549+
"source": "$external",
550+
"mechanism": "MONGODB-OIDC",
551+
"mechanism_properties": {
552+
"ENVIRONMENT": "azure",
553+
"TOKEN_RESOURCE": "mongodb://test-cluster"
554+
}
555+
}
556+
},
557+
{
558+
"description": "should accept an un-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
559+
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb://test-cluster",
560+
"valid": true,
561+
"credential": {
562+
"username": "user",
563+
"password": null,
564+
"source": "$external",
565+
"mechanism": "MONGODB-OIDC",
566+
"mechanism_properties": {
567+
"ENVIRONMENT": "azure",
568+
"TOKEN_RESOURCE": "mongodb://test-cluster"
569+
}
570+
}
571+
},
572+
{
573+
"description": "should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
574+
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abc%2Cd%25ef%3Ag%26hi",
575+
"valid": true,
576+
"credential": {
577+
"username": "user",
578+
"password": null,
579+
"source": "$external",
580+
"mechanism": "MONGODB-OIDC",
581+
"mechanism_properties": {
582+
"ENVIRONMENT": "azure",
583+
"TOKEN_RESOURCE": "abc,d%ef:g&hi"
584+
}
585+
}
586+
},
587+
{
588+
"description": "should url-encode a TOKEN_RESOURCE (MONGODB-OIDC)",
589+
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:a$b",
590+
"valid": true,
591+
"credential": {
592+
"username": "user",
593+
"password": null,
594+
"source": "$external",
595+
"mechanism": "MONGODB-OIDC",
596+
"mechanism_properties": {
597+
"ENVIRONMENT": "azure",
598+
"TOKEN_RESOURCE": "a$b"
599+
}
600+
}
601+
},
536602
{
537603
"description": "should accept a username and throw an error for a password with azure provider (MONGODB-OIDC)",
538604
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo",

driver-core/src/test/unit/com/mongodb/ConnectionStringUnitTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
import org.junit.jupiter.params.ParameterizedTest;
2121
import org.junit.jupiter.params.provider.ValueSource;
2222

23+
import java.io.UnsupportedEncodingException;
24+
import java.net.URLEncoder;
25+
import java.nio.charset.StandardCharsets;
26+
2327
import static org.junit.jupiter.api.Assertions.assertAll;
2428
import static org.junit.jupiter.api.Assertions.assertEquals;
2529
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -34,6 +38,31 @@ void defaults() {
3438
assertAll(() -> assertNull(connectionStringDefault.getServerMonitoringMode()));
3539
}
3640

41+
@Test
42+
public void mustDecodeOidcIndividually() {
43+
String string = "abc,d!@#$%^&*;ef:ghi";
44+
ConnectionString cs = new ConnectionString(
45+
"mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties="
46+
+ "ENVIRONMENT:azure,TOKEN_RESOURCE:" + encode(string));
47+
assertEquals(string, cs.getCredential().getMechanismProperty("TOKEN_RESOURCE", null));
48+
}
49+
50+
@Test
51+
public void mustDecodeNonOidcAsWhole() {
52+
ConnectionString cs2 = new ConnectionString(
53+
"mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties="
54+
+ encode("SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authSource=$external"));
55+
assertEquals("other", cs2.getCredential().getMechanismProperty("SERVICE_NAME", null));
56+
}
57+
58+
private static String encode(final String string) {
59+
try {
60+
return URLEncoder.encode(string, StandardCharsets.UTF_8.name());
61+
} catch (UnsupportedEncodingException e) {
62+
throw new RuntimeException(e);
63+
}
64+
}
65+
3766
@ParameterizedTest
3867
@ValueSource(strings = {DEFAULT_OPTIONS + "serverMonitoringMode=stream"})
3968
void equalAndHashCode(final String connectionString) {

0 commit comments

Comments
 (0)