Skip to content

Commit

Permalink
Disallow comma character in authMechanismProperties (#1412)
Browse files Browse the repository at this point in the history
JAVA-5486
# Conflicts:
#	driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java
  • Loading branch information
katcharov authored Jun 7, 2024
1 parent fe3e406 commit 679c93a
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 52 deletions.
39 changes: 3 additions & 36 deletions driver-core/src/main/com/mongodb/ConnectionString.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -240,9 +239,7 @@
* mechanism (the default).
* </li>
* <li>{@code authMechanismProperties=PROPERTY_NAME:PROPERTY_VALUE,PROPERTY_NAME2:PROPERTY_VALUE2}: This option allows authentication
* mechanism properties to be set on the connection string. Property values must be percent-encoded individually, when
* special characters are used, including {@code ,} (comma), {@code =}, {@code +}, {@code &}, and {@code %}. The
* entire substring following the {@code =} should not itself be encoded.
* mechanism properties to be set on the connection string.
* </li>
* <li>{@code gssapiServiceName=string}: This option only applies to the GSSAPI mechanism and is used to alter the service name.
* Deprecated, please use {@code authMechanismProperties=SERVICE_NAME:string} instead.
Expand Down Expand Up @@ -908,7 +905,6 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
}
}


MongoCredential credential = null;
if (mechanism != null) {
credential = createMongoCredentialWithMechanism(mechanism, userName, password, authSource, gssapiServiceName);
Expand All @@ -926,9 +922,6 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
}
String key = mechanismPropertyKeyValue[0].trim().toLowerCase();
String value = mechanismPropertyKeyValue[1].trim();
if (decodeValueOfKeyValuePair(credential.getMechanism())) {
value = urldecode(value);
}
if (MECHANISM_KEYS_DISALLOWED_IN_CONNECTION_STRING.contains(key)) {
throw new IllegalArgumentException(format("The connection string contains disallowed mechanism properties. "
+ "'%s' must be set on the credential programmatically.", key));
Expand All @@ -944,27 +937,6 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
return credential;
}

private static boolean decodeWholeOptionValue(final boolean isOidc, final String key) {
// The "whole option value" is the entire string following = in an option,
// including separators when the value is a list or list of key-values.
// This is the original parsing behaviour, but implies that users can
// encode separators (much like they might with URL parameters). This
// behaviour implies that users cannot encode "key-value" values that
// contain a comma, because this will (after this "whole value decoding)
// be parsed as a key-value separator, rather than part of a value.
return !(isOidc && key.equals("authmechanismproperties"));
}

private static boolean decodeValueOfKeyValuePair(@Nullable final String mechanismName) {
// Only authMechanismProperties should be individually decoded, and only
// when the mechanism is OIDC. These will not have been decoded.
return AuthenticationMechanism.MONGODB_OIDC.getMechanismName().equals(mechanismName);
}

private static boolean isOidc(final List<String> options) {
return options.contains("authMechanism=" + AuthenticationMechanism.MONGODB_OIDC.getMechanismName());
}

private MongoCredential createMongoCredentialWithMechanism(final AuthenticationMechanism mechanism, final String userName,
@Nullable final char[] password,
@Nullable final String authSource,
Expand Down Expand Up @@ -1049,9 +1021,7 @@ private Map<String, List<String>> parseOptions(final String optionsPart) {
return optionsMap;
}

List<String> options = Arrays.asList(optionsPart.split("&|;"));
boolean isOidc = isOidc(options);
for (final String part : options) {
for (final String part : optionsPart.split("&|;")) {
if (part.isEmpty()) {
continue;
}
Expand All @@ -1063,10 +1033,7 @@ private Map<String, List<String>> parseOptions(final String optionsPart) {
if (valueList == null) {
valueList = new ArrayList<>(1);
}
if (decodeWholeOptionValue(isOidc, key)) {
value = urldecode(value);
}
valueList.add(value);
valueList.add(urldecode(value));
optionsMap.put(key, valueList);
} else {
throw new IllegalArgumentException(format("The connection string contains an invalid option '%s'. "
Expand Down
3 changes: 3 additions & 0 deletions driver-core/src/main/com/mongodb/MongoCredential.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ public final class MongoCredential {
* Mechanism property key for specifying he URI of the target resource (sometimes called the audience),
* used in some OIDC environments.
*
* <p>A TOKEN_RESOURCE with a comma character must be given as a `MongoClient` configuration and not as
* part of the connection string. The TOKEN_RESOURCE value can contain a colon character.
*
* @see MongoCredential#ENVIRONMENT_KEY
* @see #createOidcCredential(String)
* @since 5.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@
},
{
"description": "should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abc%2Cd%25ef%3Ag%26hi",
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abcd%25ef%3Ag%26hi",
"valid": true,
"credential": {
"username": "user",
Expand All @@ -574,7 +574,7 @@
"mechanism": "MONGODB-OIDC",
"mechanism_properties": {
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "abc,d%ef:g&hi"
"TOKEN_RESOURCE": "abcd%ef:g&hi"
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@
"options": {
"authmechanism": "MONGODB-CR"
}
},
{
"description": "Colon in a key value pair",
"uri": "mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster",
"valid": true,
"warning": false,
"hosts": [
{
"type": "hostname",
"host": "example.com",
"port": null
}
],
"auth": null,
"options": {
"authmechanismProperties": {
"TOKEN_RESOURCE": "mongodb://test-cluster"
}
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,6 @@ void defaults() {
assertAll(() -> assertNull(connectionStringDefault.getServerMonitoringMode()));
}

@Test
public void mustDecodeOidcIndividually() {
String string = "abc,d!@#$%^&*;ef:ghi";
// encoded tags will fail parsing with an "invalid read preference tag"
// error if decoding is skipped.
String encodedTags = encode("dc:ny,rack:1");
ConnectionString cs = new ConnectionString(
"mongodb://localhost/?readPreference=primaryPreferred&readPreferenceTags=" + encodedTags
+ "&authMechanism=MONGODB-OIDC&authMechanismProperties="
+ "ENVIRONMENT:azure,TOKEN_RESOURCE:" + encode(string));
MongoCredential credential = Assertions.assertNotNull(cs.getCredential());
assertEquals(string, credential.getMechanismProperty("TOKEN_RESOURCE", null));
}

@Test
public void mustDecodeNonOidcAsWhole() {
// this string allows us to check if there is no double decoding
Expand Down

0 comments on commit 679c93a

Please sign in to comment.