summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Midvidy <amidvidy@gmail.com>2015-06-10 22:10:30 -0400
committerAdam Midvidy <amidvidy@gmail.com>2015-06-16 16:25:38 -0400
commit4eb15b34d157e2719fc9034f5956b698d96ef64e (patch)
treedbdb1714147eba77fd24f92d04d05db87afc0c3f
parent2bf407c955f383a29d3d10fc6be273d9c6890961 (diff)
downloadmongo-4eb15b34d157e2719fc9034f5956b698d96ef64e.tar.gz
SERVER-18236 SERVER-18292 send impersonated users and roles via metadata object
-rw-r--r--src/mongo/db/audit.cpp4
-rw-r--r--src/mongo/db/audit.h10
-rw-r--r--src/mongo/db/auth/SConscript4
-rw-r--r--src/mongo/db/auth/impersonation_session.cpp79
-rw-r--r--src/mongo/db/auth/impersonation_session.h48
-rw-r--r--src/mongo/db/commands.h4
-rw-r--r--src/mongo/db/dbcommands.cpp118
-rw-r--r--src/mongo/rpc/SConscript1
-rw-r--r--src/mongo/rpc/metadata.cpp57
-rw-r--r--src/mongo/rpc/metadata/audit_metadata.cpp89
-rw-r--r--src/mongo/rpc/metadata/audit_metadata.h93
-rw-r--r--src/mongo/s/client/sharding_connection_hook.cpp8
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.