SERVER-78466: check whether the user has been already authenticated. (#37733)

Co-authored-by: Brad Cater <brad.cater@mongodb.com>
GitOrigin-RevId: 9bf493d889221cecb88b65dbaef9530df8c6c5b7
This commit is contained in:
Solomon 2025-10-15 15:56:09 -04:00 committed by MongoDB Bot
parent b31ac5e6d1
commit f44be441d4
4 changed files with 100 additions and 1 deletions

View File

@ -0,0 +1,90 @@
/**
* This test checks that consecutive authentication attempt on the same
* ScopedDbCollection instance does not result in reauthentication warning on primary
*
* Previously we had a warning so this test will fail with any earlier version of the code
*
* @tags: [
* requires_fcv_83
* ]
*/
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {validateSessionsCollection} from "jstests/libs/sessions_collection.js";
// This test makes assertions about the number of sessions, which are not compatible with
// implicit sessions.
TestData.disableImplicitSessions = true;
const replTest = new ReplSetTest({
name: "refresh",
nodes: [
{
/* primary */
},
{/* secondary */ rsConfig: {priority: 0}},
],
keyFile: "jstests/libs/key1",
nodeOptions: {
setParameter: "connPoolMaxConnsPerHost=1",
}, // global connection pool will contain only one connection
});
const nodes = replTest.startSet();
replTest.initiate();
const kAdminUser = {
name: "admin",
pwd: "admin",
};
const primary = replTest.getPrimary();
// Create the admin user
const adminDB = primary.getDB("admin");
assert.commandWorked(adminDB.runCommand({createUser: kAdminUser.name, pwd: kAdminUser.pwd, roles: ["root"]}));
assert.eq(1, adminDB.auth(kAdminUser.name, kAdminUser.pwd));
const primaryAdmin = primary.getDB("admin");
replTest.awaitReplication();
const secondary = replTest.getSecondary();
const secondaryAdmin = secondary.getDB("admin");
assert.eq(1, secondaryAdmin.auth(kAdminUser.name, kAdminUser.pwd));
// Get the current value of the TTL index so that we can verify it's being properly applied.
let res = assert.commandWorked(primary.adminCommand({getParameter: 1, localLogicalSessionTimeoutMinutes: 1}));
let timeoutMinutes = res.localLogicalSessionTimeoutMinutes;
{
// Sessions collection doesn't yet exist on primary
validateSessionsCollection(primary, false, false, timeoutMinutes);
replTest.awaitReplication();
// Sessions collection doesn't yet exist on secondary
validateSessionsCollection(secondary, false, false, timeoutMinutes);
assert.commandWorked(primaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
// Created sessions collection
validateSessionsCollection(primary, true, true, timeoutMinutes);
}
{
replTest.awaitReplication();
// Refresh cache on secondary will authenticate a user with new ScopedDbCollection instance
assert.commandWorked(secondaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
// Another refresh on secondary will authenticate again on the same instance because we limited
// connection pool to be of size 1
assert.commandWorked(secondaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
replTest.awaitReplication();
}
// Make sure that the fix worked and no warning logged on primary and secondary
assert(
checkLog.checkContainsWithCountJson(primary, 5626700, {}, 0, null, true),
"Expecting not to find reauthentication warning in primary logs",
);
assert(
checkLog.checkContainsWithCountJson(secondary, 5626700, {}, 0, null, true),
"Expecting not to find reauthentication warning in primary logs",
);
replTest.stopSet();

View File

@ -409,6 +409,7 @@ void DBClientBase::_auth(const BSONObj& params) {
HostAndPort remote(getServerAddress()); HostAndPort remote(getServerAddress());
auth::authenticateClient(params, remote, clientName, _makeAuthRunCommandHook()).get(); auth::authenticateClient(params, remote, clientName, _makeAuthRunCommandHook()).get();
_isClientAuthenticated.store(true);
} }
void DBClientBase::authenticateInternalUser(auth::StepDownBehavior stepDownBehavior) { void DBClientBase::authenticateInternalUser(auth::StepDownBehavior stepDownBehavior) {
@ -439,6 +440,7 @@ void DBClientBase::authenticateInternalUser(auth::StepDownBehavior stepDownBehav
_makeAuthRunCommandHook(), _makeAuthRunCommandHook(),
authProvider) authProvider)
.get(); .get();
_isClientAuthenticated.store(true);
} catch (const DBException& e) { } catch (const DBException& e) {
if (!serverGlobalParams.quiet.load()) { if (!serverGlobalParams.quiet.load()) {
LOGV2(20117, LOGV2(20117,
@ -474,6 +476,7 @@ void DBClientBase::auth(const DatabaseName& dbname, StringData username, StringD
void DBClientBase::logout(const DatabaseName& dbName, BSONObj& info) { void DBClientBase::logout(const DatabaseName& dbName, BSONObj& info) {
runCommand(dbName, BSON("logout" << 1), info); runCommand(dbName, BSON("logout" << 1), info);
_isClientAuthenticated.store(false);
} }
bool DBClientBase::isPrimary(bool& isPrimary, BSONObj* info) { bool DBClientBase::isPrimary(bool& isPrimary, BSONObj* info) {

View File

@ -540,6 +540,9 @@ public:
return find(std::move(findRequest), readPref, ExhaustMode::kOff); return find(std::move(findRequest), readPref, ExhaustMode::kOff);
} }
bool isAuthenticated() const {
return _isClientAuthenticated.load();
}
/** /**
* Issues a find command described by 'findRequest' and the given read preference. Rather than * Issues a find command described by 'findRequest' and the given read preference. Rather than
* returning a cursor to the caller, iterates the cursor under the hood and calls the provided * returning a cursor to the caller, iterates the cursor under the hood and calls the provided
@ -757,6 +760,8 @@ private:
Timestamp _lastOperationTime; Timestamp _lastOperationTime;
ClientAPIVersionParameters _apiParameters; ClientAPIVersionParameters _apiParameters;
AtomicWord<bool> _isClientAuthenticated = {false};
}; // DBClientBase }; // DBClientBase
BSONElement getErrField(const BSONObj& result); BSONElement getErrField(const BSONObj& result);

View File

@ -38,6 +38,7 @@
#include "mongo/client/internal_auth.h" #include "mongo/client/internal_auth.h"
#include "mongo/client/read_preference.h" #include "mongo/client/read_preference.h"
#include "mongo/client/remote_command_targeter_factory_impl.h" #include "mongo/client/remote_command_targeter_factory_impl.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/dbdirectclient.h" #include "mongo/db/dbdirectclient.h"
#include "mongo/db/local_catalog/lock_manager/d_concurrency.h" #include "mongo/db/local_catalog/lock_manager/d_concurrency.h"
#include "mongo/db/local_catalog/lock_manager/lock_manager_defs.h" #include "mongo/db/local_catalog/lock_manager/lock_manager_defs.h"
@ -82,7 +83,7 @@ auto SessionsCollectionRS::_makePrimaryConnection(OperationContext* opCtx) {
auto conn = std::make_unique<ScopedDbConnection>(res.toString()); auto conn = std::make_unique<ScopedDbConnection>(res.toString());
// Make a connection to the primary, auth, then send // Make a connection to the primary, auth, then send
if (auth::isInternalAuthSet()) { if (auth::isInternalAuthSet() && !conn->get()->isAuthenticated()) {
conn->get()->authenticateInternalUser(); conn->get()->authenticateInternalUser();
} }