summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2022-06-08 22:22:08 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-06-15 17:51:25 +0000
commitd762bb7bc5e99c387fe16468c562132de24c5a45 (patch)
tree7c84a777cf15c3d90a6e2ed5c4010496a222efa7 /src/mongo
parent631ca9943ca90732a0bd7ff8454a13fc78948c10 (diff)
downloadmongo-d762bb7bc5e99c387fe16468c562132de24c5a45.tar.gz
SERVER-67148 Refactor ValidatedTenantId into ValidatedSecurityToken
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/client/async_client.cpp4
-rw-r--r--src/mongo/client/dbclient_base.cpp6
-rw-r--r--src/mongo/db/SConscript2
-rw-r--r--src/mongo/db/auth/SConscript4
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp7
-rw-r--r--src/mongo/db/auth/security_token_authentication_guard.cpp (renamed from src/mongo/db/auth/security_token.h)65
-rw-r--r--src/mongo/db/auth/security_token_authentication_guard.h (renamed from src/mongo/db/validated_tenant_id.h)38
-rw-r--r--src/mongo/db/auth/validated_tenancy_scope.cpp (renamed from src/mongo/db/auth/security_token.cpp)169
-rw-r--r--src/mongo/db/auth/validated_tenancy_scope.h116
-rw-r--r--src/mongo/db/auth/validated_tenancy_scope_test.cpp (renamed from src/mongo/db/validated_tenant_id_test.cpp)97
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp1
-rw-r--r--src/mongo/db/commands.cpp3
-rw-r--r--src/mongo/db/multitenancy.cpp33
-rw-r--r--src/mongo/db/multitenancy.h5
-rw-r--r--src/mongo/db/service_entry_point_common.cpp18
-rw-r--r--src/mongo/db/validated_tenant_id.cpp77
-rw-r--r--src/mongo/executor/network_interface_tl.cpp6
-rw-r--r--src/mongo/executor/remote_command_request.h3
-rw-r--r--src/mongo/logv2/logv2_test.cpp2
-rw-r--r--src/mongo/rpc/factory.cpp6
-rw-r--r--src/mongo/rpc/metadata.cpp5
-rw-r--r--src/mongo/rpc/metadata/security_token_metadata_test.cpp25
-rw-r--r--src/mongo/rpc/op_msg.cpp33
-rw-r--r--src/mongo/rpc/op_msg.h16
-rw-r--r--src/mongo/rpc/op_msg_test.cpp23
-rw-r--r--src/mongo/s/commands/cluster_explain_cmd.cpp2
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp9
-rw-r--r--src/mongo/shell/shell_utils.cpp7
28 files changed, 410 insertions, 372 deletions
diff --git a/src/mongo/client/async_client.cpp b/src/mongo/client/async_client.cpp
index 3a568fa7e31..568b93b7e81 100644
--- a/src/mongo/client/async_client.cpp
+++ b/src/mongo/client/async_client.cpp
@@ -319,7 +319,7 @@ Future<executor::RemoteCommandResponse> AsyncDBClient::runCommandRequest(
auto startTimer = Timer();
auto opMsgRequest = OpMsgRequest::fromDBAndBody(
std::move(request.dbname), std::move(request.cmdObj), std::move(request.metadata));
- opMsgRequest.securityToken = request.securityToken;
+ opMsgRequest.validatedTenancyScope = request.validatedTenancyScope;
return runCommand(std::move(opMsgRequest), baton, request.options.fireAndForget)
.then([this, startTimer = std::move(startTimer)](rpc::UniqueReply response) {
return executor::RemoteCommandResponse(*response, startTimer.elapsed());
@@ -358,7 +358,7 @@ Future<executor::RemoteCommandResponse> AsyncDBClient::beginExhaustCommandReques
executor::RemoteCommandRequest request, const BatonHandle& baton) {
auto opMsgRequest = OpMsgRequest::fromDBAndBody(
std::move(request.dbname), std::move(request.cmdObj), std::move(request.metadata));
- opMsgRequest.securityToken = request.securityToken;
+ opMsgRequest.validatedTenancyScope = request.validatedTenancyScope;
return runExhaustCommand(std::move(opMsgRequest), baton);
}
diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp
index cbef18c6867..5c2238ebec9 100644
--- a/src/mongo/client/dbclient_base.cpp
+++ b/src/mongo/client/dbclient_base.cpp
@@ -49,7 +49,7 @@
#include "mongo/client/dbclient_cursor.h"
#include "mongo/config.h"
#include "mongo/db/api_parameters_gen.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/commands.h"
#include "mongo/db/json.h"
#include "mongo/db/namespace_string.h"
@@ -190,8 +190,8 @@ void appendMetadata(OperationContext* opCtx,
request.body = bob.obj();
if (opCtx) {
- if (auto securityToken = auth::getSecurityToken(opCtx)) {
- request.securityToken = securityToken->toBSON();
+ if (auto validatedTenancyScope = auth::ValidatedTenancyScope::get(opCtx)) {
+ request.validatedTenancyScope = validatedTenancyScope;
}
}
}
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 923e64998cb..40a8c0687f9 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -84,7 +84,6 @@ env.Library(
target='multitenancy',
source=[
'multitenancy.cpp',
- 'validated_tenant_id.cpp',
],
LIBDEPS=[
'multitenancy_params',
@@ -2746,7 +2745,6 @@ if wiredtiger:
'ttl_test.cpp',
'update_index_data_test.cpp',
'user_write_block_mode_op_observer_test.cpp',
- 'validated_tenant_id_test.cpp',
'vector_clock_mongod_test.cpp',
'vector_clock_test.cpp',
'wire_version_test.cpp',
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 3df9d6922f2..0f4dcbc61bb 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -7,8 +7,9 @@ env = env.Clone()
env.Library(
target='security_token',
source=[
- 'security_token.cpp',
+ 'security_token_authentication_guard.cpp',
'security_token.idl',
+ 'validated_tenancy_scope.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
@@ -540,6 +541,7 @@ env.CppUnitTest(
'sasl_scram_test.cpp',
'security_key_test.cpp',
'user_document_parser_test.cpp',
+ 'validated_tenancy_scope_test.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index 173a19cfd58..5ebc878a07c 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -43,7 +43,7 @@
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authz_session_external_state.h"
#include "mongo/db/auth/privilege.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/bson/dotted_path_support.h"
#include "mongo/db/client.h"
#include "mongo/db/namespace_string.h"
@@ -245,14 +245,15 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
stdx::lock_guard<Client> lk(*opCtx->getClient());
- if (auto token = auth::getSecurityToken(opCtx)) {
+ auto validatedTenancyScope = auth::ValidatedTenancyScope::get(opCtx);
+ if (validatedTenancyScope && validatedTenancyScope->hasAuthenticatedUser()) {
uassert(
6161501,
"Attempt to authorize via security token on connection with established authentication",
_authenticationMode != AuthenticationMode::kConnection);
uassert(6161502,
"Attempt to authorize a user other than that present in the security token",
- token->getAuthenticatedUser() == userName);
+ validatedTenancyScope->authenticatedUser() == userName);
validateSecurityTokenUserPrivileges(user->getPrivileges());
_authenticationMode = AuthenticationMode::kSecurityToken;
} else {
diff --git a/src/mongo/db/auth/security_token.h b/src/mongo/db/auth/security_token_authentication_guard.cpp
index 315e6efcbf4..5be6de3dc75 100644
--- a/src/mongo/db/auth/security_token.h
+++ b/src/mongo/db/auth/security_token_authentication_guard.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2021-present MongoDB, Inc.
+ * Copyright (C) 2022-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
@@ -27,51 +27,40 @@
* it in the license file.
*/
-#pragma once
-#include <boost/optional.hpp>
+#include "mongo/db/auth/security_token_authentication_guard.h"
-#include "mongo/bson/bsonobj.h"
-#include "mongo/db/auth/security_token_gen.h"
-#include "mongo/db/client.h"
-#include "mongo/db/operation_context.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/logv2/log.h"
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kAccessControl
namespace mongo {
namespace auth {
-class SecurityTokenAuthenticationGuard {
-public:
- SecurityTokenAuthenticationGuard() = delete;
- SecurityTokenAuthenticationGuard(OperationContext* opCtx);
- ~SecurityTokenAuthenticationGuard();
-
-private:
- Client* _client;
-};
-
-/**
- * Takes an unsigned security token as input and applies
- * the temporary signature algorithm to extend it into a full SecurityToken.
- */
-BSONObj signSecurityToken(BSONObj obj);
-
-/**
- * Verify the contents of the provided security token
- * using the temporary signing algorithm,
- */
-SecurityToken verifySecurityToken(BSONObj obj);
+SecurityTokenAuthenticationGuard::SecurityTokenAuthenticationGuard(
+ OperationContext* opCtx, const ValidatedTenancyScope& token) {
+ if (token.hasAuthenticatedUser()) {
+ const auto& userName = token.authenticatedUser();
+ auto* client = opCtx->getClient();
+ uassertStatusOK(AuthorizationSession::get(client)->addAndAuthorizeUser(opCtx, userName));
+ _client = client;
-/**
- * Parse the validated SecurityToken from the OpMsg and place it as a decoration
- * on OperationContext.
- */
-void setSecurityToken(OperationContext* opCtx, const OpMsg& opMsg);
+ LOGV2_DEBUG(5838100,
+ 4,
+ "Authenticated with security token",
+ "token"_attr = token.getOriginalToken());
+ } else {
+ _client = nullptr;
+ }
+}
-/**
- * Retrieve the Security Token associated with this operation context
- */
-using MaybeSecurityToken = boost::optional<SecurityToken>;
-MaybeSecurityToken getSecurityToken(OperationContext* opCtx);
+SecurityTokenAuthenticationGuard::~SecurityTokenAuthenticationGuard() {
+ if (_client) {
+ // SecurityToken based users are "logged out" at the end of their request.
+ AuthorizationSession::get(_client)->logoutSecurityTokenUser(_client);
+ }
+}
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/validated_tenant_id.h b/src/mongo/db/auth/security_token_authentication_guard.h
index d5843de05f9..c73e0324e5f 100644
--- a/src/mongo/db/validated_tenant_id.h
+++ b/src/mongo/db/auth/security_token_authentication_guard.h
@@ -29,35 +29,27 @@
#pragma once
-#include "mongo/db/database_name.h"
-#include "mongo/db/tenant_id.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
+#include "mongo/db/client.h"
+#include "mongo/db/operation_context.h"
namespace mongo {
+namespace auth {
-class Client;
-struct OpMsg;
-
-class ValidatedTenantId {
+/**
+ * If ValidatedTenancyScope represents an AuthenticatedUser,
+ * that user will be authenticated against the client until this guard dies.
+ * This is used in ServiceEntryPoint to scope authentication to a single operation.
+ */
+class SecurityTokenAuthenticationGuard {
public:
- ValidatedTenantId(const ValidatedTenantId& validatedTenant) = default;
-
- /**
- * Constructs a ValidatedTenantId by parsing tenant from $tenant or security token of opMsg
- * and validating it with the auth module.
- */
- ValidatedTenantId(const OpMsg& opMsg, Client& client);
-
- /**
- * Constructs a ValidatedTenantId by treating the tenantId on DatabaseName as validated.
- */
- ValidatedTenantId(const DatabaseName& dbName);
-
- const boost::optional<TenantId>& tenantId() const {
- return _tenant;
- }
+ SecurityTokenAuthenticationGuard() = delete;
+ SecurityTokenAuthenticationGuard(OperationContext*, const ValidatedTenancyScope&);
+ ~SecurityTokenAuthenticationGuard();
private:
- boost::optional<TenantId> _tenant = boost::none;
+ Client* _client;
};
+} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/auth/security_token.cpp b/src/mongo/db/auth/validated_tenancy_scope.cpp
index f20cffe04c2..2ab66b3abd3 100644
--- a/src/mongo/db/auth/security_token.cpp
+++ b/src/mongo/db/auth/validated_tenancy_scope.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2021-present MongoDB, Inc.
+ * Copyright (C) 2022-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
@@ -27,26 +27,23 @@
* it in the license file.
*/
-
-#include "mongo/db/auth/security_token.h"
-
-#include <boost/optional.hpp>
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/base/init.h"
#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/security_token_gen.h"
+#include "mongo/db/multitenancy.h"
#include "mongo/db/multitenancy_gen.h"
#include "mongo/db/server_feature_flags_gen.h"
-#include "mongo/db/tenant_id.h"
#include "mongo/logv2/log.h"
#include "mongo/logv2/log_detail.h"
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kAccessControl
-
-namespace mongo {
-namespace auth {
+namespace mongo::auth {
namespace {
-const auto securityTokenDecoration = OperationContext::declareDecoration<MaybeSecurityToken>();
+const auto validatedTenancyScopeDecoration =
+ OperationContext::declareDecoration<boost::optional<ValidatedTenancyScope>>();
MONGO_INITIALIZER(SecurityTokenOptionValidate)(InitializerContext*) {
uassert(ErrorCodes::BadValue,
"multitenancySupport may not be specified if featureFlagMongoStore is not enabled",
@@ -54,15 +51,13 @@ MONGO_INITIALIZER(SecurityTokenOptionValidate)(InitializerContext*) {
if (gMultitenancySupport) {
logv2::detail::setGetTenantIDCallback([]() -> boost::optional<TenantId> {
auto* client = Client::getCurrent();
- if (!client)
+ if (!client) {
return boost::none;
+ }
if (auto* opCtx = client->getOperationContext()) {
- auto token = getSecurityToken(opCtx);
- if (token) {
- return token->getAuthenticatedUser().getTenant();
- } else {
- return boost::none;
+ if (auto token = ValidatedTenancyScope::get(opCtx)) {
+ return token->tenantId();
}
}
@@ -72,45 +67,10 @@ MONGO_INITIALIZER(SecurityTokenOptionValidate)(InitializerContext*) {
}
} // namespace
-SecurityTokenAuthenticationGuard::SecurityTokenAuthenticationGuard(OperationContext* opCtx) {
- auto token = getSecurityToken(opCtx);
- if (token == boost::none) {
- _client = nullptr;
- return;
- }
-
- auto client = opCtx->getClient();
- uassertStatusOK(AuthorizationSession::get(client)->addAndAuthorizeUser(
- opCtx, token->getAuthenticatedUser()));
- _client = client;
-}
-
-SecurityTokenAuthenticationGuard::~SecurityTokenAuthenticationGuard() {
- if (_client) {
- // SecurityToken based users are "logged out" at the end of their request.
- AuthorizationSession::get(_client)->logoutSecurityTokenUser(_client);
- }
-}
-
-BSONObj signSecurityToken(BSONObj obj) {
- auto authUserElem = obj[SecurityToken::kAuthenticatedUserFieldName];
- uassert(ErrorCodes::BadValue,
- "Invalid field(s) in token being signed",
- (authUserElem.type() == Object) && (obj.nFields() == 1));
-
- auto authUserObj = authUserElem.Obj();
- ConstDataRange authUserCDR(authUserObj.objdata(), authUserObj.objsize());
-
- // Placeholder algorithm.
- auto sig = SHA256Block::computeHash({authUserCDR});
-
- BSONObjBuilder signedToken(obj);
- signedToken.appendBinData(SecurityToken::kSigFieldName, sig.size(), BinDataGeneral, sig.data());
- return signedToken.obj();
-}
-
-SecurityToken verifySecurityToken(BSONObj obj) {
- uassert(ErrorCodes::BadValue, "Multitenancy not enabled", gMultitenancySupport);
+ValidatedTenancyScope::ValidatedTenancyScope(BSONObj obj, InitTag tag) : _originalToken(obj) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Multitenancy not enabled, refusing to accept securityToken",
+ gMultitenancySupport || (tag == InitTag::kInitForShell));
auto token = SecurityToken::parse({"Security Token"}, obj);
auto authenticatedUser = token.getAuthenticatedUser();
@@ -126,22 +86,97 @@ SecurityToken verifySecurityToken(BSONObj obj) {
auto computed = SHA256Block::computeHash({authUserCDR});
uassert(ErrorCodes::Unauthorized, "Token signature invalid", computed == token.getSig());
- return token;
+
+ _tenantOrUser = std::move(authenticatedUser);
}
-void setSecurityToken(OperationContext* opCtx, const OpMsg& opMsg) {
- if (opMsg.validatedTenant && opMsg.securityToken.nFields() > 0) {
- // Use the security token directly as it has been validated by ValdiatedTenantId
- // constructor.
- securityTokenDecoration(opCtx) =
- SecurityToken::parse({"Security Token"}, opMsg.securityToken);
- LOGV2_DEBUG(5838100, 4, "Accepted security token", "token"_attr = opMsg.securityToken);
+ValidatedTenancyScope::ValidatedTenancyScope(Client* client, TenantId tenant)
+ : _tenantOrUser(std::move(tenant)) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Multitenancy not enabled, refusing to accept $tenant parameter",
+ gMultitenancySupport);
+
+ uassert(ErrorCodes::Unauthorized,
+ "'$tenant' may only be specified with the useTenant action type",
+ client &&
+ AuthorizationSession::get(client)->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(), ActionType::useTenant));
+}
+
+boost::optional<ValidatedTenancyScope> ValidatedTenancyScope::create(Client* client,
+ BSONObj body,
+ BSONObj securityToken) {
+ if (!gMultitenancySupport) {
+ return boost::none;
+ }
+
+ auto dollarTenantElem = body["$tenant"_sd];
+ const bool hasToken = securityToken.nFields() > 0;
+
+ uassert(6545800,
+ "Cannot pass $tenant id if also passing securityToken",
+ dollarTenantElem.eoo() || !hasToken);
+ uassert(ErrorCodes::OperationFailed,
+ "Cannot process $tenant id when no client is available",
+ dollarTenantElem.eoo() || client);
+
+ // TODO SERVER-66822: Re-enable this uassert.
+ // uassert(ErrorCodes::Unauthorized,
+ // "Multitenancy is enabled, $tenant id or securityToken is required.",
+ // dollarTenantElem || opMsg.securityToken.nFields() > 0);
+
+ if (dollarTenantElem) {
+ return ValidatedTenancyScope(client, TenantId::parseFromBSON(dollarTenantElem));
+ } else if (hasToken) {
+ return ValidatedTenancyScope(securityToken);
+ } else {
+ return boost::none;
+ }
+}
+
+bool ValidatedTenancyScope::hasAuthenticatedUser() const {
+ return stdx::holds_alternative<UserName>(_tenantOrUser);
+}
+
+const UserName& ValidatedTenancyScope::authenticatedUser() const {
+ invariant(hasAuthenticatedUser());
+ return stdx::get<UserName>(_tenantOrUser);
+}
+
+const TenantId& ValidatedTenancyScope::tenantId() const {
+ if (hasAuthenticatedUser()) {
+ return stdx::get<UserName>(_tenantOrUser).getTenant().get();
+ } else {
+ invariant(stdx::holds_alternative<TenantId>(_tenantOrUser));
+ return stdx::get<TenantId>(_tenantOrUser);
}
}
-MaybeSecurityToken getSecurityToken(OperationContext* opCtx) {
- return securityTokenDecoration(opCtx);
+const boost::optional<ValidatedTenancyScope>& ValidatedTenancyScope::get(OperationContext* opCtx) {
+ return validatedTenancyScopeDecoration(opCtx);
+}
+
+void ValidatedTenancyScope::set(OperationContext* opCtx,
+ boost::optional<ValidatedTenancyScope> token) {
+ validatedTenancyScopeDecoration(opCtx) = std::move(token);
+}
+
+ValidatedTenancyScope::ValidatedTenancyScope(BSONObj obj, TokenForTestingTag) {
+ auto authUserElem = obj[SecurityToken::kAuthenticatedUserFieldName];
+ uassert(ErrorCodes::BadValue,
+ "Invalid field(s) in token being signed",
+ (authUserElem.type() == Object) && (obj.nFields() == 1));
+
+ auto authUserObj = authUserElem.Obj();
+ ConstDataRange authUserCDR(authUserObj.objdata(), authUserObj.objsize());
+
+ // Placeholder algorithm.
+ auto sig = SHA256Block::computeHash({authUserCDR});
+
+ BSONObjBuilder signedToken(obj);
+ signedToken.appendBinData(SecurityToken::kSigFieldName, sig.size(), BinDataGeneral, sig.data());
+ _originalToken = signedToken.obj();
+ _tenantOrUser = UserName::parseFromBSONObj(authUserObj);
}
-} // namespace auth
-} // namespace mongo
+} // namespace mongo::auth
diff --git a/src/mongo/db/auth/validated_tenancy_scope.h b/src/mongo/db/auth/validated_tenancy_scope.h
new file mode 100644
index 00000000000..302b3fdac5a
--- /dev/null
+++ b/src/mongo/db/auth/validated_tenancy_scope.h
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/auth/user_name.h"
+#include "mongo/db/tenant_id.h"
+#include "mongo/stdx/variant.h"
+
+namespace mongo {
+
+class Client;
+class OperationContext;
+
+namespace auth {
+
+class ValidatedTenancyScope {
+public:
+ ValidatedTenancyScope() = delete;
+ ValidatedTenancyScope(const ValidatedTenancyScope&) = default;
+
+ // kInitForShell allows parsing a securityToken without multitenancy enabled.
+ // This is required in the shell since we do not enable this setting in non-servers.
+ enum class InitTag {
+ kNormal,
+ kInitForShell,
+ };
+
+ /**
+ * Constructs a ValidatedTenancyScope by parsing a SecurityToken from a BSON object
+ * and verifying its cryptographic signature.
+ */
+ explicit ValidatedTenancyScope(BSONObj securityToken, InitTag tag = InitTag::kNormal);
+
+ /**
+ * Constructs a ValidatedTenancyScope for tenant only by validating that the
+ * current client is permitted to specify a tenant via the $tenant field.
+ */
+ ValidatedTenancyScope(Client* client, TenantId tenant);
+
+ /**
+ * Parses the client provided command body and securityToken for tenantId,
+ * and for securityToken respectively, the authenticatedUser as well.
+ *
+ * Returns boost::none when multitenancy support is not enabled.
+ */
+ static boost::optional<ValidatedTenancyScope> create(Client* client,
+ BSONObj body,
+ BSONObj securityToken);
+
+ bool hasAuthenticatedUser() const;
+ const UserName& authenticatedUser() const;
+ const TenantId& tenantId() const;
+
+ BSONObj getOriginalToken() const {
+ return _originalToken;
+ }
+
+ /**
+ * Get/Set a ValidatedTenancyScope as a decoration on the OperationContext
+ */
+ static const boost::optional<ValidatedTenancyScope>& get(OperationContext* opCtx);
+ static void set(OperationContext* opCtx, boost::optional<ValidatedTenancyScope> token);
+
+ /**
+ * Transitional token generator, do not use outside of test code.
+ */
+ struct TokenForTestingTag {};
+ explicit ValidatedTenancyScope(BSONObj token, TokenForTestingTag);
+
+ /**
+ * Backdoor API for use by FLE Query Analysis to setup a validated tenant without a security
+ * context.
+ */
+ struct TrustedFLEQueryAnalysisTag {};
+ explicit ValidatedTenancyScope(TenantId tenant, TrustedFLEQueryAnalysisTag)
+ : _tenantOrUser(std::move(tenant)) {}
+
+private:
+ // Preserve original token for serializing from MongoQ.
+ BSONObj _originalToken;
+
+ stdx::variant<UserName, TenantId> _tenantOrUser;
+};
+
+} // namespace auth
+} // namespace mongo
diff --git a/src/mongo/db/validated_tenant_id_test.cpp b/src/mongo/db/auth/validated_tenancy_scope_test.cpp
index b897a9cc927..f1942f757a6 100644
--- a/src/mongo/db/validated_tenant_id_test.cpp
+++ b/src/mongo/db/auth/validated_tenancy_scope_test.cpp
@@ -27,27 +27,17 @@
* it in the license file.
*/
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-
-#include "mongo/rpc/op_msg_test.h"
-
#include "mongo/platform/basic.h"
-#include "mongo/base/static_assert.h"
-#include "mongo/bson/json.h"
#include "mongo/db/auth/authorization_manager_impl.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/authorization_session_impl.h"
#include "mongo/db/auth/authz_manager_external_state_mock.h"
-#include "mongo/db/auth/security_token.h"
-#include "mongo/db/jsobj.h"
+#include "mongo/db/auth/security_token_gen.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/multitenancy_gen.h"
#include "mongo/db/service_context_test_fixture.h"
-#include "mongo/logv2/log.h"
-#include "mongo/unittest/death_test.h"
-#include "mongo/unittest/log_test.h"
#include "mongo/unittest/unittest.h"
-#include "mongo/util/hex.h"
namespace mongo {
@@ -70,8 +60,11 @@ public:
}
};
-class ValidatedTenantIdTestFixture : public mongo::ScopedGlobalServiceContextForTest,
- public unittest::Test {
+namespace auth {
+namespace {
+
+class ValidatedTenancyScopeTestFixture : public mongo::ScopedGlobalServiceContextForTest,
+ public unittest::Test {
protected:
void setUp() final {
auto authzManagerState = std::make_unique<AuthzManagerExternalStateMock>();
@@ -87,90 +80,98 @@ protected:
constexpr auto authUserFieldName = auth::SecurityToken::kAuthenticatedUserFieldName;
auto authUser = userName.toBSON(true /* serialize token */);
ASSERT_EQ(authUser["tenant"_sd].type(), jstOID);
- return auth::signSecurityToken(BSON(authUserFieldName << authUser));
+ using VTS = auth::ValidatedTenancyScope;
+ return VTS(BSON(authUserFieldName << authUser), VTS::TokenForTestingTag{})
+ .getOriginalToken();
}
ServiceContext::UniqueClient client;
};
-TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportOffWithoutTenantOK) {
+TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportOffWithoutTenantOK) {
gMultitenancySupport = false;
- auto body = fromjson("{$db: 'foo'}");
- OpMsg msg({body});
+ auto body = BSON("$db"
+ << "foo");
- ValidatedTenantId validatedTenant(msg, *(client.get()));
- ASSERT_TRUE(validatedTenant.tenantId() == boost::none);
+ auto validated = ValidatedTenancyScope::create(client.get(), body, {});
+ ASSERT_TRUE(validated == boost::none);
}
-TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportWithTenantOK) {
+TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportWithTenantOK) {
gMultitenancySupport = true;
auto kOid = OID::gen();
auto body = BSON("ping" << 1 << "$tenant" << kOid);
- OpMsg msg({body});
AuthorizationSessionImplTestHelper::grantUseTenant(*(client.get()));
- ValidatedTenantId validatedTenant(msg, *(client.get()));
- ASSERT(validatedTenant.tenantId() == TenantId(kOid));
+ auto validated = ValidatedTenancyScope::create(client.get(), body, {});
+ ASSERT_TRUE(validated != boost::none);
+ ASSERT_TRUE(validated->tenantId() == TenantId(kOid));
}
-TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportWithSecurityTokenOK) {
+TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportWithSecurityTokenOK) {
gMultitenancySupport = true;
- const auto kTenantId = TenantId(OID::gen());
+ const TenantId kTenantId(OID::gen());
auto body = BSON("ping" << 1);
- auto token = makeSecurityToken(UserName("user", "admin", kTenantId));
- OpMsg msg({body, token});
-
- ValidatedTenantId validatedTenant(msg, *(client.get()));
- ASSERT(validatedTenant.tenantId() == kTenantId);
+ UserName user("user", "admin", kTenantId);
+ auto token = makeSecurityToken(user);
+
+ auto validated = ValidatedTenancyScope::create(client.get(), body, token);
+ ASSERT_TRUE(validated != boost::none);
+ ASSERT_TRUE(validated->tenantId() == kTenantId);
+ ASSERT_TRUE(validated->hasAuthenticatedUser());
+ ASSERT_TRUE(validated->authenticatedUser() == user);
}
-TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportOffWithTenantNOK) {
+TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportOffWithTenantNOK) {
gMultitenancySupport = false;
auto kOid = OID::gen();
auto body = BSON("ping" << 1 << "$tenant" << kOid);
- OpMsg msg({body});
AuthorizationSessionImplTestHelper::grantUseTenant(*(client.get()));
- ASSERT_THROWS_CODE(
- ValidatedTenantId(msg, *(client.get())), DBException, ErrorCodes::InvalidOptions);
+ ASSERT_THROWS_CODE(ValidatedTenancyScope(client.get(), TenantId(kOid)),
+ DBException,
+ ErrorCodes::InvalidOptions);
+ ASSERT_TRUE(ValidatedTenancyScope::create(client.get(), body, {}) == boost::none);
}
-TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportWithTenantNOK) {
+TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportWithTenantNOK) {
gMultitenancySupport = true;
auto kOid = OID::gen();
auto body = BSON("ping" << 1 << "$tenant" << kOid);
- OpMsg msg({body});
ASSERT_THROWS_CODE(
- ValidatedTenantId(msg, *(client.get())), DBException, ErrorCodes::Unauthorized);
+ ValidatedTenancyScope(client.get(), TenantId(kOid)), DBException, ErrorCodes::Unauthorized);
+ ASSERT_THROWS_CODE(ValidatedTenancyScope::create(client.get(), body, {}),
+ DBException,
+ ErrorCodes::Unauthorized);
}
// TODO SERVER-66822: Re-enable this test case.
-// TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportWithoutTenantAndSecurityTokenNOK) {
+// TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportWithoutTenantAndSecurityTokenNOK) {
// gMultitenancySupport = true;
-
// auto body = BSON("ping" << 1);
-// OpMsg msg({body});
-
// AuthorizationSessionImplTestHelper::grantUseTenant(*(client.get()));
-// ASSERT_THROWS_CODE(
-// ValidatedTenantId(msg, *(client.get())), DBException, ErrorCodes::Unauthorized);
+// ASSERT_THROWS_CODE(ValidatedTenancyScope::create(client.get(), body, {}), DBException,
+// ErrorCodes::Unauthorized);
// }
-TEST_F(ValidatedTenantIdTestFixture, MultitenancySupportWithTenantAndSecurityTokenNOK) {
+TEST_F(ValidatedTenancyScopeTestFixture, MultitenancySupportWithTenantAndSecurityTokenNOK) {
gMultitenancySupport = true;
auto kOid = OID::gen();
auto body = BSON("ping" << 1 << "$tenant" << kOid);
- auto token = makeSecurityToken(UserName("user", "admin", TenantId(kOid)));
- OpMsg msg({body, token});
+ UserName user("user", "admin", TenantId(kOid));
+ auto token = makeSecurityToken(user);
AuthorizationSessionImplTestHelper::grantUseTenant(*(client.get()));
- ASSERT_THROWS_CODE(ValidatedTenantId(msg, *(client.get())), DBException, 6545800);
+ ASSERT_THROWS_CODE(
+ ValidatedTenancyScope::create(client.get(), body, token), DBException, 6545800);
}
+} // namespace
+} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index b102b7fd3e5..465bc8bb436 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -37,7 +37,6 @@
#include "mongo/bson/ordering.h"
#include "mongo/bson/simple_bsonelement_comparator.h"
#include "mongo/bson/simple_bsonobj_comparator.h"
-#include "mongo/db/auth/security_token.h"
#include "mongo/db/catalog/collection_catalog.h"
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/catalog/document_validation.h"
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index ab870a4818c..6ebe905b732 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -108,7 +108,8 @@ bool checkAuthorizationImplPreParse(OperationContext* opCtx,
uassert(ErrorCodes::Unauthorized,
str::stream() << "command " << command->getName() << " requires authentication",
!command->requiresAuth() || authzSession->isAuthenticated() ||
- request.securityToken.nFields());
+ (request.validatedTenancyScope &&
+ request.validatedTenancyScope->hasAuthenticatedUser()));
return false;
}
diff --git a/src/mongo/db/multitenancy.cpp b/src/mongo/db/multitenancy.cpp
index cb7c88663f6..f12a4f7b55d 100644
--- a/src/mongo/db/multitenancy.cpp
+++ b/src/mongo/db/multitenancy.cpp
@@ -29,42 +29,17 @@
#include "mongo/db/multitenancy.h"
-#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/auth/security_token.h"
-#include "mongo/db/multitenancy_gen.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/tenant_id.h"
-#include "mongo/logv2/log.h"
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault
-
namespace mongo {
-// Holds the tenantId for the operation if it was provided in the request on the $tenant field only
-// if the tenantId was not also provided in the security token.
-const auto dollarTenantDecoration =
- OperationContext::declareDecoration<boost::optional<mongo::TenantId>>();
-
boost::optional<TenantId> getActiveTenant(OperationContext* opCtx) {
- auto token = auth::getSecurityToken(opCtx);
- if (!token) {
- return dollarTenantDecoration(opCtx);
- }
-
- invariant(!dollarTenantDecoration(opCtx));
- return token->getAuthenticatedUser().getTenant();
-}
-
-void setDollarTenantOnOpCtx(OperationContext* opCtx, const OpMsg& opMsg) {
- if (!opMsg.validatedTenant || !opMsg.validatedTenant->tenantId()) {
- return;
- }
-
- if (opMsg.securityToken.nFields() > 0) {
- return;
+ if (auto token = auth::ValidatedTenancyScope::get(opCtx)) {
+ return token->tenantId();
}
- dollarTenantDecoration(opCtx) = opMsg.validatedTenant->tenantId();
+ return boost::none;
}
} // namespace mongo
diff --git a/src/mongo/db/multitenancy.h b/src/mongo/db/multitenancy.h
index 931074a33e0..b028286659d 100644
--- a/src/mongo/db/multitenancy.h
+++ b/src/mongo/db/multitenancy.h
@@ -41,9 +41,4 @@ namespace mongo {
*/
boost::optional<TenantId> getActiveTenant(OperationContext* opCtx);
-/**
- * Set the dollar TenantId on this OperationContext.
- */
-void setDollarTenantOnOpCtx(OperationContext* opCtx, const OpMsg& opMsg);
-
} // namespace mongo
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 64289c961cf..7b64af3ef83 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -41,7 +41,7 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/impersonation_session.h"
#include "mongo/db/auth/ldap_cumulative_operation_stats.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/security_token_authentication_guard.h"
#include "mongo/db/client.h"
#include "mongo/db/command_can_run_here.h"
#include "mongo/db/commands.h"
@@ -655,13 +655,6 @@ private:
_startOperationTime = getClientOperationTime(opCtx);
rpc::readRequestMetadata(opCtx, request, command->requiresAuth());
- uassert(ErrorCodes::Unauthorized,
- str::stream() << "Command " << command->getName()
- << " is not supported in multitenancy mode",
- command->allowedWithSecurityToken() ||
- auth::getSecurityToken(opCtx) == boost::none);
- _tokenAuthorizationSessionGuard.emplace(opCtx);
-
_invocation = command->parse(opCtx, request);
CommandInvocation::set(opCtx, _invocation);
@@ -1413,6 +1406,14 @@ void ExecCommandDatabase::_initiateCommand() {
Client* client = opCtx->getClient();
+ if (auto scope = request.validatedTenancyScope; scope && scope->hasAuthenticatedUser()) {
+ uassert(ErrorCodes::Unauthorized,
+ str::stream() << "Command " << command->getName()
+ << " is not supported in multitenancy mode",
+ command->allowedWithSecurityToken());
+ _tokenAuthorizationSessionGuard.emplace(opCtx, request.validatedTenancyScope.get());
+ }
+
if (isHello()) {
// Preload generic ClientMetadata ahead of our first hello request. After the first
// request, metaElement should always be empty.
@@ -1449,7 +1450,6 @@ void ExecCommandDatabase::_initiateCommand() {
// Start authz contract tracking before we evaluate failpoints
auto authzSession = AuthorizationSession::get(client);
-
authzSession->startContractTracking();
CommandHelpers::evaluateFailCommandFailPoint(opCtx, _invocation.get());
diff --git a/src/mongo/db/validated_tenant_id.cpp b/src/mongo/db/validated_tenant_id.cpp
deleted file mode 100644
index 58f450529c7..00000000000
--- a/src/mongo/db/validated_tenant_id.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/db/validated_tenant_id.h"
-
-#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/auth/security_token.h"
-#include "mongo/db/multitenancy_gen.h"
-
-namespace mongo {
-
-ValidatedTenantId::ValidatedTenantId(const OpMsg& opMsg, Client& client) {
- auto dollarTenantElem = opMsg.body["$tenant"];
- uassert(ErrorCodes::InvalidOptions,
- "Multitenancy not enabled, cannot set $tenant in command body",
- !dollarTenantElem || (dollarTenantElem && gMultitenancySupport));
-
- if (!gMultitenancySupport) {
- return;
- }
-
- // TODO SERVER-66822: Re-enable this uassert.
- // uassert(ErrorCodes::Unauthorized,
- // "Multitenancy is enabled, $tenant id or securityToken is required.",
- // dollarTenantElem || opMsg.securityToken.nFields() > 0);
-
- uassert(6545800,
- str::stream() << "Cannot pass $tenant id if also passing securityToken, "
- << opMsg.securityToken.toString() << ", " << dollarTenantElem.toString(),
- !(dollarTenantElem && opMsg.securityToken.nFields() > 0));
-
- if (dollarTenantElem) {
- uassert(ErrorCodes::Unauthorized,
- "'$tenant' may only be specified with the useTenant action type",
- AuthorizationSession::get(client)->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::useTenant));
- _tenant = TenantId::parseFromBSON(dollarTenantElem);
- return;
- }
-
- if (opMsg.securityToken.nFields() > 0) {
- auto verifiedToken = auth::verifySecurityToken(opMsg.securityToken);
- _tenant = verifiedToken.getAuthenticatedUser().getTenant();
- }
-}
-
-ValidatedTenantId::ValidatedTenantId(const DatabaseName& dbName) {
- _tenant = dbName.tenantId();
-}
-
-} // namespace mongo
diff --git a/src/mongo/executor/network_interface_tl.cpp b/src/mongo/executor/network_interface_tl.cpp
index 9ee2ffac0b6..d919bca3c3b 100644
--- a/src/mongo/executor/network_interface_tl.cpp
+++ b/src/mongo/executor/network_interface_tl.cpp
@@ -33,7 +33,7 @@
#include <fmt/format.h>
#include "mongo/config.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/server_options.h"
#include "mongo/db/wire_version.h"
#include "mongo/executor/connection_pool_tl.h"
@@ -71,9 +71,7 @@ Status appendMetadata(RemoteCommandRequestOnAny* request,
if (!request->opCtx)
return Status::OK();
- if (auto securityToken = auth::getSecurityToken(request->opCtx)) {
- request->securityToken = securityToken->toBSON();
- }
+ request->validatedTenancyScope = auth::ValidatedTenancyScope::get(request->opCtx);
return Status::OK();
}
diff --git a/src/mongo/executor/remote_command_request.h b/src/mongo/executor/remote_command_request.h
index 7d232a34756..cca27b03e71 100644
--- a/src/mongo/executor/remote_command_request.h
+++ b/src/mongo/executor/remote_command_request.h
@@ -33,6 +33,7 @@
#include <string>
#include "mongo/base/error_codes.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/jsobj.h"
#include "mongo/rpc/metadata.h"
#include "mongo/transport/transport_layer.h"
@@ -73,7 +74,7 @@ struct RemoteCommandRequestBase {
std::string dbname;
BSONObj metadata{rpc::makeEmptyMetadata()};
BSONObj cmdObj;
- BSONObj securityToken;
+ boost::optional<auth::ValidatedTenancyScope> validatedTenancyScope;
// OperationContext is added to each request to allow OP_Command metadata attachment access to
// the Client object. The OperationContext is only accessed on the thread that calls
diff --git a/src/mongo/logv2/logv2_test.cpp b/src/mongo/logv2/logv2_test.cpp
index e9c2cbd76ee..0144f9a86b2 100644
--- a/src/mongo/logv2/logv2_test.cpp
+++ b/src/mongo/logv2/logv2_test.cpp
@@ -40,7 +40,7 @@
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/json.h"
#include "mongo/bson/oid.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/tenant_id.h"
#include "mongo/logv2/bson_formatter.h"
#include "mongo/logv2/component_settings_filter.h"
diff --git a/src/mongo/rpc/factory.cpp b/src/mongo/rpc/factory.cpp
index 41384b0c678..7e001edc0af 100644
--- a/src/mongo/rpc/factory.cpp
+++ b/src/mongo/rpc/factory.cpp
@@ -62,11 +62,7 @@ OpMsgRequest opMsgRequestFromAnyProtocol(const Message& unownedMessage, Client*
case mongo::dbMsg:
return OpMsgRequest::parseOwned(unownedMessage, client);
case mongo::dbQuery: {
- auto request = opMsgRequestFromLegacyRequest(unownedMessage);
- if (client) {
- request.parseValidatedTenant(*client);
- }
- return request;
+ return opMsgRequestFromLegacyRequest(unownedMessage);
}
default:
uasserted(ErrorCodes::UnsupportedFormat,
diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp
index aa70c17a1af..b54a4474cc2 100644
--- a/src/mongo/rpc/metadata.cpp
+++ b/src/mongo/rpc/metadata.cpp
@@ -33,7 +33,7 @@
#include "mongo/client/read_preference.h"
#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/logical_time_validator.h"
@@ -95,8 +95,7 @@ void readRequestMetadata(OperationContext* opCtx, const OpMsg& opMsg, bool cmdRe
}
readImpersonatedUserMetadata(impersonationElem, opCtx);
- auth::setSecurityToken(opCtx, opMsg);
- setDollarTenantOnOpCtx(opCtx, opMsg);
+ auth::ValidatedTenancyScope::set(opCtx, opMsg.validatedTenancyScope);
// We check for "$client" but not "client" here, because currentOp can filter on "client" as
// a top-level field.
diff --git a/src/mongo/rpc/metadata/security_token_metadata_test.cpp b/src/mongo/rpc/metadata/security_token_metadata_test.cpp
index 69fc73b89cd..98698572af1 100644
--- a/src/mongo/rpc/metadata/security_token_metadata_test.cpp
+++ b/src/mongo/rpc/metadata/security_token_metadata_test.cpp
@@ -31,8 +31,8 @@
#include "mongo/bson/oid.h"
#include "mongo/crypto/sha256_block.h"
-#include "mongo/db/auth/security_token.h"
#include "mongo/db/auth/security_token_gen.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/client.h"
#include "mongo/db/concurrency/locker_noop_service_context_test_fixture.h"
#include "mongo/db/multitenancy_gen.h"
@@ -51,7 +51,10 @@ BSONObj makeSecurityToken(const UserName& userName) {
constexpr auto authUserFieldName = auth::SecurityToken::kAuthenticatedUserFieldName;
auto authUser = userName.toBSON(true /* serialize token */);
ASSERT_EQ(authUser["tenant"_sd].type(), jstOID);
- return auth::signSecurityToken(BSON(authUserFieldName << authUser));
+ using VTS = auth::ValidatedTenancyScope;
+ return VTS(BSON(authUserFieldName << authUser), VTS::TokenForTestingTag{})
+ .getOriginalToken()
+ .getOwned();
}
class SecurityTokenMetadataTest : public LockerNoopServiceContextTest {
@@ -84,21 +87,19 @@ TEST_F(SecurityTokenMetadataTest, BasicSuccess) {
auto msg = OpMsgBytes{0, kBodySection, kPingBody, kSecurityTokenSection, kTokenBody}.parse();
ASSERT_BSONOBJ_EQ(msg.body, kPingBody);
ASSERT_EQ(msg.sequences.size(), 0u);
- ASSERT_BSONOBJ_EQ(msg.securityToken, kTokenBody);
+ ASSERT_TRUE(msg.validatedTenancyScope != boost::none);
+ ASSERT_BSONOBJ_EQ(msg.validatedTenancyScope->getOriginalToken(), kTokenBody);
+ ASSERT_EQ(msg.validatedTenancyScope->tenantId(), kTenantId);
auto opCtx = makeOperationContext();
- ASSERT(auth::getSecurityToken(opCtx.get()) == boost::none);
+ ASSERT(auth::ValidatedTenancyScope::get(opCtx.get()) == boost::none);
- msg.parseValidatedTenant(*client.get());
- ASSERT(msg.validatedTenant);
- ASSERT(msg.validatedTenant->tenantId());
- ASSERT_EQ(msg.validatedTenant->tenantId().get(), kTenantId);
-
- auth::setSecurityToken(opCtx.get(), msg);
- auto token = auth::getSecurityToken(opCtx.get());
+ auth::ValidatedTenancyScope::set(opCtx.get(), msg.validatedTenancyScope);
+ auto token = auth::ValidatedTenancyScope::get(opCtx.get());
ASSERT(token != boost::none);
- auto authedUser = token->getAuthenticatedUser();
+ ASSERT_TRUE(token->hasAuthenticatedUser());
+ auto authedUser = token->authenticatedUser();
ASSERT_EQ(authedUser.getUser(), "user");
ASSERT_EQ(authedUser.getDB(), "admin");
ASSERT_TRUE(authedUser.getTenant() != boost::none);
diff --git a/src/mongo/rpc/op_msg.cpp b/src/mongo/rpc/op_msg.cpp
index 1f4a3ba51bf..be420d55e50 100644
--- a/src/mongo/rpc/op_msg.cpp
+++ b/src/mongo/rpc/op_msg.cpp
@@ -159,6 +159,7 @@ OpMsg OpMsg::parse(const Message& message, Client* client) try {
// TODO some validation may make more sense in the IDL parser. I've tagged them with comments.
bool haveBody = false;
OpMsg msg;
+ BSONObj securityToken;
while (!sectionsBuf.atEof()) {
const auto sectionKind = sectionsBuf.read<Section>();
switch (sectionKind) {
@@ -166,6 +167,10 @@ OpMsg OpMsg::parse(const Message& message, Client* client) try {
uassert(40430, "Multiple body sections in message", !haveBody);
haveBody = true;
msg.body = sectionsBuf.read<Validated<BSONObj>>();
+
+ uassert(ErrorCodes::InvalidOptions,
+ "Multitenancy not enabled, cannot set $tenant in command body",
+ gMultitenancySupport || !msg.body["$tenant"_sd]);
break;
}
@@ -197,7 +202,7 @@ OpMsg OpMsg::parse(const Message& message, Client* client) try {
uassert(ErrorCodes::Unauthorized,
"Unsupported Security Token provided",
gMultitenancySupport);
- msg.securityToken = sectionsBuf.read<Validated<BSONObj>>();
+ securityToken = sectionsBuf.read<Validated<BSONObj>>();
break;
}
@@ -228,9 +233,11 @@ OpMsg OpMsg::parse(const Message& message, Client* client) try {
*checksum == calculateChecksum(message));
}
#endif
- if (client != nullptr) {
- msg.parseValidatedTenant(*client);
+ if (gMultitenancySupport) {
+ msg.validatedTenancyScope =
+ auth::ValidatedTenancyScope::create(client, msg.body, securityToken);
}
+
return msg;
} catch (const DBException& ex) {
LOGV2_DEBUG(
@@ -244,17 +251,16 @@ OpMsg OpMsg::parse(const Message& message, Client* client) try {
throw;
}
-void OpMsg::parseValidatedTenant(Client& client) {
- validatedTenant = ValidatedTenantId(*this, client);
-}
-
namespace {
void serializeHelper(const std::vector<OpMsg::DocumentSequence>& sequences,
const BSONObj& body,
- const BSONObj& securityToken,
+ const boost::optional<auth::ValidatedTenancyScope>& validatedTenancyScope,
OpMsgBuilder* output) {
- if (securityToken.nFields() > 0) {
- output->setSecurityToken(securityToken);
+ if (validatedTenancyScope) {
+ auto securityToken = validatedTenancyScope->getOriginalToken();
+ if (securityToken.nFields() > 0) {
+ output->setSecurityToken(securityToken);
+ }
}
for (auto&& seq : sequences) {
auto docSeq = output->beginDocSequence(seq.name);
@@ -268,13 +274,13 @@ void serializeHelper(const std::vector<OpMsg::DocumentSequence>& sequences,
Message OpMsg::serialize() const {
OpMsgBuilder builder;
- serializeHelper(sequences, body, securityToken, &builder);
+ serializeHelper(sequences, body, validatedTenancyScope, &builder);
return builder.finish();
}
Message OpMsg::serializeWithoutSizeChecking() const {
OpMsgBuilder builder;
- serializeHelper(sequences, body, securityToken, &builder);
+ serializeHelper(sequences, body, validatedTenancyScope, &builder);
return builder.finishWithoutSizeChecking();
}
@@ -289,9 +295,6 @@ void OpMsg::shareOwnershipWith(const ConstSharedBuffer& buffer) {
}
}
}
- if (!securityToken.isOwned()) {
- securityToken.shareOwnershipWith(buffer);
- }
}
BSONObjBuilder OpMsgBuilder::beginSecurityToken() {
diff --git a/src/mongo/rpc/op_msg.h b/src/mongo/rpc/op_msg.h
index 4083d9fde13..243dbf9a344 100644
--- a/src/mongo/rpc/op_msg.h
+++ b/src/mongo/rpc/op_msg.h
@@ -36,8 +36,8 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/jsobj.h"
-#include "mongo/db/validated_tenant_id.h"
#include "mongo/rpc/message.h"
namespace mongo {
@@ -133,8 +133,6 @@ struct OpMsg {
return msg;
}
- void parseValidatedTenant(Client& client);
-
Message serialize() const;
/**
@@ -161,10 +159,16 @@ struct OpMsg {
}
BSONObj body;
- BSONObj securityToken;
std::vector<DocumentSequence> sequences;
- // The validated tenant id will not be serialized into the Message.
- boost::optional<ValidatedTenantId> validatedTenant = boost::none;
+
+ boost::optional<auth::ValidatedTenancyScope> validatedTenancyScope = boost::none;
+
+ boost::optional<TenantId> getValidatedTenantId() const {
+ if (!validatedTenancyScope) {
+ return boost::none;
+ }
+ return validatedTenancyScope->tenantId();
+ }
};
/**
diff --git a/src/mongo/rpc/op_msg_test.cpp b/src/mongo/rpc/op_msg_test.cpp
index 4a7dfb17e43..ea07a42ea31 100644
--- a/src/mongo/rpc/op_msg_test.cpp
+++ b/src/mongo/rpc/op_msg_test.cpp
@@ -38,7 +38,8 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/authorization_session_impl.h"
#include "mongo/db/auth/authz_manager_external_state_mock.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/security_token_gen.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/multitenancy_gen.h"
#include "mongo/db/service_context_test_fixture.h"
@@ -806,13 +807,15 @@ protected:
constexpr auto authUserFieldName = auth::SecurityToken::kAuthenticatedUserFieldName;
auto authUser = userName.toBSON(true /* serialize token */);
ASSERT_EQ(authUser["tenant"_sd].type(), jstOID);
- return auth::signSecurityToken(BSON(authUserFieldName << authUser));
+ using VTS = auth::ValidatedTenancyScope;
+ return VTS(BSON(authUserFieldName << authUser), VTS::TokenForTestingTag{})
+ .getOriginalToken();
}
ServiceContext::UniqueClient client;
};
-TEST_F(OpMsgWithAuth, ParseValidatedTenantIdFromSecurityToken) {
+TEST_F(OpMsgWithAuth, ParseValidatedTenancyScopeFromSecurityToken) {
gMultitenancySupport = true;
const auto kTenantId = TenantId(OID::gen());
@@ -837,11 +840,11 @@ TEST_F(OpMsgWithAuth, ParseValidatedTenantIdFromSecurityToken) {
auto body = BSON("ping" << 1);
- ASSERT(msg.validatedTenant);
- ASSERT_EQ(msg.validatedTenant->tenantId().get(), kTenantId);
+ ASSERT(msg.validatedTenancyScope);
+ ASSERT_EQ(msg.validatedTenancyScope->tenantId(), kTenantId);
}
-TEST_F(OpMsgWithAuth, ParseValidatedTenantIdFromDollarTenant) {
+TEST_F(OpMsgWithAuth, ParseValidatedTenancyScopeFromDollarTenant) {
gMultitenancySupport = true;
AuthorizationSessionImplTestHelper::grantUseTenant(*(client.get()));
@@ -862,11 +865,11 @@ TEST_F(OpMsgWithAuth, ParseValidatedTenantIdFromDollarTenant) {
}
.parse(client.get());
- ASSERT(msg.validatedTenant);
- ASSERT_EQ(msg.validatedTenant->tenantId().get(), kTenantId);
+ ASSERT(msg.validatedTenancyScope);
+ ASSERT_EQ(msg.validatedTenancyScope->tenantId(), kTenantId);
}
-TEST_F(OpMsgWithAuth, ValidatedTenantIdShouldNotBeSerialized) {
+TEST_F(OpMsgWithAuth, ValidatedTenancyScopeShouldNotBeSerialized) {
gMultitenancySupport = true;
AuthorizationSessionImplTestHelper::grantUseTenant(*(client.get()));
@@ -885,7 +888,7 @@ TEST_F(OpMsgWithAuth, ValidatedTenantIdShouldNotBeSerialized) {
},
};
auto msg = msgBytes.parse(client.get());
- ASSERT(msg.validatedTenant);
+ ASSERT(msg.validatedTenancyScope);
auto serializedMsg = msg.serialize();
testSerializer(serializedMsg,
diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp
index 6bd53032ff8..00ed9bfd521 100644
--- a/src/mongo/s/commands/cluster_explain_cmd.cpp
+++ b/src/mongo/s/commands/cluster_explain_cmd.cpp
@@ -190,7 +190,7 @@ std::unique_ptr<CommandInvocation> ClusterExplainCmd::parse(OperationContext* op
str::stream() << "Explain failed due to unknown command: " << cmdName,
explainedCommand);
auto innerRequest = std::make_unique<OpMsgRequest>(OpMsg{explainedObj});
- innerRequest->validatedTenant = request.validatedTenant;
+ innerRequest->validatedTenancyScope = request.validatedTenancyScope;
auto innerInvocation = explainedCommand->parseForExplain(opCtx, *innerRequest, verbosity);
return std::make_unique<Invocation>(
this, request, std::move(verbosity), std::move(innerRequest), std::move(innerInvocation));
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
index dd30e8e488e..dffc1163e47 100644
--- a/src/mongo/scripting/mozjs/mongo.cpp
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -257,13 +257,16 @@ void doRunCommand(JSContext* cx, JS::CallArgs args, MakeRequest makeRequest) {
auto arg = ValueWriter(cx, args.get(1)).toBSON();
auto request = makeRequest(database, arg);
- if (auto token = args.get(3); token.isObject()) {
- request.securityToken = ValueWriter(cx, token).toBSON();
+ if (auto tokenArg = args.get(3); tokenArg.isObject()) {
+ using VTS = auth::ValidatedTenancyScope;
+ if (auto token = ValueWriter(cx, tokenArg).toBSON(); token.nFields() > 0) {
+ request.validatedTenancyScope = VTS(token, VTS::InitTag::kInitForShell);
+ }
} else {
uassert(ErrorCodes::BadValue,
str::stream() << "The token parameter to " << Params::kCommandName
<< " must be an object",
- token.isUndefined());
+ tokenArg.isUndefined());
}
const auto& conn = getConnectionRef(args);
diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp
index 3c1f4a843f5..748d5a1b80c 100644
--- a/src/mongo/shell/shell_utils.cpp
+++ b/src/mongo/shell/shell_utils.cpp
@@ -48,7 +48,8 @@
#include "mongo/base/shim.h"
#include "mongo/client/dbclient_base.h"
#include "mongo/client/replica_set_monitor.h"
-#include "mongo/db/auth/security_token.h"
+#include "mongo/db/auth/security_token_gen.h"
+#include "mongo/db/auth/validated_tenancy_scope.h"
#include "mongo/db/hasher.h"
#include "mongo/logv2/log.h"
#include "mongo/platform/decimal128.h"
@@ -436,7 +437,9 @@ BSONObj _createSecurityToken(const BSONObj& args, void* data) {
constexpr auto authUserFieldName = auth::SecurityToken::kAuthenticatedUserFieldName;
auto authUser = args.firstElement().Obj();
- return BSON("" << auth::signSecurityToken(BSON(authUserFieldName << authUser)));
+ using VTS = auth::ValidatedTenancyScope;
+ auto token = VTS(BSON(authUserFieldName << authUser), VTS::TokenForTestingTag{});
+ return BSON("" << token.getOriginalToken());
}
BSONObj replMonitorStats(const BSONObj& a, void* data) {