diff options
23 files changed, 491 insertions, 222 deletions
diff --git a/jstests/auth/curop_auth_info.js b/jstests/auth/curop_auth_info.js new file mode 100644 index 00000000000..f59a4388916 --- /dev/null +++ b/jstests/auth/curop_auth_info.js @@ -0,0 +1,77 @@ +(function() { + 'use strict'; + + const runTest = function(conn, failPointConn) { + jsTestLog("Setting up users"); + const db = conn.getDB("admin"); + assert.commandWorked( + db.runCommand({createUser: "admin", pwd: "pwd", roles: jsTest.adminUserRoles})); + assert.eq(db.auth("admin", "pwd"), 1); + assert.commandWorked(db.runCommand({createUser: "testuser", pwd: "pwd", roles: []})); + db.grantRolesToUser("testuser", [{role: "readWrite", db: "test"}]); + + const queryFn = function() { + assert.eq(db.getSiblingDB("admin").auth("testuser", "pwd"), 1); + let testDB = db.getSiblingDB("test"); + testDB.test.insert({}); + assert.eq(testDB.test.find({}).comment("curop_auth_info.js query").itcount(), 1); + }; + + jsTestLog("blocking finds and starting parallel shell to create op"); + assert.commandWorked(failPointConn.getDB("admin").runCommand( + {configureFailPoint: "waitInFindBeforeMakingBatch", mode: "alwaysOn"})); + let finderWait = startParallelShell(queryFn, conn.port); + let myOp; + + assert.soon(function() { + const curOpResults = db.runCommand({currentOp: 1}); + assert.commandWorked(curOpResults); + print(tojson(curOpResults)); + const myOps = curOpResults["inprog"].filter((op) => { + return (op["command"]["comment"] == "curop_auth_info.js query"); + }); + + if (myOps.length == 0) { + return false; + } + myOp = myOps[0]; + return true; + }); + + jsTestLog("found op"); + assert.commandWorked(failPointConn.getDB("admin").runCommand( + {configureFailPoint: "waitInFindBeforeMakingBatch", mode: "off"})); + finderWait(); + + const authedUsers = myOp["effectiveUsers"]; + const impersonators = myOp["userImpersonators"]; + print(tojson(authedUsers), tojson(impersonators)); + if (impersonators) { + assert.eq(authedUsers.length, 1); + assert.docEq(authedUsers[0], {user: "testuser", db: "admin"}); + assert(impersonators); + assert.eq(impersonators.length, 1); + assert.docEq(impersonators[0], {user: "__system", db: "local"}); + } else { + assert(authedUsers); + assert.eq(authedUsers.length, 1); + assert.docEq(authedUsers[0], {user: "testuser", db: "admin"}); + } + }; + + const m = MongoRunner.runMongod(); + runTest(m, m); + MongoRunner.stopMongod(m); + + const st = new ShardingTest({ + shards: 1, + mongos: 1, + config: 1, + keyFile: 'jstests/libs/key1', + other: { + shardAsReplicaSet: false, + } + }); + runTest(st.s0, st.d0); + st.stop(); +})(); diff --git a/src/mongo/db/audit.cpp b/src/mongo/db/audit.cpp index 0ae7a5bb946..c8d783d1546 100644 --- a/src/mongo/db/audit.cpp +++ b/src/mongo/db/audit.cpp @@ -171,7 +171,4 @@ void mongo::audit::logShardCollection(Client* client, const BSONObj& keyPattern, bool unique) {} -void mongo::audit::writeImpersonatedUsersToMetadata(OperationContext* opCtx, - BSONObjBuilder* metadata) {} - #endif diff --git a/src/mongo/db/audit.h b/src/mongo/db/audit.h index 60619a4b776..6e2c3fae986 100644 --- a/src/mongo/db/audit.h +++ b/src/mongo/db/audit.h @@ -311,13 +311,5 @@ void logRemoveShard(Client* client, StringData shardname); */ void logShardCollection(Client* client, StringData ns, const BSONObj& keyPattern, bool unique); - -/* - * Appends an array of user/db pairs and an array of role/db pairs - * 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 writeImpersonatedUsersToMetadata(OperationContext* opCtx, BSONObjBuilder* metadataBob); - } // namespace audit } // namespace mongo diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index 35c418f448d..a8f89e223b1 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -18,16 +18,6 @@ generateActionTypes = env.Command( env.Alias('generated-sources', generateActionTypes) env.Library( - target='auth_rolename', - source=[ - 'role_name.cpp' - ], - LIBDEPS=[ - '$BUILD_DIR/mongo/base', - ], -) - -env.Library( target='authentication_restriction', source=[ 'restriction_environment.cpp', @@ -58,24 +48,12 @@ env.Library( 'authorization_manager.cpp', 'authorization_session.cpp', 'auth_decorations.cpp', - ], - LIBDEPS=[ - 'auth_rolename', - 'user_name', - '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/service_context', - '$BUILD_DIR/mongo/rpc/audit_metadata', - ], -) - - -env.Library( - target='user_name', - source=[ 'user_name.cpp', + 'role_name.cpp' ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context', ], ) @@ -154,7 +132,6 @@ env.Library( LIBDEPS=[ 'address_restriction', 'auth', - 'auth_rolename', 'authentication_restriction', 'authorization_manager_global', 'authprivilege', @@ -196,6 +173,9 @@ env.Library( '$BUILD_DIR/mongo/bson/mutable/mutable_bson', '$BUILD_DIR/mongo/db/common', ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/rpc/metadata_impersonated_user', + ] ) env.Library( @@ -299,7 +279,6 @@ env.Library( 'authprivilege', 'sasl_options', 'user', - 'user_name', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/base/secure_allocator', '$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}', diff --git a/src/mongo/db/auth/auth_types.idl b/src/mongo/db/auth/auth_types.idl new file mode 100644 index 00000000000..f035dc7f488 --- /dev/null +++ b/src/mongo/db/auth/auth_types.idl @@ -0,0 +1,38 @@ +# Copyright (C) 2018 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/>. +# + +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/db/auth/user_name.h" + - "mongo/db/auth/role_name.h" + +imports: + - "mongo/idl/basic_types.idl" + +types: + UserName: + bson_serialization_type: any + description: "A struct representing a UserName" + cpp_type: "UserName" + deserializer: "mongo::UserName::parseFromBSON" + serializer: "mongo::UserName::serializeToBSON" + + RoleName: + bson_serialization_type: any + description: "A struct representing a Role" + cpp_type: "RoleName" + deserializer: "mongo::RoleName::parseFromBSON" + serializer: "mongo::RoleName::serializeToBSON" diff --git a/src/mongo/db/auth/impersonation_session.cpp b/src/mongo/db/auth/impersonation_session.cpp index 8b9a5f09fc7..666522cd043 100644 --- a/src/mongo/db/auth/impersonation_session.cpp +++ b/src/mongo/db/auth/impersonation_session.cpp @@ -39,7 +39,7 @@ #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/rpc/metadata/impersonated_user_metadata.h" #include "mongo/util/assert_util.h" #include "mongo/util/destructor_guard.h" @@ -47,28 +47,24 @@ namespace mongo { ImpersonationSessionGuard::ImpersonationSessionGuard(OperationContext* opCtx) : _opCtx(opCtx) { auto authSession = AuthorizationSession::get(_opCtx->getClient()); - - const auto& impersonatedUsersAndRoles = - rpc::AuditMetadata::get(opCtx).getImpersonatedUsersAndRoles(); - - if (impersonatedUsersAndRoles != boost::none) { + const auto impersonatedUsersAndRoles = rpc::getImpersonatedUserMetadata(opCtx); + if (impersonatedUsersAndRoles) { 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)); + authSession->setImpersonatedUserData(impersonatedUsersAndRoles->getUsers(), + impersonatedUsersAndRoles->getRoles()); _active = true; + return; } } ImpersonationSessionGuard::~ImpersonationSessionGuard() { - DESTRUCTOR_GUARD(if (_active) { + if (_active) { AuthorizationSession::get(_opCtx->getClient())->clearImpersonatedUserData(); - }) + } } } // namespace mongo diff --git a/src/mongo/db/auth/impersonation_session.h b/src/mongo/db/auth/impersonation_session.h index a2986cb0e86..e32363be4e9 100644 --- a/src/mongo/db/auth/impersonation_session.h +++ b/src/mongo/db/auth/impersonation_session.h @@ -27,6 +27,7 @@ */ #include "mongo/base/disallow_copying.h" +#include "mongo/rpc/metadata/impersonated_user_metadata.h" namespace mongo { class OperationContext; @@ -43,6 +44,7 @@ public: ~ImpersonationSessionGuard(); private: + rpc::MaybeImpersonatedUserMetadata _oldImpersonationData; OperationContext* _opCtx; bool _active{false}; }; diff --git a/src/mongo/db/auth/role_name.cpp b/src/mongo/db/auth/role_name.cpp index 6c22d03a7c5..a49b80e1fba 100644 --- a/src/mongo/db/auth/role_name.cpp +++ b/src/mongo/db/auth/role_name.cpp @@ -33,6 +33,7 @@ #include <string> #include "mongo/base/string_data.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/util/assert_util.h" namespace mongo { @@ -52,4 +53,41 @@ std::ostream& operator<<(std::ostream& os, const RoleName& name) { return os << name.getFullName(); } +RoleName RoleName::parseFromBSON(const BSONElement& elem) { + auto obj = elem.embeddedObjectUserCheck(); + std::array<BSONElement, 2> fields; + obj.getFields( + {AuthorizationManager::ROLE_NAME_FIELD_NAME, AuthorizationManager::ROLE_DB_FIELD_NAME}, + &fields); + const auto& nameField = fields[0]; + uassert(ErrorCodes::BadValue, + str::stream() << "user name must contain a string field named: " + << AuthorizationManager::ROLE_NAME_FIELD_NAME, + nameField.type() == String); + + const auto& dbField = fields[1]; + uassert(ErrorCodes::BadValue, + str::stream() << "role name must contain a string field named: " + << AuthorizationManager::ROLE_DB_FIELD_NAME, + nameField.type() == String); + + return RoleName(nameField.valueStringData(), dbField.valueStringData()); +} + +void RoleName::serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const { + BSONObjBuilder sub(bob->subobjStart(fieldName)); + _serializeToSubObj(&sub); +} + +void RoleName::serializeToBSON(BSONArrayBuilder* bob) const { + BSONObjBuilder sub(bob->subobjStart()); + _serializeToSubObj(&sub); +} + +void RoleName::_serializeToSubObj(BSONObjBuilder* sub) const { + sub->append(AuthorizationManager::ROLE_NAME_FIELD_NAME, getRole()); + sub->append(AuthorizationManager::ROLE_DB_FIELD_NAME, getDB()); +} + + } // namespace mongo diff --git a/src/mongo/db/auth/role_name.h b/src/mongo/db/auth/role_name.h index 5d9981cf9dc..809aa2c4d69 100644 --- a/src/mongo/db/auth/role_name.h +++ b/src/mongo/db/auth/role_name.h @@ -36,6 +36,8 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/string_data.h" +#include "mongo/bson/bsonelement.h" +#include "mongo/bson/bsonobjbuilder.h" #include "mongo/platform/hash_namespace.h" #include "mongo/util/assert_util.h" @@ -51,6 +53,11 @@ public: RoleName() : _splitPoint(0) {} RoleName(StringData role, StringData dbname); + // Added for IDL support + static RoleName parseFromBSON(const BSONElement& elem); + void serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const; + void serializeToBSON(BSONArrayBuilder* bob) const; + /** * Gets the name of the role excluding the "@dbname" component. */ @@ -88,6 +95,8 @@ public: private: std::string _fullName; // The full name, stored as a string. "role@db". size_t _splitPoint; // The index of the "@" separating the role and db name parts. + + void _serializeToSubObj(BSONObjBuilder* sub) const; }; static inline bool operator==(const RoleName& lhs, const RoleName& rhs) { @@ -213,4 +222,13 @@ RoleNameIterator makeRoleNameIteratorForContainer(const Container& container) { return makeRoleNameIterator(container.begin(), container.end()); } +template <typename Container> +Container roleNameIteratorToContainer(RoleNameIterator it) { + Container container; + while (it.more()) { + container.emplace_back(it.next()); + } + return container; +} + } // namespace mongo diff --git a/src/mongo/db/auth/user_name.cpp b/src/mongo/db/auth/user_name.cpp index 3bcdf3ad813..dd16bc993b3 100644 --- a/src/mongo/db/auth/user_name.cpp +++ b/src/mongo/db/auth/user_name.cpp @@ -32,6 +32,7 @@ #include <string> #include "mongo/base/string_data.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/util/assert_util.h" namespace mongo { @@ -62,6 +63,49 @@ StatusWith<UserName> UserName::parse(StringData userNameStr) { return UserName(userNamePortion, userDBPortion); } +UserName UserName::parseFromBSON(const BSONElement& elem) { + if (elem.type() == String) { + return uassertStatusOK(UserName::parse(elem.valueStringData())); + } else if (elem.type() == Object) { + const auto obj = elem.embeddedObject(); + std::array<BSONElement, 2> fields; + obj.getFields( + {AuthorizationManager::USER_NAME_FIELD_NAME, AuthorizationManager::USER_DB_FIELD_NAME}, + &fields); + + const auto& nameField = fields[0]; + uassert(ErrorCodes::BadValue, + str::stream() << "username must contain a string field named: " + << AuthorizationManager::USER_NAME_FIELD_NAME, + nameField.type() == String); + + const auto& dbField = fields[1]; + uassert(ErrorCodes::BadValue, + str::stream() << "username must contain a string field named: " + << AuthorizationManager::USER_DB_FIELD_NAME, + dbField.type() == String); + + return UserName(nameField.valueStringData(), dbField.valueStringData()); + } else { + uasserted(ErrorCodes::BadValue, "username must be either a string or an object"); + } +} + +void UserName::serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const { + BSONObjBuilder sub(bob->subobjStart(fieldName)); + _serializeToSubObj(&sub); +} + +void UserName::serializeToBSON(BSONArrayBuilder* bab) const { + BSONObjBuilder sub(bab->subobjStart()); + _serializeToSubObj(&sub); +} + +void UserName::_serializeToSubObj(BSONObjBuilder* sub) const { + *sub << AuthorizationManager::USER_NAME_FIELD_NAME << getUser() + << AuthorizationManager::USER_DB_FIELD_NAME << getDB(); +} + std::ostream& operator<<(std::ostream& os, const UserName& name) { return os << name.getFullName(); } diff --git a/src/mongo/db/auth/user_name.h b/src/mongo/db/auth/user_name.h index a1b04f010d4..22c62db2d82 100644 --- a/src/mongo/db/auth/user_name.h +++ b/src/mongo/db/auth/user_name.h @@ -36,6 +36,8 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status_with.h" #include "mongo/base/string_data.h" +#include "mongo/bson/bsonelement.h" +#include "mongo/bson/bsonobjbuilder.h" namespace mongo { @@ -54,6 +56,13 @@ public: */ static StatusWith<UserName> parse(StringData userNameStr); + /* + * These methods support parsing usernames from IDL + */ + static UserName parseFromBSON(const BSONElement& elem); + void serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const; + void serializeToBSON(BSONArrayBuilder* bob) const; + /** * Gets the user part of a UserName. */ @@ -95,6 +104,8 @@ public: } private: + void _serializeToSubObj(BSONObjBuilder* sub) const; + std::string _fullName; // The full name, stored as a string. "user@db". size_t _splitPoint; // The index of the "@" separating the user and db name parts. }; diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp index dbe90b901f4..a8576d29120 100644 --- a/src/mongo/db/curop.cpp +++ b/src/mongo/db/curop.cpp @@ -38,6 +38,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/bson/mutable/document.h" +#include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/commands/server_status_metric.h" @@ -47,6 +48,7 @@ #include "mongo/db/query/plan_summary_stats.h" #include "mongo/rpc/metadata/client_metadata.h" #include "mongo/rpc/metadata/client_metadata_ismaster.h" +#include "mongo/rpc/metadata/impersonated_user_metadata.h" #include "mongo/util/hex.h" #include "mongo/util/log.h" #include "mongo/util/net/socket_utils.h" @@ -249,6 +251,32 @@ void CurOp::reportCurrentOpForClient(OperationContext* opCtx, infoBuilder->append("currentOpTime", opCtx->getServiceContext()->getPreciseClockSource()->now().toString()); + auto authSession = AuthorizationSession::get(client); + // Depending on whether we're impersonating or not, this might be "effectiveUsers" or + // "userImpersonators". + const auto serializeAuthenticatedUsers = [&](StringData name) { + if (authSession->isAuthenticated()) { + BSONArrayBuilder users(infoBuilder->subarrayStart(name)); + for (auto userIt = authSession->getAuthenticatedUserNames(); userIt.more(); + userIt.next()) { + userIt->serializeToBSON(&users); + } + } + }; + + auto maybeImpersonationData = rpc::getImpersonatedUserMetadata(clientOpCtx); + if (maybeImpersonationData) { + BSONArrayBuilder users(infoBuilder->subarrayStart("effectiveUsers")); + for (const auto& user : maybeImpersonationData->getUsers()) { + user.serializeToBSON(&users); + } + + users.doneFast(); + serializeAuthenticatedUsers("userImpersonators"_sd); + } else { + serializeAuthenticatedUsers("effectiveUsers"_sd); + } + if (clientOpCtx) { infoBuilder->append("opid", clientOpCtx->getOpID()); if (clientOpCtx->isKillPending()) { diff --git a/src/mongo/embedded/embedded_auth_session.cpp b/src/mongo/embedded/embedded_auth_session.cpp index fecb4e6d561..e01e0210e0b 100644 --- a/src/mongo/embedded/embedded_auth_session.cpp +++ b/src/mongo/embedded/embedded_auth_session.cpp @@ -80,7 +80,9 @@ public: } bool isAuthenticated() override { - UASSERT_NOT_IMPLEMENTED; + // It should always be okay to check whether you're authenticated, but on embedded + // it should always return false + return false; } UserNameIterator getAuthenticatedUserNames() override { diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript index 53757b9fcb7..f7883dbc9e2 100644 --- a/src/mongo/rpc/SConscript +++ b/src/mongo/rpc/SConscript @@ -80,7 +80,7 @@ env.Library( ], LIBDEPS=[ 'client_metadata', - 'audit_metadata', + 'metadata_impersonated_user', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/bson/util/bson_extract', '$BUILD_DIR/mongo/client/read_preference', @@ -90,20 +90,21 @@ env.Library( ], ) -env.Clone().InjectModule("enterprise").Library( +env.Library( target=[ - 'audit_metadata', + 'metadata_impersonated_user' ], source=[ - 'metadata/audit_metadata.cpp', - ], - LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/bson/util/bson_extract', - '$BUILD_DIR/mongo/db/auth/auth_rolename', - '$BUILD_DIR/mongo/db/auth/user_name', + 'metadata/impersonated_user_metadata.cpp', + env.Idlc('metadata/impersonated_user_metadata.idl')[0], ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/server_options', ], ) diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp index 74360ead237..6359c9e1952 100644 --- a/src/mongo/rpc/metadata.cpp +++ b/src/mongo/rpc/metadata.cpp @@ -36,9 +36,9 @@ #include "mongo/db/jsobj.h" #include "mongo/db/logical_clock.h" #include "mongo/db/logical_time_validator.h" -#include "mongo/rpc/metadata/audit_metadata.h" #include "mongo/rpc/metadata/client_metadata_ismaster.h" #include "mongo/rpc/metadata/config_server_metadata.h" +#include "mongo/rpc/metadata/impersonated_user_metadata.h" #include "mongo/rpc/metadata/logical_time_metadata.h" #include "mongo/rpc/metadata/sharding_metadata.h" #include "mongo/rpc/metadata/tracking_metadata.h" @@ -53,18 +53,16 @@ BSONObj makeEmptyMetadata() { void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj, bool requiresAuth) { BSONElement readPreferenceElem; - BSONElement auditElem; BSONElement configSvrElem; BSONElement trackingElem; BSONElement clientElem; BSONElement logicalTimeElem; + BSONElement impersonationElem; for (const auto& metadataElem : metadataObj) { auto fieldName = metadataElem.fieldNameStringData(); if (fieldName == "$readPreference") { readPreferenceElem = metadataElem; - } else if (fieldName == AuditMetadata::fieldName()) { - auditElem = metadataElem; } else if (fieldName == ConfigServerMetadata::fieldName()) { configSvrElem = metadataElem; } else if (fieldName == ClientMetadata::fieldName()) { @@ -73,6 +71,8 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj, bo trackingElem = metadataElem; } else if (fieldName == LogicalTimeMetadata::fieldName()) { logicalTimeElem = metadataElem; + } else if (fieldName == kImpersonationMetadataSectionName) { + impersonationElem = metadataElem; } } @@ -81,7 +81,7 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj, bo uassertStatusOK(ReadPreferenceSetting::fromInnerBSON(readPreferenceElem)); } - AuditMetadata::get(opCtx) = uassertStatusOK(AuditMetadata::readFromMetadata(auditElem)); + readImpersonatedUserMetadata(impersonationElem, opCtx); uassertStatusOK(ClientMetadataIsMasterState::readFromMetadata(opCtx, clientElem)); diff --git a/src/mongo/rpc/metadata/audit_metadata.cpp b/src/mongo/rpc/metadata/audit_metadata.cpp deleted file mode 100644 index 1dacabd7dc4..00000000000 --- a/src/mongo/rpc/metadata/audit_metadata.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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{}; -} - -StatusWith<AuditMetadata> AuditMetadata::readFromMetadata(const BSONElement&) { - return AuditMetadata{}; -} - -Status AuditMetadata::writeToMetadata(BSONObjBuilder*) const { - return Status::OK(); -} - -#endif - -const boost::optional<AuditMetadata::UsersAndRoles>& AuditMetadata::getImpersonatedUsersAndRoles() - const { - return _impersonatedUsersAndRoles; -} - -} // namespace rpc -} // namespace mongo diff --git a/src/mongo/rpc/metadata/audit_metadata.h b/src/mongo/rpc/metadata/audit_metadata.h deleted file mode 100644 index 0724001d97c..00000000000 --- a/src/mongo/rpc/metadata/audit_metadata.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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/auth/role_name.h" -#include "mongo/db/auth/user_name.h" -#include "mongo/db/operation_context.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); - - /** - * Parses AuditMetadata from a pre-extracted BSONElement. When reading a metadata object, this - * form is more efficient as it permits parsing the metadata in one pass. - */ - static StatusWith<AuditMetadata> readFromMetadata(const BSONElement& metadataElem); - - Status writeToMetadata(BSONObjBuilder* metadataBob) const; - - using UsersAndRoles = std::tuple<std::vector<UserName>, std::vector<RoleName>>; - - const boost::optional<UsersAndRoles>& getImpersonatedUsersAndRoles() const; - - AuditMetadata(boost::optional<UsersAndRoles> impersonatedUsersAndRoles); - - static StringData fieldName() { - return "$audit"; - } - -private: - boost::optional<UsersAndRoles> _impersonatedUsersAndRoles; -}; - -} // namespace rpc -} // namespace mongo diff --git a/src/mongo/rpc/metadata/impersonated_user_metadata.cpp b/src/mongo/rpc/metadata/impersonated_user_metadata.cpp new file mode 100644 index 00000000000..b8e9e77c595 --- /dev/null +++ b/src/mongo/rpc/metadata/impersonated_user_metadata.cpp @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2018 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/impersonated_user_metadata.h" + +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/server_options.h" + +namespace mongo { +namespace rpc { +namespace { + +static const OperationContext::Decoration<MaybeImpersonatedUserMetadata> getForOpCtx = + OperationContext::declareDecoration<MaybeImpersonatedUserMetadata>(); +} // namespace + +MaybeImpersonatedUserMetadata getImpersonatedUserMetadata(OperationContext* opCtx) { + return opCtx ? getForOpCtx(opCtx) : boost::none; +} + +void readImpersonatedUserMetadata(const BSONElement& elem, OperationContext* opCtx) { + // If we have no opCtx, which does appear to happen, don't do anything. + if (!opCtx) { + return; + } + + // Always reset the current impersonation data to boost::none. + getForOpCtx(opCtx) = boost::none; + if (elem.type() == Object) { + IDLParserErrorContext errCtx(kImpersonationMetadataSectionName); + auto data = ImpersonatedUserMetadata::parse(errCtx, elem.embeddedObject()); + + // Set the impersonation data only if there are actually impersonated + // users/roles. + if ((!data.getUsers().empty()) || (!data.getRoles().empty())) { + getForOpCtx(opCtx) = std::move(data); + } + } +} + +void writeAuthDataToImpersonatedUserMetadata(OperationContext* opCtx, BSONObjBuilder* out) { + // If we have no opCtx, which does appear to happen, don't do anything. + if (!opCtx) { + return; + } + + // Otherwise construct a metadata section from the list of authenticated users/roles + auto authSession = AuthorizationSession::get(opCtx->getClient()); + ImpersonatedUserMetadata metadata; + metadata.setUsers(userNameIteratorToContainer<std::vector<UserName>>( + authSession->getAuthenticatedUserNames())); + + metadata.setRoles(roleNameIteratorToContainer<std::vector<RoleName>>( + authSession->getAuthenticatedRoleNames())); + + // If there are no users/roles being impersonated just exit + if (metadata.getUsers().empty() && metadata.getRoles().empty()) { + return; + } + + BSONObjBuilder section(out->subobjStart(kImpersonationMetadataSectionName)); + metadata.serialize(§ion); +} + +} // namespace rpc +} // namespace mongo diff --git a/src/mongo/rpc/metadata/impersonated_user_metadata.h b/src/mongo/rpc/metadata/impersonated_user_metadata.h new file mode 100644 index 00000000000..f379b509d4c --- /dev/null +++ b/src/mongo/rpc/metadata/impersonated_user_metadata.h @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2018 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 "mongo/db/operation_context.h" +#include "mongo/rpc/metadata/impersonated_user_metadata_gen.h" + +namespace mongo { +namespace rpc { + +/* + * The name of the BSON element in message metadata that contains the impersonated user data + * + * This is called "$audit" because in pre-4.2 the enterprise audit subsystem already passed + * the impersonated users info around in metadata for auditing purposes. This has been lifted + * into the community edition and appears in network messages as "$audit" for backwards + * compatibility. + * + * This metadata should only appear in requests from mongos to mongod. + */ +static constexpr auto kImpersonationMetadataSectionName = "$audit"_sd; + +/* + * A type that maybe contains parsed ImpersonationUserMetadata + */ +using MaybeImpersonatedUserMetadata = boost::optional<ImpersonatedUserMetadata>; + +/* + * Gets the current impersonation data from the OpCtx (assumes readImpersonatedUserMetadata + * has already been called) + */ +MaybeImpersonatedUserMetadata getImpersonatedUserMetadata(OperationContext* opCtx); + +/* + * Parses any impersonation data out of a metdata bson obj and into the opCtx + */ +void readImpersonatedUserMetadata(const BSONElement& elem, OperationContext* opCtx); + +/* + * Writes the current impersonation metadata off the opCtx and into a BSONObjBuilder + */ +void writeAuthDataToImpersonatedUserMetadata(OperationContext* opCtx, BSONObjBuilder* out); + +} // namespace rpc +} // namespace mongo diff --git a/src/mongo/rpc/metadata/impersonated_user_metadata.idl b/src/mongo/rpc/metadata/impersonated_user_metadata.idl new file mode 100644 index 00000000000..c2b15e325fc --- /dev/null +++ b/src/mongo/rpc/metadata/impersonated_user_metadata.idl @@ -0,0 +1,35 @@ +# Copyright (C) 2018 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/>. +# + +global: + cpp_namespace: "mongo::rpc" + +imports: + - "mongo/idl/basic_types.idl" + - "mongo/db/auth/auth_types.idl" + +structs: + # This is the IDL version of the old enterprise-only Audit metadata struct + ImpersonatedUserMetadata: + description: "A struct representing the impersonated users from a mongos" + strict: true + fields: + "$impersonatedUsers": + type: array<UserName> + cpp_name: "users" + "$impersonatedRoles": + type: array<RoleName> + cpp_name: "roles" + diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 522419420c8..1dbc4f3d9a0 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -333,7 +333,6 @@ env.Library( 'sharding_egress_metadata_hook.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/audit', '$BUILD_DIR/mongo/util/concurrency/thread_pool', 'grid', ], diff --git a/src/mongo/s/sharding_egress_metadata_hook.cpp b/src/mongo/s/sharding_egress_metadata_hook.cpp index d0f9a2f39e2..0c42bd93cc7 100644 --- a/src/mongo/s/sharding_egress_metadata_hook.cpp +++ b/src/mongo/s/sharding_egress_metadata_hook.cpp @@ -33,10 +33,10 @@ #include <string> #include "mongo/base/status.h" -#include "mongo/db/audit.h" #include "mongo/db/service_context.h" #include "mongo/rpc/metadata/client_metadata_ismaster.h" #include "mongo/rpc/metadata/config_server_metadata.h" +#include "mongo/rpc/metadata/impersonated_user_metadata.h" #include "mongo/rpc/metadata/metadata_hook.h" #include "mongo/rpc/metadata/repl_set_metadata.h" #include "mongo/s/client/shard_registry.h" @@ -55,7 +55,7 @@ ShardingEgressMetadataHook::ShardingEgressMetadataHook(ServiceContext* serviceCo Status ShardingEgressMetadataHook::writeRequestMetadata(OperationContext* opCtx, BSONObjBuilder* metadataBob) { try { - audit::writeImpersonatedUsersToMetadata(opCtx, metadataBob); + writeAuthDataToImpersonatedUserMetadata(opCtx, metadataBob); ClientMetadataIsMasterState::writeToMetadata(opCtx, metadataBob); rpc::ConfigServerMetadata(_getConfigServerOpTime()).writeToMetadata(metadataBob); return Status::OK(); diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index 95a167f88c3..cbdad7a5273 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -111,7 +111,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/auth/auth_rolename', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/third_party/shim_asio', 'network', 'socket', |