Skip to content

Commit 282e326

Browse files
committed
RUBY-3303 Add OIDC machine workflow auth
1 parent c7b5736 commit 282e326

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2333
-379
lines changed

.evergreen/config.yml

+160-1
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,48 @@ functions:
454454
455455
CRYPT_SHARED_LIB_PATH="${CRYPT_SHARED_LIB_PATH}" SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" FLE="${FLE}" SERVERLESS_MONGODB_VERSION="${SERVERLESS_MONGODB_VERSION}" .evergreen/run-tests-serverless.sh
456456
457+
"run oidc vm tests":
458+
- command: subprocess.exec
459+
type: test
460+
params:
461+
working_dir: src
462+
binary: bash
463+
env:
464+
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
465+
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
466+
RVM_RUBY: ${RVM_RUBY}
467+
TEST_SCRIPT: ${TEST_SCRIPT}
468+
args:
469+
- .evergreen/${RUN_SCRIPT}
470+
471+
"run oidc prose tests":
472+
- command: subprocess.exec
473+
type: test
474+
params:
475+
working_dir: src
476+
binary: bash
477+
env:
478+
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
479+
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
480+
ENVIRONMENT: ${ENVIRONMENT}
481+
RVM_RUBY: ${RVM_RUBY}
482+
args:
483+
- .evergreen/run-tests-oidc-prose.sh
484+
485+
"run oidc unified tests":
486+
- command: subprocess.exec
487+
type: test
488+
params:
489+
working_dir: src
490+
binary: bash
491+
env:
492+
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
493+
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
494+
ENVIRONMENT: ${ENVIRONMENT}
495+
RVM_RUBY: ${RVM_RUBY}
496+
args:
497+
- .evergreen/run-tests-oidc-unified.sh
498+
457499
pre:
458500
- func: "fetch source"
459501
- func: "create expansions"
@@ -721,6 +763,77 @@ task_groups:
721763
tasks:
722764
- testazurekms-task
723765

766+
- name: test_oidc_task_group
767+
setup_group:
768+
- func: fetch source
769+
- func: create expansions
770+
- command: ec2.assume_role
771+
params:
772+
role_arn: ${aws_test_secrets_role}
773+
- command: subprocess.exec
774+
params:
775+
binary: bash
776+
include_expansions_in_env:
777+
- AWS_ACCESS_KEY_ID
778+
- AWS_SECRET_ACCESS_KEY
779+
- AWS_SESSION_TOKEN
780+
env:
781+
MONGODB_VERSION: '8.0'
782+
args:
783+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/setup.sh
784+
setup_group_can_fail_task: true
785+
setup_group_timeout_secs: 1800
786+
tasks:
787+
- oidc-auth-test-latest
788+
789+
- name: test_oidc_azure_task_group
790+
setup_group:
791+
- func: fetch source
792+
- func: create expansions
793+
- command: shell.exec
794+
params:
795+
shell: bash
796+
script: |-
797+
set -o errexit
798+
${PREPARE_SHELL}
799+
export AZUREOIDC_VMNAME_PREFIX="RUBY_DRIVER"
800+
$DRIVERS_TOOLS/.evergreen/auth_oidc/azure/setup.sh
801+
teardown_task:
802+
- command: shell.exec
803+
params:
804+
shell: bash
805+
script: |-
806+
${PREPARE_SHELL}
807+
$DRIVERS_TOOLS/.evergreen/auth_oidc/azure/teardown.sh
808+
setup_group_can_fail_task: true
809+
setup_group_timeout_secs: 1800
810+
tasks:
811+
- oidc-auth-test-azure-latest
812+
813+
- name: test_oidc_gcp_task_group
814+
setup_group:
815+
- func: fetch source
816+
- func: create expansions
817+
- command: shell.exec
818+
params:
819+
shell: bash
820+
script: |-
821+
set -o errexit
822+
${PREPARE_SHELL}
823+
export GCPOIDC_VMNAME_PREFIX="RUBY_DRIVER"
824+
$DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/setup.sh
825+
teardown_task:
826+
- command: shell.exec
827+
params:
828+
shell: bash
829+
script: |-
830+
${PREPARE_SHELL}
831+
$DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/teardown.sh
832+
setup_group_can_fail_task: true
833+
setup_group_timeout_secs: 1800
834+
tasks:
835+
- oidc-auth-test-gcp-latest
836+
724837
tasks:
725838
- name: "test-atlas"
726839
commands:
@@ -865,8 +978,41 @@ tasks:
865978
LAMBDA_STACK_NAME: "dbx-ruby-lambda"
866979
RVM_RUBY: ruby-3.2
867980
MONGODB_URI: ${MONGODB_URI}
868-
axes:
869981

982+
- name: oidc-auth-test-latest
983+
commands:
984+
- func: "run oidc vm tests"
985+
vars:
986+
TEST_SCRIPT: run-tests-oidc-prose.sh
987+
RUN_SCRIPT: run-tests-oidc-test.sh
988+
- func: "run oidc vm tests"
989+
vars:
990+
TEST_SCRIPT: run-tests-oidc-unified.sh
991+
RUN_SCRIPT: run-tests-oidc-test.sh
992+
993+
- name: oidc-auth-test-azure-latest
994+
commands:
995+
- func: "run oidc vm tests"
996+
vars:
997+
TEST_SCRIPT: run-tests-oidc-prose.sh
998+
RUN_SCRIPT: run-tests-oidc-azure.sh
999+
- func: "run oidc vm tests"
1000+
vars:
1001+
TEST_SCRIPT: run-tests-oidc-unified.sh
1002+
RUN_SCRIPT: run-tests-oidc-azure.sh
1003+
1004+
- name: oidc-auth-test-gcp-latest
1005+
commands:
1006+
- func: "run oidc vm tests"
1007+
vars:
1008+
TEST_SCRIPT: run-tests-oidc-prose.sh
1009+
RUN_SCRIPT: run-tests-oidc-gcp.sh
1010+
- func: "run oidc vm tests"
1011+
vars:
1012+
TEST_SCRIPT: run-tests-oidc-unified.sh
1013+
RUN_SCRIPT: run-tests-oidc-gcp.sh
1014+
1015+
axes:
8701016
- id: preload
8711017
display_name: Preload server
8721018
values:
@@ -1856,3 +2002,16 @@ buildvariants:
18562002
display_name: "AWS Lambda"
18572003
tasks:
18582004
- name: test_aws_lambda_task_group
2005+
2006+
- matrix_name: test-oidc-variant
2007+
matrix_spec:
2008+
ruby: "ruby-3.2"
2009+
fle: helper
2010+
topology: standalone
2011+
os: ubuntu2204
2012+
mongodb-version: latest
2013+
display_name: "OIDC auth tests: latest ruby-3.2"
2014+
tasks:
2015+
- test_oidc_task_group
2016+
- test_oidc_azure_task_group
2017+
- test_oidc_gcp_task_group

.evergreen/run-tests-oidc-azure.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
set -o xtrace # Write all commands first to stderr
3+
set -o errexit # Exit the script with error if any of the commands fail
4+
5+
export AZUREOIDC_DRIVERS_TAR_FILE=/tmp/mongo-ruby-driver.tgz
6+
tar czf $AZUREOIDC_DRIVERS_TAR_FILE .
7+
export AZUREOIDC_TEST_CMD="source ./env.sh && ENVIRONMENT=azure RVM_RUBY=${RVM_RUBY} ./.evergreen/${TEST_SCRIPT}"
8+
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
9+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh

.evergreen/run-tests-oidc-gcp.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
set -o xtrace # Write all commands first to stderr
3+
set -o errexit # Exit the script with error if any of the commands fail
4+
5+
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-ruby-driver.tgz
6+
tar czf $GCPOIDC_DRIVERS_TAR_FILE .
7+
export GCPOIDC_TEST_CMD="source ./secrets-export.sh drivers/gcpoidc && ENVIRONMENT=gcp RVM_RUBY=${RVM_RUBY} ./.evergreen/${TEST_SCRIPT}"
8+
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
9+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh

.evergreen/run-tests-oidc-prose.sh

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
3+
set -ex
4+
5+
ENVIRONMENT=${ENVIRONMENT:-"test"}
6+
7+
. `dirname "$0"`/../spec/shared/shlib/distro.sh
8+
. `dirname "$0"`/../spec/shared/shlib/set_env.sh
9+
. `dirname "$0"`/functions.sh
10+
11+
set_env_vars
12+
set_env_python
13+
set_env_ruby
14+
15+
sudo apt-get -y install libyaml-dev cmake
16+
17+
bundle_install
18+
bundle exec rspec -fd spec/integration/oidc/${ENVIRONMENT}_machine_auth_flow_prose_spec.rb
19+
20+
test_status=$?
21+
22+
kill_jruby
23+
24+
exit ${test_status}

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

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
set -o xtrace # Write all commands first to stderr
3+
set -o errexit # Exit the script with error if any of the commands fail
4+
5+
source $DRIVERS_TOOLS/.evergreen/auth_oidc/secrets-export.sh
6+
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
7+
export ENVIRONMENT=$ENVIRONMENT
8+
export AWS_WEB_IDENTITY_TOKEN_FILE=$OIDC_TOKEN_FILE
9+
bash ./.evergreen/${TEST_SCRIPT}

.evergreen/run-tests-oidc-unified.sh

Whitespace-only changes.

.mod/drivers-evergreen-tools

lib/mongo/auth.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
require 'mongo/auth/cr'
2828
require 'mongo/auth/gssapi'
2929
require 'mongo/auth/ldap'
30+
require 'mongo/auth/oidc'
3031
require 'mongo/auth/scram'
3132
require 'mongo/auth/scram256'
3233
require 'mongo/auth/x509'
@@ -70,6 +71,7 @@ module Auth
7071
aws: Aws,
7172
gssapi: Gssapi,
7273
mongodb_cr: CR,
74+
mongodb_oidc: Oidc,
7375
mongodb_x509: X509,
7476
plain: LDAP,
7577
scram: Scram,
@@ -89,7 +91,7 @@ module Auth
8991
# value of speculativeAuthenticate field of hello response of
9092
# the handshake on the specified connection.
9193
#
92-
# @return [ Auth::Aws | Auth::CR | Auth::Gssapi | Auth::LDAP |
94+
# @return [ Auth::Aws | Auth::CR | Auth::Gssapi | Auth::LDAP | Auth::Oidc
9395
# Auth::Scram | Auth::Scram256 | Auth::X509 ] The authenticator.
9496
#
9597
# @since 2.0.0

lib/mongo/auth/oidc.rb

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# frozen_string_literal: true
2+
# rubocop:todo all
3+
4+
# Copyright (C) 2024 MongoDB, Inc.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
module Mongo
19+
module Auth
20+
21+
# Defines behavior for OIDC authentication.
22+
#
23+
# @api private
24+
class Oidc < Base
25+
attr_reader :speculative_auth_result, :cache, :machine_workflow
26+
27+
# The authentication mechanism string.
28+
#
29+
# @since 2.20.0
30+
MECHANISM = 'MONGODB-OIDC'.freeze
31+
32+
# Initializes the OIDC authenticator.
33+
#
34+
# @param [ Auth::User ] user The user to authenticate.
35+
# @param [ Mongo::Connection ] connection The connection to authenticate over.
36+
#
37+
# @option opts [ BSON::Document | nil ] speculative_auth_result The
38+
# value of speculativeAuthenticate field of hello response of
39+
# the handshake on the specified connection.
40+
def initialize(user, connection, **opts)
41+
super
42+
@cache = TokenCache.new
43+
@speculative_auth_result = opts[:speculative_auth_result]
44+
@machine_workflow = MachineWorkflow::new(
45+
auth_mech_properties: user.auth_mech_properties,
46+
username: user.name
47+
)
48+
end
49+
50+
# Log the user in on the current connection.
51+
#
52+
# @return [ BSON::Document ] The document of the authentication response.
53+
def login
54+
execute_workflow(connection: connection, conversation: conversation)
55+
end
56+
57+
private
58+
59+
def execute_workflow(connection:, conversation:)
60+
# If there is a cached access token, try to authenticate with it. If
61+
# authentication fails with an Authentication error (18),
62+
# invalidate the access token, fetch a new access token, and try
63+
# to authenticate again.
64+
# If the server fails for any other reason, do not clear the cache.
65+
if cache.access_token?
66+
token = cache.access_token
67+
msg = conversation.start(connection: connection, token: token)
68+
begin
69+
dispatch_msg(connection, conversation, msg)
70+
rescue AuthError => error
71+
cache.invalidate(token: token)
72+
execute_workflow(connection: connection, conversation: conversation)
73+
end
74+
end
75+
# This is the normal flow when no token is in the cache. Execute the
76+
# machine callback to get the token, put it in the caches, and then
77+
# send the saslStart to the server.
78+
token = machine_workflow.execute
79+
if token.nil? || !token[:access_token]
80+
raise Error::OidcError,
81+
"OIDC machine workflows must return a valid response with an access token but #{token} was returned"
82+
end
83+
cache.access_token = token[:access_token]
84+
connection.access_token = token[:access_token]
85+
msg = conversation.start(connection: connection, token: token[:access_token])
86+
dispatch_msg(connection, conversation, msg)
87+
end
88+
end
89+
end
90+
end
91+
92+
require 'mongo/auth/oidc/conversation'
93+
require 'mongo/auth/oidc/machine_workflow'
94+
require 'mongo/auth/oidc/token_cache'

0 commit comments

Comments
 (0)