summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2021-10-13 21:04:55 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-12-07 00:23:48 +0000
commitd7845457fd30cd1798f70444c2a66d725ab361b4 (patch)
tree335bd948371be032d209a26c564a140b4ea215a4 /src
parent4a915072ab5279480305a6023db6671e3f32cfd0 (diff)
downloadmongo-d7845457fd30cd1798f70444c2a66d725ab361b4.tar.gz
SERVER-61615 Parse authenticated user from security token and add to authorization session
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/auth/SConscript1
-rw-r--r--src/mongo/db/auth/authorization_session.h11
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp69
-rw-r--r--src/mongo/db/auth/authorization_session_impl.h8
-rw-r--r--src/mongo/db/auth/security_token.cpp40
-rw-r--r--src/mongo/db/auth/security_token.h11
-rw-r--r--src/mongo/db/auth/security_token.idl12
-rw-r--r--src/mongo/db/commands.cpp3
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp25
-rw-r--r--src/mongo/db/commands/user_management_commands.idl6
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp13
-rw-r--r--src/mongo/db/multitenancy.cpp2
-rw-r--r--src/mongo/db/service_entry_point_common.cpp4
-rw-r--r--src/mongo/embedded/embedded_auth_session.cpp8
-rw-r--r--src/mongo/rpc/metadata/security_token_metadata_test.cpp28
-rw-r--r--src/mongo/shell/shell_utils.cpp27
17 files changed, 247 insertions, 22 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 844b1b76db7..3ef6beaf34d 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -14,6 +14,7 @@ env.Library(
'$BUILD_DIR/mongo/base',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/auth/auth',
'$BUILD_DIR/mongo/db/multitenancy_params',
'$BUILD_DIR/mongo/db/server_feature_flags',
'$BUILD_DIR/mongo/db/service_context',
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index 3631c8049b4..b23fbfcf171 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -169,6 +169,9 @@ public:
// Gets an iterator over the roles of all authenticated users stored in this manager.
virtual RoleNameIterator getAuthenticatedRoleNames() = 0;
+ // Removes all authenticated principals while in kSecurityToken authentication mode.
+ virtual void logoutSecurityTokenUser(Client* client) = 0;
+
// Removes any authenticated principals and revokes any privileges that were granted via those
// principals. This function modifies state. Synchronizes with the Client lock.
virtual void logoutAllDatabases(Client* client, StringData reason) = 0;
@@ -178,6 +181,14 @@ public:
// modifies state. Synchronizes with the Client lock.
virtual void logoutDatabase(Client* client, StringData dbname, StringData reason) = 0;
+ // How the active session is authenticated.
+ enum class AuthenticationMode {
+ kNone, // Not authenticated.
+ kConnection, // For the duration of the connection, or until logged out.
+ kSecurityToken, // By operation scoped security token.
+ };
+ virtual AuthenticationMode getAuthenticationMode() const = 0;
+
// Adds the internalSecurity user to the set of authenticated users.
// Used to grant internal threads full access. Takes in the Client
// as a parameter so it can take out a lock on the client.
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index 01ec890270d..be2ad897b27 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -44,6 +44,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/bson/dotted_path_support.h"
#include "mongo/db/client.h"
#include "mongo/db/namespace_string.h"
@@ -101,6 +102,20 @@ AuthorizationManager& AuthorizationSessionImpl::getAuthorizationManager() {
void AuthorizationSessionImpl::startRequest(OperationContext* opCtx) {
_externalState->startRequest(opCtx);
_refreshUserInfoAsNeeded(opCtx);
+ if (_authenticationMode == AuthenticationMode::kSecurityToken) {
+ // Previously authenticated using SecurityToken,
+ // clear that user and reset to unauthenticated state.
+ invariant(_authenticatedUsers.count() <= 1);
+ if (auto users = std::exchange(_authenticatedUsers, {}); users.count()) {
+ LOGV2_DEBUG(6161507,
+ 3,
+ "security token based user still authenticated at start of request, "
+ "clearing from authentication state",
+ "user"_attr = users.getNames().get().toBSON(true /* encode tenant */));
+ _buildAuthenticatedRolesVector();
+ }
+ _authenticationMode = AuthenticationMode::kNone;
+ }
}
void AuthorizationSessionImpl::startContractTracking() {
@@ -177,6 +192,19 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
}
stdx::lock_guard<Client> lk(*opCtx->getClient());
+
+ if (auto token = auth::getSecurityToken(opCtx)) {
+ 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);
+ _authenticationMode = AuthenticationMode::kSecurityToken;
+ } else {
+ _authenticationMode = AuthenticationMode::kConnection;
+ }
_authenticatedUsers.add(std::move(user));
// If there are any users and roles in the impersonation data, clear it out.
@@ -214,9 +242,35 @@ User* AuthorizationSessionImpl::getSingleUser() {
return lookupUser(userName);
}
+void AuthorizationSessionImpl::logoutSecurityTokenUser(Client* client) {
+ stdx::lock_guard<Client> lk(*client);
+
+ uassert(6161503,
+ "Attempted to deauth a security token user while using standard login",
+ _authenticationMode != AuthenticationMode::kConnection);
+
+ auto users = std::exchange(_authenticatedUsers, {});
+ invariant(users.count() <= 1);
+ if (users.count() == 1) {
+ LOGV2_DEBUG(6161506,
+ 5,
+ "security token based user explicitly logged out",
+ "user"_attr = users.getNames().get().toBSON(true /* encode tenant */));
+ }
+
+ // Explicitly skip auditing the logout event,
+ // security tokens don't represent a permanent login.
+ clearImpersonatedUserData();
+ _buildAuthenticatedRolesVector();
+}
+
void AuthorizationSessionImpl::logoutAllDatabases(Client* client, StringData reason) {
stdx::lock_guard<Client> lk(*client);
+ uassert(6161504,
+ "May not log out while using a security token based authentication",
+ _authenticationMode != AuthenticationMode::kSecurityToken);
+
auto users = std::exchange(_authenticatedUsers, {});
if (users.count() == 0) {
return;
@@ -234,6 +288,10 @@ void AuthorizationSessionImpl::logoutDatabase(Client* client,
StringData reason) {
stdx::lock_guard<Client> lk(*client);
+ uassert(6161505,
+ "May not log out while using a security token based authentication",
+ _authenticationMode != AuthenticationMode::kSecurityToken);
+
// Emit logout audit event and then remove all users logged into dbname.
UserSet updatedUsers(_authenticatedUsers);
updatedUsers.removeByDBName(dbname);
@@ -410,7 +468,7 @@ bool AuthorizationSessionImpl::isAuthorizedForActionsOnNamespace(const Namespace
return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), actions));
}
-static const int resourceSearchListCapacity = 7;
+constexpr int resourceSearchListCapacity = 7;
/**
* Builds from "target" an exhaustive list of all ResourcePatterns that match "target".
*
@@ -664,14 +722,17 @@ void AuthorizationSessionImpl::_refreshUserInfoAsNeeded(OperationContext* opCtx)
void AuthorizationSessionImpl::_buildAuthenticatedRolesVector() {
_authenticatedRoleNames.clear();
- for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
- ++it) {
- RoleNameIterator roles = (*it)->getIndirectRoles();
+ for (const auto& userHandle : _authenticatedUsers) {
+ RoleNameIterator roles = userHandle->getIndirectRoles();
while (roles.more()) {
RoleName roleName = roles.next();
_authenticatedRoleNames.push_back(RoleName(roleName.getRole(), roleName.getDB()));
}
}
+
+ if (_authenticatedUsers.count() == 0) {
+ _authenticationMode = AuthenticationMode::kNone;
+ }
}
bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnAnyResourceInDB(StringData db) {
diff --git a/src/mongo/db/auth/authorization_session_impl.h b/src/mongo/db/auth/authorization_session_impl.h
index 71c07ab949a..2ec581af5c2 100644
--- a/src/mongo/db/auth/authorization_session_impl.h
+++ b/src/mongo/db/auth/authorization_session_impl.h
@@ -91,9 +91,14 @@ public:
RoleNameIterator getAuthenticatedRoleNames() override;
+ void logoutSecurityTokenUser(Client* client) override;
void logoutAllDatabases(Client* client, StringData reason) override;
void logoutDatabase(Client* client, StringData dbname, StringData reason) override;
+ AuthenticationMode getAuthenticationMode() const override {
+ return _authenticationMode;
+ }
+
void grantInternalAuthorization(Client* client) override;
void grantInternalAuthorization(OperationContext* opCtx) override;
@@ -161,6 +166,9 @@ protected:
// All Users who have been authenticated on this connection.
UserSet _authenticatedUsers;
+ // What authentication mode we're currently operating in.
+ AuthenticationMode _authenticationMode = AuthenticationMode::kNone;
+
// The roles of the authenticated users. This vector is generated when the authenticated
// users set is changed.
std::vector<RoleName> _authenticatedRoleNames;
diff --git a/src/mongo/db/auth/security_token.cpp b/src/mongo/db/auth/security_token.cpp
index 5ebdfeda60d..b38fe1e0b9b 100644
--- a/src/mongo/db/auth/security_token.cpp
+++ b/src/mongo/db/auth/security_token.cpp
@@ -35,6 +35,7 @@
#include "mongo/base/init.h"
#include "mongo/bson/oid.h"
+#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/multitenancy_gen.h"
#include "mongo/db/server_feature_flags_gen.h"
#include "mongo/logv2/log.h"
@@ -54,15 +55,42 @@ MONGO_INITIALIZER(SecurityTokenOptionValidate)(InitializerContext*) {
auto* opCtx = client ? client->getOperationContext() : nullptr;
auto token = getSecurityToken(opCtx);
if (token) {
- return token->getTenant();
+ return token->getAuthenticatedUser().getTenant();
} else {
return boost::none;
}
});
}
}
+
+// Placeholder algorithm.
+void validateSecurityTokenSignature(BSONObj authUser, const SHA256Block& sig) {
+ auto computed =
+ SHA256Block::computeHash({ConstDataRange(authUser.objdata(), authUser.objsize())});
+ uassert(ErrorCodes::Unauthorized, "Token signature invalid", computed == sig);
+}
} // 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);
+ }
+}
+
void readSecurityTokenMetadata(OperationContext* opCtx, BSONObj securityToken) try {
if (securityToken.nFields() == 0) {
return;
@@ -70,7 +98,15 @@ void readSecurityTokenMetadata(OperationContext* opCtx, BSONObj securityToken) t
uassert(ErrorCodes::BadValue, "Multitenancy not enabled", gMultitenancySupport);
- securityTokenDecoration(opCtx) = SecurityToken::parse({"Security Token"}, securityToken);
+ auto token = SecurityToken::parse({"Security Token"}, securityToken);
+ auto authenticatedUser = token.getAuthenticatedUser();
+ uassert(ErrorCodes::BadValue,
+ "Security token authenticated user requires a valid Tenant ID",
+ authenticatedUser.getTenant());
+
+ validateSecurityTokenSignature(securityToken["authenticatedUser"].Obj(), token.getSig());
+
+ securityTokenDecoration(opCtx) = std::move(token);
LOGV2_DEBUG(5838100, 4, "Accepted security token", "token"_attr = securityToken);
} catch (const DBException& ex) {
diff --git a/src/mongo/db/auth/security_token.h b/src/mongo/db/auth/security_token.h
index 52e57e2625e..7a30424e78b 100644
--- a/src/mongo/db/auth/security_token.h
+++ b/src/mongo/db/auth/security_token.h
@@ -33,11 +33,22 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/db/auth/security_token_gen.h"
+#include "mongo/db/client.h"
#include "mongo/db/operation_context.h"
namespace mongo {
namespace auth {
+class SecurityTokenAuthenticationGuard {
+public:
+ SecurityTokenAuthenticationGuard() = delete;
+ SecurityTokenAuthenticationGuard(OperationContext* opCtx);
+ ~SecurityTokenAuthenticationGuard();
+
+private:
+ Client* _client;
+};
+
/**
* Parse any SecurityToken from the OpMsg and place it as a decoration
* on OperationContext
diff --git a/src/mongo/db/auth/security_token.idl b/src/mongo/db/auth/security_token.idl
index 03d401ecf65..62076ac8b31 100644
--- a/src/mongo/db/auth/security_token.idl
+++ b/src/mongo/db/auth/security_token.idl
@@ -30,6 +30,8 @@ global:
cpp_namespace: "mongo::auth"
imports:
+ - "mongo/db/auth/auth_types.idl"
+ - "mongo/crypto/sha256_block.idl"
- "mongo/idl/basic_types.idl"
structs:
@@ -37,7 +39,11 @@ structs:
description: "Security Token as passed in OP_MSG"
strict: true
fields:
- tenant:
- description: Tenant identifier
- type: objectid
+ authenticatedUser:
+ description: Authenticated user for which this token grants authorizations
+ type: UserName
+ sig:
+ # WIP This is temporarily a SHA256 hash of the authenticatedUser BSON object.
+ description: Validated signature on this security token
+ type: sha256Block
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 851c0f98bef..23967c70d60 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -109,7 +109,8 @@ bool checkAuthorizationImplPreParse(OperationContext* opCtx,
return false; // Still can't decide on auth because of the localhost bypass.
uassert(ErrorCodes::Unauthorized,
str::stream() << "command " << command->getName() << " requires authentication",
- !command->requiresAuth() || authzSession->isAuthenticated());
+ !command->requiresAuth() || authzSession->isAuthenticated() ||
+ request.securityToken.nFields());
return false;
}
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index b10e156fa71..c40622a2f36 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -161,6 +161,7 @@ env.Library(
'$BUILD_DIR/mongo/db/logical_session_id',
'$BUILD_DIR/mongo/db/logical_session_id_helpers',
'$BUILD_DIR/mongo/db/multitenancy',
+ '$BUILD_DIR/mongo/db/multitenancy_params',
'$BUILD_DIR/mongo/db/pipeline/change_stream_pipeline',
'$BUILD_DIR/mongo/db/pipeline/pipeline',
'$BUILD_DIR/mongo/db/repl/isself',
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 199b127126c..178bd5c27f5 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -206,6 +206,16 @@ Status checkOkayToGrantPrivilegesToRole(const RoleName& role, const PrivilegeVec
return Status::OK();
}
+// Temporary placeholder pending availability of NamespaceWithTenant.
+NamespaceString getNamespaceWithTenant(const NamespaceString& nss,
+ const boost::optional<OID>& tenant) {
+ if (tenant) {
+ return NamespaceString(str::stream() << tenant.get() << '_' << nss.db(), nss.coll());
+ } else {
+ return nss;
+ }
+}
+
/**
* Finds all documents matching "query" in "collectionName". For each document returned,
* calls the function resultProcessor on it.
@@ -439,9 +449,11 @@ Status removeRoleDocuments(OperationContext* opCtx,
/**
* Creates the given user object in the given database.
*/
-Status insertPrivilegeDocument(OperationContext* opCtx, const BSONObj& userObj) {
- Status status =
- insertAuthzDocument(opCtx, AuthorizationManager::usersCollectionNamespace, userObj);
+Status insertPrivilegeDocument(OperationContext* opCtx,
+ const BSONObj& userObj,
+ const boost::optional<OID>& tenant = boost::none) {
+ auto nss = getNamespaceWithTenant(AuthorizationManager::usersCollectionNamespace, tenant);
+ Status status = insertAuthzDocument(opCtx, nss, userObj);
if (status.isOK()) {
return status;
}
@@ -1005,7 +1017,7 @@ void CmdUMCTyped<CreateUserCommand>::Invocation::typedRun(OperationContext* opCt
uassert(ErrorCodes::BadValue,
"Username cannot contain NULL characters",
cmd.getCommandParameter().find('\0') == std::string::npos);
- UserName userName(cmd.getCommandParameter(), dbname);
+ UserName userName(cmd.getCommandParameter(), dbname, cmd.getTenantOverride());
uassert(ErrorCodes::BadValue,
"Must provide a 'pwd' field for all user documents, except those"
@@ -1042,8 +1054,7 @@ void CmdUMCTyped<CreateUserCommand>::Invocation::typedRun(OperationContext* opCt
BSONObjBuilder userObjBuilder;
userObjBuilder.append("_id", userName.getUnambiguousName());
UUID::gen().appendToBuilder(&userObjBuilder, AuthorizationManager::USERID_FIELD_NAME);
- userObjBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, userName.getUser());
- userObjBuilder.append(AuthorizationManager::USER_DB_FIELD_NAME, userName.getDB());
+ userName.appendToBSON(&userObjBuilder);
auto* serviceContext = opCtx->getClient()->getServiceContext();
auto* authzManager = AuthorizationManager::get(serviceContext);
@@ -1089,7 +1100,7 @@ void CmdUMCTyped<CreateUserCommand>::Invocation::typedRun(OperationContext* opCt
authRestrictionsArray);
// Must invalidate even on bad status
- auto status = insertPrivilegeDocument(opCtx, userObj);
+ auto status = insertPrivilegeDocument(opCtx, userObj, userName.getTenant());
authzManager->invalidateUserByName(opCtx, userName);
uassertStatusOK(status);
}
diff --git a/src/mongo/db/commands/user_management_commands.idl b/src/mongo/db/commands/user_management_commands.idl
index 86911ba5528..720e8663ab5 100644
--- a/src/mongo/db/commands/user_management_commands.idl
+++ b/src/mongo/db/commands/user_management_commands.idl
@@ -127,6 +127,12 @@ commands:
description: "List of valid authentication mechanisms for the user"
type: array<string>
optional: true
+ "$tenant":
+ # Only available with enableTestCommands and multitenancySupport
+ description: "Associate this user with a specific tenant"
+ type: objectid
+ cpp_name: tenantOverride
+ optional: true
updateUser:
description: "Modify a user"
diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp
index bc89b9efed2..e6c4539a724 100644
--- a/src/mongo/db/commands/user_management_commands_common.cpp
+++ b/src/mongo/db/commands/user_management_commands_common.cpp
@@ -41,11 +41,14 @@
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/resource_pattern.h"
+#include "mongo/db/auth/security_token_gen.h"
#include "mongo/db/auth/user.h"
#include "mongo/db/auth/user_management_commands_parser.h"
+#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/commands/user_management_commands_gen.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/multitenancy.h"
+#include "mongo/db/multitenancy_gen.h"
#include "mongo/util/sequence_util.h"
#include "mongo/util/str.h"
@@ -188,6 +191,16 @@ void checkAuthForTypedCommand(OperationContext* opCtx, const CreateUserCommand&
as->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname),
ActionType::createUser));
+ if (request.getTenantOverride() != boost::none) {
+ const bool isNotTokenAuth = (as->getAuthenticationMode() !=
+ AuthorizationSession::AuthenticationMode::kSecurityToken);
+
+ uassert(ErrorCodes::Unauthorized,
+ "$tenant parameter to createUser command only accepted in "
+ "test mode with security tokens enabled but not in use",
+ getTestCommandsEnabled() && gMultitenancySupport && isNotTokenAuth);
+ }
+
auto resolvedRoles = resolveRoleNames(request.getRoles(), dbname);
uassertStatusOK(checkAuthorizedToGrantRoles(as, resolvedRoles));
diff --git a/src/mongo/db/multitenancy.cpp b/src/mongo/db/multitenancy.cpp
index 2db3089e091..fbe5d6e651a 100644
--- a/src/mongo/db/multitenancy.cpp
+++ b/src/mongo/db/multitenancy.cpp
@@ -44,7 +44,7 @@ boost::optional<OID> getActiveTenant(OperationContext* opCtx) {
return boost::none;
}
- return token->getTenant();
+ return token->getAuthenticatedUser().getTenant();
}
} // namespace mongo
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 5acaa1e2614..8fa9892aacf 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -44,6 +44,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/client.h"
#include "mongo/db/command_can_run_here.h"
#include "mongo/db/commands.h"
@@ -634,6 +635,7 @@ private:
OperationSessionInfoFromClient _sessionOptions;
boost::optional<ResourceConsumption::ScopedMetricsCollector> _scopedMetrics;
boost::optional<ImpersonationSessionGuard> _impersonationSessionGuard;
+ boost::optional<auth::SecurityTokenAuthenticationGuard> _tokenAuthorizationSessionGuard;
std::unique_ptr<PolymorphicScoped> _scoped;
bool _refreshedDatabase = false;
bool _refreshedCollection = false;
@@ -1288,6 +1290,8 @@ void ExecCommandDatabase::_initiateCommand() {
});
rpc::readRequestMetadata(opCtx, request, command->requiresAuth());
+ _tokenAuthorizationSessionGuard.emplace(opCtx);
+
rpc::TrackingMetadata::get(opCtx).initWithOperName(command->getName());
auto const replCoord = repl::ReplicationCoordinator::get(opCtx);
diff --git a/src/mongo/embedded/embedded_auth_session.cpp b/src/mongo/embedded/embedded_auth_session.cpp
index c26f16c3781..9b7f07c1658 100644
--- a/src/mongo/embedded/embedded_auth_session.cpp
+++ b/src/mongo/embedded/embedded_auth_session.cpp
@@ -215,6 +215,14 @@ public:
// Do nothing
}
+ AuthenticationMode getAuthenticationMode() const override {
+ return AuthenticationMode::kNone;
+ }
+
+ void logoutSecurityTokenUser(Client* client) override {
+ UASSERT_NOT_IMPLEMENTED;
+ }
+
protected:
std::tuple<std::vector<UserName>*, std::vector<RoleName>*> _getImpersonations() override {
UASSERT_NOT_IMPLEMENTED;
diff --git a/src/mongo/rpc/metadata/security_token_metadata_test.cpp b/src/mongo/rpc/metadata/security_token_metadata_test.cpp
index 00c664c4fc7..1319dae55c7 100644
--- a/src/mongo/rpc/metadata/security_token_metadata_test.cpp
+++ b/src/mongo/rpc/metadata/security_token_metadata_test.cpp
@@ -30,6 +30,7 @@
#include "mongo/platform/basic.h"
#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/client.h"
@@ -43,14 +44,28 @@ namespace rpc {
namespace test {
namespace {
+constexpr auto kAuthenticatedUserFieldName = "authenticatedUser"_sd;
constexpr auto kPingFieldName = "ping"_sd;
-constexpr auto kTenantFieldName = "tenant"_sd;
+constexpr auto kSigFieldName = "sig"_sd;
+
+BSONObj makeSecurityToken(const UserName& userName) {
+ auto authUser = userName.toBSON(true /* serialize token */);
+ ASSERT_EQ(authUser["tenant"_sd].type(), jstOID);
+
+ BSONObjBuilder token;
+ token.append(kAuthenticatedUserFieldName, authUser);
+
+ auto block = SHA256Block::computeHash({ConstDataRange(authUser.objdata(), authUser.objsize())});
+ token.appendBinData(kSigFieldName, block.size(), BinDataGeneral, block.data());
+
+ return token.obj();
+}
class SecurityTokenMetadataTest : public LockerNoopServiceContextTest {};
TEST_F(SecurityTokenMetadataTest, SecurityTokenNotAccepted) {
const auto kPingBody = BSON(kPingFieldName << 1);
- const auto kTokenBody = BSON(kTenantFieldName << OID::gen());
+ const auto kTokenBody = makeSecurityToken(UserName("user", "admin", OID::gen()));
gMultitenancySupport = false;
auto msgBytes = OpMsgBytes{0, kBodySection, kPingBody, kSecurityTokenSection, kTokenBody};
@@ -63,7 +78,7 @@ TEST_F(SecurityTokenMetadataTest, SecurityTokenNotAccepted) {
TEST_F(SecurityTokenMetadataTest, BasicSuccess) {
const auto kOid = OID::gen();
const auto kPingBody = BSON(kPingFieldName << 1);
- const auto kTokenBody = BSON(kTenantFieldName << kOid);
+ const auto kTokenBody = makeSecurityToken(UserName("user", "admin", kOid));
gMultitenancySupport = true;
auto msg = OpMsgBytes{0, kBodySection, kPingBody, kSecurityTokenSection, kTokenBody}.parse();
@@ -77,7 +92,12 @@ TEST_F(SecurityTokenMetadataTest, BasicSuccess) {
auth::readSecurityTokenMetadata(opCtx.get(), msg.securityToken);
auto token = auth::getSecurityToken(opCtx.get());
ASSERT(token != boost::none);
- ASSERT_EQ(token->getTenant(), kOid);
+
+ auto authedUser = token->getAuthenticatedUser();
+ ASSERT_EQ(authedUser.getUser(), "user");
+ ASSERT_EQ(authedUser.getDB(), "admin");
+ ASSERT_TRUE(authedUser.getTenant() != boost::none);
+ ASSERT_EQ(authedUser.getTenant().get(), kOid);
}
} // namespace
diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp
index aec44e0c723..79f89b28c57 100644
--- a/src/mongo/shell/shell_utils.cpp
+++ b/src/mongo/shell/shell_utils.cpp
@@ -415,6 +415,32 @@ BSONObj convertShardKeyToHashed(const BSONObj& a, void* data) {
return BSON("" << key);
}
+/**
+ * Generate a security token suitable for passing in an OpMsg payload token field.
+ *
+ * @param user object - { user: 'name', db: 'dbname', tenant: OID }
+ * @return object - { authenticatedUser: {...user object...}, sig: BinDataGeneral(Signature) }
+ */
+BSONObj _createSecurityToken(const BSONObj& args, void* data) {
+ uassert(6161500,
+ "_createSecurityToken requires a single object argument",
+ (args.nFields() == 1) && (args.firstElement().type() == Object));
+ auto authUser = args.firstElement().Obj();
+
+ // Temporary algorithm.
+ auto digest =
+ SHA256Block::computeHash({ConstDataRange(authUser.objdata(), authUser.objsize())});
+
+ BSONObjBuilder ret;
+ {
+ BSONObjBuilder token(ret.subobjStart(""_sd));
+ token.append("authenticatedUser"_sd, authUser);
+ token.appendBinData("sig"_sd, digest.size(), BinDataGeneral, digest.data());
+ token.doneFast();
+ }
+ return ret.obj();
+}
+
BSONObj replMonitorStats(const BSONObj& a, void* data) {
uassert(17134,
"replMonitorStats requires a single string argument (the ReplSet name)",
@@ -478,6 +504,7 @@ BSONObj numberDecimalsEqual(const BSONObj& input, void*) {
void installShellUtils(Scope& scope) {
scope.injectNative("getMemInfo", JSGetMemInfo);
+ scope.injectNative("_createSecurityToken", _createSecurityToken);
scope.injectNative("_replMonitorStats", replMonitorStats);
scope.injectNative("_srand", JSSrand);
scope.injectNative("_rand", JSRand);