summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/auth/pinned_users.js146
-rw-r--r--src/mongo/db/auth/SConscript5
-rw-r--r--src/mongo/db/auth/authorization_manager.h8
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.cpp351
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.h37
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp7
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp6
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h28
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.cpp29
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.h4
-rw-r--r--src/mongo/db/auth/user_cache_invalidator_job.cpp13
-rw-r--r--src/mongo/db/commands/sleep_command.cpp39
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp248
-rw-r--r--src/mongo/db/op_observer_impl.cpp2
-rw-r--r--src/mongo/db/repl/oplog.cpp5
-rw-r--r--src/mongo/embedded/embedded_auth_manager.cpp10
-rw-r--r--src/mongo/s/commands/cluster_user_management_commands.cpp26
-rw-r--r--src/mongo/s/server.cpp2
18 files changed, 739 insertions, 227 deletions
diff --git a/jstests/auth/pinned_users.js b/jstests/auth/pinned_users.js
new file mode 100644
index 00000000000..758905f7503
--- /dev/null
+++ b/jstests/auth/pinned_users.js
@@ -0,0 +1,146 @@
+/*
+ * This test checks that we can recover from an operation that has taken out a write lock
+ * for a long time (potentially deadlocking the server). This comes out of a number of BF's
+ * related to jstests/core/mr_killop.js in the parallel suite (see BF-6259 for the full
+ * write-up).
+ *
+ * @tags: [requires_replication]
+ *
+ */
+(function() {
+ 'use strict';
+ jsTest.setOption("enableTestCommands", true);
+ // Start a mongod with the user cache size set to zero, so we know that users who have
+ // logged out always get fetched cleanly from disk.
+ const rs = new ReplSetTest({
+ nodes: 3,
+ nodeOptions: {auth: "", setParameter: "authorizationManagerCacheSize=0"},
+ keyFile: "jstests/libs/key1"
+ });
+
+ rs.startSet();
+ rs.initiate();
+ const mongod = rs.getPrimary();
+ const admin = mongod.getDB("admin");
+
+ admin.createUser({user: "admin", pwd: "admin", roles: ["root"]});
+ admin.auth("admin", "admin");
+
+ // Mark the "admin2" user as pinned in memory, we'll use this later on to recover from
+ // the deadlock
+ assert.commandWorked(admin.runCommand({
+ setParameter: 1,
+ authorizationManagerPinnedUsers: [
+ {user: "admin2", db: "admin"},
+ ],
+ logLevel: 1
+ }));
+
+ admin.createUser({user: "admin2", pwd: "admin", roles: ["root"]});
+
+ let secondConn = new Mongo(mongod.host);
+ let secondAdmin = secondConn.getDB("admin");
+ secondAdmin.auth("admin2", "admin");
+
+ // Invalidate the user cache so we know only "admin" is in there
+ assert.commandWorked(admin.runCommand({invalidateUserCache: 1}));
+ print("User cache after initialization: ",
+ tojson(admin.aggregate([{$listCachedAndActiveUsers: {}}]).toArray()));
+
+ const waitForCommand = function(waitingFor, opFilter) {
+ let opId = -1;
+ assert.soon(function() {
+ print(`Checking for ${waitingFor}`);
+ const curopRes = admin.currentOp();
+ assert.commandWorked(curopRes);
+ const foundOp = curopRes["inprog"].filter(opFilter);
+
+ if (foundOp.length == 1) {
+ opId = foundOp[0]["opid"];
+ }
+ return (foundOp.length == 1);
+ });
+ return opId;
+ };
+
+ // The deadlock happens in two phases. First we run a command that acquires a read lock and
+ // holds it for forever.
+ let readLockShell = startParallelShell(function() {
+ assert.eq(db.getSiblingDB("admin").auth("admin", "admin"), 1);
+ assert.commandFailed(db.adminCommand(
+ {sleep: 1, secs: 500, lock: "r", lockTarget: "admin", $comment: "Read lock sleep"}));
+ }, mongod.port);
+
+ // Wait for that command to appear in currentOp
+ const readID = waitForCommand(
+ "readlock",
+ op => (op["ns"] == "admin.$cmd" && op["command"]["$comment"] == "Read lock sleep"));
+
+ // Then we run a command that tries to acquire a write lock, which will wait for forever
+ // because we're already holding a read lock, but will also prevent any new read locks from
+ // being taken.
+ let writeLockShell = startParallelShell(function() {
+ assert.eq(db.getSiblingDB("admin").auth("admin", "admin"), 1);
+ assert.commandFailed(db.adminCommand(
+ {sleep: 1, secs: 500, lock: "w", lockTarget: "admin", $comment: "Write lock sleep"}));
+ }, mongod.port);
+
+ // Wait for that to appear in currentOp
+ const writeID = waitForCommand(
+ "writeLock",
+ op => (op["ns"] == "admin.$cmd" && op["command"]["$comment"] == "Write lock sleep"));
+
+ print("killing ops and moving on!");
+
+ // If "admin2" wasn't pinned in memory, then these would hang.
+ assert.commandWorked(secondAdmin.currentOp());
+ assert.commandWorked(secondAdmin.killOp(readID));
+ assert.commandWorked(secondAdmin.killOp(writeID));
+
+ readLockShell();
+ writeLockShell();
+
+ admin.logout();
+ secondAdmin.logout();
+ rs.stopSet();
+})();
+
+// This checks that removing a user document actually unpins a user. This is a round-about way
+// of making sure that updates to the authz manager by the opObserver correctly invalidates the
+// cache and that pinned users don't stick around after they're removed.
+(function() {
+ 'use strict';
+ jsTest.setOption("enableTestCommands", true);
+ // Start a mongod with the user cache size set to zero, so we know that users who have
+ // logged out always get fetched cleanly from disk.
+ const mongod =
+ MongoRunner.runMongod({auth: "", setParameter: "authorizationManagerCacheSize=0"});
+ let admin = mongod.getDB("admin");
+
+ admin.createUser({user: "admin", pwd: "admin", roles: ["root"]});
+ admin.auth("admin", "admin");
+
+ // Mark the "admin2" user as pinned in memory
+ assert.commandWorked(admin.runCommand({
+ setParameter: 1,
+ logLevel: 1,
+ authorizationManagerPinnedUsers: [
+ {user: "admin2", db: "admin"},
+ ],
+ }));
+
+ admin.createUser({user: "admin2", pwd: "admin", roles: ["root"]});
+
+ // Invalidate the user cache so we know only "admin" is in there
+ assert.commandWorked(admin.runCommand({invalidateUserCache: 1}));
+ print("User cache after initialization: ",
+ tojson(admin.aggregate([{$listCachedAndActiveUsers: {}}]).toArray()));
+
+ assert.commandWorked(admin.getCollection("system.users").remove({user: "admin2"}));
+
+ print("User cache after removing user doc: ",
+ tojson(admin.aggregate([{$listCachedAndActiveUsers: {}}]).toArray()));
+
+ assert.eq(admin.auth("admin2", "admin"), 0);
+ MongoRunner.stopMongod(mongod);
+})();
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index a8f89e223b1..dd21cdc2811 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -49,7 +49,7 @@ env.Library(
'authorization_session.cpp',
'auth_decorations.cpp',
'user_name.cpp',
- 'role_name.cpp'
+ 'role_name.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
@@ -314,6 +314,9 @@ env.Library(
'$BUILD_DIR/mongo/db/repl/repl_coordinator_interface',
'$BUILD_DIR/mongo/db/server_parameters',
],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/concurrency/lock_manager',
+ ],
)
env.Library(
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 2c19797aad3..ad2c4b629c1 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -264,12 +264,12 @@ public:
/**
* Marks the given user as invalid and removes it from the user cache.
*/
- virtual void invalidateUserByName(const UserName& user) = 0;
+ virtual void invalidateUserByName(OperationContext* opCtx, const UserName& user) = 0;
/**
* Invalidates all users who's source is "dbname" and removes them from the user cache.
*/
- virtual void invalidateUsersFromDB(StringData dbname) = 0;
+ virtual void invalidateUsersFromDB(OperationContext* opCtx, StringData dbname) = 0;
/**
* Initializes the authorization manager. Depending on what version the authorization
@@ -281,7 +281,7 @@ public:
/**
* Invalidates all of the contents of the user cache.
*/
- virtual void invalidateUserCache() = 0;
+ virtual void invalidateUserCache(OperationContext* opCtx) = 0;
/**
* Parses privDoc and fully initializes the user object (credentials, roles, and privileges)
@@ -311,6 +311,8 @@ public:
};
virtual std::vector<CachedUserInfo> getUserCacheInfo() const = 0;
+
+ virtual void setInUserManagementCommand(OperationContext* opCtx, bool val) = 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..c13e3594301 100644
--- a/src/mongo/db/auth/authorization_manager_impl.cpp
+++ b/src/mongo/db/auth/authorization_manager_impl.cpp
@@ -54,6 +54,7 @@
#include "mongo/db/auth/sasl_options.h"
#include "mongo/db/auth/user.h"
#include "mongo/db/auth/user_document_parser.h"
+#include "mongo/db/auth/user_management_commands_parser.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/auth/user_name_hash.h"
#include "mongo/db/global_settings.h"
@@ -112,6 +113,112 @@ MONGO_INITIALIZER_GENERAL(SetupInternalSecurityUser,
}
MONGO_EXPORT_STARTUP_SERVER_PARAMETER(authorizationManagerCacheSize, int, 100);
+
+class PinnedUserSetParameter final : public ServerParameter {
+public:
+ PinnedUserSetParameter()
+ : ServerParameter(
+ ServerParameterSet::getGlobal(), "authorizationManagerPinnedUsers", true, true) {}
+
+ void append(OperationContext* opCtx, BSONObjBuilder& b, const std::string& name) override {
+ BSONArrayBuilder sub(b.subarrayStart(name));
+ for (const auto& username : _userNames) {
+ BSONObjBuilder nameObj(sub.subobjStart());
+ nameObj << AuthorizationManager::USER_NAME_FIELD_NAME << username.getUser()
+ << AuthorizationManager::USER_DB_FIELD_NAME << username.getDB();
+ }
+ }
+
+ Status set(const BSONElement& newValueElement) override {
+ if (newValueElement.type() == String) {
+ return setFromString(newValueElement.valuestrsafe());
+ } else if (newValueElement.type() == Array) {
+ auto array = static_cast<BSONArray>(newValueElement.embeddedObject());
+ std::vector<UserName> out;
+ auto status = auth::parseUserNamesFromBSONArray(array, "", &out);
+ if (!status.isOK())
+ return status;
+
+ status = _checkForSystemUser(out);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ std::swap(_userNames, out);
+ auto authzManager = _authzManager;
+ if (!authzManager) {
+ return Status::OK();
+ }
+
+ lk.unlock();
+
+ authzManager->invalidateUserCache(Client::getCurrent()->getOperationContext());
+ return Status::OK();
+ } else {
+ return {ErrorCodes::BadValue,
+ "authorizationManagerPinnedUsers must be either a string or a BSON array"};
+ }
+ }
+
+ Status setFromString(const std::string& str) override {
+ std::vector<std::string> strList;
+ splitStringDelim(str, &strList, ',');
+
+ std::vector<UserName> out;
+ for (const auto& nameStr : strList) {
+ auto swUserName = UserName::parse(nameStr);
+ if (!swUserName.isOK()) {
+ return swUserName.getStatus();
+ }
+ out.push_back(std::move(swUserName.getValue()));
+ }
+
+ auto status = _checkForSystemUser(out);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ auto authzManager = _authzManager;
+ if (!authzManager) {
+ return Status::OK();
+ }
+
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ std::swap(out, _userNames);
+ lk.unlock();
+
+ authzManager->invalidateUserCache(Client::getCurrent()->getOperationContext());
+ return Status::OK();
+ }
+
+ void setAuthzManager(AuthorizationManager* authzManager) {
+ stdx::lock_guard<stdx::mutex> lk(_mutex);
+ _authzManager = authzManager;
+ }
+
+ std::vector<UserName> getUserNames() {
+ stdx::lock_guard<stdx::mutex> lk(_mutex);
+ return _userNames;
+ }
+
+private:
+ Status _checkForSystemUser(const std::vector<UserName>& names) {
+ if (std::any_of(names.begin(), names.end(), [&](const UserName& userName) {
+ return (userName == internalSecurity.user->getName());
+ })) {
+ return {ErrorCodes::BadValue,
+ "Cannot set __system as a pinned user, it is always pinned"};
+ }
+ return Status::OK();
+ }
+ stdx::mutex _mutex;
+ std::vector<UserName> _userNames;
+ AuthorizationManager* _authzManager;
+} authorizationManagerPinnedUsers;
+
+const auto inUserManagementCommandsFlag = OperationContext::declareDecoration<bool>();
+
} // namespace
@@ -144,29 +251,19 @@ MONGO_REGISTER_SHIM(AuthorizationManager::create)()->std::unique_ptr<Authorizati
* The cached data has an associated counter, called the cache generation. If the cache
* generation changes while a guard is in fetch phase, the fetched data should not be stored
* into the cache, because some invalidation event occurred during the fetch phase.
- *
- * NOTE: It is not safe to enter fetch phase while holding a database lock. Fetch phase
- * operations are allowed to acquire database locks themselves, so entering fetch while holding
- * a database lock may lead to deadlock.
*/
class AuthorizationManagerImpl::CacheGuard {
MONGO_DISALLOW_COPYING(CacheGuard);
public:
- enum FetchSynchronization { fetchSynchronizationAutomatic, fetchSynchronizationManual };
-
/**
* Constructs a cache guard, locking the mutex that synchronizes user cache accesses.
*/
- CacheGuard(AuthorizationManagerImpl* authzManager,
- const FetchSynchronization sync = fetchSynchronizationAutomatic)
+ explicit CacheGuard(OperationContext* opCtx, AuthorizationManagerImpl* authzManager)
: _isThisGuardInFetchPhase(false),
_authzManager(authzManager),
- _lock(authzManager->_cacheMutex) {
- if (fetchSynchronizationAutomatic == sync) {
- synchronizeWithFetchPhase();
- }
- }
+ _stateLock(_authzManager->_externalState->lock(opCtx)),
+ _lock(authzManager->_cacheWriteMutex) {}
/**
* Releases the mutex that synchronizes user cache access, if held, and notifies
@@ -186,7 +283,7 @@ public:
/**
* Returns true of the authzManager reports that it is in fetch phase.
*/
- bool otherUpdateInFetchPhase() {
+ bool otherUpdateInFetchPhase() const {
return _authzManager->_isFetchPhaseBusy;
}
@@ -203,18 +300,26 @@ public:
* cache generation.
*/
void beginFetchPhase() {
+ beginFetchPhaseNoYield();
+ _lock.unlock();
+ }
+
+ /**
+ * Sets up the fetch phase without releasing _authzManager->_cacheMutex
+ */
+ void beginFetchPhaseNoYield() {
fassert(17191, !_authzManager->_isFetchPhaseBusy);
_isThisGuardInFetchPhase = true;
_authzManager->_isFetchPhaseBusy = true;
_startGeneration = _authzManager->_fetchGeneration;
- _lock.unlock();
}
/**
* Exits the fetch phase, reacquiring the _authzManager->_cacheMutex.
*/
void endFetchPhase() {
- _lock.lock();
+ if (!_lock.owns_lock())
+ _lock.lock();
// We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until
// ~CacheGuard(), for two reasons. First, there's no value to notifying the waiters
// before you're ready to release the mutex, because they'll just go to sleep on the
@@ -237,17 +342,10 @@ public:
}
private:
- void synchronizeWithFetchPhase() {
- while (otherUpdateInFetchPhase())
- wait();
- fassert(17192, !_authzManager->_isFetchPhaseBusy);
- _isThisGuardInFetchPhase = true;
- _authzManager->_isFetchPhaseBusy = true;
- }
-
OID _startGeneration;
bool _isThisGuardInFetchPhase;
AuthorizationManagerImpl* _authzManager;
+ std::unique_ptr<AuthzManagerExternalState::StateLock> _stateLock;
stdx::unique_lock<stdx::mutex> _lock;
};
@@ -262,9 +360,8 @@ AuthorizationManagerImpl::AuthorizationManagerImpl(
_externalState(std::move(externalState)),
_version(schemaVersionInvalid),
_userCache(authorizationManagerCacheSize, UserCacheInvalidator()),
- _isFetchPhaseBusy(false) {
- _updateCacheGeneration_inlock();
-}
+ _fetchGeneration(OID::gen()),
+ _isFetchPhaseBusy(false) {}
AuthorizationManagerImpl::~AuthorizationManagerImpl() {}
@@ -283,7 +380,7 @@ bool AuthorizationManagerImpl::shouldValidateAuthSchemaOnStartup() {
}
Status AuthorizationManagerImpl::getAuthorizationVersion(OperationContext* opCtx, int* version) {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ CacheGuard guard(opCtx, this);
int newVersion = _version;
if (schemaVersionInvalid == newVersion) {
while (guard.otherUpdateInFetchPhase())
@@ -307,7 +404,7 @@ Status AuthorizationManagerImpl::getAuthorizationVersion(OperationContext* opCtx
}
OID AuthorizationManagerImpl::getCacheGeneration() {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ stdx::lock_guard<stdx::mutex> lk(_cacheWriteMutex);
return _fetchGeneration;
}
@@ -327,6 +424,7 @@ bool AuthorizationManagerImpl::hasAnyPrivilegeDocuments(OperationContext* opCtx)
}
lk.unlock();
+ auto stateLk = _externalState->lock(opCtx);
bool privDocsExist = _externalState->hasAnyPrivilegeDocuments(opCtx);
lk.lock();
@@ -377,6 +475,7 @@ Status AuthorizationManagerImpl::_initializeUserFromPrivilegeDocument(User* user
Status AuthorizationManagerImpl::getUserDescription(OperationContext* opCtx,
const UserName& userName,
BSONObj* result) {
+ auto lk = _externalState->lock(opCtx);
return _externalState->getUserDescription(opCtx, userName, result);
}
@@ -385,6 +484,7 @@ Status AuthorizationManagerImpl::getRoleDescription(OperationContext* opCtx,
PrivilegeFormat privileges,
AuthenticationRestrictionsFormat restrictions,
BSONObj* result) {
+ auto lk = _externalState->lock(opCtx);
return _externalState->getRoleDescription(opCtx, roleName, privileges, restrictions, result);
}
@@ -393,6 +493,7 @@ Status AuthorizationManagerImpl::getRolesDescription(OperationContext* opCtx,
PrivilegeFormat privileges,
AuthenticationRestrictionsFormat restrictions,
BSONObj* result) {
+ auto lk = _externalState->lock(opCtx);
return _externalState->getRolesDescription(opCtx, roleName, privileges, restrictions, result);
}
@@ -404,11 +505,13 @@ Status AuthorizationManagerImpl::getRoleDescriptionsForDB(
AuthenticationRestrictionsFormat restrictions,
bool showBuiltinRoles,
vector<BSONObj>* result) {
+ auto lk = _externalState->lock(opCtx);
return _externalState->getRoleDescriptionsForDB(
opCtx, dbname, privileges, restrictions, showBuiltinRoles, result);
}
void AuthorizationManagerImpl::UserCacheInvalidator::operator()(User* user) {
+ LOG(1) << "Invalidating user " << user->getName().toString();
user->_invalidate();
}
@@ -418,22 +521,65 @@ StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* o
return internalSecurity.user;
}
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- boost::optional<std::shared_ptr<User>> cachedUser;
+ boost::optional<UserHandle> cachedUser = _userCache.get(userName);
+ auto returnUser = [&](boost::optional<UserHandle> cachedUser) {
+ auto ret = *cachedUser;
+ fassert(16914, ret.get());
+ fassert(17003, ret->isValid());
+
+ LOG(1) << "Returning user " << userName << " from cache";
+ return ret;
+ };
+
+ // The userCache is thread-safe, so if we can get a user out of the cache we don't need to
+ // take any locks!
+ if (cachedUser) {
+ return returnUser(cachedUser);
+ }
+
+ // Otherwise make sure we have the locks we need and check whether and wait on another
+ // thread is fetching into the cache
+ CacheGuard guard(opCtx, this);
+
+ auto pinnedIt =
+ std::find_if(_pinnedUsers.begin(), _pinnedUsers.end(), [&](const auto& userHandle) {
+ return (userHandle->getName() == userName);
+ });
+ if (pinnedIt != _pinnedUsers.end()) {
+ return *pinnedIt;
+ }
+
while ((boost::none == (cachedUser = _userCache.get(userName))) &&
guard.otherUpdateInFetchPhase()) {
guard.wait();
}
if (cachedUser != boost::none) {
- auto ret = *cachedUser;
- fassert(16914, ret.get());
- fassert(17003, ret->isValid());
- return ret;
+ return returnUser(cachedUser);
}
- int authzVersion = _version;
guard.beginFetchPhase();
+ // If there's still no user in the cache, then we need to go to disk. Take the slow path.
+ LOG(1) << "Getting user " << userName << " from disk";
+ auto ret = _acquireUserSlowPath(guard, opCtx, userName);
+
+ guard.endFetchPhase();
+ if (!ret.isOK()) {
+ return ret.getStatus();
+ }
+
+ auto user = std::move(ret.getValue());
+ if (user->isValid()) {
+ _updateCacheGeneration_inlock(guard);
+ }
+
+ return user;
+}
+
+StatusWith<UserHandle> AuthorizationManagerImpl::_acquireUserSlowPath(CacheGuard& guard,
+ OperationContext* opCtx,
+ const UserName& userName) {
+ int authzVersion = _version;
// Number of times to retry a user document that fetches due to transient
// AuthSchemaIncompatible errors. These errors should only ever occur during and shortly
@@ -475,9 +621,12 @@ StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* o
authzVersion = schemaVersionInvalid;
}
+
if (!status.isOK())
return status;
+ // All this does is re-acquire the _cacheWriteMutex if we don't hold it already - a caller
+ // may also call endFetchPhase() after this returns.
guard.endFetchPhase();
if (guard.isSameCacheGeneration()) {
@@ -493,6 +642,72 @@ StatusWith<UserHandle> AuthorizationManagerImpl::acquireUser(OperationContext* o
}
}
+void AuthorizationManagerImpl::_recachePinnedUsers(CacheGuard& guard, OperationContext* opCtx) {
+ const auto findPinnedUser = [&](const auto& userName) {
+ return std::find_if(_pinnedUsers.begin(), _pinnedUsers.end(), [&](const auto& userHandle) {
+ return (userHandle->getName() == userName);
+ });
+ };
+
+ // Get the list of users to pin
+ const auto usersToPin = authorizationManagerPinnedUsers.getUserNames();
+ if (usersToPin.empty()) {
+ // If there are pinned users, clear them all out so they fall out of the cache
+ if (!_pinnedUsers.empty()) {
+ _pinnedUsers.clear();
+ }
+ return;
+ }
+
+ // Remove any users that shouldn't be pinned anymore or that are invalid.
+ std::vector<UserHandle> newPinnedUsers;
+ std::copy_if(_pinnedUsers.begin(),
+ _pinnedUsers.end(),
+ std::back_inserter(newPinnedUsers),
+ [&](const UserHandle& user) {
+ if (!user->isValid())
+ return false;
+ return std::any_of(
+ usersToPin.begin(), usersToPin.end(), [&](const UserName& userName) {
+ return (user->getName() == userName);
+ });
+ });
+
+
+ while (guard.otherUpdateInFetchPhase()) {
+ guard.wait();
+ }
+
+ // Begin the fetch phase but don't yield any locks. We want re-pinning of users to block other
+ // operations until all the users are refreshed.
+ guard.beginFetchPhaseNoYield();
+
+ bool cacheUpdated = false;
+ for (const auto& userName : usersToPin) {
+ if (findPinnedUser(userName) != _pinnedUsers.end()) {
+ continue;
+ }
+
+ cacheUpdated = true;
+ auto swUser = _acquireUserSlowPath(guard, opCtx, userName);
+ if (swUser.isOK()) {
+ newPinnedUsers.emplace(newPinnedUsers.end(), std::move(swUser.getValue()));
+ } else {
+ const auto& status = swUser.getStatus();
+ // If the user is not found, then it might just not exist yet. Skip this user for now.
+ if (status != ErrorCodes::UserNotFound) {
+ warning() << "Unable to fetch pinned user " << userName.toString() << ": "
+ << status;
+ }
+ }
+ }
+
+ if (cacheUpdated)
+ _updateCacheGeneration_inlock(guard);
+
+ _pinnedUsers = std::move(newPinnedUsers);
+}
+
Status AuthorizationManagerImpl::_fetchUserV2(OperationContext* opCtx,
const UserName& userName,
std::unique_ptr<User>* out) {
@@ -513,26 +728,33 @@ Status AuthorizationManagerImpl::_fetchUserV2(OperationContext* opCtx,
return Status::OK();
}
-void AuthorizationManagerImpl::invalidateUserByName(const UserName& userName) {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- _updateCacheGeneration_inlock();
+void AuthorizationManagerImpl::invalidateUserByName(OperationContext* opCtx,
+ const UserName& userName) {
+ CacheGuard guard(opCtx, this);
+ _updateCacheGeneration_inlock(guard);
_userCache.invalidate(userName);
+
+ _recachePinnedUsers(guard, opCtx);
}
-void AuthorizationManagerImpl::invalidateUsersFromDB(StringData dbname) {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- _updateCacheGeneration_inlock();
+void AuthorizationManagerImpl::invalidateUsersFromDB(OperationContext* opCtx, StringData dbname) {
+ CacheGuard guard(opCtx, this);
+ _updateCacheGeneration_inlock(guard);
_userCache.invalidateIf(
[&](const UserName& user, const User*) { return user.getDB() == dbname; });
+
+ _recachePinnedUsers(guard, opCtx);
}
-void AuthorizationManagerImpl::invalidateUserCache() {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- _invalidateUserCache_inlock();
+void AuthorizationManagerImpl::invalidateUserCache(OperationContext* opCtx) {
+ CacheGuard guard(opCtx, this);
+ _invalidateUserCache_inlock(guard);
+
+ _recachePinnedUsers(guard, opCtx);
}
-void AuthorizationManagerImpl::_invalidateUserCache_inlock() {
- _updateCacheGeneration_inlock();
+void AuthorizationManagerImpl::_invalidateUserCache_inlock(const CacheGuard& guard) {
+ _updateCacheGeneration_inlock(guard);
_userCache.invalidateIf([](const UserName& a, const User*) { return true; });
// Reread the schema version before acquiring the next user.
@@ -540,11 +762,12 @@ void AuthorizationManagerImpl::_invalidateUserCache_inlock() {
}
Status AuthorizationManagerImpl::initialize(OperationContext* opCtx) {
- invalidateUserCache();
Status status = _externalState->initialize(opCtx);
if (!status.isOK())
return status;
+ authorizationManagerPinnedUsers.setAuthzManager(this);
+ invalidateUserCache(opCtx);
return Status::OK();
}
@@ -616,17 +839,26 @@ StatusWith<UserName> extractUserNameFromIdString(StringData idstr) {
} // namespace
-void AuthorizationManagerImpl::_updateCacheGeneration_inlock() {
+void AuthorizationManagerImpl::_updateCacheGeneration_inlock(const CacheGuard&) {
_fetchGeneration = OID::gen();
}
-void AuthorizationManagerImpl::_invalidateRelevantCacheData(const char* op,
+void AuthorizationManagerImpl::_invalidateRelevantCacheData(OperationContext* opCtx,
+ const char* op,
const NamespaceString& ns,
const BSONObj& o,
const BSONObj* o2) {
+ // When we're doing a user management command we lock the admin DB for the duration
+ // of the command and invalidate the cache at the end of the command, so we don't need
+ // to invalidate it based on calls to logOp().
+ if (inUserManagementCommandsFlag(opCtx)) {
+ LOG(1) << "Skipping cache invalidation in opObserver because of active user command";
+ return;
+ }
+
if (ns == AuthorizationManager::rolesCollectionNamespace ||
ns == AuthorizationManager::versionCollectionNamespace) {
- invalidateUserCache();
+ invalidateUserCache(opCtx);
return;
}
@@ -643,12 +875,12 @@ void AuthorizationManagerImpl::_invalidateRelevantCacheData(const char* op,
warning() << "Invalidating user cache based on user being updated failed, will "
"invalidate the entire cache instead: "
<< userName.getStatus();
- invalidateUserCache();
+ invalidateUserCache(opCtx);
return;
}
- invalidateUserByName(userName.getValue());
+ invalidateUserByName(opCtx, userName.getValue());
} else {
- invalidateUserCache();
+ invalidateUserCache(opCtx);
}
}
@@ -658,8 +890,13 @@ void AuthorizationManagerImpl::logOp(OperationContext* opCtx,
const BSONObj& o,
const BSONObj* o2) {
if (appliesToAuthzData(op, nss, o)) {
+ // AuthzManagerExternalState's logOp method registers a RecoveryUnit::Change
+ // and to do so we need to have begun a UnitOfWork
+ WriteUnitOfWork wuow(opCtx);
_externalState->logOp(opCtx, op, nss, o, o2);
- _invalidateRelevantCacheData(op, nss, o, o2);
+ wuow.commit();
+
+ _invalidateRelevantCacheData(opCtx, op, nss, o, o2);
}
}
@@ -676,4 +913,8 @@ std::vector<AuthorizationManager::CachedUserInfo> AuthorizationManagerImpl::getU
return ret;
}
+void AuthorizationManagerImpl::setInUserManagementCommand(OperationContext* opCtx, bool val) {
+ inUserManagementCommandsFlag(opCtx) = val;
+}
+
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h
index c29f3790d62..d17f65cb70f 100644
--- a/src/mongo/db/auth/authorization_manager_impl.h
+++ b/src/mongo/db/auth/authorization_manager_impl.h
@@ -118,13 +118,13 @@ public:
StatusWith<UserHandle> acquireUser(OperationContext* opCtx, const UserName& userName) override;
- void invalidateUserByName(const UserName& user) override;
+ void invalidateUserByName(OperationContext* opCtx, const UserName& user) override;
- void invalidateUsersFromDB(StringData dbname) override;
+ void invalidateUsersFromDB(OperationContext* opCtx, StringData dbname) override;
Status initialize(OperationContext* opCtx) override;
- void invalidateUserCache() override;
+ void invalidateUserCache(OperationContext* opCtx) override;
Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc) override;
@@ -136,6 +136,8 @@ public:
std::vector<CachedUserInfo> getUserCacheInfo() const override;
+ void setInUserManagementCommand(OperationContext* opCtx, bool val) override;
+
private:
/**
* Type used to guard accesses and updates to the user cache.
@@ -147,14 +149,15 @@ private:
* Invalidates all User objects in the cache and removes them from the cache.
* Should only be called when already holding _cacheMutex.
*/
- void _invalidateUserCache_inlock();
+ void _invalidateUserCache_inlock(const CacheGuard&);
/**
* Given the objects describing an oplog entry that affects authorization data, invalidates
* the portion of the user cache that is affected by that operation. Should only be called
* with oplog entries that have been pre-verified to actually affect authorization data.
*/
- void _invalidateRelevantCacheData(const char* op,
+ void _invalidateRelevantCacheData(OperationContext* opCtx,
+ const char* op,
const NamespaceString& ns,
const BSONObj& o,
const BSONObj* o2);
@@ -162,7 +165,14 @@ private:
/**
* Updates _cacheGeneration to a new OID
*/
- void _updateCacheGeneration_inlock();
+ void _updateCacheGeneration_inlock(const CacheGuard&);
+
+
+ void _recachePinnedUsers(CacheGuard& guard, OperationContext* opCtx);
+
+ StatusWith<UserHandle> _acquireUserSlowPath(CacheGuard& guard,
+ OperationContext* opCtx,
+ const UserName& userName);
/**
* Fetches user information from a v2-schema user document for the named user,
@@ -219,6 +229,13 @@ private:
};
InvalidatingLRUCache<UserName, User, UserCacheInvalidator> _userCache;
+ std::vector<UserHandle> _pinnedUsers;
+
+ /**
+ * Protects _cacheGeneration, _version and _isFetchPhaseBusy. Manipulated
+ * via CacheGuard.
+ */
+ stdx::mutex _cacheWriteMutex;
/**
* Current generation of cached data. Updated every time part of the cache gets
@@ -235,15 +252,11 @@ private:
bool _isFetchPhaseBusy;
/**
- * Protects _userCache, _cacheGeneration, _version and _isFetchPhaseBusy. Manipulated
- * via CacheGuard.
- */
- stdx::mutex _cacheMutex;
-
- /**
* Condition used to signal that it is OK for another CacheGuard to enter a fetch phase.
* Manipulated via CacheGuard.
*/
stdx::condition_variable _fetchPhaseIsReady;
+
+ AtomicBool _inUserManagementCommand{false};
};
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp
index d782ec3567b..48eee3f23e0 100644
--- a/src/mongo/db/auth/authorization_manager_test.cpp
+++ b/src/mongo/db/auth/authorization_manager_test.cpp
@@ -78,7 +78,7 @@ class AuthorizationManagerTest : public ServiceContextTest {
public:
virtual ~AuthorizationManagerTest() {
if (authzManager)
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx.get());
}
AuthorizationManagerTest() {
@@ -265,11 +265,6 @@ private:
class AuthorizationManagerWithExplicitUserPrivilegesTest : public ::mongo::unittest::Test {
public:
- virtual ~AuthorizationManagerWithExplicitUserPrivilegesTest() {
- if (authzManager)
- authzManager->invalidateUserCache();
- }
-
virtual void setUp() {
auto localExternalState =
stdx::make_unique<AuthzManagerExternalStateMockWithExplicitUserPrivileges>();
diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp
index cad01f09e9b..35eb7e5c56d 100644
--- a/src/mongo/db/auth/authorization_session_test.cpp
+++ b/src/mongo/db/auth/authorization_session_test.cpp
@@ -453,7 +453,7 @@ TEST_F(AuthorizationSessionTest, InvalidateUser) {
BSONObj()));
// Make sure that invalidating the user causes the session to reload its privileges.
- authzManager->invalidateUserByName(user->getName());
+ authzManager->invalidateUserByName(_opCtx.get(), user->getName());
authzSession->startRequest(_opCtx.get()); // Refreshes cached data for invalid users
ASSERT_TRUE(
authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
@@ -472,7 +472,7 @@ TEST_F(AuthorizationSessionTest, InvalidateUser) {
&ignored)
.transitional_ignore();
// Make sure that invalidating the user causes the session to reload its privileges.
- authzManager->invalidateUserByName(user->getName());
+ authzManager->invalidateUserByName(_opCtx.get(), user->getName());
authzSession->startRequest(_opCtx.get()); // Refreshes cached data for invalid users
ASSERT_FALSE(
authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
@@ -533,7 +533,7 @@ TEST_F(AuthorizationSessionTest, UseOldUserInfoInFaceOfConnectivityProblems) {
// Even though the user's privileges have been reduced, since we've configured user
// document lookup to fail, the authz session should continue to use its known out-of-date
// privilege data.
- authzManager->invalidateUserByName(user->getName());
+ authzManager->invalidateUserByName(_opCtx.get(), user->getName());
authzSession->startRequest(_opCtx.get()); // Refreshes cached data for invalid users
ASSERT_TRUE(
authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h
index 3fb750a192d..8a044eb2038 100644
--- a/src/mongo/db/auth/authz_manager_external_state.h
+++ b/src/mongo/db/auth/authz_manager_external_state.h
@@ -165,7 +165,35 @@ public:
const NamespaceString& ns,
const BSONObj& o,
const BSONObj* o2) {}
+ /**
+ * Represents a lock_guard on the storage for this implementation of the external state.
+ */
+ class StateLock {
+ StateLock(StateLock&) = delete;
+ StateLock& operator=(StateLock&) = delete;
+
+ public:
+ StateLock() = default;
+ virtual ~StateLock() = default;
+ };
+
+ /**
+ * Returns a Lock on the external state for the given operation context.
+ *
+ * By default this returns an empty/noop StateLock.
+ */
+ virtual std::unique_ptr<StateLock> lock(OperationContext* opCtx) {
+ return std::make_unique<StateLock>();
+ };
+ /**
+ * Returns true if you must acquire a StateLock before fetching a user description.
+ *
+ * By default this returns false since only mongod actually needs to do locking at this level.
+ */
+ virtual bool needsLockForUserName(OperationContext* opCtx, const UserName& user) {
+ return false;
+ }
protected:
AuthzManagerExternalState(); // This class should never be instantiated directly.
diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp
index 11e350ee253..2c5b4a307bb 100644
--- a/src/mongo/db/auth/authz_manager_external_state_d.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp
@@ -59,6 +59,35 @@ AuthzManagerExternalStateMongod::makeAuthzSessionExternalState(AuthorizationMana
return stdx::make_unique<AuthzSessionExternalStateMongod>(authzManager);
}
+class AuthzLock : public AuthzManagerExternalState::StateLock {
+public:
+ explicit AuthzLock(OperationContext* opCtx)
+ : _lock(opCtx,
+ AuthorizationManager::usersCollectionNamespace.db(),
+ MODE_S,
+ opCtx->getDeadline()) {}
+
+ static bool isLocked(OperationContext* opCtx);
+
+private:
+ Lock::DBLock _lock;
+};
+
+bool AuthzLock::isLocked(OperationContext* opCtx) {
+ return opCtx->lockState()->isDbLockedForMode(
+ AuthorizationManager::usersCollectionNamespace.db(), MODE_S);
+}
+
+std::unique_ptr<AuthzManagerExternalState::StateLock> AuthzManagerExternalStateMongod::lock(
+ OperationContext* opCtx) {
+ return std::make_unique<AuthzLock>(opCtx);
+}
+
+bool AuthzManagerExternalStateMongod::needsLockForUserName(OperationContext* opCtx,
+ const UserName& name) {
+ return (shouldUseRolesFromConnection(opCtx, name) == false);
+}
+
Status AuthzManagerExternalStateMongod::query(
OperationContext* opCtx,
const NamespaceString& collectionName,
diff --git a/src/mongo/db/auth/authz_manager_external_state_d.h b/src/mongo/db/auth/authz_manager_external_state_d.h
index a620528e46f..c946d5a52cc 100644
--- a/src/mongo/db/auth/authz_manager_external_state_d.h
+++ b/src/mongo/db/auth/authz_manager_external_state_d.h
@@ -52,6 +52,10 @@ public:
std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
AuthorizationManager* authzManager) override;
+ std::unique_ptr<StateLock> lock(OperationContext* opCtx) final;
+
+ bool needsLockForUserName(OperationContext* opCtx, const UserName& name) final;
+
virtual Status findOne(OperationContext* opCtx,
const NamespaceString& collectionName,
const BSONObj& query,
diff --git a/src/mongo/db/auth/user_cache_invalidator_job.cpp b/src/mongo/db/auth/user_cache_invalidator_job.cpp
index 33b247af580..18dc03d3b8f 100644
--- a/src/mongo/db/auth/user_cache_invalidator_job.cpp
+++ b/src/mongo/db/auth/user_cache_invalidator_job.cpp
@@ -172,14 +172,23 @@ void UserCacheInvalidator::run() {
<< currentGeneration.getStatus();
}
// When in doubt, invalidate the cache
- _authzManager->invalidateUserCache();
+ try {
+ _authzManager->invalidateUserCache(opCtx.get());
+ } catch (const DBException& e) {
+ warning() << "Error invalidating user cache: " << e.toStatus();
+ }
continue;
}
if (currentGeneration.getValue() != _previousCacheGeneration) {
log() << "User cache generation changed from " << _previousCacheGeneration << " to "
<< currentGeneration.getValue() << "; invalidating user cache";
- _authzManager->invalidateUserCache();
+ try {
+ _authzManager->invalidateUserCache(opCtx.get());
+ } catch (const DBException& e) {
+ warning() << "Error invalidating user cache: " << e.toStatus();
+ }
+
_previousCacheGeneration = currentGeneration.getValue();
}
}
diff --git a/src/mongo/db/commands/sleep_command.cpp b/src/mongo/db/commands/sleep_command.cpp
index ecf04ba6519..366232dbcb4 100644
--- a/src/mongo/db/commands/sleep_command.cpp
+++ b/src/mongo/db/commands/sleep_command.cpp
@@ -68,14 +68,24 @@ public:
const BSONObj& cmdObj,
std::vector<Privilege>* out) const {}
- void _sleepInReadLock(mongo::OperationContext* opCtx, long long millis) {
- Lock::GlobalRead lk(opCtx);
- opCtx->sleepFor(Milliseconds(millis));
- }
-
- void _sleepInWriteLock(mongo::OperationContext* opCtx, long long millis) {
- Lock::GlobalWrite lk(opCtx);
- opCtx->sleepFor(Milliseconds(millis));
+ void _sleepInLock(mongo::OperationContext* opCtx,
+ long long millis,
+ LockMode mode,
+ const StringData& ns) {
+ if (ns.empty()) {
+ Lock::GlobalLock lk(opCtx, mode, opCtx->getDeadline(), Lock::InterruptBehavior::kThrow);
+ opCtx->sleepFor(Milliseconds(millis));
+ } else if (nsIsDbOnly(ns)) {
+ uassert(50961, "lockTarget is not a valid namespace", NamespaceString::validDBName(ns));
+ Lock::DBLock lk(opCtx, ns, mode, opCtx->getDeadline());
+ opCtx->sleepFor(Milliseconds(millis));
+ } else {
+ uassert(50962,
+ "lockTarget is not a valid namespace",
+ NamespaceString::validCollectionComponent(ns));
+ Lock::CollectionLock lk(opCtx->lockState(), ns, mode, opCtx->getDeadline());
+ opCtx->sleepFor(Milliseconds(millis));
+ }
}
CmdSleep() : BasicCommand("sleep") {}
@@ -99,12 +109,17 @@ public:
millis = 10 * 1000;
}
+ StringData lockTarget;
+ if (cmdObj["lockTarget"]) {
+ lockTarget = cmdObj["lockTarget"].checkAndGetStringData();
+ }
+
if (!cmdObj["lock"]) {
// Legacy implementation
if (cmdObj.getBoolField("w")) {
- _sleepInWriteLock(opCtx, millis);
+ _sleepInLock(opCtx, millis, MODE_X, lockTarget);
} else {
- _sleepInReadLock(opCtx, millis);
+ _sleepInLock(opCtx, millis, MODE_S, lockTarget);
}
} else {
uassert(34346, "Only one of 'w' and 'lock' may be set.", !cmdObj["w"]);
@@ -113,10 +128,10 @@ public:
if (lock == "none") {
opCtx->sleepFor(Milliseconds(millis));
} else if (lock == "w") {
- _sleepInWriteLock(opCtx, millis);
+ _sleepInLock(opCtx, millis, MODE_X, lockTarget);
} else {
uassert(34347, "'lock' must be one of 'r', 'w', 'none'.", lock == "r");
- _sleepInReadLock(opCtx, millis);
+ _sleepInLock(opCtx, millis, MODE_S, lockTarget);
}
}
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 84fff5c50a8..7f1d12cc6de 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -59,6 +59,7 @@
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/run_aggregate.h"
+#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context.h"
@@ -87,9 +88,6 @@ using std::vector;
namespace {
-// Used to obtain mutex that guards modifications to persistent authorization data
-const auto getAuthzDataMutex = ServiceContext::declareDecoration<stdx::mutex>();
-
Status useDefaultCode(const Status& status, ErrorCodes::Error defaultCode) {
if (status.code() != ErrorCodes::UnknownError)
return status;
@@ -147,7 +145,6 @@ Status getCurrentUserRoles(OperationContext* opCtx,
AuthorizationManager* authzManager,
const UserName& userName,
stdx::unordered_set<RoleName>* roles) {
- 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();
@@ -565,14 +562,45 @@ Status writeAuthSchemaVersionIfNeeded(OperationContext* opCtx,
return status;
}
+class AuthzLockGuard {
+ AuthzLockGuard(AuthzLockGuard&) = delete;
+ AuthzLockGuard& operator=(AuthzLockGuard&) = delete;
+
+public:
+ AuthzLockGuard(OperationContext* opCtx, LockMode mode)
+ : _opCtx(opCtx),
+ _lock(_opCtx,
+ AuthorizationManager::usersCollectionNamespace.db(),
+ mode,
+ _opCtx->getDeadline()) {
+ auto authzMgr = AuthorizationManager::get(_opCtx->getServiceContext());
+ authzMgr->setInUserManagementCommand(_opCtx, true);
+ }
+
+ ~AuthzLockGuard() {
+ auto authzMgr = AuthorizationManager::get(_opCtx->getServiceContext());
+ authzMgr->setInUserManagementCommand(_opCtx, false);
+ }
+
+ AuthzLockGuard(AuthzLockGuard&&) = default;
+
+private:
+ OperationContext* _opCtx;
+ Lock::DBLock _lock;
+};
+
/**
* Returns Status::OK() if the current Auth schema version is at least the auth schema version
* for the MongoDB 3.0 SCRAM auth mode.
* Returns an error otherwise.
*/
-Status requireWritableAuthSchema28SCRAM(OperationContext* opCtx,
- AuthorizationManager* authzManager) {
+StatusWith<AuthzLockGuard> requireWritableAuthSchema28SCRAM(OperationContext* opCtx,
+ AuthorizationManager* authzManager) {
int foundSchemaVersion;
+ // We take a MODE_X lock during writes because we want to be sure that we can read any pinned
+ // user documents back out of the database after writing them during the user management
+ // commands, and to ensure only one user management command is running at a time.
+ AuthzLockGuard lk(opCtx, MODE_X);
Status status = authzManager->getAuthorizationVersion(opCtx, &foundSchemaVersion);
if (!status.isOK()) {
return status;
@@ -587,7 +615,12 @@ Status requireWritableAuthSchema28SCRAM(OperationContext* opCtx,
<< " but found "
<< foundSchemaVersion);
}
- return writeAuthSchemaVersionIfNeeded(opCtx, authzManager, foundSchemaVersion);
+ status = writeAuthSchemaVersionIfNeeded(opCtx, authzManager, foundSchemaVersion);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return std::move(lk);
}
/**
@@ -602,9 +635,10 @@ Status requireWritableAuthSchema28SCRAM(OperationContext* opCtx,
* If records are added thinking we're at one schema level, then the default is changed,
* then the auth database would wind up in an inconsistent state.
*/
-Status requireReadableAuthSchema26Upgrade(OperationContext* opCtx,
- AuthorizationManager* authzManager) {
+StatusWith<AuthzLockGuard> requireReadableAuthSchema26Upgrade(OperationContext* opCtx,
+ AuthorizationManager* authzManager) {
int foundSchemaVersion;
+ AuthzLockGuard lk(opCtx, MODE_IS);
Status status = authzManager->getAuthorizationVersion(opCtx, &foundSchemaVersion);
if (!status.isOK()) {
return status;
@@ -618,7 +652,8 @@ Status requireReadableAuthSchema26Upgrade(OperationContext* opCtx,
<< " but found "
<< foundSchemaVersion);
}
- return Status::OK();
+
+ return std::move(lk);
}
Status buildCredentials(BSONObjBuilder* builder, const auth::CreateOrUpdateUserArgs& args) {
@@ -802,6 +837,9 @@ public:
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
+
int authzVersion;
status = authzManager->getAuthorizationVersion(opCtx, &authzVersion);
uassertStatusOK(status);
@@ -827,11 +865,6 @@ public:
status = parser.checkValidUserDocument(userObj);
uassertStatusOK(status);
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
-
// Role existence has to be checked after acquiring the update lock
for (size_t i = 0; i < args.roles.size(); ++i) {
BSONObj ignored;
@@ -846,6 +879,7 @@ public:
args.roles,
args.authenticationRestrictions);
status = insertPrivilegeDocument(opCtx, userObj);
+ authzManager->invalidateUserByName(opCtx, args.userName);
uassertStatusOK(status);
return true;
}
@@ -950,11 +984,9 @@ public:
}
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
// Role existence has to be checked after acquiring the update lock
if (args.hasRoles) {
@@ -975,7 +1007,7 @@ public:
status = updatePrivilegeDocument(
opCtx, args.userName, queryBuilder.done(), updateDocumentBuilder.done());
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserByName(args.userName);
+ authzManager->invalidateUserByName(opCtx, args.userName);
uassertStatusOK(status);
return true;
}
@@ -1020,10 +1052,9 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
audit::logDropUser(Client::getCurrent(), userName);
@@ -1035,7 +1066,7 @@ public:
<< userName.getDB()),
&nMatched);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserByName(userName);
+ authzManager->invalidateUserByName(opCtx, userName);
uassertStatusOK(status);
if (nMatched == 0) {
@@ -1081,11 +1112,9 @@ public:
Status status = auth::parseAndValidateDropAllUsersFromDatabaseCommand(cmdObj, dbname);
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
audit::logDropAllUsersFromDatabase(Client::getCurrent(), dbname);
@@ -1093,7 +1122,7 @@ public:
status = removePrivilegeDocuments(
opCtx, BSON(AuthorizationManager::USER_DB_FIELD_NAME << dbname), &numRemoved);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUsersFromDB(dbname);
+ authzManager->invalidateUsersFromDB(opCtx, dbname);
uassertStatusOK(status);
result.append("n", numRemoved);
@@ -1139,11 +1168,9 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
UserName userName(userNameString, dbname);
stdx::unordered_set<RoleName> userRoles;
@@ -1164,7 +1191,7 @@ public:
status = updatePrivilegeDocument(
opCtx, userName, BSON("$set" << BSON("roles" << newRolesBSONArray)));
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserByName(userName);
+ authzManager->invalidateUserByName(opCtx, userName);
uassertStatusOK(status);
return true;
}
@@ -1208,11 +1235,9 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
UserName userName(userNameString, dbname);
stdx::unordered_set<RoleName> userRoles;
@@ -1233,7 +1258,7 @@ public:
status = updatePrivilegeDocument(
opCtx, userName, BSON("$set" << BSON("roles" << newRolesBSONArray)));
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserByName(userName);
+ authzManager->invalidateUserByName(opCtx, userName);
uassertStatusOK(status);
return true;
}
@@ -1270,8 +1295,8 @@ public:
Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args);
uassertStatusOK(status);
- status = requireReadableAuthSchema26Upgrade(opCtx, getGlobalAuthorizationManager());
- uassertStatusOK(status);
+ AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext());
+ auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager));
if ((args.target != auth::UsersInfoArgs::Target::kExplicitUsers || args.filter) &&
(args.showPrivileges ||
@@ -1289,8 +1314,7 @@ public:
// user.
for (size_t i = 0; i < args.userNames.size(); ++i) {
BSONObj userDetails;
- status = getGlobalAuthorizationManager()->getUserDescription(
- opCtx, args.userNames[i], &userDetails);
+ status = authzManager->getUserDescription(opCtx, args.userNames[i], &userDetails);
if (status.code() == ErrorCodes::UserNotFound) {
continue;
}
@@ -1475,11 +1499,9 @@ public:
}
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
// Role existence has to be checked after acquiring the update lock
status = checkOkayToGrantRolesToRole(opCtx, args.roleName, args.roles, authzManager);
@@ -1564,11 +1586,9 @@ public:
}
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
// Role existence has to be checked after acquiring the update lock
BSONObj ignored;
@@ -1603,7 +1623,7 @@ public:
status = updateRoleDocument(opCtx, args.roleName, updateDocumentBuilder.obj());
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
return true;
}
@@ -1647,11 +1667,9 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
if (RoleGraph::isBuiltinRole(roleName)) {
uasserted(ErrorCodes::InvalidRoleModification,
@@ -1699,7 +1717,7 @@ public:
status = updateRoleDocument(opCtx, roleName, updateBSONBuilder.done());
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
return true;
}
@@ -1743,11 +1761,8 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
if (RoleGraph::isBuiltinRole(roleName)) {
uasserted(ErrorCodes::InvalidRoleModification,
@@ -1800,7 +1815,7 @@ public:
updateObj.writeTo(&updateBSONBuilder);
status = updateRoleDocument(opCtx, roleName, updateBSONBuilder.done());
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
return true;
}
@@ -1851,11 +1866,9 @@ public:
}
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
// Role existence has to be checked after acquiring the update lock
BSONObj roleDoc;
@@ -1882,7 +1895,7 @@ public:
status = updateRoleDocument(
opCtx, roleName, BSON("$set" << BSON("roles" << rolesVectorToBSONArray(directRoles))));
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
return true;
}
@@ -1926,11 +1939,9 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
RoleName roleName(roleNameString, dbname);
if (RoleGraph::isBuiltinRole(roleName)) {
@@ -1961,7 +1972,7 @@ public:
status = updateRoleDocument(
opCtx, roleName, BSON("$set" << BSON("roles" << rolesVectorToBSONArray(roles))));
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
return true;
}
@@ -2006,11 +2017,9 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
if (RoleGraph::isBuiltinRole(roleName)) {
uasserted(ErrorCodes::InvalidRoleModification,
@@ -2022,6 +2031,18 @@ public:
status = authzManager->getRoleDescription(opCtx, roleName, &roleDoc);
uassertStatusOK(status);
+ // From here on, we always want to invalidate the user cache before returning.
+ auto invalidateGuard = MakeGuard([&] {
+ try {
+ authzManager->invalidateUserCache(opCtx);
+ } catch (const DBException& e) {
+ // Since this may be called after a uassert, we want to catch any uasserts
+ // that come out of invalidating the user cache and explicitly append it to
+ // the command response.
+ CommandHelpers::appendCommandStatusNoThrow(result, e.toStatus());
+ }
+ });
+
// Remove this role from all users
long long nMatched;
status = updateAuthzDocuments(
@@ -2038,8 +2059,6 @@ public:
false,
true,
&nMatched);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
if (!status.isOK()) {
uassertStatusOK(useDefaultCode(status, ErrorCodes::UserModificationFailed)
.withContext(str::stream() << "Failed to remove role "
@@ -2062,8 +2081,6 @@ public:
false,
true,
&nMatched);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
if (!status.isOK()) {
uassertStatusOK(
useDefaultCode(status, ErrorCodes::RoleModificationFailed)
@@ -2080,8 +2097,6 @@ public:
<< AuthorizationManager::ROLE_DB_FIELD_NAME
<< roleName.getDB()),
&nMatched);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
if (!status.isOK()) {
uassertStatusOK(status.withContext(
str::stream() << "Removed role " << roleName.getFullName()
@@ -2138,11 +2153,20 @@ public:
uassertStatusOK(status);
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
+ // From here on, we always want to invalidate the user cache before returning.
+ auto invalidateGuard = MakeGuard([&] {
+ try {
+ authzManager->invalidateUserCache(opCtx);
+ } catch (const DBException& e) {
+ // Since this may be called after a uassert, we want to catch any uasserts
+ // that come out of invalidating the user cache and explicitly append it to
+ // the command response.
+ CommandHelpers::appendCommandStatusNoThrow(result, e.toStatus());
+ }
+ });
// Remove these roles from all users
long long nMatched;
@@ -2155,8 +2179,6 @@ public:
false,
true,
&nMatched);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
if (!status.isOK()) {
uassertStatusOK(useDefaultCode(status, ErrorCodes::UserModificationFailed)
.withContext(str::stream() << "Failed to remove roles from \""
@@ -2176,8 +2198,6 @@ public:
false,
true,
&nMatched);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
if (!status.isOK()) {
uassertStatusOK(useDefaultCode(status, ErrorCodes::RoleModificationFailed)
.withContext(str::stream() << "Failed to remove roles from \""
@@ -2189,8 +2209,6 @@ public:
// Finally, remove the actual role documents
status = removeRoleDocuments(
opCtx, BSON(AuthorizationManager::ROLE_DB_FIELD_NAME << dbname), &nMatched);
- // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserCache();
if (!status.isOK()) {
uassertStatusOK(status.withContext(
str::stream() << "Removed roles from \"" << dbname
@@ -2259,18 +2277,17 @@ public:
Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args);
uassertStatusOK(status);
- status = requireReadableAuthSchema26Upgrade(opCtx, getGlobalAuthorizationManager());
- uassertStatusOK(status);
+ AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext());
+ auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager));
if (args.allForDB) {
std::vector<BSONObj> rolesDocs;
- status = getGlobalAuthorizationManager()->getRoleDescriptionsForDB(
- opCtx,
- dbname,
- args.privilegeFormat,
- args.authenticationRestrictionsFormat,
- args.showBuiltinRoles,
- &rolesDocs);
+ status = authzManager->getRoleDescriptionsForDB(opCtx,
+ dbname,
+ args.privilegeFormat,
+ args.authenticationRestrictionsFormat,
+ args.showBuiltinRoles,
+ &rolesDocs);
uassertStatusOK(status);
if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) {
@@ -2284,12 +2301,11 @@ public:
result.append("roles", rolesArrayBuilder.arr());
} else {
BSONObj roleDetails;
- status = getGlobalAuthorizationManager()->getRolesDescription(
- opCtx,
- args.roleNames,
- args.privilegeFormat,
- args.authenticationRestrictionsFormat,
- &roleDetails);
+ status = authzManager->getRolesDescription(opCtx,
+ args.roleNames,
+ args.privilegeFormat,
+ args.authenticationRestrictionsFormat,
+ &roleDetails);
uassertStatusOK(status);
if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) {
@@ -2335,7 +2351,8 @@ public:
const BSONObj& cmdObj,
BSONObjBuilder& result) {
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
- authzManager->invalidateUserCache();
+ auto lk = requireReadableAuthSchema26Upgrade(opCtx, authzManager);
+ authzManager->invalidateUserCache(opCtx);
return true;
}
@@ -2750,11 +2767,20 @@ public:
}
ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
- stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));
-
AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
- status = requireWritableAuthSchema28SCRAM(opCtx, authzManager);
- uassertStatusOK(status);
+
+ auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
+ // From here on, we always want to invalidate the user cache before returning.
+ auto invalidateGuard = MakeGuard([&] {
+ try {
+ authzManager->invalidateUserCache(opCtx);
+ } catch (const DBException& e) {
+ // Since this may be called after a uassert, we want to catch any uasserts
+ // that come out of invalidating the user cache and explicitly append it to
+ // the command response.
+ CommandHelpers::appendCommandStatusNoThrow(result, e.toStatus());
+ }
+ });
if (!args.usersCollName.empty()) {
Status status =
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index bbf00c3b02c..95be3fa2e4f 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -1120,7 +1120,7 @@ void OpObserverImpl::onReplicationRollback(OperationContext* opCtx,
if (rollbackNamespaces.count(AuthorizationManager::versionCollectionNamespace) == 1 ||
rollbackNamespaces.count(AuthorizationManager::usersCollectionNamespace) == 1 ||
rollbackNamespaces.count(AuthorizationManager::rolesCollectionNamespace) == 1) {
- AuthorizationManager::get(opCtx->getServiceContext())->invalidateUserCache();
+ AuthorizationManager::get(opCtx->getServiceContext())->invalidateUserCache(opCtx);
}
// If there were ops rolled back that were part of operations on a session, then invalidate
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 499312b3b14..cacb87ade6a 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -1668,11 +1668,8 @@ Status applyCommand_inlock(OperationContext* opCtx,
}
}
- // AuthorizationManager's logOp method registers a RecoveryUnit::Change
- // and to do so we need to have begun a UnitOfWork
- WriteUnitOfWork wuow(opCtx);
+
getGlobalAuthorizationManager()->logOp(opCtx, opType, nss, o, nullptr);
- wuow.commit();
return Status::OK();
}
diff --git a/src/mongo/embedded/embedded_auth_manager.cpp b/src/mongo/embedded/embedded_auth_manager.cpp
index fc32fe4e103..522ee32531b 100644
--- a/src/mongo/embedded/embedded_auth_manager.cpp
+++ b/src/mongo/embedded/embedded_auth_manager.cpp
@@ -101,11 +101,11 @@ public:
UASSERT_NOT_IMPLEMENTED;
}
- void invalidateUserByName(const UserName& user) override {
+ void invalidateUserByName(OperationContext*, const UserName& user) override {
UASSERT_NOT_IMPLEMENTED;
}
- void invalidateUsersFromDB(const StringData dbname) override {
+ void invalidateUsersFromDB(OperationContext*, const StringData dbname) override {
UASSERT_NOT_IMPLEMENTED;
}
@@ -113,7 +113,7 @@ public:
UASSERT_NOT_IMPLEMENTED;
}
- void invalidateUserCache() override {
+ void invalidateUserCache(OperationContext*) override {
UASSERT_NOT_IMPLEMENTED;
}
@@ -132,6 +132,10 @@ public:
UASSERT_NOT_IMPLEMENTED;
}
+ void setInUserManagementCommand(OperationContext*, bool) override {
+ UASSERT_NOT_IMPLEMENTED;
+ }
+
private:
bool _shouldValidate = false;
};
diff --git a/src/mongo/s/commands/cluster_user_management_commands.cpp b/src/mongo/s/commands/cluster_user_management_commands.cpp
index 0cd46d1a192..7e398fd9ac1 100644
--- a/src/mongo/s/commands/cluster_user_management_commands.cpp
+++ b/src/mongo/s/commands/cluster_user_management_commands.cpp
@@ -139,7 +139,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserByName(args.userName);
+ authzManager->invalidateUserByName(opCtx, args.userName);
return ok;
}
@@ -190,7 +190,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserByName(userName);
+ authzManager->invalidateUserByName(opCtx, userName);
return ok;
}
@@ -232,7 +232,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUsersFromDB(dbname);
+ authzManager->invalidateUsersFromDB(opCtx, dbname);
return ok;
}
@@ -281,7 +281,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserByName(UserName(userNameString, dbname));
+ authzManager->invalidateUserByName(opCtx, UserName(userNameString, dbname));
return ok;
}
@@ -330,7 +330,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserByName(UserName(userNameString, dbname));
+ authzManager->invalidateUserByName(opCtx, UserName(userNameString, dbname));
return ok;
}
@@ -440,7 +440,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -483,7 +483,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -525,7 +525,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -567,7 +567,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -609,7 +609,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -654,7 +654,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -701,7 +701,7 @@ public:
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return ok;
}
@@ -773,7 +773,7 @@ public:
BSONObjBuilder& result) {
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
invariant(authzManager);
- authzManager->invalidateUserCache();
+ authzManager->invalidateUserCache(opCtx);
return true;
}
diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp
index 0ac5e2c17e1..80ea5479d6b 100644
--- a/src/mongo/s/server.cpp
+++ b/src/mongo/s/server.cpp
@@ -408,7 +408,7 @@ ExitCode runMongosServer(ServiceContext* serviceContext) {
startMongoSFTDC();
- Status status = AuthorizationManager::get(serviceContext)->initialize(NULL);
+ Status status = AuthorizationManager::get(serviceContext)->initialize(opCtx.get());
if (!status.isOK()) {
error() << "Initializing authorization data failed: " << status;
return EXIT_SHARDING_ERROR;