Skip to content

Commit 106ee4d

Browse files
katcharovstIncMale
andauthored
OIDC Add remaining environments (azure, gcp), evergreen testing, API naming updates (#1371)
JAVA-5353 JAVA-5395 JAVA-4834 JAVA-4932 --------- Co-authored-by: Valentin Kovalenko <[email protected]>
1 parent 8b80055 commit 106ee4d

File tree

16 files changed

+1186
-383
lines changed

16 files changed

+1186
-383
lines changed

.evergreen/.evg.yml

+149-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ stepback: true
1212
# Actual testing tasks are marked with `type: test`
1313
command_type: system
1414

15-
# Protect ourself against rogue test case, or curl gone wild, that runs forever
16-
# 12 minutes is the longest we'll ever run
17-
exec_timeout_secs: 3600 # 12 minutes is the longest we'll ever run
15+
# Protect ourselves against rogue test case, or curl gone wild, that runs forever
16+
exec_timeout_secs: 3600
1817

1918
# What to do when evergreen hits the timeout (`post:` tasks are run automatically)
2019
timeout:
@@ -968,6 +967,60 @@ tasks:
968967
- func: "run load-balancer"
969968
- func: "run load-balancer tests"
970969

970+
- name: "oidc-auth-test"
971+
commands:
972+
- command: subprocess.exec
973+
type: test
974+
params:
975+
working_dir: "src"
976+
binary: bash
977+
include_expansions_in_env: ["DRIVERS_TOOLS", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
978+
env:
979+
OIDC_ENV: "test"
980+
args:
981+
- .evergreen/run-mongodb-oidc-test.sh
982+
983+
- name: "oidc-auth-test-azure"
984+
commands:
985+
- command: shell.exec
986+
params:
987+
shell: bash
988+
env:
989+
JAVA_HOME: ${JAVA_HOME}
990+
script: |-
991+
set -o errexit
992+
${PREPARE_SHELL}
993+
cd src
994+
git add .
995+
git commit -m "add files"
996+
# uncompressed tar used to allow appending .git folder
997+
export AZUREOIDC_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar
998+
git archive -o $AZUREOIDC_DRIVERS_TAR_FILE HEAD
999+
tar -rf $AZUREOIDC_DRIVERS_TAR_FILE .git
1000+
export AZUREOIDC_TEST_CMD="OIDC_ENV=azure ./.evergreen/run-mongodb-oidc-test.sh"
1001+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
1002+
1003+
- name: "oidc-auth-test-gcp"
1004+
commands:
1005+
- command: shell.exec
1006+
params:
1007+
shell: bash
1008+
script: |-
1009+
set -o errexit
1010+
${PREPARE_SHELL}
1011+
cd src
1012+
git add .
1013+
git commit -m "add files"
1014+
# uncompressed tar used to allow appending .git folder
1015+
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar
1016+
git archive -o $GCPOIDC_DRIVERS_TAR_FILE HEAD
1017+
tar -rf $GCPOIDC_DRIVERS_TAR_FILE .git
1018+
# Define the command to run on the VM.
1019+
# Ensure that we source the environment file created for us, set up any other variables we need,
1020+
# and then run our test suite on the vm.
1021+
export GCPOIDC_TEST_CMD="OIDC_ENV=gcp ./.evergreen/run-mongodb-oidc-test.sh"
1022+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
1023+
9711024
- name: serverless-test
9721025
commands:
9731026
- func: "run serverless"
@@ -2065,6 +2118,78 @@ task_groups:
20652118
tasks:
20662119
- test-aws-lambda-deployed
20672120

2121+
- name: testoidc_task_group
2122+
setup_group:
2123+
- func: fetch source
2124+
- func: prepare resources
2125+
- func: fix absolute paths
2126+
- command: ec2.assume_role
2127+
params:
2128+
role_arn: ${aws_test_secrets_role}
2129+
- command: subprocess.exec
2130+
params:
2131+
binary: bash
2132+
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
2133+
args:
2134+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/setup.sh
2135+
teardown_task:
2136+
- command: subprocess.exec
2137+
params:
2138+
binary: bash
2139+
args:
2140+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/teardown.sh
2141+
setup_group_can_fail_task: true
2142+
setup_group_timeout_secs: 1800
2143+
tasks:
2144+
- oidc-auth-test
2145+
2146+
- name: testazureoidc_task_group
2147+
setup_group:
2148+
- func: fetch source
2149+
- func: prepare resources
2150+
- func: fix absolute paths
2151+
- command: subprocess.exec
2152+
params:
2153+
binary: bash
2154+
env:
2155+
AZUREOIDC_VMNAME_PREFIX: "JAVA_DRIVER"
2156+
args:
2157+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/create-and-setup-vm.sh
2158+
teardown_task:
2159+
- command: subprocess.exec
2160+
params:
2161+
binary: bash
2162+
args:
2163+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/delete-vm.sh
2164+
setup_group_can_fail_task: true
2165+
setup_group_timeout_secs: 1800
2166+
tasks:
2167+
- oidc-auth-test-azure
2168+
2169+
- name: testgcpoidc_task_group
2170+
setup_group:
2171+
- func: fetch source
2172+
- func: prepare resources
2173+
- func: fix absolute paths
2174+
- command: subprocess.exec
2175+
params:
2176+
binary: bash
2177+
env:
2178+
GCPOIDC_VMNAME_PREFIX: "JAVA_DRIVER"
2179+
GCPKMS_MACHINETYPE: "e2-medium" # comparable elapsed time to Azure; default was starved, caused timeouts
2180+
args:
2181+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
2182+
teardown_task:
2183+
- command: subprocess.exec
2184+
params:
2185+
binary: bash
2186+
args:
2187+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
2188+
setup_group_can_fail_task: true
2189+
setup_group_timeout_secs: 1800
2190+
tasks:
2191+
- oidc-auth-test-gcp
2192+
20682193
buildvariants:
20692194

20702195
# Test packaging and other release related routines
@@ -2216,6 +2341,27 @@ buildvariants:
22162341
tasks:
22172342
- name: "test_atlas_task_group_search_indexes"
22182343

2344+
- name: "oidc-auth-test"
2345+
display_name: "OIDC Auth"
2346+
run_on: ubuntu2204-small
2347+
tasks:
2348+
- name: testoidc_task_group
2349+
batchtime: 20160 # 14 days
2350+
2351+
- name: testazureoidc-variant
2352+
display_name: "OIDC Auth Azure"
2353+
run_on: ubuntu2204-small
2354+
tasks:
2355+
- name: testazureoidc_task_group
2356+
batchtime: 20160 # 14 days
2357+
2358+
- name: testgcpoidc-variant
2359+
display_name: "OIDC Auth GCP"
2360+
run_on: ubuntu2204-small
2361+
tasks:
2362+
- name: testgcpoidc_task_group
2363+
batchtime: 20160 # 14 days
2364+
22192365
- matrix_name: "aws-auth-test"
22202366
matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17", "jdk21"], version: ["4.4", "5.0", "6.0", "7.0", "latest"], os: "ubuntu",
22212367
aws-credential-provider: "*" }

.evergreen/run-mongodb-oidc-test.sh

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
3+
set +x # Disable debug trace
4+
set -eu
5+
6+
echo "Running MONGODB-OIDC authentication tests"
7+
echo "OIDC_ENV $OIDC_ENV"
8+
9+
if [ $OIDC_ENV == "test" ]; then
10+
if [ -z "$DRIVERS_TOOLS" ]; then
11+
echo "Must specify DRIVERS_TOOLS"
12+
exit 1
13+
fi
14+
source ${DRIVERS_TOOLS}/.evergreen/auth_oidc/secrets-export.sh
15+
# java will not need to be installed, but we need to config
16+
RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE:-$0}")"
17+
source "${RELATIVE_DIR_PATH}/javaConfig.bash"
18+
elif [ $OIDC_ENV == "azure" ]; then
19+
source ./env.sh
20+
elif [ $OIDC_ENV == "gcp" ]; then
21+
source ./secrets-export.sh
22+
else
23+
echo "Unrecognized OIDC_ENV $OIDC_ENV"
24+
exit 1
25+
fi
26+
27+
28+
if ! which java ; then
29+
echo "Installing java..."
30+
sudo apt install openjdk-17-jdk -y
31+
echo "Installed java."
32+
fi
33+
34+
which java
35+
export OIDC_TESTS_ENABLED=true
36+
37+
./gradlew -Dorg.mongodb.test.uri="$MONGODB_URI" \
38+
--stacktrace --debug --info --no-build-cache driver-core:cleanTest \
39+
driver-sync:test --tests OidcAuthenticationProseTests --tests UnifiedAuthTest \
40+
driver-reactive-streams:test --tests OidcAuthenticationAsyncProseTests \

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

+40-8
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;
@@ -229,17 +230,19 @@
229230
* </ul>
230231
* <p>Authentication configuration:</p>
231232
* <ul>
232-
* <li>{@code authMechanism=MONGO-CR|GSSAPI|PLAIN|MONGODB-X509}: The authentication mechanism to use if a credential was supplied.
233+
* <li>{@code authMechanism=MONGO-CR|GSSAPI|PLAIN|MONGODB-X509|MONGODB-OIDC}: The authentication mechanism to use if a credential was supplied.
233234
* The default is unspecified, in which case the client will pick the most secure mechanism available based on the sever version. For the
234-
* GSSAPI and MONGODB-X509 mechanisms, no password is accepted, only the username.
235+
* GSSAPI, MONGODB-X509, and MONGODB-OIDC mechanisms, no password is accepted, only the username.
235236
* </li>
236237
* <li>{@code authSource=string}: The source of the authentication credentials. This is typically the database that
237238
* the credentials have been created. The value defaults to the database specified in the path portion of the connection string.
238239
* If the database is specified in neither place, the default value is "admin". This option is only respected when using the MONGO-CR
239240
* mechanism (the default).
240241
* </li>
241242
* <li>{@code authMechanismProperties=PROPERTY_NAME:PROPERTY_VALUE,PROPERTY_NAME2:PROPERTY_VALUE2}: This option allows authentication
242-
* mechanism properties to be set on the connection string.
243+
* mechanism properties to be set on the connection string. Property values must be percent-encoded individually, when
244+
* separator or escape characters are used (including {@code ,} (comma), {@code =}, {@code +}, {@code &}, and {@code %}). The
245+
* entire substring following the {@code =} should not itself be encoded.
243246
* </li>
244247
* <li>{@code gssapiServiceName=string}: This option only applies to the GSSAPI mechanism and is used to alter the service name.
245248
* Deprecated, please use {@code authMechanismProperties=SERVICE_NAME:string} instead.
@@ -916,13 +919,16 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
916919

917920
if (credential != null && authMechanismProperties != null) {
918921
for (String part : authMechanismProperties.split(",")) {
919-
String[] mechanismPropertyKeyValue = part.split(":");
922+
String[] mechanismPropertyKeyValue = part.split(":", 2);
920923
if (mechanismPropertyKeyValue.length != 2) {
921924
throw new IllegalArgumentException(format("The connection string contains invalid authentication properties. "
922925
+ "'%s' is not a key value pair", part));
923926
}
924927
String key = mechanismPropertyKeyValue[0].trim().toLowerCase();
925928
String value = mechanismPropertyKeyValue[1].trim();
929+
if (decodeValueOfKeyValuePair(credential.getMechanism())) {
930+
value = urldecode(value);
931+
}
926932
if (MECHANISM_KEYS_DISALLOWED_IN_CONNECTION_STRING.contains(key)) {
927933
throw new IllegalArgumentException(format("The connection string contains disallowed mechanism properties. "
928934
+ "'%s' must be set on the credential programmatically.", key));
@@ -938,6 +944,27 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
938944
return credential;
939945
}
940946

947+
private static boolean decodeWholeOptionValue(final boolean isOidc, final String key) {
948+
// The "whole option value" is the entire string following = in an option,
949+
// including separators when the value is a list or list of key-values.
950+
// This is the original parsing behaviour, but implies that users can
951+
// encode separators (much like they might with URL parameters). This
952+
// behaviour implies that users cannot encode "key-value" values that
953+
// contain a comma, because this will (after this "whole value decoding)
954+
// be parsed as a key-value separator, rather than part of a value.
955+
return !(isOidc && key.equals("authmechanismproperties"));
956+
}
957+
958+
private static boolean decodeValueOfKeyValuePair(@Nullable final String mechanismName) {
959+
// Only authMechanismProperties should be individually decoded, and only
960+
// when the mechanism is OIDC. These will not have been decoded.
961+
return AuthenticationMechanism.MONGODB_OIDC.getMechanismName().equals(mechanismName);
962+
}
963+
964+
private static boolean isOidc(final List<String> options) {
965+
return options.contains("authMechanism=" + AuthenticationMechanism.MONGODB_OIDC.getMechanismName());
966+
}
967+
941968
private MongoCredential createMongoCredentialWithMechanism(final AuthenticationMechanism mechanism, final String userName,
942969
@Nullable final char[] password,
943970
@Nullable final String authSource,
@@ -1018,12 +1045,14 @@ private String getLastValue(final Map<String, List<String>> optionsMap, final St
10181045

10191046
private Map<String, List<String>> parseOptions(final String optionsPart) {
10201047
Map<String, List<String>> optionsMap = new HashMap<>();
1021-
if (optionsPart.length() == 0) {
1048+
if (optionsPart.isEmpty()) {
10221049
return optionsMap;
10231050
}
10241051

1025-
for (final String part : optionsPart.split("&|;")) {
1026-
if (part.length() == 0) {
1052+
List<String> options = Arrays.asList(optionsPart.split("&|;"));
1053+
boolean isOidc = isOidc(options);
1054+
for (final String part : options) {
1055+
if (part.isEmpty()) {
10271056
continue;
10281057
}
10291058
int idx = part.indexOf("=");
@@ -1034,7 +1063,10 @@ private Map<String, List<String>> parseOptions(final String optionsPart) {
10341063
if (valueList == null) {
10351064
valueList = new ArrayList<>(1);
10361065
}
1037-
valueList.add(urldecode(value));
1066+
if (decodeWholeOptionValue(isOidc, key)) {
1067+
value = urldecode(value);
1068+
}
1069+
valueList.add(value);
10381070
optionsMap.put(key, valueList);
10391071
} else {
10401072
throw new IllegalArgumentException(format("The connection string contains an invalid option '%s'. "

0 commit comments

Comments
 (0)