diff options
author | Adam Midvidy <amidvidy@gmail.com> | 2015-06-10 22:10:30 -0400 |
---|---|---|
committer | Adam Midvidy <amidvidy@gmail.com> | 2015-06-16 16:25:38 -0400 |
commit | 4eb15b34d157e2719fc9034f5956b698d96ef64e (patch) | |
tree | dbdb1714147eba77fd24f92d04d05db87afc0c3f | |
parent | 2bf407c955f383a29d3d10fc6be273d9c6890961 (diff) | |
download | mongo-4eb15b34d157e2719fc9034f5956b698d96ef64e.tar.gz |
SERVER-18236 SERVER-18292 send impersonated users and roles via metadata object
-rw-r--r-- | src/mongo/db/audit.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/audit.h | 10 | ||||
-rw-r--r-- | src/mongo/db/auth/SConscript | 4 | ||||
-rw-r--r-- | src/mongo/db/auth/impersonation_session.cpp | 79 | ||||
-rw-r--r-- | src/mongo/db/auth/impersonation_session.h | 48 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 4 | ||||
-rw-r--r-- | src/mongo/db/dbcommands.cpp | 118 | ||||
-rw-r--r-- | src/mongo/rpc/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/rpc/metadata.cpp | 57 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/audit_metadata.cpp | 89 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/audit_metadata.h | 93 | ||||
-rw-r--r-- | src/mongo/s/client/sharding_connection_hook.cpp | 8 |
12 files changed, 402 insertions, 113 deletions
diff --git a/src/mongo/db/audit.cpp b/src/mongo/db/audit.cpp index 4e08cdb2a5b..b07c137c8ab 100644 --- a/src/mongo/db/audit.cpp +++ b/src/mongo/db/audit.cpp @@ -196,17 +196,15 @@ namespace audit { const BSONObj& keyPattern, bool unique) MONGO_AUDIT_STUB - void appendImpersonatedUsers(BSONObjBuilder* cmd) MONGO_AUDIT_STUB + void writeImpersonatedUsersToMetadata(BSONObjBuilder* metadata) MONGO_AUDIT_STUB void parseAndRemoveImpersonatedUsersField( BSONObj cmdObj, - AuthorizationSession* authSession, std::vector<UserName>* parsedUserNames, bool* fieldIsPresent) MONGO_AUDIT_STUB void parseAndRemoveImpersonatedRolesField( BSONObj cmdObj, - AuthorizationSession* authSession, std::vector<RoleName>* parsedRoleNames, bool* fieldIsPresent) MONGO_AUDIT_STUB diff --git a/src/mongo/db/audit.h b/src/mongo/db/audit.h index 74fff7581cc..fa8604871f5 100644 --- a/src/mongo/db/audit.h +++ b/src/mongo/db/audit.h @@ -328,12 +328,10 @@ namespace audit { /* * Appends an array of user/db pairs and an array of role/db pairs - * to the provided Document. The users and roles are extracted from the current client. + * to the provided metadata builder. The users and roles are extracted from the current client. * They are to be the impersonated users and roles for a Command run by an internal user. */ - void appendImpersonatedUsers(BSONObjBuilder* cmd); - const char cmdOptionImpersonatedUsers[] = "impersonatedUsers"; - const char cmdOptionImpersonatedRoles[] = "impersonatedRoles"; + void writeImpersonatedUsersToMetadata(BSONObjBuilder* metadataBob); /* * Looks for an 'impersonatedUsers' field. This field is used by mongos to @@ -345,13 +343,11 @@ namespace audit { * command BSON to efficiently remove the field before returning. * * cmdObj [in, out]: If any impersonated users field exists, it will be parsed and removed. - * authSession [in]: current authorization session * parsedUserNames [out]: populated with parsed usernames * fieldIsPresent [out]: true if impersonatedUsers field was present in the object */ void parseAndRemoveImpersonatedUsersField( BSONObj cmdObj, - AuthorizationSession* authSession, std::vector<UserName>* parsedUserNames, bool* fieldIsPresent); @@ -365,13 +361,11 @@ namespace audit { * command BSON to efficiently remove the field before returning. * * cmdObj [in, out]: If any impersonated roles field exists, it will be parsed and removed. - * authSession [in]: current authorization session * parsedRoleNames [out]: populated with parsed user rolenames * fieldIsPresent [out]: true if impersonatedRoles field was present in the object */ void parseAndRemoveImpersonatedRolesField( BSONObj cmdObj, - AuthorizationSession* authSession, std::vector<RoleName>* parsedRoleNames, bool* fieldIsPresent); diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index c07d1cb04bb..a3847c0c101 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -55,6 +55,7 @@ env.Library('authorization_manager_global', ], LIBDEPS=[ 'authcore', + '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/service_context', ]) @@ -99,7 +100,8 @@ env.Library('saslauth', env.Library('authmongod', ['authz_manager_external_state_d.cpp', 'authz_session_external_state_d.cpp', - 'auth_index_d.cpp'], + 'auth_index_d.cpp', + 'impersonation_session.cpp'], LIBDEPS=[ 'authservercommon', diff --git a/src/mongo/db/auth/impersonation_session.cpp b/src/mongo/db/auth/impersonation_session.cpp new file mode 100644 index 00000000000..eb8bf54a6f9 --- /dev/null +++ b/src/mongo/db/auth/impersonation_session.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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/platform/basic.h" + +#include "mongo/db/auth/impersonation_session.h" + +#include <boost/optional.hpp> +#include <tuple> + +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/client.h" +#include "mongo/db/operation_context.h" +#include "mongo/rpc/metadata/audit_metadata.h" +#include "mongo/util/assert_util.h" + +namespace mongo { + + ImpersonationSessionGuard::ImpersonationSessionGuard(OperationContext* txn) + : _txn(txn) { + + auto authSession = AuthorizationSession::get(_txn->getClient()); + + const auto& impersonatedUsersAndRoles = + rpc::AuditMetadata::get(txn).getImpersonatedUsersAndRoles(); + + if (impersonatedUsersAndRoles != boost::none) { + + uassert(ErrorCodes::Unauthorized, + "Unauthorized use of impersonation metadata.", + authSession->isAuthorizedForPrivilege( + Privilege(ResourcePattern::forClusterResource(), + ActionType::impersonate))); + + fassert(ErrorCodes::InternalError, !authSession->isImpersonating()); + + authSession->setImpersonatedUserData(std::get<0>(*impersonatedUsersAndRoles), + std::get<1>(*impersonatedUsersAndRoles)); + _active = true; + } + } + + ImpersonationSessionGuard::~ImpersonationSessionGuard() { + DESTRUCTOR_GUARD( + if (_active) { + AuthorizationSession::get(_txn->getClient())->clearImpersonatedUserData(); + } + ) + } + +} // namespace mongo diff --git a/src/mongo/db/auth/impersonation_session.h b/src/mongo/db/auth/impersonation_session.h new file mode 100644 index 00000000000..ab05bf1e5c3 --- /dev/null +++ b/src/mongo/db/auth/impersonation_session.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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/base/disallow_copying.h" + +namespace mongo { + class OperationContext; + + /** + * RAII class to optionally set an impersonated username list into the authorization session + * for the duration of the life of this object. + */ + class ImpersonationSessionGuard { + MONGO_DISALLOW_COPYING(ImpersonationSessionGuard); + public: + ImpersonationSessionGuard(OperationContext* txn); + ~ImpersonationSessionGuard(); + private: + OperationContext* _txn; + bool _active{false}; + }; + +} // namespace mongo diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 615d6c07581..2c1a9edf76a 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -106,14 +106,10 @@ namespace mutablebson { /** * Translation point between the new request/response types and the legacy types. * - * TODO: Remove interposedCmd once we have refactored metadata (SERVER-18236). * Then we won't need to mutate the command object. At that point we can also make * this method virtual so commands can override it directly. - * - * This function is also temporarily defined in dbcommands.cpp */ /*virtual*/ bool run(OperationContext* txn, - const BSONObj& interposedCmd, const rpc::RequestInterface& request, rpc::ReplyBuilderInterface* replyBuilder); diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index ba2ef712ff9..1f8ba52123d 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -44,6 +44,7 @@ #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/impersonation_session.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/user_management_commands_parser.h" #include "mongo/db/auth/user_name.h" @@ -1124,36 +1125,6 @@ namespace mongo { bool maintenanceModeSet; }; - - /** - * RAII class to optionally set an impersonated username list into the authorization session - * for the duration of the life of this object - */ - class ImpersonationSessionGuard { - MONGO_DISALLOW_COPYING(ImpersonationSessionGuard); - public: - ImpersonationSessionGuard(AuthorizationSession* authSession, - bool fieldIsPresent, - const std::vector<UserName> &parsedUserNames, - const std::vector<RoleName> &parsedRoleNames): - _authSession(authSession), _impersonation(false) { - if (fieldIsPresent) { - massert(17317, "impersonation unexpectedly active", - !authSession->isImpersonating()); - authSession->setImpersonatedUserData(parsedUserNames, parsedRoleNames); - _impersonation = true; - } - } - ~ImpersonationSessionGuard() { - if (_impersonation) { - _authSession->clearImpersonatedUserData(); - } - } - private: - AuthorizationSession* _authSession; - bool _impersonation; - }; - /** * this handles - auth @@ -1180,10 +1151,6 @@ namespace mongo { dassert(replyBuilder->getState() == rpc::ReplyBuilderInterface::State::kMetadata); - // Right now our metadata handling relies on mutating the command object. - // This will go away when SERVER-18236 is implemented - BSONObj interposedCmd = request.getCommandArgs(); - std::string dbname = request.getDatabase().toString(); unique_ptr<MaintenanceModeSetter> mmSetter; @@ -1193,40 +1160,14 @@ namespace mongo { return; } - // Handle command option impersonatedUsers and impersonatedRoles. - // This must come before _checkAuthorization(), as there is some command parsing logic - // in that code path that must not see the impersonated user and roles array elements. - std::vector<UserName> parsedUserNames; - std::vector<RoleName> parsedRoleNames; - AuthorizationSession* authSession = AuthorizationSession::get(txn->getClient()); - bool rolesFieldIsPresent = false; - bool usersFieldIsPresent = false; - - // TODO: Remove these once the metadata refactor (SERVER-18236) is complete. - // Then we can construct the ImpersonationSessionGuard directly from the contents of the - // metadata object rather than slicing elements off of the command object. - audit::parseAndRemoveImpersonatedRolesField(interposedCmd, - authSession, - &parsedRoleNames, - &rolesFieldIsPresent); - audit::parseAndRemoveImpersonatedUsersField(interposedCmd, - authSession, - &parsedUserNames, - &usersFieldIsPresent); - - uassert(ErrorCodes::IncompatibleAuditMetadata, - "Audit metadata does not include both user and role information.", - rolesFieldIsPresent == usersFieldIsPresent); - - ImpersonationSessionGuard impersonationSession(authSession, - usersFieldIsPresent, - parsedUserNames, - parsedRoleNames); - - uassertStatusOK(_checkAuthorization(command, - txn->getClient(), - dbname, - interposedCmd)); + ImpersonationSessionGuard guard(txn); + + uassertStatusOK( + _checkAuthorization(command, + txn->getClient(), + dbname, + request.getCommandArgs()) + ); { repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); @@ -1283,12 +1224,12 @@ namespace mongo { // Handle command option maxTimeMS. int maxTimeMS = uassertStatusOK( - LiteParsedQuery::parseMaxTimeMSCommand(interposedCmd) + LiteParsedQuery::parseMaxTimeMSCommand(request.getCommandArgs()) ); uassert(ErrorCodes::InvalidOptions, "no such command option $maxTimeMs; use maxTimeMS instead", - !interposedCmd.hasField("$maxTimeMS")); + !request.getCommandArgs().hasField("$maxTimeMS")); CurOp::get(txn)->setMaxTimeMicros(static_cast<unsigned long long>(maxTimeMS) * 1000); @@ -1302,7 +1243,7 @@ namespace mongo { command->_commandsExecuted.increment(); - retval = command->run(txn, interposedCmd, request, replyBuilder); + retval = command->run(txn, request, replyBuilder); dassert(replyBuilder->getState() == rpc::ReplyBuilderInterface::State::kOutputDocs); @@ -1320,32 +1261,16 @@ namespace mongo { // structure. // It will be moved back as part of SERVER-18236. bool Command::run(OperationContext* txn, - const BSONObj& prevInterposedCmd, const rpc::RequestInterface& request, rpc::ReplyBuilderInterface* replyBuilder) { - // Implementation just forwards to the old method signature for now. - std::string errmsg; BSONObjBuilder replyBuilderBob; - // run expects non-const bsonobj - BSONObj interposedCmd = prevInterposedCmd; - - // run expects const db std::string (can't bind to temporary) - const std::string db = request.getDatabase().toString(); - - int queryFlags = 0; - std::tie(std::ignore, queryFlags) = uassertStatusOK( - rpc::downconvertRequestMetadata(request.getCommandArgs(), - request.getMetadata()) - ); - repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); - { // Handle read after opTime. repl::ReadAfterOpTimeArgs readAfterOptimeSettings; - auto readAfterParseStatus = readAfterOptimeSettings.initialize(interposedCmd); + auto readAfterParseStatus = readAfterOptimeSettings.initialize(request.getCommandArgs()); if (!readAfterParseStatus.isOK()) { replyBuilder ->setMetadata(rpc::makeEmptyMetadata()) @@ -1363,7 +1288,17 @@ namespace mongo { } } - bool result = this->run(txn, db, interposedCmd, queryFlags, errmsg, replyBuilderBob); + // run expects non-const bsonobj + BSONObj cmd = request.getCommandArgs(); + // Implementation just forwards to the old method signature for now. + std::string errmsg; + + // run expects const db std::string (can't bind to temporary) + const std::string db = request.getDatabase().toString(); + + // TODO: remove queryOptions parameter from command's run method. + bool result = this->run(txn, db, cmd, 0, errmsg, replyBuilderBob); + BSONObjBuilder metadataBob; // For commands from mongos, append some info to help getLastError(w) work. @@ -1376,16 +1311,17 @@ namespace mongo { ).writeToMetadata(&metadataBob); } - replyBuilder->setMetadata(metadataBob.done()); - + auto metadata = metadataBob.done(); auto cmdResponse = replyBuilderBob.done(); if (result) { + replyBuilder->setMetadata(std::move(metadata)); replyBuilder->setCommandReply(std::move(cmdResponse)); } else { // maintain existing behavior of returning all data appended to builder // even if command returned false + replyBuilder->setMetadata(std::move(metadata)); replyBuilder->setCommandReply(Status(ErrorCodes::CommandFailed, errmsg), std::move(cmdResponse)); } diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript index 23a682b8b8b..b976ad2643d 100644 --- a/src/mongo/rpc/SConscript +++ b/src/mongo/rpc/SConscript @@ -114,6 +114,7 @@ env.Library( ], source=[ 'metadata.cpp', + 'metadata/audit_metadata.cpp', 'metadata/server_selection_metadata.cpp', 'metadata/sharding_metadata.cpp', ], diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp index 43e4cf37eff..e7725dd4a74 100644 --- a/src/mongo/rpc/metadata.cpp +++ b/src/mongo/rpc/metadata.cpp @@ -50,6 +50,11 @@ namespace rpc { } ServerSelectionMetadata::get(txn) = std::move(swServerSelectionMetadata.getValue()); + auto swAuditMetadata = AuditMetadata::readFromMetadata(metadataObj); + if (!swAuditMetadata.isOK()) { + return swAuditMetadata.getStatus(); + } + AuditMetadata::get(txn) = std::move(swAuditMetadata.getValue()); return Status::OK(); } @@ -65,24 +70,66 @@ namespace rpc { } StatusWith<CommandAndMetadata> upconvertRequestMetadata(BSONObj legacyCmdObj, int queryFlags) { - BSONObjBuilder commandBob; + // We can reuse the same metadata BOB for every upconvert call, but we need to keep + // making new command BOBs as each metadata bob will need to remove fields. We can not use + // mutablebson here because the ServerSelectionMetadata upconvert routine performs + // manipulations (replacing a root with its child) that mutablebson doesn't + // support. BSONObjBuilder metadataBob; + // Ordering is important here - ServerSelectionMetadata must be upconverted + // first, then AuditMetadata. + BSONObjBuilder ssmCommandBob; auto upconvertStatus = ServerSelectionMetadata::upconvert(legacyCmdObj, queryFlags, - &commandBob, + &ssmCommandBob, &metadataBob); if (!upconvertStatus.isOK()) { return upconvertStatus; } - return std::make_tuple(commandBob.obj(), metadataBob.obj()); + + BSONObjBuilder auditCommandBob; + upconvertStatus = AuditMetadata::upconvert(ssmCommandBob.done(), + queryFlags, + &auditCommandBob, + &metadataBob); + + if (!upconvertStatus.isOK()) { + return upconvertStatus; + } + + + return std::make_tuple(auditCommandBob.obj(), metadataBob.obj()); } StatusWith<LegacyCommandAndFlags> downconvertRequestMetadata(BSONObj cmdObj, BSONObj metadata) { int legacyQueryFlags = 0; - BSONObjBuilder legacyCommandBob; + BSONObjBuilder auditCommandBob; + // Ordering is important here - AuditingMetadata must be downconverted first, + // then ServerSelectionMetadata. + auto downconvertStatus = AuditMetadata::downconvert(cmdObj, + metadata, + &auditCommandBob, + &legacyQueryFlags); + if (!downconvertStatus.isOK()) { + return downconvertStatus; + } + + + BSONObjBuilder ssmCommandBob; + downconvertStatus = ServerSelectionMetadata::downconvert(auditCommandBob.done(), + metadata, + &ssmCommandBob, + &legacyQueryFlags); + if (!downconvertStatus.isOK()) { + return downconvertStatus; + } + + + return std::make_tuple(ssmCommandBob.obj(), std::move(legacyQueryFlags)); + } StatusWith<CommandReplyWithMetadata> upconvertReplyMetadata(BSONObj legacyReply) { BSONObjBuilder commandReplyBob; @@ -108,7 +155,7 @@ namespace rpc { return downconvertStatus; } - return std::make_tuple(legacyCommandBob.obj(), std::move(legacyQueryFlags)); + return legacyCommandReplyBob.obj(); } } // namespace rpc diff --git a/src/mongo/rpc/metadata/audit_metadata.cpp b/src/mongo/rpc/metadata/audit_metadata.cpp new file mode 100644 index 00000000000..4b17101147f --- /dev/null +++ b/src/mongo/rpc/metadata/audit_metadata.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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/platform/basic.h" + +#include "mongo/rpc/metadata/audit_metadata.h" + +#include <utility> + +#include "mongo/base/status_with.h" +#include "mongo/bson/util/bson_extract.h" +#include "mongo/db/auth/role_name.h" +#include "mongo/db/auth/user_name.h" + +namespace mongo { +namespace rpc { + + const OperationContext::Decoration<AuditMetadata> AuditMetadata::get = + OperationContext::declareDecoration<AuditMetadata>(); + + AuditMetadata::AuditMetadata( + boost::optional<UsersAndRoles> impersonatedUsersAndRoles + ) + : _impersonatedUsersAndRoles(std::move(impersonatedUsersAndRoles)) + {} + +#if !defined(MONGO_ENTERPRISE_VERSION) + + StatusWith<AuditMetadata> AuditMetadata::readFromMetadata(const BSONObj&) { + return AuditMetadata(boost::none); + } + + Status AuditMetadata::writeToMetadata(BSONObjBuilder*) const { + return Status::OK(); + } + + Status AuditMetadata::downconvert(const BSONObj& command, + const BSONObj&, + BSONObjBuilder* commandBob, + int*) { + commandBob->appendElements(command); + return Status::OK(); + } + + Status AuditMetadata::upconvert(const BSONObj& command, + const int, + BSONObjBuilder* commandBob, + BSONObjBuilder*) { + commandBob->appendElements(command); + return Status::OK(); + } + +#endif + + const boost::optional<AuditMetadata::UsersAndRoles>& + AuditMetadata::getImpersonatedUsersAndRoles() const { + return _impersonatedUsersAndRoles; + } + + const char kLegacyImpersonatedUsersFieldName[] = "impersonatedUsers"; + const char kLegacyImpersonatedRolesFieldName[] = "impersonatedRoles"; + +} // namespace rpc +} // namespace mongo diff --git a/src/mongo/rpc/metadata/audit_metadata.h b/src/mongo/rpc/metadata/audit_metadata.h new file mode 100644 index 00000000000..70cc54d7482 --- /dev/null +++ b/src/mongo/rpc/metadata/audit_metadata.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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 <vector> + +#include "mongo/base/disallow_copying.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/auth/user_name.h" +#include "mongo/db/auth/role_name.h" + +namespace mongo { + class BSONObj; + class BSONObjBuilder; + class Status; + template <typename T> class StatusWith; + +namespace rpc { + + /** + * This class comprises the request metadata fields involving auditing. + */ + class AuditMetadata { + public: + static const OperationContext::Decoration<AuditMetadata> get; + + // Decorable requires a default constructor. + AuditMetadata() = default; + + static StatusWith<AuditMetadata> readFromMetadata(const BSONObj& metadataObj); + + Status writeToMetadata(BSONObjBuilder* metadataBob) const; + + static Status downconvert(const BSONObj& command, + const BSONObj& metadata, + BSONObjBuilder* legacyCommandBob, + int* legacyQueryFlags); + + static Status upconvert(const BSONObj& legacyCommand, + const int legacyQueryFlags, + BSONObjBuilder* commandBob, + BSONObjBuilder* metadataBob); + + using UsersAndRoles = std::tuple<std::vector<UserName>, + std::vector<RoleName>>; + + const boost::optional<UsersAndRoles>& getImpersonatedUsersAndRoles() const; + + AuditMetadata(boost::optional<UsersAndRoles> impersonatedUsersAndRoles); + + private: + boost::optional<UsersAndRoles> _impersonatedUsersAndRoles; + }; + + /** + * The legacy field name used to hold impersonated users. + */ + extern const char kLegacyImpersonatedUsersFieldName[]; + + /** + * The legacy field name used to hold impersonated roles. + */ + extern const char kLegacyImpersonatedRolesFieldName[]; + +} // namespace rpc +} // namespace mongo diff --git a/src/mongo/s/client/sharding_connection_hook.cpp b/src/mongo/s/client/sharding_connection_hook.cpp index 49e27684bfd..b1567441d83 100644 --- a/src/mongo/s/client/sharding_connection_hook.cpp +++ b/src/mongo/s/client/sharding_connection_hook.cpp @@ -36,7 +36,10 @@ #include "mongo/db/audit.h" #include "mongo/db/auth/authorization_manager_global.h" +#include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/internal_user_auth.h" +#include "mongo/db/client.h" +#include "mongo/rpc/metadata/audit_metadata.h" #include "mongo/s/client/scc_fast_query_handler.h" #include "mongo/s/cluster_last_error_info.h" #include "mongo/s/version_manager.h" @@ -114,7 +117,10 @@ namespace { // For every DBClient created by mongos, add a hook that will append impersonated users // to the end of every runCommand. mongod uses this information to produce auditing // records attributed to the proper authenticated user(s). - conn->setRunCommandHook(stdx::bind(&audit::appendImpersonatedUsers, stdx::placeholders::_1)); + conn->setRequestMetadataWriter([](BSONObjBuilder* metadataBob) -> Status { + audit::writeImpersonatedUsersToMetadata(metadataBob); + return Status::OK(); + }); // For every SCC created, add a hook that will allow fastest-config-first config reads if // the appropriate server options are set. |