summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorBen Caimano <ben.caimano@10gen.com>2018-07-31 18:26:07 -0400
committerBen Caimano <ben.caimano@10gen.com>2018-07-31 18:32:19 -0400
commit6711ab3bbc02ca98da1af2fe115f07e37bb076e4 (patch)
treef1859ba3692a982b0afbd06fcb523c830136c25c /src/mongo/db
parent35757e8ef3134fad1a09cb09a69882929d9ebb76 (diff)
downloadmongo-6711ab3bbc02ca98da1af2fe115f07e37bb076e4.tar.gz
Revert "SERVER-35890 refactor User cache into InvalidatingLRUCache and UserHandle"
This reverts commit 78dec3622268ad27bb855eda4c6a4ed345412fd9.
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/action_types.txt1
-rw-r--r--src/mongo/db/auth/authorization_manager.h37
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.cpp137
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.h17
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp40
-rw-r--r--src/mongo/db/auth/authorization_session_for_test.cpp17
-rw-r--r--src/mongo/db/auth/authorization_session_for_test.h2
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp99
-rw-r--r--src/mongo/db/auth/role_graph_builtin_roles.cpp4
-rw-r--r--src/mongo/db/auth/sasl_mechanism_registry.cpp15
-rw-r--r--src/mongo/db/auth/sasl_plain_server_conversation.cpp11
-rw-r--r--src/mongo/db/auth/sasl_scram_server_conversation.cpp10
-rw-r--r--src/mongo/db/auth/user.cpp24
-rw-r--r--src/mongo/db/auth/user.h37
-rw-r--r--src/mongo/db/auth/user_set.cpp93
-rw-r--r--src/mongo/db/auth/user_set.h40
-rw-r--r--src/mongo/db/auth/user_set_test.cpp24
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp10
-rw-r--r--src/mongo/db/kill_sessions.cpp4
-rw-r--r--src/mongo/db/pipeline/SConscript1
-rw-r--r--src/mongo/db/pipeline/document_source_list_cached_and_active_users.cpp83
-rw-r--r--src/mongo/db/pipeline/document_source_list_cached_and_active_users.h113
22 files changed, 394 insertions, 425 deletions
diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt
index c59f4c2318d..d641378e162 100644
--- a/src/mongo/db/auth/action_types.txt
+++ b/src/mongo/db/auth/action_types.txt
@@ -71,7 +71,6 @@
"killAnySession",
"killCursors",
"killop",
-"listCachedAndActiveUsers",
"listCollections",
"listCursors",
"listDatabases",
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 2c19797aad3..a2f170b62aa 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -64,7 +64,7 @@ class UserDocumentParser;
* Internal secret key info.
*/
struct AuthInfo {
- UserHandle user;
+ User* user;
};
extern AuthInfo internalSecurity; // set at startup and not changed after initialization.
@@ -252,14 +252,24 @@ public:
std::vector<BSONObj>* result) = 0;
/**
- * Returns a Status or UserHandle for the given userName. If the user cache already has a
- * user object for this user, it returns a handle from the cache, otherwise it reads the
- * user document from disk or LDAP - this may block for a long time.
- *
- * The returned user may be invalid by the time the caller gets access to it.
+ * Returns the User object for the given userName in the out parameter "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 returned user may
+ * be invalid by the time the caller gets access to it.
+ * The AuthorizationManager retains ownership of the returned User object.
+ * On non-OK Status return values, acquiredUser will not be modified.
*/
- virtual StatusWith<UserHandle> acquireUser(OperationContext* opCtx,
- const UserName& userName) = 0;
+ virtual Status acquireUser(OperationContext* opCtx,
+ const UserName& userName,
+ User** acquiredUser) = 0;
+
+ /**
+ * 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.
+ */
+ virtual void releaseUser(User* user) = 0;
/**
* Marks the given user as invalid and removes it from the user cache.
@@ -300,17 +310,6 @@ public:
const NamespaceString& nss,
const BSONObj& obj,
const BSONObj* patt) = 0;
-
- /*
- * Represents a user in the user cache.
- */
- struct CachedUserInfo {
- UserName userName; // The username of the user
- bool active; // Whether the user is currently in use by a thread (a thread has
- // called acquireUser and still owns the returned shared_ptr)
- };
-
- virtual std::vector<CachedUserInfo> getUserCacheInfo() const = 0;
};
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp
index be6c356495b..054aa9ff939 100644
--- a/src/mongo/db/auth/authorization_manager_impl.cpp
+++ b/src/mongo/db/auth/authorization_manager_impl.cpp
@@ -59,7 +59,6 @@
#include "mongo/db/global_settings.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/mongod_options.h"
-#include "mongo/db/server_parameters.h"
#include "mongo/platform/compiler.h"
#include "mongo/stdx/memory.h"
#include "mongo/stdx/mutex.h"
@@ -78,12 +77,14 @@ using std::back_inserter;
using std::string;
using std::vector;
+
MONGO_INITIALIZER_GENERAL(SetupInternalSecurityUser,
("EndStartupOptionStorage"),
("CreateAuthorizationManager"))
(InitializerContext* const context) try {
- UserHandle user = std::make_shared<User>(UserName("__system", "local"));
+ User* user = new User(UserName("__system", "local"));
+ user->incrementRefCount(); // Pin this user so the ref count never drops below 1.
ActionSet allActions;
allActions.addAllActions();
PrivilegeVector privileges;
@@ -110,8 +111,6 @@ MONGO_INITIALIZER_GENERAL(SetupInternalSecurityUser,
} catch (...) {
return exceptionToStatus();
}
-
-MONGO_EXPORT_STARTUP_SERVER_PARAMETER(authorizationManagerCacheSize, int, 100);
} // namespace
@@ -206,7 +205,7 @@ public:
fassert(17191, !_authzManager->_isFetchPhaseBusy);
_isThisGuardInFetchPhase = true;
_authzManager->_isFetchPhaseBusy = true;
- _startGeneration = _authzManager->_fetchGeneration;
+ _startGeneration = _authzManager->_cacheGeneration;
_lock.unlock();
}
@@ -225,7 +224,7 @@ public:
}
/**
- * Returns true if _authzManager->_fetchGeneration remained the same while this guard was
+ * Returns true if _authzManager->_cacheGeneration remained the same while this guard was
* in fetch phase. Behavior is undefined if this guard never entered fetch phase.
*
* If this returns true, do not update the cached data with this
@@ -233,7 +232,7 @@ public:
bool isSameCacheGeneration() const {
fassert(17223, _isThisGuardInFetchPhase);
fassert(17231, _lock.owns_lock());
- return _startGeneration == _authzManager->_fetchGeneration;
+ return _startGeneration == _authzManager->_cacheGeneration;
}
private:
@@ -261,12 +260,18 @@ AuthorizationManagerImpl::AuthorizationManagerImpl(
_privilegeDocsExist(false),
_externalState(std::move(externalState)),
_version(schemaVersionInvalid),
- _userCache(authorizationManagerCacheSize, UserCacheInvalidator()),
_isFetchPhaseBusy(false) {
_updateCacheGeneration_inlock();
}
-AuthorizationManagerImpl::~AuthorizationManagerImpl() {}
+AuthorizationManagerImpl::~AuthorizationManagerImpl() {
+ for (stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin();
+ it != _userCache.end();
+ ++it) {
+ fassert(17265, it->second != internalSecurity.user);
+ delete it->second;
+ }
+}
std::unique_ptr<AuthorizationSession> AuthorizationManagerImpl::makeAuthorizationSession() {
return std::make_unique<AuthorizationSessionImpl>(
@@ -308,7 +313,7 @@ Status AuthorizationManagerImpl::getAuthorizationVersion(OperationContext* opCtx
OID AuthorizationManagerImpl::getCacheGeneration() {
CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- return _fetchGeneration;
+ return _cacheGeneration;
}
void AuthorizationManagerImpl::setAuthEnabled(bool enabled) {
@@ -408,30 +413,33 @@ Status AuthorizationManagerImpl::getRoleDescriptionsForDB(
opCtx, dbname, privileges, restrictions, showBuiltinRoles, result);
}
-void AuthorizationManagerImpl::UserCacheInvalidator::operator()(User* user) {
- user->_invalidate();
-}
-
-StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* opCtx,
- const UserName& userName) {
+Status AuthorizationManagerImpl::acquireUser(OperationContext* opCtx,
+ const UserName& userName,
+ User** acquiredUser) {
if (userName == internalSecurity.user->getName()) {
- return internalSecurity.user;
+ *acquiredUser = internalSecurity.user;
+ return Status::OK();
}
+ stdx::unordered_map<UserName, User*>::iterator it;
+
CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- boost::optional<std::shared_ptr<User>> cachedUser;
- while ((boost::none == (cachedUser = _userCache.get(userName))) &&
+ while ((_userCache.end() == (it = _userCache.find(userName))) &&
guard.otherUpdateInFetchPhase()) {
guard.wait();
}
- if (cachedUser != boost::none) {
- auto ret = *cachedUser;
- fassert(16914, ret.get());
- fassert(17003, ret->isValid());
- return ret;
+ if (it != _userCache.end()) {
+ fassert(16914, it->second);
+ fassert(17003, it->second->isValid());
+ fassert(17008, it->second->getRefCount() > 0);
+ it->second->incrementRefCount();
+ *acquiredUser = it->second;
+ return Status::OK();
}
+ std::unique_ptr<User> user;
+
int authzVersion = _version;
guard.beginFetchPhase();
@@ -440,7 +448,6 @@ StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* o
// after schema upgrades.
static const int maxAcquireRetries = 2;
Status status = Status::OK();
- std::unique_ptr<User> user;
for (int i = 0; i < maxAcquireRetries; ++i) {
if (authzVersion == schemaVersionInvalid) {
Status status = _externalState->getStoredAuthorizationVersion(opCtx, &authzVersion);
@@ -480,50 +487,87 @@ StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* o
guard.endFetchPhase();
+ user->incrementRefCount();
+ // NOTE: It is not safe to throw an exception from here to the end of the method.
if (guard.isSameCacheGeneration()) {
+ _userCache.insert(std::make_pair(userName, user.get()));
if (_version == schemaVersionInvalid)
_version = authzVersion;
- return _userCache.insertOrAssignAndGet(userName, std::move(user));
} else {
// If the cache generation changed while this thread was in fetch mode, the data
// associated with the user may now be invalid, so we must mark it as such. The caller
// may still opt to use the information for a short while, but not indefinitely.
- user->_invalidate();
- return UserHandle(std::move(user));
+ user->invalidate();
}
+ *acquiredUser = user.release();
+
+ return Status::OK();
}
Status AuthorizationManagerImpl::_fetchUserV2(OperationContext* opCtx,
const UserName& userName,
- std::unique_ptr<User>* out) {
+ std::unique_ptr<User>* acquiredUser) {
BSONObj userObj;
Status status = getUserDescription(opCtx, userName, &userObj);
if (!status.isOK()) {
return status;
}
- auto user = std::make_unique<User>(userName);
+ // Put the new user into an unique_ptr temporarily in case there's an error while
+ // initializing the user.
+ auto user = stdx::make_unique<User>(userName);
status = _initializeUserFromPrivilegeDocument(user.get(), userObj);
if (!status.isOK()) {
return status;
}
-
- std::swap(*out, user);
+ acquiredUser->reset(user.release());
return Status::OK();
}
+void AuthorizationManagerImpl::releaseUser(User* user) {
+ if (user == internalSecurity.user) {
+ return;
+ }
+
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ user->decrementRefCount();
+ if (user->getRefCount() == 0) {
+ // If it's been invalidated then it's not in the _userCache anymore.
+ if (user->isValid()) {
+ MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName());
+ dassert(erased);
+ }
+ delete user;
+ }
+}
+
void AuthorizationManagerImpl::invalidateUserByName(const UserName& userName) {
CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
_updateCacheGeneration_inlock();
- _userCache.invalidate(userName);
+ stdx::unordered_map<UserName, User*>::iterator it = _userCache.find(userName);
+ if (it == _userCache.end()) {
+ return;
+ }
+
+ User* user = it->second;
+ _userCache.erase(it);
+ user->invalidate();
}
void AuthorizationManagerImpl::invalidateUsersFromDB(StringData dbname) {
CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
_updateCacheGeneration_inlock();
- _userCache.invalidateIf(
- [&](const UserName& user, const User*) { return user.getDB() == dbname; });
+ stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin();
+ while (it != _userCache.end()) {
+ User* user = it->second;
+ if (user->getName().getDB() == dbname) {
+ _userCache.erase(it++);
+ user->invalidate();
+ } else {
+ ++it;
+ }
+ }
}
void AuthorizationManagerImpl::invalidateUserCache() {
@@ -533,7 +577,13 @@ void AuthorizationManagerImpl::invalidateUserCache() {
void AuthorizationManagerImpl::_invalidateUserCache_inlock() {
_updateCacheGeneration_inlock();
- _userCache.invalidateIf([](const UserName& a, const User*) { return true; });
+ for (stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin();
+ it != _userCache.end();
+ ++it) {
+ fassert(17266, it->second != internalSecurity.user);
+ it->second->invalidate();
+ }
+ _userCache.clear();
// Reread the schema version before acquiring the next user.
_version = schemaVersionInvalid;
@@ -617,7 +667,7 @@ StatusWith<UserName> extractUserNameFromIdString(StringData idstr) {
} // namespace
void AuthorizationManagerImpl::_updateCacheGeneration_inlock() {
- _fetchGeneration = OID::gen();
+ _cacheGeneration = OID::gen();
}
void AuthorizationManagerImpl::_invalidateRelevantCacheData(const char* op,
@@ -663,17 +713,4 @@ void AuthorizationManagerImpl::logOp(OperationContext* opCtx,
}
}
-std::vector<AuthorizationManager::CachedUserInfo> AuthorizationManagerImpl::getUserCacheInfo()
- const {
- auto cacheData = _userCache.getCacheInfo();
- std::vector<AuthorizationManager::CachedUserInfo> ret;
- ret.reserve(cacheData.size());
- std::transform(
- cacheData.begin(), cacheData.end(), std::back_inserter(ret), [](const auto& info) {
- return AuthorizationManager::CachedUserInfo{info.key, info.active};
- });
-
- return ret;
-}
-
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h
index c29f3790d62..4147ed01369 100644
--- a/src/mongo/db/auth/authorization_manager_impl.h
+++ b/src/mongo/db/auth/authorization_manager_impl.h
@@ -52,7 +52,6 @@
#include "mongo/stdx/functional.h"
#include "mongo/stdx/mutex.h"
#include "mongo/stdx/unordered_map.h"
-#include "mongo/util/invalidating_lru_cache.h"
namespace mongo {
class AuthorizationSession;
@@ -116,7 +115,11 @@ public:
bool showBuiltinRoles,
std::vector<BSONObj>* result) override;
- StatusWith<UserHandle> acquireUser(OperationContext* opCtx, const UserName& userName) override;
+ Status acquireUser(OperationContext* opCtx,
+ const UserName& userName,
+ User** acquiredUser) override;
+
+ void releaseUser(User* user) override;
void invalidateUserByName(const UserName& user) override;
@@ -134,8 +137,6 @@ public:
const BSONObj& obj,
const BSONObj* patt) override;
- std::vector<CachedUserInfo> getUserCacheInfo() const override;
-
private:
/**
* Type used to guard accesses and updates to the user cache.
@@ -214,17 +215,13 @@ private:
* has a reference count - the AuthorizationManager must not delete a User object in the
* cache unless its reference count is zero.
*/
- struct UserCacheInvalidator {
- void operator()(User* user);
- };
-
- InvalidatingLRUCache<UserName, User, UserCacheInvalidator> _userCache;
+ stdx::unordered_map<UserName, User*> _userCache;
/**
* Current generation of cached data. Updated every time part of the cache gets
* invalidated. Protected by CacheGuard.
*/
- OID _fetchGeneration;
+ OID _cacheGeneration;
/**
* True if there is an update to the _userCache in progress, and that update is currently in
diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp
index dc3701e2ecf..e0ba470d4f5 100644
--- a/src/mongo/db/auth/authorization_manager_test.cpp
+++ b/src/mongo/db/auth/authorization_manager_test.cpp
@@ -148,11 +148,11 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) {
<< "admin"))),
BSONObj()));
- auto swu = authzManager->acquireUser(opCtx.get(), UserName("v2read", "test"));
- ASSERT_OK(swu.getStatus());
- auto v2read = std::move(swu.getValue());
+ User* v2read;
+ ASSERT_OK(authzManager->acquireUser(opCtx.get(), UserName("v2read", "test"), &v2read));
ASSERT_EQUALS(UserName("v2read", "test"), v2read->getName());
ASSERT(v2read->isValid());
+ ASSERT_EQUALS(1U, v2read->getRefCount());
RoleNameIterator roles = v2read->getRoles();
ASSERT_EQUALS(RoleName("read", "test"), roles.next());
ASSERT_FALSE(roles.more());
@@ -160,12 +160,13 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) {
auto testDBPrivilege = privilegeMap[ResourcePattern::forDatabaseName("test")];
ASSERT(testDBPrivilege.getActions().contains(ActionType::find));
// Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
+ authzManager->releaseUser(v2read);
- swu = authzManager->acquireUser(opCtx.get(), UserName("v2cluster", "admin"));
- ASSERT_OK(swu.getStatus());
- auto v2cluster = std::move(swu.getValue());
+ User* v2cluster;
+ ASSERT_OK(authzManager->acquireUser(opCtx.get(), UserName("v2cluster", "admin"), &v2cluster));
ASSERT_EQUALS(UserName("v2cluster", "admin"), v2cluster->getName());
ASSERT(v2cluster->isValid());
+ ASSERT_EQUALS(1U, v2cluster->getRefCount());
RoleNameIterator clusterRoles = v2cluster->getRoles();
ASSERT_EQUALS(RoleName("clusterAdmin", "admin"), clusterRoles.next());
ASSERT_FALSE(clusterRoles.more());
@@ -173,6 +174,7 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) {
auto clusterPrivilege = privilegeMap[ResourcePattern::forClusterResource()];
ASSERT(clusterPrivilege.getActions().contains(ActionType::serverStatus));
// Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
+ authzManager->releaseUser(v2cluster);
}
#ifdef MONGO_CONFIG_SSL
@@ -181,9 +183,9 @@ TEST_F(AuthorizationManagerTest, testLocalX509Authorization) {
session,
SSLPeerInfo(buildX509Name(), {RoleName("read", "test"), RoleName("readWrite", "test")}));
- auto swu = authzManager->acquireUser(opCtx.get(), UserName("CN=mongodb.com", "$external"));
- ASSERT_OK(swu.getStatus());
- auto x509User = std::move(swu.getValue());
+ User* x509User;
+ ASSERT_OK(
+ authzManager->acquireUser(opCtx.get(), UserName("CN=mongodb.com", "$external"), &x509User));
ASSERT(x509User->isValid());
stdx::unordered_set<RoleName> expectedRoles{RoleName("read", "test"),
@@ -200,6 +202,9 @@ TEST_F(AuthorizationManagerTest, testLocalX509Authorization) {
auto privilegeIt = privileges.find(ResourcePattern::forDatabaseName("test"));
ASSERT(privilegeIt != privileges.end());
ASSERT(privilegeIt->second.includesAction(ActionType::insert));
+
+
+ authzManager->releaseUser(x509User);
}
#endif
@@ -208,15 +213,17 @@ TEST_F(AuthorizationManagerTest, testLocalX509AuthorizationInvalidUser) {
session,
SSLPeerInfo(buildX509Name(), {RoleName("read", "test"), RoleName("write", "test")}));
+ User* x509User;
ASSERT_NOT_OK(
- authzManager->acquireUser(opCtx.get(), UserName("CN=10gen.com", "$external")).getStatus());
+ authzManager->acquireUser(opCtx.get(), UserName("CN=10gen.com", "$external"), &x509User));
}
TEST_F(AuthorizationManagerTest, testLocalX509AuthenticationNoAuthorization) {
setX509PeerInfo(session, {});
- ASSERT_NOT_OK(authzManager->acquireUser(opCtx.get(), UserName("CN=mongodb.com", "$external"))
- .getStatus());
+ User* x509User;
+ ASSERT_NOT_OK(
+ authzManager->acquireUser(opCtx.get(), UserName("CN=mongodb.com", "$external"), &x509User));
}
/**
@@ -313,11 +320,11 @@ TEST_F(AuthorizationManagerTest, testAcquireV2UserWithUnrecognizedActions) {
<< "insert")))),
BSONObj()));
- auto swu = authzManager->acquireUser(opCtx.get(), UserName("myUser", "test"));
- ASSERT_OK(swu.getStatus());
- auto myUser = std::move(swu.getValue());
+ User* myUser;
+ ASSERT_OK(authzManager->acquireUser(opCtx.get(), UserName("myUser", "test"), &myUser));
ASSERT_EQUALS(UserName("myUser", "test"), myUser->getName());
ASSERT(myUser->isValid());
+ ASSERT_EQUALS(1U, myUser->getRefCount());
RoleNameIterator roles = myUser->getRoles();
ASSERT_EQUALS(RoleName("myRole", "test"), roles.next());
ASSERT_FALSE(roles.more());
@@ -329,6 +336,9 @@ TEST_F(AuthorizationManagerTest, testAcquireV2UserWithUnrecognizedActions) {
actions.removeAction(ActionType::find);
actions.removeAction(ActionType::insert);
ASSERT(actions.empty());
+
+ // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
+ authzManager->releaseUser(myUser);
}
// These tests ensure that the AuthorizationManager registers a
diff --git a/src/mongo/db/auth/authorization_session_for_test.cpp b/src/mongo/db/auth/authorization_session_for_test.cpp
index 8c4684c1408..93169e7d923 100644
--- a/src/mongo/db/auth/authorization_session_for_test.cpp
+++ b/src/mongo/db/auth/authorization_session_for_test.cpp
@@ -52,27 +52,28 @@ void AuthorizationSessionForTest::assumePrivilegesForDB(Privilege privilege, Str
void AuthorizationSessionForTest::assumePrivilegesForDB(PrivilegeVector privileges,
StringData dbName) {
- auto user = std::make_shared<User>(UserName("authorizationSessionForTestUser", dbName));
+ auto user = stdx::make_unique<User>(UserName("authorizationSessionForTestUser", dbName));
user->addPrivileges(privileges);
- _authenticatedUsers.add(user);
+ _authenticatedUsers.add(user.get());
_testUsers.emplace_back(std::move(user));
_buildAuthenticatedRolesVector();
}
void AuthorizationSessionForTest::revokePrivilegesForDB(StringData dbName) {
_authenticatedUsers.removeByDBName(dbName);
- _testUsers.erase(
- std::remove_if(_testUsers.begin(),
- _testUsers.end(),
- [&](const auto& user) { return dbName == user->getName().getDB(); }),
- _testUsers.end());
+ _testUsers.erase(std::remove_if(_testUsers.begin(),
+ _testUsers.end(),
+ [&](const std::unique_ptr<User>& user) {
+ return dbName == user->getName().getDB();
+ }),
+ _testUsers.end());
}
void AuthorizationSessionForTest::revokeAllPrivileges() {
_testUsers.erase(std::remove_if(_testUsers.begin(),
_testUsers.end(),
- [&](const auto& user) {
+ [&](const std::unique_ptr<User>& user) {
_authenticatedUsers.removeByDBName(user->getName().getDB());
return true;
}),
diff --git a/src/mongo/db/auth/authorization_session_for_test.h b/src/mongo/db/auth/authorization_session_for_test.h
index 3d9fe10873f..6e9f63418de 100644
--- a/src/mongo/db/auth/authorization_session_for_test.h
+++ b/src/mongo/db/auth/authorization_session_for_test.h
@@ -76,6 +76,6 @@ public:
void revokeAllPrivileges();
private:
- std::vector<UserHandle> _testUsers;
+ std::vector<std::unique_ptr<User>> _testUsers;
};
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index c1920d36d63..f334a20ab58 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -92,13 +92,47 @@ Status checkAuthForCreateOrModifyView(AuthorizationSession* authzSession,
isMongos);
}
+/** Deleter for User*.
+ * Will release a User* back to its owning AuthorizationManager on destruction.
+ * If a borrowing UserSet and the iterator it uses to store the User* is provided, this
+ * deleter will release the User* from the set if the iterator still points to the deleting User*.
+ */
+class UserReleaser {
+public:
+ explicit UserReleaser(AuthorizationManager* owner) : _owner(owner), _borrower(nullptr) {}
+ UserReleaser(AuthorizationManager* owner, UserSet* borrower, UserSet::iterator borrowerIt)
+ : _owner(owner), _borrower(borrower), _it(borrowerIt) {}
+
+ void operator()(User* user) {
+ // Remove the user from the borrower if it hasn't already been swapped out.
+ if (_borrower && *_it == user) {
+ fassert(40546, _borrower->removeAt(_it) == user);
+ }
+ _owner->releaseUser(user);
+ }
+
+protected:
+ AuthorizationManager* _owner;
+ UserSet* _borrower;
+ UserSet::iterator _it;
+};
+/** Holder for User*s. If this Holder falls out of scope while holding a User*, it will release
+ * the User* from its AuthorizationManager, and extract it from a UserSet if the set still contains
+ * it. Use this object to guard User*s which will need to be destroyed in the event of an exception.
+ */
+using UserHolder = std::unique_ptr<User, UserReleaser>;
+
} // namespace
AuthorizationSessionImpl::AuthorizationSessionImpl(
std::unique_ptr<AuthzSessionExternalState> externalState, InstallMockForTestingOrAuthImpl)
: _externalState(std::move(externalState)), _impersonationFlag(false) {}
-AuthorizationSessionImpl::~AuthorizationSessionImpl() {}
+AuthorizationSessionImpl::~AuthorizationSessionImpl() {
+ for (auto& user : _authenticatedUsers) {
+ getAuthorizationManager().releaseUser(user);
+ }
+}
AuthorizationManager& AuthorizationSessionImpl::getAuthorizationManager() {
return _externalState->getAuthorizationManager();
@@ -111,15 +145,16 @@ void AuthorizationSessionImpl::startRequest(OperationContext* opCtx) {
Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
const UserName& userName) {
+ User* user;
AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext());
- auto swUser = authzManager->acquireUser(opCtx, userName);
- if (!swUser.isOK()) {
- return swUser.getStatus();
+ Status status = authzManager->acquireUser(opCtx, userName, &user);
+ if (!status.isOK()) {
+ return status;
}
- auto user = std::move(swUser.getValue());
+ UserHolder userHolder(user, UserReleaser(authzManager));
- const auto& restrictionSet = user->getRestrictions();
+ const auto& restrictionSet = userHolder->getRestrictions();
if (opCtx->getClient() == nullptr) {
return Status(ErrorCodes::AuthenticationFailed,
"Unable to evaluate restrictions, OperationContext has no Client");
@@ -133,7 +168,9 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
return AuthorizationManager::authenticationFailedStatus;
}
- _authenticatedUsers.add(std::move(user));
+ // Calling add() on the UserSet may return a user that was replaced because it was from the
+ // same database.
+ userHolder.reset(_authenticatedUsers.add(userHolder.release()));
// If there are any users and roles in the impersonation data, clear it out.
clearImpersonatedUserData();
@@ -143,7 +180,7 @@ Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx,
}
User* AuthorizationSessionImpl::lookupUser(const UserName& name) {
- return _authenticatedUsers.lookup(name).get();
+ return _authenticatedUsers.lookup(name);
}
User* AuthorizationSessionImpl::getSingleUser() {
@@ -163,7 +200,10 @@ User* AuthorizationSessionImpl::getSingleUser() {
}
void AuthorizationSessionImpl::logoutDatabase(StringData dbname) {
- _authenticatedUsers.removeByDBName(dbname);
+ User* removedUser = _authenticatedUsers.removeByDBName(dbname);
+ if (removedUser) {
+ getAuthorizationManager().releaseUser(removedUser);
+ }
clearImpersonatedUserData();
_buildAuthenticatedRolesVector();
}
@@ -601,7 +641,7 @@ bool AuthorizationSessionImpl::isAuthorizedToCreateRole(
// The user may create a role if the localhost exception is enabled, and they already own the
// role. This implies they have obtained the role through an external authorization mechanism.
if (_externalState->shouldAllowLocalhost()) {
- for (const auto& user : _authenticatedUsers) {
+ for (const User* const user : _authenticatedUsers) {
if (user->hasRole(args.roleName)) {
return true;
}
@@ -773,27 +813,25 @@ bool AuthorizationSessionImpl::isAuthenticated() {
void AuthorizationSessionImpl::_refreshUserInfoAsNeeded(OperationContext* opCtx) {
AuthorizationManager& authMan = getAuthorizationManager();
UserSet::iterator it = _authenticatedUsers.begin();
-
while (it != _authenticatedUsers.end()) {
- auto& user = *it;
- if (!user->isValid()) {
- // The user is invalid, so make sure that we erase it from _authenticateUsers at the
- // end of this block.
- auto removeGuard = MakeGuard([&] { _authenticatedUsers.removeAt(it++); });
+ User* user = *it;
+ if (!user->isValid()) {
// Make a good faith effort to acquire an up-to-date user object, since the one
// we've cached is marked "out-of-date."
UserName name = user->getName();
- UserHandle updatedUser;
+ User* updatedUser;
- auto swUser = authMan.acquireUser(opCtx, name);
- auto& status = swUser.getStatus();
+ Status status = authMan.acquireUser(opCtx, name, &updatedUser);
switch (status.code()) {
case ErrorCodes::OK: {
- updatedUser = std::move(swUser.getValue());
+
+ // Verify the updated user object's authentication restrictions.
+ UserHolder userHolder(user, UserReleaser(&authMan, &_authenticatedUsers, it));
+ UserHolder updatedUserHolder(updatedUser, UserReleaser(&authMan));
try {
const auto& restrictionSet =
- updatedUser->getRestrictions(); // Owned by updatedUser
+ updatedUserHolder->getRestrictions(); // Owned by updatedUser
invariant(opCtx->getClient());
Status restrictionStatus = restrictionSet.validate(
RestrictionEnvironment::get(*opCtx->getClient()));
@@ -813,19 +851,24 @@ void AuthorizationSessionImpl::_refreshUserInfoAsNeeded(OperationContext* opCtx)
}
// Success! Replace the old User object with the updated one.
- removeGuard.Dismiss();
- _authenticatedUsers.replaceAt(it, std::move(updatedUser));
+ fassert(17067,
+ _authenticatedUsers.replaceAt(it, updatedUserHolder.release()) ==
+ userHolder.get());
LOG(1) << "Updated session cache of user information for " << name;
break;
}
case ErrorCodes::UserNotFound: {
// User does not exist anymore; remove it from _authenticatedUsers.
+ fassert(17068, _authenticatedUsers.removeAt(it) == user);
+ authMan.releaseUser(user);
log() << "Removed deleted user " << name
<< " from session cache of user information.";
continue; // No need to advance "it" in this case.
}
case ErrorCodes::UnsupportedFormat: {
// An auth subsystem has explicitly indicated a failure.
+ fassert(40555, _authenticatedUsers.removeAt(it) == user);
+ authMan.releaseUser(user);
log() << "Removed user " << name
<< " from session cache of user information because of refresh failure:"
<< " '" << status << "'.";
@@ -837,7 +880,6 @@ void AuthorizationSessionImpl::_refreshUserInfoAsNeeded(OperationContext* opCtx)
warning() << "Could not fetch updated user privilege information for " << name
<< "; continuing to use old information. Reason is "
<< redact(status);
- removeGuard.Dismiss();
break;
}
}
@@ -913,7 +955,7 @@ bool AuthorizationSessionImpl::isAuthorizedForAnyActionOnResource(const Resource
buildResourceSearchList(resource, resourceSearchList.data());
for (int i = 0; i < resourceSearchListLength; ++i) {
- for (const auto& user : _authenticatedUsers) {
+ for (const User* user : _authenticatedUsers) {
if (user->hasActionsForResource(resourceSearchList[i])) {
return true;
}
@@ -947,14 +989,15 @@ bool AuthorizationSessionImpl::_isAuthorizedForPrivilege(const Privilege& privil
}
}
- for (const auto& user : _authenticatedUsers) {
+ for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
+ ++it) {
+ User* user = *it;
for (int i = 0; i < resourceSearchListLength; ++i) {
ActionSet userActions = user->getActionsForResource(resourceSearchList[i]);
unmetRequirements.removeAllActionsFromSet(userActions);
- if (unmetRequirements.empty()) {
+ if (unmetRequirements.empty())
return true;
- }
}
}
diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp
index 90c6a6e97f2..a243ba3baf6 100644
--- a/src/mongo/db/auth/role_graph_builtin_roles.cpp
+++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp
@@ -362,9 +362,7 @@ void addUserAdminAnyDbPrivileges(PrivilegeVector* privileges) {
Privilege(ResourcePattern::forClusterResource(), ActionType::invalidateUserCache));
Privilege::addPrivilegeToPrivilegeVector(
privileges, Privilege(ResourcePattern::forClusterResource(), ActionType::viewUser));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), ActionType::listCachedAndActiveUsers));
+
ActionSet readRoleAndIndexActions;
readRoleAndIndexActions += readRoleActions;
diff --git a/src/mongo/db/auth/sasl_mechanism_registry.cpp b/src/mongo/db/auth/sasl_mechanism_registry.cpp
index 49d95c7c64e..cb588b9b829 100644
--- a/src/mongo/db/auth/sasl_mechanism_registry.cpp
+++ b/src/mongo/db/auth/sasl_mechanism_registry.cpp
@@ -84,10 +84,14 @@ void SASLServerMechanismRegistry::advertiseMechanismNamesForUser(OperationContex
AuthorizationManager* authManager = AuthorizationManager::get(opCtx->getServiceContext());
- UserHandle user;
- const auto swUser = authManager->acquireUser(opCtx, userName);
- if (!swUser.isOK()) {
- auto& status = swUser.getStatus();
+ User* user = nullptr;
+ const auto status = authManager->acquireUser(opCtx, userName, &user);
+ auto guard = MakeGuard([authManager, &user] {
+ if (user) {
+ authManager->releaseUser(user);
+ }
+ });
+ if (!status.isOK()) {
if (status.code() == ErrorCodes::UserNotFound) {
log() << "Supported SASL mechanisms requested for unknown user '" << userName
<< "'";
@@ -96,7 +100,6 @@ void SASLServerMechanismRegistry::advertiseMechanismNamesForUser(OperationContex
uassertStatusOK(status);
}
- user = std::move(swUser.getValue());
BSONArrayBuilder mechanismsBuilder;
auto& map = _getMapRef(userName.getDB());
@@ -108,7 +111,7 @@ void SASLServerMechanismRegistry::advertiseMechanismNamesForUser(OperationContex
continue;
}
- if (factoryIt.second->canMakeMechanismForUser(user.get())) {
+ if (factoryIt.second->canMakeMechanismForUser(user)) {
mechanismsBuilder << factoryIt.first;
}
}
diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.cpp b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
index 4dd64e4f11e..7d89c2eaeb8 100644
--- a/src/mongo/db/auth/sasl_plain_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
@@ -122,16 +122,17 @@ StatusWith<std::tuple<bool, std::string>> SASLPlainServerMechanism::stepImpl(
mongoutils::str::stream() << "Incorrectly formatted PLAIN client message");
}
+ User* userObj;
// The authentication database is also the source database for the user.
- auto swUser = authManager->acquireUser(
- opCtx, UserName(ServerMechanismBase::_principalName, _authenticationDatabase));
+ Status status = authManager->acquireUser(
+ opCtx, UserName(ServerMechanismBase::_principalName, _authenticationDatabase), &userObj);
- if (!swUser.isOK()) {
- return swUser.getStatus();
+ if (!status.isOK()) {
+ return status;
}
- auto userObj = std::move(swUser.getValue());
const auto creds = userObj->getCredentials();
+ authManager->releaseUser(userObj);
const auto sha256Status = trySCRAM<SHA256Block>(creds, pwd->c_str());
if (!sha256Status.isOK()) {
diff --git a/src/mongo/db/auth/sasl_scram_server_conversation.cpp b/src/mongo/db/auth/sasl_scram_server_conversation.cpp
index cab83885352..d3ded3c88f8 100644
--- a/src/mongo/db/auth/sasl_scram_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_scram_server_conversation.cpp
@@ -191,17 +191,19 @@ StatusWith<std::tuple<bool, std::string>> SaslSCRAMServerMechanism<Policy>::_fir
}
// The authentication database is also the source database for the user.
+ User* userObj;
auto authManager = AuthorizationManager::get(opCtx->getServiceContext());
- auto swUser = authManager->acquireUser(opCtx, user);
- if (!swUser.isOK()) {
- return swUser.getStatus();
+ Status status = authManager->acquireUser(opCtx, user, &userObj);
+ if (!status.isOK()) {
+ return status;
}
- auto userObj = std::move(swUser.getValue());
User::CredentialData credentials = userObj->getCredentials();
UserName userName = userObj->getName();
+ authManager->releaseUser(userObj);
+
_scramCredentials = credentials.scram<HashBlock>();
if (!_scramCredentials.isValid()) {
diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp
index 4e25d8ab5ea..ade182208b9 100644
--- a/src/mongo/db/auth/user.cpp
+++ b/src/mongo/db/auth/user.cpp
@@ -51,7 +51,12 @@ SHA256Block computeDigest(const UserName& name) {
} // namespace
-User::User(const UserName& name) : _name(name), _digest(computeDigest(_name)) {}
+User::User(const UserName& name)
+ : _name(name), _digest(computeDigest(_name)), _refCount(0), _isValid(1) {}
+
+User::~User() {
+ dassert(_refCount == 0);
+}
template <>
User::SCRAMCredentials<SHA1Block>& User::CredentialData::scram<SHA1Block>() {
@@ -88,9 +93,12 @@ const User::CredentialData& User::getCredentials() const {
}
bool User::isValid() const {
- return _isValid.loadRelaxed();
+ return _isValid.loadRelaxed() == 1;
}
+uint32_t User::getRefCount() const {
+ return _refCount;
+}
const ActionSet User::getActionsForResource(const ResourcePattern& resource) const {
stdx::unordered_map<ResourcePattern, Privilege>::const_iterator it = _privileges.find(resource);
@@ -162,8 +170,16 @@ void User::setRestrictions(RestrictionDocuments restrictions)& {
_restrictions = std::move(restrictions);
}
-void User::_invalidate() {
- _isValid.store(false);
+void User::invalidate() {
+ _isValid.store(0);
}
+void User::incrementRefCount() {
+ ++_refCount;
+}
+
+void User::decrementRefCount() {
+ dassert(_refCount > 0);
+ --_refCount;
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h
index a22bdca7231..b974a0a3a7c 100644
--- a/src/mongo/db/auth/user.h
+++ b/src/mongo/db/auth/user.h
@@ -101,6 +101,7 @@ public:
typedef stdx::unordered_map<ResourcePattern, Privilege> ResourcePrivilegeMap;
explicit User(const UserName& name);
+ ~User();
/**
* Returns the user name for this user.
@@ -161,6 +162,12 @@ public:
*/
bool isValid() const;
+ /**
+ * This returns the reference count for this User. The AuthorizationManager should be the
+ * only caller of this.
+ */
+ uint32_t getRefCount() const;
+
// Mutators below. Mutation functions should *only* be called by the AuthorizationManager
/**
@@ -217,15 +224,30 @@ public:
}
void getRestrictions() && = delete;
-protected:
- friend class AuthorizationManagerImpl;
/**
* Marks this instance of the User object as invalid, most likely because information about
* the user has been updated and needs to be reloaded from the AuthorizationManager.
*
* This method should *only* be called by the AuthorizationManager.
*/
- void _invalidate();
+ void invalidate();
+
+ /**
+ * Increments the reference count for this User object, which records how many threads have
+ * a reference to it.
+ *
+ * This method should *only* be called by the AuthorizationManager.
+ */
+ void incrementRefCount();
+
+ /**
+ * Decrements the reference count for this User object, which records how many threads have
+ * a reference to it. Once the reference count goes to zero, the AuthorizationManager is
+ * allowed to destroy this instance.
+ *
+ * This method should *only* be called by the AuthorizationManager.
+ */
+ void decrementRefCount();
private:
UserName _name;
@@ -248,10 +270,11 @@ private:
// Restrictions which must be met by a Client in order to authenticate as this user.
RestrictionDocuments _restrictions;
- // Indicates whether the user has been marked as invalid by the AuthorizationManager.
- AtomicBool _isValid{true};
+ // _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
};
-using UserHandle = std::shared_ptr<User>;
-
} // namespace mongo
diff --git a/src/mongo/db/auth/user_set.cpp b/src/mongo/db/auth/user_set.cpp
index c54a340cd10..fe8d273a2ca 100644
--- a/src/mongo/db/auth/user_set.cpp
+++ b/src/mongo/db/auth/user_set.cpp
@@ -27,8 +27,6 @@
#include "mongo/db/auth/user_set.h"
-#include <algorithm>
-#include <iostream>
#include <string>
#include <vector>
@@ -42,8 +40,7 @@ class UserSetNameIteratorImpl : public UserNameIterator::Impl {
MONGO_DISALLOW_COPYING(UserSetNameIteratorImpl);
public:
- UserSetNameIteratorImpl(const UserSet::const_iterator& begin,
- const UserSet::const_iterator& end)
+ UserSetNameIteratorImpl(const UserSet::iterator& begin, const UserSet::iterator& end)
: _curr(begin), _end(end) {}
virtual ~UserSetNameIteratorImpl() {}
virtual bool more() const {
@@ -60,58 +57,78 @@ public:
}
private:
- UserSet::const_iterator _curr;
- UserSet::const_iterator _end;
+ UserSet::iterator _curr;
+ UserSet::iterator _end;
};
} // namespace
-void UserSet::add(UserHandle user) {
- auto it = std::find_if(_users.begin(), _users.end(), [&](const auto& storedUser) {
- return user->getName().getDB() == storedUser->getName().getDB();
- });
- if (it == _users.end()) {
- _users.push_back(std::move(user));
+UserSet::UserSet() : _users(), _usersEnd(_users.end()) {}
+UserSet::~UserSet() {}
+
+User* UserSet::add(User* user) {
+ for (mutable_iterator it = mbegin(); it != mend(); ++it) {
+ User* current = *it;
+ if (current->getName().getDB() == user->getName().getDB()) {
+ // There can be only one user per database.
+ *it = user;
+ return current;
+ }
+ }
+ if (_usersEnd == _users.end()) {
+ _users.push_back(user);
+ _usersEnd = _users.end();
} else {
- *it = std::move(user);
+ *_usersEnd = user;
+ ++_usersEnd;
}
+ return NULL;
}
-void UserSet::removeByDBName(StringData dbname) {
- auto it = std::find_if(_users.begin(), _users.end(), [&](const auto& user) {
- return user->getName().getDB() == dbname;
- });
- if (it != _users.end()) {
- _users.erase(it);
+User* UserSet::removeByDBName(StringData dbname) {
+ for (iterator it = begin(); it != end(); ++it) {
+ User* current = *it;
+ if (current->getName().getDB() == dbname) {
+ return removeAt(it);
+ }
}
+ return NULL;
}
-void UserSet::replaceAt(iterator it, UserHandle replacement) {
- *it = std::move(replacement);
+User* UserSet::replaceAt(iterator it, User* replacement) {
+ size_t offset = it - begin();
+ User* old = _users[offset];
+ _users[offset] = replacement;
+ return old;
}
-void UserSet::removeAt(iterator it) {
- _users.erase(it);
+User* UserSet::removeAt(iterator it) {
+ size_t offset = it - begin();
+ User* old = _users[offset];
+ --_usersEnd;
+ _users[offset] = *_usersEnd;
+ *_usersEnd = NULL;
+ return old;
}
-UserHandle UserSet::lookup(const UserName& name) const {
- auto it = std::find_if(_users.begin(), _users.end(), [&](const auto& user) {
- invariant(user);
- return user->getName() == name;
- });
-
- return (it != _users.end()) ? *it : nullptr;
+User* UserSet::lookup(const UserName& name) const {
+ User* user = lookupByDBName(name.getDB());
+ if (user && user->getName() == name) {
+ return user;
+ }
+ return NULL;
}
-UserHandle UserSet::lookupByDBName(StringData dbname) const {
- auto it = std::find_if(_users.begin(), _users.end(), [&](const auto& user) {
- invariant(user);
- return user->getName().getDB() == dbname;
- });
- return (it != _users.end()) ? *it : nullptr;
+User* UserSet::lookupByDBName(StringData dbname) const {
+ for (iterator it = begin(); it != end(); ++it) {
+ User* current = *it;
+ if (current->getName().getDB() == dbname) {
+ return current;
+ }
+ }
+ return NULL;
}
UserNameIterator UserSet::getNames() const {
- return UserNameIterator(
- std::make_unique<UserSetNameIteratorImpl>(_users.cbegin(), _users.cend()));
+ return UserNameIterator(std::make_unique<UserSetNameIteratorImpl>(begin(), end()));
}
} // namespace mongo
diff --git a/src/mongo/db/auth/user_set.h b/src/mongo/db/auth/user_set.h
index 3b1472c6c8f..9ad0ab03202 100644
--- a/src/mongo/db/auth/user_set.h
+++ b/src/mongo/db/auth/user_set.h
@@ -35,7 +35,7 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/user.h"
#include "mongo/db/auth/user_name.h"
-#include "mongo/stdx/list.h"
+
namespace mongo {
@@ -48,14 +48,16 @@ class UserSet {
MONGO_DISALLOW_COPYING(UserSet);
public:
- using iterator = stdx::list<UserHandle>::iterator;
- using const_iterator = stdx::list<UserHandle>::const_iterator;
+ typedef std::vector<User*>::const_iterator iterator;
- UserSet() = default;
+ UserSet();
+ ~UserSet();
/**
* Adds a User to the UserSet.
*
+ * The UserSet does not take ownership of the User.
+ *
* As there can only be one user per database in the UserSet, if a User already exists for
* the new User's database, the old user will be removed from the set and returned. It is
* the caller's responsibility to then release that user. If no user already exists for the
@@ -63,54 +65,64 @@ public:
*
* Invalidates any outstanding iterators or NameIterators.
*/
- void add(UserHandle user);
+ User* add(User* user);
/**
* Replaces the user at "it" with "replacement." Does not take ownership of the User.
* Returns a pointer to the old user referenced by "it". Does _not_ invalidate "iterator"
* instances.
*/
- void replaceAt(iterator it, UserHandle replacement);
+ User* replaceAt(iterator it, User* replacement);
/**
* Removes the user at "it", and returns a pointer to it. After this call, "it" remains
* valid. It will either equal "end()", or refer to some user between the values of "it"
* and "end()" before this call was made.
*/
- void removeAt(iterator it);
+ User* removeAt(iterator it);
/**
* Removes the User whose authentication credentials came from dbname, and returns that
* user. It is the caller's responsibility to then release that user back to the
* authorizationManger. If no user exists for the given database, returns NULL;
*/
- void removeByDBName(StringData dbname);
+ User* removeByDBName(StringData dbname);
// Returns the User with the given name, or NULL if not found.
// Ownership of the returned User remains with the UserSet. The pointer
// returned is only guaranteed to remain valid until the next non-const method is called
// on the UserSet.
- UserHandle lookup(const UserName& name) const;
+ User* lookup(const UserName& name) const;
// Gets the user whose authentication credentials came from dbname, or NULL if none
// exist. There should be at most one such user.
- UserHandle lookupByDBName(StringData dbname) const;
+ User* lookupByDBName(StringData dbname) const;
// Gets an iterator over the names of the users stored in the set. The iterator is
// valid until the next non-const method is called on the UserSet.
UserNameIterator getNames() const;
- iterator begin() {
+ iterator begin() const {
return _users.begin();
}
- iterator end() {
- return _users.end();
+ iterator end() const {
+ return _usersEnd;
}
private:
+ typedef std::vector<User*>::iterator mutable_iterator;
+
+ mutable_iterator mbegin() {
+ return _users.begin();
+ }
+ mutable_iterator mend() {
+ return _usersEnd;
+ }
+
// The UserSet maintains ownership of the Users in it, and is responsible for
// returning them to the AuthorizationManager when done with them.
- stdx::list<UserHandle> _users;
+ std::vector<User*> _users;
+ std::vector<User*>::iterator _usersEnd;
};
} // namespace mongo
diff --git a/src/mongo/db/auth/user_set_test.cpp b/src/mongo/db/auth/user_set_test.cpp
index d8797dcc049..d8c4290d7c8 100644
--- a/src/mongo/db/auth/user_set_test.cpp
+++ b/src/mongo/db/auth/user_set_test.cpp
@@ -42,9 +42,13 @@ namespace {
TEST(UserSetTest, BasicTest) {
UserSet set;
- UserHandle p1 = std::make_shared<User>(UserName("Bob", "test"));
- UserHandle p2 = std::make_shared<User>(UserName("George", "test"));
- UserHandle p3 = std::make_shared<User>(UserName("Bob", "test2"));
+ User* p1 = new User(UserName("Bob", "test"));
+ User* p2 = new User(UserName("George", "test"));
+ User* p3 = new User(UserName("Bob", "test2"));
+
+ const std::unique_ptr<User> delp1(p1);
+ const std::unique_ptr<User> delp2(p2);
+ const std::unique_ptr<User> delp3(p3);
ASSERT_NULL(set.lookup(UserName("Bob", "test")));
ASSERT_NULL(set.lookup(UserName("George", "test")));
@@ -52,7 +56,7 @@ TEST(UserSetTest, BasicTest) {
ASSERT_NULL(set.lookupByDBName("test"));
ASSERT_NULL(set.lookupByDBName("test2"));
- set.add(p1);
+ ASSERT_NULL(set.add(p1));
ASSERT_EQUALS(p1, set.lookup(UserName("Bob", "test")));
ASSERT_EQUALS(p1, set.lookupByDBName("test"));
@@ -61,7 +65,7 @@ TEST(UserSetTest, BasicTest) {
ASSERT_NULL(set.lookupByDBName("test2"));
// This should not replace the existing user "Bob" because they are different databases
- set.add(p3);
+ ASSERT_NULL(set.add(p3));
ASSERT_EQUALS(p1, set.lookup(UserName("Bob", "test")));
ASSERT_EQUALS(p1, set.lookupByDBName("test"));
@@ -69,16 +73,18 @@ TEST(UserSetTest, BasicTest) {
ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
- set.add(p2); // This should replace Bob since they're on the same database
+ User* replaced = set.add(p2); // This should replace Bob since they're on the same database
+ ASSERT_EQUALS(replaced, p1);
ASSERT_NULL(set.lookup(UserName("Bob", "test")));
ASSERT_EQUALS(p2, set.lookup(UserName("George", "test")));
ASSERT_EQUALS(p2, set.lookupByDBName("test"));
ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
- set.removeByDBName("test"_sd);
+ User* removed = set.removeByDBName("test");
+ ASSERT_EQUALS(removed, p2);
ASSERT_NULL(set.lookup(UserName("Bob", "test")));
ASSERT_NULL(set.lookup(UserName("George", "test")));
ASSERT_NULL(set.lookupByDBName("test"));
@@ -96,8 +102,8 @@ TEST(UserSetTest, IterateNames) {
UserNameIterator iter = pset.getNames();
ASSERT(!iter.more());
- UserHandle user = std::make_shared<User>(UserName("bob", "test"));
- pset.add(std::move(user));
+ std::unique_ptr<User> user(new User(UserName("bob", "test")));
+ ASSERT_NULL(pset.add(user.get()));
iter = pset.getNames();
ASSERT(iter.more());
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 2fca9a9973f..82823e3cfcc 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -147,17 +147,17 @@ Status getCurrentUserRoles(OperationContext* opCtx,
AuthorizationManager* authzManager,
const UserName& userName,
stdx::unordered_set<RoleName>* roles) {
+ User* user;
authzManager->invalidateUserByName(userName); // Need to make sure cache entry is up to date
- auto swUser = authzManager->acquireUser(opCtx, userName);
- if (!swUser.isOK()) {
- return swUser.getStatus();
+ Status status = authzManager->acquireUser(opCtx, userName, &user);
+ if (!status.isOK()) {
+ return status;
}
- auto user = std::move(swUser.getValue());
-
RoleNameIterator rolesIt = user->getRoles();
while (rolesIt.more()) {
roles->insert(rolesIt.next());
}
+ authzManager->releaseUser(user);
return Status::OK();
}
diff --git a/src/mongo/db/kill_sessions.cpp b/src/mongo/db/kill_sessions.cpp
index 1d83b587d89..1ac38112c3d 100644
--- a/src/mongo/db/kill_sessions.cpp
+++ b/src/mongo/db/kill_sessions.cpp
@@ -110,10 +110,12 @@ KillAllSessionsByPattern makeKillAllSessionsByPattern(OperationContext* opCtx,
auto authMgr = AuthorizationManager::get(opCtx->getServiceContext());
+ User* user;
UserName un(kasu.getUser(), kasu.getDb());
- auto user = uassertStatusOK(authMgr->acquireUser(opCtx, un));
+ uassertStatusOK(authMgr->acquireUser(opCtx, un, &user));
kasbp.setUid(user->getDigest());
+ authMgr->releaseUser(user);
return kasbp;
}
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 0195c38f596..a9bad067648 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -287,7 +287,6 @@ pipelineeEnv.Library(
'document_source_internal_inhibit_optimization.cpp',
'document_source_internal_split_pipeline.cpp',
'document_source_limit.cpp',
- 'document_source_list_cached_and_active_users.cpp',
'document_source_list_local_cursors.cpp',
'document_source_list_local_sessions.cpp',
'document_source_list_sessions.cpp',
diff --git a/src/mongo/db/pipeline/document_source_list_cached_and_active_users.cpp b/src/mongo/db/pipeline/document_source_list_cached_and_active_users.cpp
deleted file mode 100644
index 0c9fd53fefe..00000000000
--- a/src/mongo/db/pipeline/document_source_list_cached_and_active_users.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * 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/db/pipeline/document_source_list_cached_and_active_users.h"
-
-#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/auth/user_name.h"
-#include "mongo/db/commands/test_commands_enabled.h"
-#include "mongo/db/logical_session_id_helpers.h"
-
-namespace mongo {
-
-REGISTER_TEST_DOCUMENT_SOURCE(listCachedAndActiveUsers,
- DocumentSourceListCachedAndActiveUsers::LiteParsed::parse,
- DocumentSourceListCachedAndActiveUsers::createFromBson);
-
-const char* DocumentSourceListCachedAndActiveUsers::kStageName = "$listCachedAndActiveUsers";
-
-DocumentSource::GetNextResult DocumentSourceListCachedAndActiveUsers::getNext() {
- pExpCtx->checkForInterrupt();
-
- if (!_users.empty()) {
- const auto info = std::move(_users.back());
- _users.pop_back();
- return Document(BSON("username" << info.userName.getUser() << "db" << info.userName.getDB()
- << "active"
- << info.active));
- }
-
- return GetNextResult::makeEOF();
-}
-
-boost::intrusive_ptr<DocumentSource> DocumentSourceListCachedAndActiveUsers::createFromBson(
- BSONElement spec, const boost::intrusive_ptr<ExpressionContext>& pExpCtx) {
-
- uassert(
- ErrorCodes::InvalidNamespace,
- str::stream() << kStageName
- << " must be run against the database with {aggregate: 1}, not a collection",
- pExpCtx->ns.isCollectionlessAggregateNS());
-
- uassert(ErrorCodes::BadValue,
- str::stream() << kStageName << " must be run as { " << kStageName << ": {}}",
- spec.isABSONObj() && spec.Obj().isEmpty());
-
- return new DocumentSourceListCachedAndActiveUsers(pExpCtx);
-}
-
-DocumentSourceListCachedAndActiveUsers::DocumentSourceListCachedAndActiveUsers(
- const boost::intrusive_ptr<ExpressionContext>& pExpCtx)
- : DocumentSource(pExpCtx), _users() {
- auto authMgr = AuthorizationManager::get(pExpCtx->opCtx->getServiceContext());
- _users = authMgr->getUserCacheInfo();
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h b/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h
deleted file mode 100644
index c46fe59cbaa..00000000000
--- a/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * 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 "mongo/bson/bsonmisc.h"
-#include "mongo/bson/bsonobj.h"
-#include "mongo/db/auth/authorization_manager.h"
-#include "mongo/db/pipeline/document_source.h"
-#include "mongo/db/pipeline/lite_parsed_document_source.h"
-
-namespace mongo {
-
-/*
- * This implements an aggregation document source that lists the active/cached users in the
- * authorization manager. It is intended for diagnostic and reporting purposes.
- */
-class DocumentSourceListCachedAndActiveUsers final : public DocumentSource {
-public:
- static const char* kStageName;
-
- class LiteParsed final : public LiteParsedDocumentSource {
- public:
- static std::unique_ptr<LiteParsed> parse(const AggregationRequest& request,
- const BSONElement& spec) {
- return stdx::make_unique<LiteParsed>();
- }
-
- stdx::unordered_set<NamespaceString> getInvolvedNamespaces() const final {
- return stdx::unordered_set<NamespaceString>();
- }
-
- PrivilegeVector requiredPrivileges(bool isMongos) const final {
- return {Privilege(ResourcePattern::forAnyNormalResource(),
- ActionType::listCachedAndActiveUsers)};
- }
-
- bool isInitialSource() const final {
- return true;
- }
-
- bool allowedToForwardFromMongos() const final {
- return false;
- }
-
- void assertSupportsReadConcern(const repl::ReadConcernArgs& readConcern) const {
- uassert(ErrorCodes::InvalidOptions,
- str::stream() << "Aggregation stage " << kStageName << " cannot run with a "
- << "readConcern other than 'local', or in a multi-document "
- << "transaction. Current readConcern: "
- << readConcern.toString(),
- readConcern.getLevel() == repl::ReadConcernLevel::kLocalReadConcern);
- }
- };
-
- GetNextResult getNext() final;
-
- const char* getSourceName() const final {
- return kStageName;
- }
-
- Value serialize(boost::optional<ExplainOptions::Verbosity> explain = boost::none) const final {
- return Value(Document{{getSourceName(), Document{}}});
- }
-
- StageConstraints constraints(Pipeline::SplitState pipeState) const final {
- StageConstraints constraints(StreamType::kStreaming,
- PositionRequirement::kFirst,
- HostTypeRequirement::kLocalOnly,
- DiskUseRequirement::kNoDiskUse,
- FacetRequirement::kNotAllowed,
- TransactionRequirement::kNotAllowed);
-
- constraints.isIndependentOfAnyCollection = true;
- constraints.requiresInputDocSource = false;
- return constraints;
- }
-
- static boost::intrusive_ptr<DocumentSource> createFromBson(
- BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& pExpCtx);
-
-private:
- DocumentSourceListCachedAndActiveUsers(const boost::intrusive_ptr<ExpressionContext>& pExpCtx);
-
- std::vector<AuthorizationManager::CachedUserInfo> _users;
-};
-
-} // namespace mongo