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

feat(NODE-4929): Add OIDC Azure workflow #3670

Merged
merged 40 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7fd4c02
feat(NODE-4929): Add OIDC Azure workflow
durran May 12, 2023
e15b4b8
refactor: token caches
durran May 12, 2023
cb72e60
test: update configs
durran May 16, 2023
31a5e1e
fix: add azure error
durran May 16, 2023
71ba05e
test: point at new branch
durran May 18, 2023
a8fa022
test: update permissions
durran May 18, 2023
b9a3ba4
test: fix unit test
durran May 18, 2023
8cfa756
test: setup scripts
durran May 19, 2023
42ddd33
test: install deps
durran May 19, 2023
5614f45
test: fix unit tests
durran May 22, 2023
3e7c17a
fix: make azure error public
durran May 22, 2023
7ce4c4d
test: first 2 prose tests
durran May 25, 2023
5c80981
test: fix validation, more prose tests
durran May 25, 2023
3e65c3f
test: add reauth test
durran May 25, 2023
0af4f95
test: debug tests
durran May 25, 2023
31a0b37
test: update cache getter
durran May 25, 2023
2b0bf48
test: get token audience from props
durran May 25, 2023
a15ad09
chore: debug
durran May 25, 2023
38b5d7b
test: update uris
durran May 25, 2023
0409aa8
test: updatescript
durran May 25, 2023
2e28cca
fix: token audience property
durran May 25, 2023
339ec68
test: parse tokens
durran May 25, 2023
722a814
test: fix logic
durran May 25, 2023
c71adf1
test: more debug
durran May 25, 2023
de3d9f0
test: move validation
durran May 25, 2023
29288c6
test: await fail point
durran May 25, 2023
fb316d7
test: monitor commands
durran May 25, 2023
25ec3da
chore: remove console logging
durran May 25, 2023
a32bd39
test: fix script
durran May 25, 2023
fce0ccd
test: fix unit test
durran May 25, 2023
d160ac5
fix: more mech properties type safety
durran May 26, 2023
95e17e7
test: use drivers tools master
durran May 26, 2023
0b4e3b6
test: update per suggestions
durran May 30, 2023
506e6b6
test: fix prose test
durran May 30, 2023
29ed901
test: fix ubuntu distro
durran May 30, 2023
23be573
test: run correct script
durran May 30, 2023
236a64c
test: use auth dir
durran May 30, 2023
384eaa1
test: use subprocess
durran May 30, 2023
0064fdd
chore: update comment
durran Jun 2, 2023
d901e52
fix: lint
durran Jun 6, 2023
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
2 changes: 2 additions & 0 deletions .evergreen/ci_matrix_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const DEFAULT_OS = 'rhel80-large';
const WINDOWS_OS = 'windows-vsCurrent-large';
const MACOS_OS = 'macos-1100';
const UBUNTU_OS = 'ubuntu1804-large';
const UBUNTU_20_OS = 'ubuntu2004-small'
const DEBIAN_OS = 'debian11-small';

module.exports = {
Expand All @@ -32,5 +33,6 @@ module.exports = {
WINDOWS_OS,
MACOS_OS,
UBUNTU_OS,
UBUNTU_20_OS,
DEBIAN_OS
};
42 changes: 42 additions & 0 deletions .evergreen/config.in.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,20 @@ tasks:
args:
- src/.evergreen/run-azure-kms-tests.sh

- name: "oidc-auth-test-azure-latest"
commands:
- func: "install dependencies"
- command: subprocess.exec
params:
working_dir: src
binary: bash
env:
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
AZUREOIDC_CLIENTID: ${testazureoidc_clientid}
PROVIDER_NAME: azure
args:
- .evergreen/run-oidc-tests-azure.sh

task_groups:
- name: serverless_task_group
Expand Down Expand Up @@ -1348,6 +1362,34 @@ task_groups:
tasks:
- test-azurekms-task

- name: testazureoidc_task_group
setup_group:
- func: fetch source
- command: shell.exec
params:
shell: bash
script: |-
set -o errexit
${PREPARE_SHELL}
export AZUREOIDC_CLIENTID="${testazureoidc_clientid}"
export AZUREOIDC_TENANTID="${testazureoic_tenantid}"
export AZUREOIDC_SECRET="${testazureoidc_secret}"
export AZUREOIDC_KEYVAULT=${testazureoidc_keyvault}
export AZUREOIDC_DRIVERS_TOOLS="$DRIVERS_TOOLS"
export AZUREOIDC_VMNAME_PREFIX="NODE_DRIVER"
$DRIVERS_TOOLS/.evergreen/auth_oidc/azure/create-and-setup-vm.sh
teardown_group:
- command: shell.exec
params:
shell: bash
script: |-
${PREPARE_SHELL}
$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-latest

pre:
- func: "fetch source"
- func: "windows fix"
Expand Down
47 changes: 47 additions & 0 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,20 @@ tasks:
EXPECTED_AZUREKMS_OUTCOME: failure
args:
- src/.evergreen/run-azure-kms-tests.sh
- name: oidc-auth-test-azure-latest
commands:
- func: install dependencies
- command: subprocess.exec
params:
working_dir: src
binary: bash
env:
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
AZUREOIDC_CLIENTID: ${testazureoidc_clientid}
PROVIDER_NAME: azure
args:
- .evergreen/run-oidc-tests-azure.sh
- name: test-latest-server
tags:
- latest
Expand Down Expand Up @@ -3420,6 +3434,33 @@ task_groups:
- ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/delete-vm.sh
tasks:
- test-azurekms-task
- name: testazureoidc_task_group
setup_group:
- func: fetch source
- command: shell.exec
params:
shell: bash
script: |-
set -o errexit
${PREPARE_SHELL}
export AZUREOIDC_CLIENTID="${testazureoidc_clientid}"
export AZUREOIDC_TENANTID="${testazureoic_tenantid}"
export AZUREOIDC_SECRET="${testazureoidc_secret}"
export AZUREOIDC_KEYVAULT=${testazureoidc_keyvault}
export AZUREOIDC_DRIVERS_TOOLS="$DRIVERS_TOOLS"
export AZUREOIDC_VMNAME_PREFIX="NODE_DRIVER"
$DRIVERS_TOOLS/.evergreen/auth_oidc/azure/create-and-setup-vm.sh
teardown_group:
- command: shell.exec
params:
shell: bash
script: |-
${PREPARE_SHELL}
$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-latest
pre:
- func: fetch source
- func: windows fix
Expand Down Expand Up @@ -3999,6 +4040,12 @@ buildvariants:
tasks:
- test_azurekms_task_group
- test-azurekms-fail-task
- name: ubuntu20-test-azure-oidc
display_name: Azure OIDC
run_on: ubuntu2004-small
batchtime: 20160
tasks:
- testazureoidc_task_group
- name: rhel8-no-auth-tests
display_name: No Auth Tests
run_on: rhel80-large
Expand Down
9 changes: 9 additions & 0 deletions .evergreen/generate_evergreen_tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const {
WINDOWS_OS,
MACOS_OS,
UBUNTU_OS,
UBUNTU_20_OS,
DEBIAN_OS
} = require('./ci_matrix_constants');

Expand Down Expand Up @@ -754,6 +755,14 @@ BUILD_VARIANTS.push({
tasks: ['test_azurekms_task_group', 'test-azurekms-fail-task']
});

BUILD_VARIANTS.push({
name: 'ubuntu20-test-azure-oidc',
display_name: 'Azure OIDC',
run_on: UBUNTU_20_OS,
batchtime: 20160,
tasks: ['testazureoidc_task_group']
});

BUILD_VARIANTS.push({
name: 'rhel8-no-auth-tests',
display_name: 'No Auth Tests',
Expand Down
11 changes: 11 additions & 0 deletions .evergreen/run-oidc-tests-azure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -o xtrace # Write all commands first to stderr
set -o errexit # Exit the script with error if any of the commands fail

export AZUREOIDC_DRIVERS_TAR_FILE=/tmp/node-mongodb-native.tgz
tar czf $AZUREOIDC_DRIVERS_TAR_FILE .
export AZUREOIDC_TEST_CMD="source ./env.sh && PROVIDER_NAME=azure ./.evergreen/run-oidc-tests.sh"
export AZUREOIDC_CLIENTID=$AZUREOIDC_CLIENTID
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
export PROVIDER_NAME=$PROVIDER_NAME
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
30 changes: 25 additions & 5 deletions .evergreen/run-oidc-tests.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,34 @@
set -o errexit # Exit the script with error if any of the commands fail
set -o xtrace # Write all commands first to stderr

PROVIDER_NAME=${PROVIDER_NAME:-"aws"}
PROJECT_DIRECTORY=${PROJECT_DIRECTORY:-"."}
source "${PROJECT_DIRECTORY}/.evergreen/init-node-and-npm-env.sh"

MONGODB_URI=${MONGODB_URI:-"mongodb://127.0.0.1:27017"}
MONGODB_URI_SINGLE="${MONGODB_URI}/?authMechanism=MONGODB-OIDC&authMechanismProperties=DEVICE_NAME:aws"

echo $MONGODB_URI_SINGLE

export MONGODB_URI="$MONGODB_URI_SINGLE"
export OIDC_TOKEN_DIR=${OIDC_TOKEN_DIR}

npm run check:oidc
export MONGODB_URI=${MONGODB_URI:-"mongodb://localhost"}

if [ "$PROVIDER_NAME" = "aws" ]; then
export MONGODB_URI_SINGLE="${MONGODB_URI}/?authMechanism=MONGODB-OIDC"
export MONGODB_URI_MULTIPLE="${MONGODB_URI}:27018/?authMechanism=MONGODB-OIDC&directConnection=true"

if [ -z "${OIDC_TOKEN_DIR}" ]; then
echo "Must specify OIDC_TOKEN_DIR"
exit 1
fi
npm run check:oidc
elif [ "$PROVIDER_NAME" = "azure" ]; then
if [ -z "${AZUREOIDC_CLIENTID}" ]; then
echo "Must specify an AZUREOIDC_CLIENTID"
exit 1
fi
MONGODB_URI="${MONGODB_URI}/?authMechanism=MONGODB-OIDC"
MONGODB_URI="${MONGODB_URI}&authMechanismProperties=PROVIDER_NAME:azure"
export MONGODB_URI="${MONGODB_URI},TOKEN_AUDIENCE:api%3A%2F%2F${AZUREOIDC_CLIENTID}"
npm run check:oidc-azure
Copy link
Member Author

Choose a reason for hiding this comment

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

Azure needs to run its prose tests in a separate environment so they are split out.

Copy link
Contributor

Choose a reason for hiding this comment

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

With the GCP/Azure KMS, the tests live in the integration folder but only run on the specialized hosts/environment due to beforeEach skipping. It seems like we're approaching the specialized environment stuff in different ways, I don't feel strongly but should we do away with the manual/ dir (for this work) in favor of encoding things into mocha?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've moved to test/integration/auth and put the skip logic in now.

else
npm run check:oidc
fi
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"check:adl": "mocha --config test/mocha_mongodb.json test/manual/atlas-data-lake-testing",
"check:aws": "nyc mocha --config test/mocha_mongodb.json test/integration/auth/mongodb_aws.test.ts",
"check:oidc": "mocha --config test/mocha_mongodb.json test/manual/mongodb_oidc.prose.test.ts",
"check:oidc-azure": "mocha --config test/mocha_mongodb.json test/integration/auth/mongodb_oidc_azure.prose.test.ts",
"check:ocsp": "mocha --config test/manual/mocharc.json test/manual/ocsp_support.test.js",
"check:kerberos": "nyc mocha --config test/manual/mocharc.json test/manual/kerberos.test.ts",
"check:tls": "mocha --config test/manual/mocharc.json test/manual/tls_support.test.js",
Expand Down
24 changes: 21 additions & 3 deletions src/cmap/auth/mongo_credentials.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Resolves the default auth mechanism according to
// Resolves the default auth mechanism according to
import type { Document } from '../../bson';
import {
MongoAPIError,
MongoAzureError,
MongoInvalidArgumentError,
MongoMissingCredentialsError
} from '../../error';
Expand Down Expand Up @@ -30,6 +32,7 @@ function getDefaultAuthMechanism(hello?: Document): AuthMechanism {
return AuthMechanism.MONGODB_CR;
}

const ALLOWED_PROVIDER_NAMES: AuthMechanismProperties['PROVIDER_NAME'][] = ['aws', 'azure'];
const ALLOWED_HOSTS_ERROR = 'Auth mechanism property ALLOWED_HOSTS must be an array of strings.';

/** @internal */
Expand All @@ -42,6 +45,10 @@ export const DEFAULT_ALLOWED_HOSTS = [
'::1'
];

/** Error for when the token audience is missing in the environment. */
const TOKEN_AUDIENCE_MISSING_ERROR =
'TOKEN_AUDIENCE must be set in the auth mechanism properties when PROVIDER_NAME is azure.';

/** @public */
export interface AuthMechanismProperties extends Document {
SERVICE_HOST?: string;
Expand All @@ -54,9 +61,11 @@ export interface AuthMechanismProperties extends Document {
/** @experimental */
REFRESH_TOKEN_CALLBACK?: OIDCRefreshFunction;
/** @experimental */
PROVIDER_NAME?: 'aws';
PROVIDER_NAME?: 'aws' | 'azure';
/** @experimental */
ALLOWED_HOSTS?: string[];
/** @experimental */
TOKEN_AUDIENCE?: string;
}

/** @public */
Expand Down Expand Up @@ -176,12 +185,21 @@ export class MongoCredentials {
);
}

if (
this.mechanismProperties.PROVIDER_NAME === 'azure' &&
!this.mechanismProperties.TOKEN_AUDIENCE
) {
throw new MongoAzureError(TOKEN_AUDIENCE_MISSING_ERROR);
}

if (
this.mechanismProperties.PROVIDER_NAME &&
this.mechanismProperties.PROVIDER_NAME !== 'aws'
!ALLOWED_PROVIDER_NAMES.includes(this.mechanismProperties.PROVIDER_NAME)
) {
throw new MongoInvalidArgumentError(
`Currently only a PROVIDER_NAME of 'aws' is supported for mechanism '${this.mechanism}'.`
`Currently only a PROVIDER_NAME in ${ALLOWED_PROVIDER_NAMES.join(
','
)} is supported for mechanism '${this.mechanism}'.`
);
}

Expand Down
62 changes: 1 addition & 61 deletions src/cmap/auth/mongodb_aws.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import * as crypto from 'crypto';
import * as http from 'http';
import * as url from 'url';
import { promisify } from 'util';

import type { Binary, BSONSerializeOptions } from '../../bson';
Expand All @@ -12,7 +10,7 @@ import {
MongoMissingCredentialsError,
MongoRuntimeError
} from '../../error';
import { ByteUtils, maxWireVersion, ns } from '../../utils';
import { ByteUtils, maxWireVersion, ns, request } from '../../utils';
import { type AuthContext, AuthProvider } from './auth_provider';
import { MongoCredentials } from './mongo_credentials';
import { AuthMechanism } from './providers';
Expand Down Expand Up @@ -253,61 +251,3 @@ function deriveRegion(host: string) {

return parts[1];
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Moving this all out of the AWS module into utils as Azure will reuse this as well as GCP.

interface RequestOptions {
json?: boolean;
method?: string;
timeout?: number;
headers?: http.OutgoingHttpHeaders;
}

async function request(uri: string): Promise<Record<string, any>>;
async function request(
uri: string,
options?: { json?: true } & RequestOptions
): Promise<Record<string, any>>;
async function request(uri: string, options?: { json: false } & RequestOptions): Promise<string>;
async function request(
uri: string,
options: RequestOptions = {}
): Promise<string | Record<string, any>> {
return new Promise<string | Record<string, any>>((resolve, reject) => {
const requestOptions = {
method: 'GET',
timeout: 10000,
json: true,
...url.parse(uri),
...options
};

const req = http.request(requestOptions, res => {
res.setEncoding('utf8');

let data = '';
res.on('data', d => {
data += d;
});

res.once('end', () => {
if (options.json === false) {
resolve(data);
return;
}

try {
const parsed = JSON.parse(data);
resolve(parsed);
} catch {
// TODO(NODE-3483)
reject(new MongoRuntimeError(`Invalid JSON response: "${data}"`));
}
});
});

req.once('timeout', () =>
req.destroy(new MongoAWSError(`AWS request to ${uri} timed out after ${options.timeout} ms`))
);
req.once('error', error => reject(error));
req.end();
});
}
4 changes: 3 additions & 1 deletion src/cmap/auth/mongodb_oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Connection } from '../connection';
import { type AuthContext, AuthProvider } from './auth_provider';
import type { MongoCredentials } from './mongo_credentials';
import { AwsServiceWorkflow } from './mongodb_oidc/aws_service_workflow';
import { AzureServiceWorkflow } from './mongodb_oidc/azure_service_workflow';
import { CallbackWorkflow } from './mongodb_oidc/callback_workflow';

/** Error when credentials are missing. */
Expand Down Expand Up @@ -60,7 +61,7 @@ export type OIDCRefreshFunction = (
context: OIDCCallbackContext
) => Promise<IdPServerResponse>;

type ProviderName = 'aws' | 'callback';
type ProviderName = 'aws' | 'azure' | 'callback';

export interface Workflow {
/**
Expand All @@ -84,6 +85,7 @@ export interface Workflow {
export const OIDC_WORKFLOWS: Map<ProviderName, Workflow> = new Map();
OIDC_WORKFLOWS.set('callback', new CallbackWorkflow());
OIDC_WORKFLOWS.set('aws', new AwsServiceWorkflow());
OIDC_WORKFLOWS.set('azure', new AzureServiceWorkflow());

/**
* OIDC auth provider.
Expand Down
Loading