Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OIDC Add remaining environments (azure, gcp), evergreen testing, API naming updates #1371

Merged
merged 25 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
240af4c
Add remaining environments (azure, gcp), evergreen testing, API namin…
katcharov Apr 19, 2024
b84ca99
Add remaining tests, refactor, increase GCP test machine
katcharov Apr 22, 2024
df3ef8d
Cleanup, update since annotations, updates to match spec API
katcharov Apr 23, 2024
581ae2e
Test fixes
katcharov Apr 23, 2024
00e4c15
PR fixes
katcharov Apr 23, 2024
6898b4f
Remove admin credentials
katcharov Apr 24, 2024
2621ae8
PR fixes
katcharov Apr 25, 2024
c842d22
PR fixes
katcharov Apr 25, 2024
3cea409
Apply suggestions from code review
katcharov Apr 26, 2024
e883279
Update driver-core/src/main/com/mongodb/MongoCredential.java
katcharov Apr 26, 2024
bc30a2f
Update driver-core/src/main/com/mongodb/internal/authentication/Crede…
katcharov Apr 26, 2024
d856d84
Implement OIDC map value splitting
katcharov Apr 26, 2024
479fcdd
PR fixes, doc updates
katcharov Apr 26, 2024
4a844b1
PR fixes for OIDC feature branch
katcharov Apr 26, 2024
cc1c7ec
Update connection-string latest specifications/pull/1569
katcharov Apr 26, 2024
678d7b7
PR fixes
katcharov Apr 26, 2024
fcb65dc
PR fixes
katcharov Apr 26, 2024
71b3846
PR fixes
katcharov Apr 26, 2024
f6cb3da
PR Fixes
katcharov Apr 26, 2024
be63643
PR fixes
katcharov Apr 27, 2024
0532a87
PR fixes
katcharov Apr 29, 2024
761918e
PR fixes: mustDecodeNonOidcAsWhole
katcharov Apr 29, 2024
7428fd1
PR fixes
katcharov Apr 29, 2024
fcdab29
Update driver-sync/src/test/functional/com/mongodb/internal/connectio…
katcharov Apr 29, 2024
8971a79
Doc fix
katcharov Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 149 additions & 3 deletions .evergreen/.evg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ stepback: true
# Actual testing tasks are marked with `type: test`
command_type: system

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

# What to do when evergreen hits the timeout (`post:` tasks are run automatically)
timeout:
Expand Down Expand Up @@ -968,6 +967,60 @@ tasks:
- func: "run load-balancer"
- func: "run load-balancer tests"

- name: "oidc-auth-test"
commands:
- command: subprocess.exec
type: test
params:
working_dir: "src"
binary: bash
include_expansions_in_env: ["DRIVERS_TOOLS", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
env:
OIDC_ENV: "test"
args:
- .evergreen/run-mongodb-oidc-test.sh

- name: "oidc-auth-test-azure"
commands:
- command: shell.exec
params:
shell: bash
env:
JAVA_HOME: ${JAVA_HOME}
script: |-
set -o errexit
${PREPARE_SHELL}
cd src
git add .
git commit -m "add files"
# uncompressed tar used to allow appending .git folder
export AZUREOIDC_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar
git archive -o $AZUREOIDC_DRIVERS_TAR_FILE HEAD
tar -rf $AZUREOIDC_DRIVERS_TAR_FILE .git
export AZUREOIDC_TEST_CMD="OIDC_ENV=azure ./.evergreen/run-mongodb-oidc-test.sh"
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh

- name: "oidc-auth-test-gcp"
commands:
- command: shell.exec
params:
shell: bash
script: |-
set -o errexit
${PREPARE_SHELL}
cd src
git add .
git commit -m "add files"
# uncompressed tar used to allow appending .git folder
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar
git archive -o $GCPOIDC_DRIVERS_TAR_FILE HEAD
tar -rf $GCPOIDC_DRIVERS_TAR_FILE .git
# Define the command to run on the VM.
# Ensure that we source the environment file created for us, set up any other variables we need,
# and then run our test suite on the vm.
export GCPOIDC_TEST_CMD="OIDC_ENV=gcp ./.evergreen/run-mongodb-oidc-test.sh"
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh

- name: serverless-test
commands:
- func: "run serverless"
Expand Down Expand Up @@ -2065,6 +2118,78 @@ task_groups:
tasks:
- test-aws-lambda-deployed

- name: testoidc_task_group
setup_group:
- func: fetch source
- func: prepare resources
- func: fix absolute paths
- command: ec2.assume_role
params:
role_arn: ${aws_test_secrets_role}
- command: subprocess.exec
params:
binary: bash
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/setup.sh
teardown_task:
- command: subprocess.exec
params:
binary: bash
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/teardown.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test

- name: testazureoidc_task_group
setup_group:
- func: fetch source
- func: prepare resources
- func: fix absolute paths
- command: subprocess.exec
params:
binary: bash
env:
AZUREOIDC_VMNAME_PREFIX: "JAVA_DRIVER"
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/create-and-setup-vm.sh
teardown_task:
- command: subprocess.exec
params:
binary: bash
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/delete-vm.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test-azure

- name: testgcpoidc_task_group
setup_group:
- func: fetch source
- func: prepare resources
- func: fix absolute paths
- command: subprocess.exec
params:
binary: bash
env:
GCPOIDC_VMNAME_PREFIX: "JAVA_DRIVER"
GCPKMS_MACHINETYPE: "e2-medium" # comparable elapsed time to Azure; default was starved, caused timeouts
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
teardown_task:
- command: subprocess.exec
params:
binary: bash
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test-gcp

buildvariants:

# Test packaging and other release related routines
Expand Down Expand Up @@ -2216,6 +2341,27 @@ buildvariants:
tasks:
- name: "test_atlas_task_group_search_indexes"

- name: "oidc-auth-test"
display_name: "OIDC Auth"
run_on: ubuntu2204-small
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We explicitly test on Windows and MacOS in PyMongo, but I'd consider that optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of our tests use linux/ubuntu as the os, so I took those out.

tasks:
- name: testoidc_task_group
batchtime: 20160 # 14 days

- name: testazureoidc-variant
display_name: "OIDC Auth Azure"
run_on: ubuntu2204-small
tasks:
- name: testazureoidc_task_group
batchtime: 20160 # 14 days

- name: testgcpoidc-variant
display_name: "OIDC Auth GCP"
run_on: ubuntu2204-small
tasks:
- name: testgcpoidc_task_group
batchtime: 20160 # 14 days

- matrix_name: "aws-auth-test"
matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17", "jdk21"], version: ["4.4", "5.0", "6.0", "7.0", "latest"], os: "ubuntu",
aws-credential-provider: "*" }
Expand Down
40 changes: 40 additions & 0 deletions .evergreen/run-mongodb-oidc-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

set +x # Disable debug trace
set -eu

echo "Running MONGODB-OIDC authentication tests"
echo "OIDC_ENV $OIDC_ENV"

if [ $OIDC_ENV == "test" ]; then
if [ -z "$DRIVERS_TOOLS" ]; then
echo "Must specify DRIVERS_TOOLS"
exit 1
fi
source ${DRIVERS_TOOLS}/.evergreen/auth_oidc/secrets-export.sh
# java will not need to be installed, but we need to config
RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE:-$0}")"
source "${RELATIVE_DIR_PATH}/javaConfig.bash"
elif [ $OIDC_ENV == "azure" ]; then
source ./env.sh
elif [ $OIDC_ENV == "gcp" ]; then
source ./secrets-export.sh
else
echo "Unrecognized OIDC_ENV $OIDC_ENV"
exit 1
fi


if ! which java ; then
echo "Installing java..."
sudo apt install openjdk-17-jdk -y
echo "Installed java."
fi

which java
export OIDC_TESTS_ENABLED=true

./gradlew -Dorg.mongodb.test.uri="$MONGODB_URI" \
--stacktrace --debug --info --no-build-cache driver-core:cleanTest \
driver-sync:test --tests OidcAuthenticationProseTests --tests UnifiedAuthTest \
driver-reactive-streams:test --tests OidcAuthenticationAsyncProseTests \
47 changes: 39 additions & 8 deletions driver-core/src/main/com/mongodb/ConnectionString.java
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
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 @@ -229,17 +230,18 @@
* </ul>
* <p>Authentication configuration:</p>
* <ul>
* <li>{@code authMechanism=MONGO-CR|GSSAPI|PLAIN|MONGODB-X509}: The authentication mechanism to use if a credential was supplied.
* <li>{@code authMechanism=MONGO-CR|GSSAPI|PLAIN|MONGODB-X509|MONGODB-OIDC}: The authentication mechanism to use if a credential was supplied.
* The default is unspecified, in which case the client will pick the most secure mechanism available based on the sever version. For the
* GSSAPI and MONGODB-X509 mechanisms, no password is accepted, only the username.
* GSSAPI, MONGODB-X509, and MONGODB-OIDC mechanisms, no password is accepted, only the username.
* </li>
* <li>{@code authSource=string}: The source of the authentication credentials. This is typically the database that
* the credentials have been created. The value defaults to the database specified in the path portion of the connection string.
* If the database is specified in neither place, the default value is "admin". This option is only respected when using the MONGO-CR
* 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.
* mechanism properties to be set on the connection string. Property values must be percent-encoded individually, as needed. The
* entire substring following the {@code =} should not itself be encoded.
* </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 @@ -916,13 +918,16 @@ private MongoCredential createCredentials(final Map<String, List<String>> option

if (credential != null && authMechanismProperties != null) {
for (String part : authMechanismProperties.split(",")) {
String[] mechanismPropertyKeyValue = part.split(":");
String[] mechanismPropertyKeyValue = part.split(":", 2);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ignores the second : (a test passes in a value that decodes to a string that includes :).

if (mechanismPropertyKeyValue.length != 2) {
throw new IllegalArgumentException(format("The connection string contains invalid authentication properties. "
+ "'%s' is not a key value pair", part));
}
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 @@ -938,6 +943,27 @@ 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 @@ -1018,12 +1044,14 @@ private String getLastValue(final Map<String, List<String>> optionsMap, final St

private Map<String, List<String>> parseOptions(final String optionsPart) {
Map<String, List<String>> optionsMap = new HashMap<>();
if (optionsPart.length() == 0) {
if (optionsPart.isEmpty()) {
return optionsMap;
}

for (final String part : optionsPart.split("&|;")) {
if (part.length() == 0) {
List<String> options = Arrays.asList(optionsPart.split("&|;"));
boolean isOidc = isOidc(options);
for (final String part : options) {
if (part.isEmpty()) {
continue;
}
int idx = part.indexOf("=");
Expand All @@ -1034,7 +1062,10 @@ private Map<String, List<String>> parseOptions(final String optionsPart) {
if (valueList == null) {
valueList = new ArrayList<>(1);
}
valueList.add(urldecode(value));
if (decodeWholeOptionValue(isOidc, key)) {
value = urldecode(value);
stIncMale marked this conversation as resolved.
Show resolved Hide resolved
}
valueList.add(value);
optionsMap.put(key, valueList);
} else {
throw new IllegalArgumentException(format("The connection string contains an invalid option '%s'. "
Expand Down
Loading