summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-06-27 17:10:39 -0400
committerSpencer T Brody <spencer@10gen.com>2013-07-01 17:41:53 -0400
commit90f1d8947a26b330accfaf69dc25ee1d892891d1 (patch)
tree320cfc0363fbbbb09b85f1743b200cb76cd0949b /src/mongo
parentd15b27260f62349d9d9aac0b53d60eaf284492c3 (diff)
downloadmongo-90f1d8947a26b330accfaf69dc25ee1d892891d1.tar.gz
SERVER-9518 Initial implementation of acquire/releaseUser methods in AuthorizationManager
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/SConscript11
-rw-r--r--src/mongo/db/auth/SConscript8
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp204
-rw-r--r--src/mongo/db/auth/authorization_manager.h50
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp98
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.cpp68
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.h23
-rw-r--r--src/mongo/db/auth/user.cpp1
-rw-r--r--src/mongo/db/auth/user.h3
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