diff options
author | Spencer T Brody <spencer@10gen.com> | 2013-06-27 17:10:39 -0400 |
---|---|---|
committer | Spencer T Brody <spencer@10gen.com> | 2013-07-01 17:41:53 -0400 |
commit | 90f1d8947a26b330accfaf69dc25ee1d892891d1 (patch) | |
tree | 320cfc0363fbbbb09b85f1743b200cb76cd0949b /src/mongo | |
parent | d15b27260f62349d9d9aac0b53d60eaf284492c3 (diff) | |
download | mongo-90f1d8947a26b330accfaf69dc25ee1d892891d1.tar.gz |
SERVER-9518 Initial implementation of acquire/releaseUser methods in AuthorizationManager
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/SConscript | 11 | ||||
-rw-r--r-- | src/mongo/db/auth/SConscript | 8 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.cpp | 204 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 50 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager_test.cpp | 98 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_mock.cpp | 68 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_mock.h | 23 | ||||
-rw-r--r-- | src/mongo/db/auth/user.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/user.h | 3 |
9 files changed, 446 insertions, 20 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 8c5676551ad..80959452bee 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -618,7 +618,8 @@ env.CppUnitTest("shard_conn_test", [ "s/shard_conn_test.cpp" ], "coredb", "dbcmdline", "mongodandmongos", - "mocklib"], + "mocklib", + "$BUILD_DIR/mongo/db/auth/authmocks"], NO_CRUTCH=True) env.CppUnitTest("shard_test", [ "s/shard_test.cpp" ], @@ -735,7 +736,10 @@ Default( mongod ) # tools allToolFiles = [ "tools/tool.cpp", "tools/stat_util.cpp" ] -env.StaticLibrary("alltools", allToolFiles, LIBDEPS=["serveronly", "coreserver", "coredb"]) +env.StaticLibrary("alltools", allToolFiles, LIBDEPS=["serveronly", + "coreserver", + "coredb", + "$BUILD_DIR/mongo/db/auth/authmocks"]) normalTools = [ "dump", "restore", "export", "import", "stat", "top", "oplog" ] env.Alias( "tools", [ "#/${PROGPREFIX}mongo" + x + "${PROGSUFFIX}" for x in normalTools ] ) @@ -792,7 +796,8 @@ test = testEnv.Install( "testframework", "gridfs", "s/upgrade", - "mocklib"])) + "mocklib", + "$BUILD_DIR/mongo/db/auth/authmocks"])) if len(testEnv.subst('$PROGSUFFIX')): testEnv.Alias( "test", "#/${PROGPREFIX}test${PROGSUFFIX}" ) diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index b0176d75842..994d49f40ea 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -44,11 +44,15 @@ env.StaticLibrary('authmongos', 'authz_session_external_state_s.cpp'], LIBDEPS=['authservercommon']) +env.StaticLibrary('authmocks', + ['authz_manager_external_state_mock.cpp'], + LIBDEPS=['$BUILD_DIR/mongo/expressions']) + env.CppUnitTest('action_set_test', 'action_set_test.cpp', LIBDEPS=['authcore']) env.CppUnitTest('principal_set_test', 'principal_set_test.cpp', LIBDEPS=['authcore']) env.CppUnitTest('privilege_set_test', 'privilege_set_test.cpp', LIBDEPS=['authcore']) env.CppUnitTest('role_graph_test', 'role_graph_test.cpp', LIBDEPS=['authcore']) env.CppUnitTest('authorization_manager_test', 'authorization_manager_test.cpp', - LIBDEPS=['authcore']) + LIBDEPS=['authcore', 'authmocks']) env.CppUnitTest('authorization_session_test', 'authorization_session_test.cpp', - LIBDEPS=['authcore']) + LIBDEPS=['authcore', 'authmocks']) diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 85e64c68a8d..703d6d3f0e2 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -14,19 +14,27 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "mongo/platform/basic.h" + +#include "mongo/db/auth/authorization_manager.h" + +#include <boost/thread/mutex.hpp> +#include <memory> +#include <string> +#include <vector> + #include "mongo/base/init.h" #include "mongo/base/status.h" #include "mongo/db/auth/action_set.h" -#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/privilege_set.h" +#include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" +#include "mongo/db/auth/user_name_hash.h" #include "mongo/db/jsobj.h" +#include "mongo/platform/unordered_map.h" #include "mongo/util/mongoutils/str.h" -#include <string> -#include <vector> - namespace mongo { AuthInfo::AuthInfo() { @@ -61,6 +69,12 @@ namespace { const std::string SYSTEM_ROLE_USER_ADMIN_ANY_DB = "userAdminAnyDatabase"; const std::string SYSTEM_ROLE_DB_ADMIN_ANY_DB = "dbAdminAnyDatabase"; + // System roles for backwards compatibility with 2.2 and prior + const std::string SYSTEM_ROLE_V0_READ = "oldRead"; + const std::string SYSTEM_ROLE_V0_READ_WRITE= "oldReadWrite"; + const std::string SYSTEM_ROLE_V0_ADMIN_READ = "oldAdminRead"; + const std::string SYSTEM_ROLE_V0_ADMIN_READ_WRITE= "oldAdminReadWrite"; + // ActionSets for the various system roles. These ActionSets contain all the actions that // a user of each system role is granted. ActionSet readRoleActions; @@ -159,6 +173,7 @@ namespace { clusterAdminRoleReadActions.addAction(ActionType::setShardVersion); // TODO: should this be internal? clusterAdminRoleReadActions.addAction(ActionType::serverStatus); clusterAdminRoleReadActions.addAction(ActionType::splitVector); + // Shutdown is in read actions b/c that's how it was in 2.2 clusterAdminRoleReadActions.addAction(ActionType::shutdown); clusterAdminRoleReadActions.addAction(ActionType::top); clusterAdminRoleReadActions.addAction(ActionType::touch); @@ -250,7 +265,14 @@ namespace { AuthorizationManager::AuthorizationManager(AuthzManagerExternalState* externalState) : - _externalState(externalState) {} + _version(1), _externalState(externalState) {} + + AuthorizationManager::~AuthorizationManager() { + for (unordered_map<UserName, User*>::iterator it = _userCache.begin(); + it != _userCache.end(); ++it) { + delete it->second ; + } + } AuthzManagerExternalState* AuthorizationManager::getExternalState() const { return _externalState.get(); @@ -504,6 +526,12 @@ namespace { else if (role == SYSTEM_ROLE_DB_ADMIN) { outPrivileges->push_back(Privilege(dbname, dbAdminRoleActions)); } + else if (role == SYSTEM_ROLE_V0_READ) { + outPrivileges->push_back(Privilege(dbname, compatibilityReadOnlyActions)); + } + else if (role == SYSTEM_ROLE_V0_READ_WRITE) { + outPrivileges->push_back(Privilege(dbname, compatibilityReadWriteActions)); + } else if (isAdminDB && role == SYSTEM_ROLE_READ_ANY_DB) { outPrivileges->push_back(Privilege(PrivilegeSet::WILDCARD_RESOURCE, readRoleActions)); } @@ -523,6 +551,14 @@ namespace { outPrivileges->push_back( Privilege(PrivilegeSet::WILDCARD_RESOURCE, clusterAdminRoleActions)); } + else if (isAdminDB && role == SYSTEM_ROLE_V0_ADMIN_READ) { + outPrivileges->push_back( + Privilege(PrivilegeSet::WILDCARD_RESOURCE, compatibilityReadOnlyAdminActions)); + } + else if (isAdminDB && role == SYSTEM_ROLE_V0_ADMIN_READ_WRITE) { + outPrivileges->push_back( + Privilege(PrivilegeSet::WILDCARD_RESOURCE, compatibilityReadWriteAdminActions)); + } else { warning() << "No such role, \"" << role << "\", in database " << dbname << ". No privileges will be acquired from this role" << endl; @@ -611,4 +647,162 @@ namespace { result->grantPrivileges(acquiredPrivileges, user); return Status::OK(); } + + Status AuthorizationManager::acquireUser(const UserName& userName, User** acquiredUser) { + boost::lock_guard<boost::mutex> lk(_lock); + unordered_map<UserName, User*>::iterator it = _userCache.find(userName); + if (it != _userCache.end()) { + fassert(16914, it->second); + it->second->incrementRefCount(); + *acquiredUser = it->second; + return Status::OK(); + } + + // Put the new user into an auto_ptr temporarily in case there's an error while + // initializing the user. + auto_ptr<User> userHolder(new User(userName)); + User* user = userHolder.get(); + + BSONObj userObj; + if (_version == 1) { + Status status = _externalState->getPrivilegeDocument(userName.getDB().toString(), + userName, + &userObj); + if (!status.isOK()) { + return status; + } + } else { + return Status(ErrorCodes::UnsupportedFormat, + mongoutils::str::stream() << + "Unrecognized authorization format version: " << _version); + } + + + Status status = _initializeUserFromPrivilegeDocument(user, userObj); + if (!status.isOK()) { + return status; + } + + user->incrementRefCount(); + _userCache.insert(make_pair(userName, userHolder.release())); + *acquiredUser = user; + return Status::OK(); + } + + void AuthorizationManager::releaseUser(User* user) { + boost::lock_guard<boost::mutex> lk(_lock); + user->decrementRefCount(); + if (user->getRefCount() == 0) { + _userCache.erase(user->getName()); + delete user; + } + } + + /** + * Parses privDoc and initializes the user's "credentials" field with the credential + * information extracted from the privilege document. + */ + Status _initializeUserCredentialsFromPrivilegeDocument(User* user, const BSONObj& privDoc) { + User::CredentialData credentials; + if (privDoc.hasField("pwd")) { + credentials.password = privDoc["pwd"].String(); + credentials.isExternal = false; + } + else if (privDoc.hasField("userSource")) { + std::string userSource = privDoc["userSource"].String(); + if (userSource != "$external") { + return Status(ErrorCodes::FailedToParse, + "Cannot extract credentials from user documents without a password " + "and with userSource != \"$external\""); + } else { + credentials.isExternal = true; + } + } else { + return Status(ErrorCodes::FailedToParse, + "Invalid user document: must have one of \"pwd\" and \"userSource\""); + } + + user->setCredentials(credentials); + return Status::OK(); + } + + void _initializeUserRolesFromV0PrivilegeDocument( + User* user, const BSONObj& privDoc, const StringData& dbname) { + bool readOnly = privDoc["readOnly"].trueValue(); + if (dbname == "admin") { + if (readOnly) { + user->addRole(RoleName(SYSTEM_ROLE_V0_ADMIN_READ, "admin")); + } else { + user->addRole(RoleName(SYSTEM_ROLE_V0_ADMIN_READ_WRITE, "admin")); + } + } else { + if (readOnly) { + user->addRole(RoleName(SYSTEM_ROLE_V0_READ, dbname)); + } else { + user->addRole(RoleName(SYSTEM_ROLE_V0_READ_WRITE, dbname)); + } + } + } + + Status _initializeUserRolesFromV1PrivilegeDocument( + User* user, const BSONObj& privDoc, const StringData& dbname) { + static const char privilegesTypeMismatchMessage[] = + "Roles in V1 user documents must be enumerated in an array of strings."; + + for (BSONObjIterator iter(privDoc["roles"].embeddedObject()); iter.more(); iter.next()) { + BSONElement roleElement = *iter; + if (roleElement.type() != String) + return Status(ErrorCodes::TypeMismatch, privilegesTypeMismatchMessage); + + user->addRole(RoleName(roleElement.String(), dbname)); + } + return Status::OK(); + } + + /** + * Parses privDoc and initializes the user's "roles" field with the role list extracted + * from the privilege document. + */ + Status _initializeUserRolesFromPrivilegeDocument( + User* user, const BSONObj& privDoc, const StringData& dbname) { + if (!privDoc.hasField("roles")) { + _initializeUserRolesFromV0PrivilegeDocument(user, privDoc, dbname); + } else { + return _initializeUserRolesFromV1PrivilegeDocument(user, privDoc, dbname); + } + // TODO(spencer): dassert that if you have a V0 or V1 privilege document that the _version + // of the system is 1. + return Status::OK(); + } + + /** + * Modifies the given User object by inspecting its roles and giving it the relevant + * privileges from those roles. + */ + void _initializeUserPrivilegesFromRoles(User* user) { + std::vector<Privilege> privileges; + + RoleNameIterator it = user->getRoles(); + while (it.more()) { + const RoleName& roleName = it.next(); + _addPrivilegesForSystemRole(roleName.getDB().toString(), + roleName.getRole().toString(), + &privileges); + } + user->addPrivileges(privileges); + } + + Status AuthorizationManager::_initializeUserFromPrivilegeDocument( + User* user, const BSONObj& privDoc) const { + Status status = _initializeUserCredentialsFromPrivilegeDocument(user, privDoc); + if (!status.isOK()) { + return status; + } + status = _initializeUserRolesFromPrivilegeDocument(user, privDoc, user->getName().getDB()); + if (!status.isOK()) { + return status; + } + _initializeUserPrivilegesFromRoles(user); + return Status::OK(); + } } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 730f72f6a11..956578fdbc4 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -17,6 +17,7 @@ #pragma once #include <boost/scoped_ptr.hpp> +#include <boost/thread/mutex.hpp> #include <string> #include "mongo/base/disallow_copying.h" @@ -24,8 +25,11 @@ #include "mongo/db/auth/action_set.h" #include "mongo/db/auth/authz_manager_external_state.h" #include "mongo/db/auth/privilege_set.h" +#include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" +#include "mongo/db/auth/user_name_hash.h" // TODO(spencer): Including this will slow compilation #include "mongo/db/jsobj.h" +#include "mongo/platform/unordered_map.h" namespace mongo { @@ -49,6 +53,8 @@ namespace mongo { // The newly constructed AuthorizationManager takes ownership of "externalState" explicit AuthorizationManager(AuthzManagerExternalState* externalState); + ~AuthorizationManager(); + static const std::string SERVER_RESOURCE_NAME; static const std::string CLUSTER_RESOURCE_NAME; @@ -119,6 +125,23 @@ namespace mongo { // include internal-only actions. ActionSet getAllUserActions() const; + /** + * Returns the User object for the given userName in the out param "acquiredUser". + * If the user cache already has a user object for this user, it increments the refcount + * on that object and gives out a pointer to it. If no user object for this user name + * exists yet in the cache, reads the user's privilege document from disk, builds up + * a User object, sets the refcount to 1, and gives that out. + * The AuthorizationManager retains ownership of the returned User object. + * On non-OK Status return values, acquiredUser will not be modified. + */ + Status acquireUser(const UserName& userName, User** acquiredUser); + + /** + * Decrements the refcount of the given User object. If the refcount has gone to zero, + * deletes the User. Caller must stop using its pointer to "user" after calling this. + */ + void releaseUser(User* user); + private: // Parses the old-style (pre 2.4) privilege document and returns a PrivilegeSet of all the @@ -140,6 +163,14 @@ namespace mongo { const BSONObj& privilegeDocument, PrivilegeSet* result) const; + /** + * Parses privDoc and initializes the user object with the information extracted from the + * privilege document. + */ + Status _initializeUserFromPrivilegeDocument(User* user, + const BSONObj& privDoc) const; + + static bool _doesSupportOldStylePrivileges; // True if access control enforcement is enabled on this node (ie it was started with @@ -147,7 +178,26 @@ namespace mongo { // This is a config setting, set at startup and not changing after initialization. static bool _authEnabled; + // Integer that represents what format version the privilege documents in the system are. + // The current version is 2. When upgrading to v2.6 or later from v2.4 or prior, the + // version is 1. After running the upgrade process to upgrade to the new privilege document + // format, the version will be 2. + int _version; + scoped_ptr<AuthzManagerExternalState> _externalState; + + /** + * Caches User objects with information about user privileges, to avoid the need to + * go to disk to read user privilege documents whenever possible. Every User object + * has a reference count - the AuthorizationManager must not delete a User object in the + * cache unless its reference count is zero. + */ + unordered_map<UserName, User*> _userCache; + + /** + * Protects _userCache. + */ + boost::mutex _lock; }; } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index a0aa62ffeaf..8adc3a478c6 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -18,6 +18,8 @@ */ #include "mongo/base/status.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authz_session_external_state_mock.h" #include "mongo/db/auth/authz_manager_external_state_mock.h" #include "mongo/db/auth/authorization_manager.h" @@ -36,8 +38,10 @@ namespace { class AuthorizationManagerTest : public ::mongo::unittest::Test { public: scoped_ptr<AuthorizationManager> authzManager; + AuthzManagerExternalStateMock* externalState; void setUp() { - authzManager.reset(new AuthorizationManager(new AuthzManagerExternalStateMock())); + externalState = new AuthzManagerExternalStateMock(); + authzManager.reset(new AuthorizationManager(externalState)); } }; @@ -493,5 +497,97 @@ namespace { "roles" << BSONArrayBuilder().arr()))); } + TEST_F(AuthorizationManagerTest, testAquireV0User) { + externalState->insertPrivilegeDocument("test", + BSON("user" << "v0RW" << + "pwd" << "password")); + externalState->insertPrivilegeDocument("admin", + BSON("user" << "v0AdminRO" << + "pwd" << "password" << + "readOnly" << true)); + + User* v0RW; + ASSERT_OK(authzManager->acquireUser(UserName("v0RW", "test"), &v0RW)); + ASSERT(UserName("v0RW", "test") == v0RW->getName()); + ASSERT(v0RW->isValid()); + ASSERT_EQUALS((uint32_t)1, v0RW->getRefCount()); + RoleNameIterator it = v0RW->getRoles(); + ASSERT(it.more()); + RoleName roleName = it.next(); + ASSERT_EQUALS("test", roleName.getDB()); + ASSERT_EQUALS("oldReadWrite", roleName.getRole()); + ASSERT_FALSE(it.more()); + ActionSet actions = v0RW->getActionsForResource("test"); + ASSERT(actions.contains(ActionType::find)); + ASSERT(actions.contains(ActionType::insert)); + ASSERT_FALSE(actions.contains(ActionType::shutdown)); + actions = v0RW->getActionsForResource("test2"); + ASSERT(actions.empty()); + actions = v0RW->getActionsForResource("admin"); + ASSERT(actions.empty()); + + User* v0AdminRO; + ASSERT_OK(authzManager->acquireUser(UserName("v0AdminRO", "admin"), &v0AdminRO)); + ASSERT(UserName("v0AdminRO", "admin") == v0AdminRO->getName()); + ASSERT(v0AdminRO->isValid()); + ASSERT_EQUALS((uint32_t)1, v0AdminRO->getRefCount()); + it = v0AdminRO->getRoles(); + ASSERT(it.more()); + roleName = it.next(); + ASSERT_EQUALS("admin", roleName.getDB()); + ASSERT_EQUALS("oldAdminRead", roleName.getRole()); + ASSERT_FALSE(it.more()); + actions = v0AdminRO->getActionsForResource("*"); + ASSERT(actions.contains(ActionType::find)); + ASSERT(actions.contains(ActionType::listDatabases)); + ASSERT_FALSE(actions.contains(ActionType::insert)); + ASSERT_FALSE(actions.contains(ActionType::dropDatabase)); + } + + TEST_F(AuthorizationManagerTest, testAquireV1User) { + externalState->insertPrivilegeDocument("test", + BSON("user" << "v1read" << + "pwd" << "password" << + "roles" << BSON_ARRAY("read"))); + externalState->insertPrivilegeDocument("admin", + BSON("user" << "v1cluster" << + "pwd" << "password" << + "roles" << BSON_ARRAY("clusterAdmin"))); + + User* v1read; + ASSERT_OK(authzManager->acquireUser(UserName("v1read", "test"), &v1read)); + ASSERT(UserName("v1read", "test") == v1read->getName()); + ASSERT(v1read->isValid()); + ASSERT_EQUALS((uint32_t)1, v1read->getRefCount()); + RoleNameIterator it = v1read->getRoles(); + ASSERT(it.more()); + RoleName roleName = it.next(); + ASSERT_EQUALS("test", roleName.getDB()); + ASSERT_EQUALS("read", roleName.getRole()); + ASSERT_FALSE(it.more()); + ActionSet actions = v1read->getActionsForResource("test"); + ASSERT(actions.contains(ActionType::find)); + ASSERT_FALSE(actions.contains(ActionType::insert)); + actions = v1read->getActionsForResource("test2"); + ASSERT(actions.empty()); + actions = v1read->getActionsForResource("admin"); + ASSERT(actions.empty()); + + User* v1cluster; + ASSERT_OK(authzManager->acquireUser(UserName("v1cluster", "admin"), &v1cluster)); + ASSERT(UserName("v1cluster", "admin") == v1cluster->getName()); + ASSERT(v1cluster->isValid()); + ASSERT_EQUALS((uint32_t)1, v1cluster->getRefCount()); + it = v1cluster->getRoles(); + ASSERT(it.more()); + roleName = it.next(); + ASSERT_EQUALS("admin", roleName.getDB()); + ASSERT_EQUALS("clusterAdmin", roleName.getRole()); + ASSERT_FALSE(it.more()); + actions = v1cluster->getActionsForResource("*"); + ASSERT(actions.contains(ActionType::listDatabases)); + ASSERT(actions.contains(ActionType::dropDatabase)); + ASSERT_FALSE(actions.contains(ActionType::find)); + } } // namespace } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp new file mode 100644 index 00000000000..7e2e97d824b --- /dev/null +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -0,0 +1,68 @@ +/* +* Copyright (C) 2013 10gen 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/>. +*/ + +#include "mongo/db/auth/authz_manager_external_state_mock.h" + +#include <string> + +#include "mongo/base/status.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/matcher/expression_parser.h" +#include "mongo/db/namespacestring.h" +#include "mongo/platform/unordered_map.h" + +namespace mongo { + + Status AuthzManagerExternalStateMock::insertPrivilegeDocument(const std::string& dbname, + const BSONObj& userObj) const { + return Status::OK(); + } + + Status AuthzManagerExternalStateMock::updatePrivilegeDocument(const UserName& user, + const BSONObj& updateObj) const { + return Status::OK(); + } + + Status AuthzManagerExternalStateMock::insertPrivilegeDocument(const std::string& dbname, + const BSONObj& userObj) { + _userDocuments.insert(make_pair(dbname, userObj)); + return Status::OK(); + } + + void AuthzManagerExternalStateMock::clearPrivilegeDocuments() { + _userDocuments.clear(); + } + + bool AuthzManagerExternalStateMock::_findUser(const std::string& usersNamespace, + const BSONObj& query, + BSONObj* result) const { + StatusWithMatchExpression parseResult = MatchExpressionParser::parse(query); + if (!parseResult.isOK()) { + return false; + } + MatchExpression* matcher = parseResult.getValue(); + + for (unordered_map<std::string, BSONObj>::const_iterator it = _userDocuments.begin(); + it != _userDocuments.end(); ++it) { + if (nsToDatabase(usersNamespace) == it->first && matcher->matchesBSON(it->second)) { + *result = it->second; + return true; + } + } + return false; + } + +} // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.h b/src/mongo/db/auth/authz_manager_external_state_mock.h index 3a2eadd5456..dd026c2764f 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.h +++ b/src/mongo/db/auth/authz_manager_external_state_mock.h @@ -22,6 +22,7 @@ #include "mongo/base/status.h" #include "mongo/db/auth/authz_manager_external_state.h" #include "mongo/db/jsobj.h" +#include "mongo/platform/unordered_map.h" namespace mongo { @@ -35,21 +36,25 @@ namespace mongo { AuthzManagerExternalStateMock() {}; + // no-op for the mock virtual Status insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj) const { - return Status::OK(); - } + const BSONObj& userObj) const; + // no-op for the mock virtual Status updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj) const { - return Status::OK(); - } + const BSONObj& updateObj) const; + + // Non-const version that puts document into a vector that can be accessed later + Status insertPrivilegeDocument(const std::string& dbname, const BSONObj& userObj); + + void clearPrivilegeDocuments(); virtual bool _findUser(const std::string& usersNamespace, const BSONObj& query, - BSONObj* result) const { - return false; - } + BSONObj* result) const; + + private: + unordered_map<std::string, BSONObj> _userDocuments; // dbname to user document }; } // namespace mongo diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp index 896bb63a8aa..c54f7215fe7 100644 --- a/src/mongo/db/auth/user.cpp +++ b/src/mongo/db/auth/user.cpp @@ -22,6 +22,7 @@ #include "mongo/db/auth/role_name.h" #include "mongo/db/auth/user_name.h" #include "mongo/platform/atomic_word.h" +#include "mongo/util/assert_util.h" namespace mongo { diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h index 77bacf92af5..3795e3f4c6d 100644 --- a/src/mongo/db/auth/user.h +++ b/src/mongo/db/auth/user.h @@ -46,6 +46,7 @@ namespace mongo { public: struct CredentialData { std::string password; + bool isExternal; }; explicit User(const UserName& name); @@ -153,6 +154,8 @@ namespace mongo { // _refCount and _isInvalidated are modified exclusively by the AuthorizationManager + // _isInvalidated can be read by any consumer of User, but _refCount can only be + // meaningfully read by the AuthorizationManager, as _refCount is guarded by the AM's _lock uint32_t _refCount; AtomicUInt32 _isValid; // Using as a boolean |