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

Implement OIDC SASL mechanism #1134

Merged
merged 6 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 50 additions & 0 deletions .evergreen/prepare-oidc-get-tokens-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash
katcharov marked this conversation as resolved.
Show resolved Hide resolved

set -o xtrace
set -o errexit # Exit the script with error if any of the commands fail

############################################
# Main Program #
############################################

# Supported/used environment variables:
# DRIVERS_TOOLS The path to evergreeen tools
# OIDC_AWS_* Required OIDC_AWS_* env variables must be configured
#
# Environment variables used as output:
# OIDC_TESTS_ENABLED Allows running OIDC tests
# OIDC_TOKEN_DIR The path to generated OIDC AWS tokens
# AWS_WEB_IDENTITY_TOKEN_FILE The path to AWS token for device workflow

if [ -z ${DRIVERS_TOOLS+x} ]; then
echo "DRIVERS_TOOLS. is not set";
exit 1
fi

if [ -z ${OIDC_AWS_ROLE_ARN+x} ]; then
echo "OIDC_AWS_ROLE_ARN. is not set";
exit 1
fi

if [ -z ${OIDC_AWS_SECRET_ACCESS_KEY+x} ]; then
echo "OIDC_AWS_SECRET_ACCESS_KEY. is not set";
exit 1
fi

if [ -z ${OIDC_AWS_ACCESS_KEY_ID+x} ]; then
echo "OIDC_AWS_ACCESS_KEY_ID. is not set";
exit 1
fi

export AWS_ROLE_ARN=${OIDC_AWS_ROLE_ARN}
export AWS_SECRET_ACCESS_KEY=${OIDC_AWS_SECRET_ACCESS_KEY}
export AWS_ACCESS_KEY_ID=${OIDC_AWS_ACCESS_KEY_ID}
export OIDC_FOLDER=${DRIVERS_TOOLS}/.evergreen/auth_oidc
export OIDC_TOKEN_DIR=${OIDC_FOLDER}/test_tokens
export AWS_WEB_IDENTITY_TOKEN_FILE=${OIDC_TOKEN_DIR}/test1
export OIDC_TESTS_ENABLED=true

echo "Configuring OIDC server for local authentication tests"

cd ${OIDC_FOLDER}
DRIVERS_TOOLS=${DRIVERS_TOOLS} ./oidc_get_tokens.sh
50 changes: 50 additions & 0 deletions .evergreen/prepare-oidc-server-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash

set -o xtrace
set -o errexit # Exit the script with error if any of the commands fail

############################################
# Main Program #
############################################

# Supported/used environment variables:
# DRIVERS_TOOLS The path to evergreeen tools
# OIDC_AWS_* OIDC_AWS_* env variables must be configured
#
# Environment variables used as output:
# OIDC_TESTS_ENABLED Allows running OIDC tests
# OIDC_TOKEN_DIR The path to generated tokens
# AWS_WEB_IDENTITY_TOKEN_FILE The path to AWS token for device workflow

if [ -z ${DRIVERS_TOOLS+x} ]; then
echo "DRIVERS_TOOLS. is not set";
exit 1
fi

if [ -z ${OIDC_AWS_ROLE_ARN+x} ]; then
echo "OIDC_AWS_ROLE_ARN. is not set";
exit 1
fi

if [ -z ${OIDC_AWS_SECRET_ACCESS_KEY+x} ]; then
echo "OIDC_AWS_SECRET_ACCESS_KEY. is not set";
exit 1
fi

if [ -z ${OIDC_AWS_ACCESS_KEY_ID+x} ]; then
echo "OIDC_AWS_ACCESS_KEY_ID. is not set";
exit 1
fi

export AWS_ROLE_ARN=${OIDC_AWS_ROLE_ARN}
export AWS_SECRET_ACCESS_KEY=${OIDC_AWS_SECRET_ACCESS_KEY}
export AWS_ACCESS_KEY_ID=${OIDC_AWS_ACCESS_KEY_ID}
export OIDC_FOLDER=${DRIVERS_TOOLS}/.evergreen/auth_oidc
export OIDC_TOKEN_DIR=${OIDC_FOLDER}/test_tokens
export AWS_WEB_IDENTITY_TOKEN_FILE=${OIDC_TOKEN_DIR}/test1
export OIDC_TESTS_ENABLED=true

echo "Configuring OIDC server for local authentication tests"

cd ${OIDC_FOLDER}
DRIVERS_TOOLS=${DRIVERS_TOOLS} ./start_local_server.sh
12 changes: 8 additions & 4 deletions bson/src/test/unit/util/ThreadTestHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,19 @@ private ThreadTestHelpers() {
}

public static void executeAll(final int nThreads, final Runnable c) {
executeAll(Collections.nCopies(nThreads, c).toArray(new Runnable[0]));
}

public static void executeAll(final Runnable... runnables) {
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(nThreads);
CountDownLatch latch = new CountDownLatch(nThreads);
service = Executors.newFixedThreadPool(runnables.length);
CountDownLatch latch = new CountDownLatch(runnables.length);
List<Throwable> failures = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < nThreads; i++) {
for (final Runnable runnable : runnables) {
service.submit(() -> {
try {
c.run();
runnable.run();
} catch (Throwable e) {
failures.add(e);
} finally {
Expand Down
7 changes: 7 additions & 0 deletions driver-core/src/main/com/mongodb/AuthenticationMechanism.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public enum AuthenticationMechanism {
*/
MONGODB_AWS("MONGODB-AWS"),

/**
* The MONGODB-OIDC mechanism.
* @since 4.10
* @mongodb.server.release 7.0
*/
MONGODB_OIDC("MONGODB-OIDC"),

/**
* The MongoDB X.509 mechanism. This mechanism is available only with client certificates over SSL.
*/
Expand Down
16 changes: 16 additions & 0 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 @@ -47,7 +47,11 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.mongodb.MongoCredential.ALLOWED_HOSTS_KEY;
import static com.mongodb.internal.connection.OidcAuthenticator.OidcValidator.validateCreateOidcCredential;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
Expand Down Expand Up @@ -281,6 +285,9 @@ public class ConnectionString {
private static final Set<String> ALLOWED_OPTIONS_IN_TXT_RECORD =
new HashSet<>(asList("authsource", "replicaset", "loadbalanced"));
private static final Logger LOGGER = Loggers.getLogger("uri");
private static final List<String> MECHANISM_KEYS_DISALLOWED_IN_CONNECTION_STRING = Stream.of(ALLOWED_HOSTS_KEY)
.map(k -> k.toLowerCase())
.collect(Collectors.toList());

private final MongoCredential credential;
private final boolean isSrvProtocol;
Expand Down Expand Up @@ -916,6 +923,11 @@ private MongoCredential createCredentials(final Map<String, List<String>> option
}
String key = mechanismPropertyKeyValue[0].trim().toLowerCase();
String value = mechanismPropertyKeyValue[1].trim();
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));
}

if (key.equals("canonicalize_host_name")) {
credential = credential.withMechanismProperty(key, Boolean.valueOf(value));
} else {
Expand Down Expand Up @@ -975,6 +987,10 @@ private MongoCredential createMongoCredentialWithMechanism(final AuthenticationM
case MONGODB_AWS:
credential = MongoCredential.createAwsCredential(userName, password);
break;
case MONGODB_OIDC:
validateCreateOidcCredential(password);
credential = MongoCredential.createOidcCredential(userName);
break;
default:
throw new UnsupportedOperationException(format("The connection string contains an invalid authentication mechanism'. "
+ "'%s' is not a supported authentication mechanism",
Expand Down