mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-24 16:46:00 +01:00
876f66eb13
GitOrigin-RevId: 88dd3ef2b4325ca332b8e7e6180f1e8f9c7534ea
217 lines
7.0 KiB
JavaScript
217 lines
7.0 KiB
JavaScript
/**
|
|
* Tests that 'defaultMaxTimeMS' is correctly bypassed when the 'bypassDefaultMaxTimeMS' privilege
|
|
* is granted.
|
|
*
|
|
* @tags: [
|
|
* creates_and_authenticates_user,
|
|
* requires_fcv_80,
|
|
* # Transactions aborted upon fcv upgrade or downgrade; cluster parameters use internal txns.
|
|
* requires_auth,
|
|
* requires_replication,
|
|
* requires_sharding,
|
|
* # Uses $function
|
|
* requires_scripting,
|
|
* uses_transactions,
|
|
* featureFlagSecurityToken,
|
|
* ]
|
|
*/
|
|
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
|
|
|
function setDefaultReadMaxTimeMS(db, newValue) {
|
|
assert.commandWorked(
|
|
db.runCommand({setClusterParameter: {defaultMaxTimeMS: {readOperations: newValue}}}));
|
|
|
|
// Currently, the mongos cluster parameter cache is not updated on setClusterParameter. An
|
|
// explicit call to getClusterParameter will refresh the cache.
|
|
assert.commandWorked(db.runCommand({getClusterParameter: "defaultMaxTimeMS"}));
|
|
}
|
|
|
|
function setup(conn, getConn, multiTenancy = false) {
|
|
// Create a global admin user.
|
|
{
|
|
const adminDB = conn.getDB("admin");
|
|
adminDB.createUser({user: 'admin', pwd: 'admin', roles: ['root']});
|
|
}
|
|
|
|
// Fetch a new connection, this might seem redundant, but is intended to make this work for the
|
|
// multi-tenancy case.
|
|
const adminDB = getConn('admin', 'admin', 'admin');
|
|
|
|
// Prepare a regular user without the 'bypassDefaultMaxtimeMS' privilege.
|
|
adminDB.createUser({user: 'regularUser', pwd: 'password', roles: ["readAnyDatabase"]});
|
|
|
|
// Prepare a user with the 'bypassDefaultMaxtimeMS' privilege.
|
|
adminDB.createRole({
|
|
role: "bypassDefaultMaxtimeMSRole",
|
|
privileges: [
|
|
{resource: {cluster: true}, actions: ["bypassDefaultMaxTimeMS"]},
|
|
],
|
|
roles: []
|
|
});
|
|
|
|
adminDB.createUser({
|
|
user: 'bypassUser',
|
|
pwd: 'password',
|
|
roles: ["readAnyDatabase", "bypassDefaultMaxtimeMSRole"]
|
|
});
|
|
|
|
if (multiTenancy) {
|
|
return {sleep: 1, millis: 300};
|
|
} else {
|
|
const dbName = jsTestName();
|
|
const testDB = adminDB.getSiblingDB(dbName);
|
|
const collName = "test";
|
|
const coll = testDB.getCollection(collName);
|
|
|
|
// Insert some data to be queried
|
|
for (let i = 0; i < 10; ++i) {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
}
|
|
|
|
const slowStage = {
|
|
$match: {
|
|
$expr: {
|
|
$function: {
|
|
body: function() {
|
|
sleep(1000);
|
|
return true;
|
|
},
|
|
args: [],
|
|
lang: "js"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
return {
|
|
aggregate: collName,
|
|
pipeline: [slowStage],
|
|
cursor: {},
|
|
};
|
|
}
|
|
}
|
|
|
|
function runBypassTests(getConn, commandToRun, dbName = jsTestName()) {
|
|
const adminDB = getConn('admin', 'admin', 'admin');
|
|
// Sets the default maxTimeMS for read operations with a small value.
|
|
setDefaultReadMaxTimeMS(adminDB, 1);
|
|
|
|
// Expect failure for the regular user.
|
|
const regularUserDB = getConn(dbName, 'regularUser', 'password');
|
|
// Note the error could manifest as an Interrupted error sometimes due to the JavaScript
|
|
// execution being interrupted.
|
|
assert.commandFailedWithCode(regularUserDB.runCommand(commandToRun),
|
|
[ErrorCodes.Interrupted, ErrorCodes.MaxTimeMSExpired]);
|
|
|
|
// Expect a user with 'bypassDefaultMaxTimeMS' to succeed.
|
|
const bypassUserDB = getConn(dbName, 'bypassUser', 'password');
|
|
assert.commandWorked(bypassUserDB.runCommand(commandToRun));
|
|
|
|
// Expect a user with 'bypassDefaultMaxTimeMS', but that specified a maxTimeMS on the query, to
|
|
// fail due to timeout.
|
|
assert.commandFailedWithCode(bypassUserDB.runCommand({...commandToRun, maxTimeMS: 1}),
|
|
[ErrorCodes.Interrupted, ErrorCodes.MaxTimeMSExpired]);
|
|
|
|
// Expect root user to bypass the default.
|
|
const rootUserDB = adminDB.getSiblingDB(dbName);
|
|
assert.commandWorked(rootUserDB.runCommand(commandToRun));
|
|
|
|
// Unsets the default MaxTimeMS to make queries not to time out in the
|
|
// following code.
|
|
setDefaultReadMaxTimeMS(adminDB, 0);
|
|
}
|
|
|
|
const keyFile = "jstests/libs/key1";
|
|
// Standard replica set test.
|
|
{
|
|
const rst = new ReplSetTest({nodes: 1, keyFile: keyFile});
|
|
rst.startSet();
|
|
rst.initiate();
|
|
|
|
const conn = rst.getPrimary();
|
|
const getConn = (dbName, user, password) => {
|
|
const newConn = new Mongo(conn.host);
|
|
const adminDB = newConn.getDB("admin");
|
|
assert.eq(1, adminDB.auth(user, password));
|
|
return adminDB.getSiblingDB(dbName);
|
|
};
|
|
|
|
const commandToRun = setup(conn, getConn);
|
|
runBypassTests(getConn, commandToRun);
|
|
|
|
rst.stopSet();
|
|
}
|
|
|
|
// Sharded test.
|
|
{
|
|
const st = new ShardingTest({
|
|
mongos: 1,
|
|
shards: {nodes: 1},
|
|
config: {nodes: 1},
|
|
keyFile: keyFile,
|
|
mongosOptions:
|
|
{setParameter: {'failpoint.skipClusterParameterRefresh': "{'mode':'alwaysOn'}"}},
|
|
});
|
|
|
|
const conn = st.s;
|
|
const getConn = (dbName, user, password) => {
|
|
const newConn = new Mongo(conn.host);
|
|
const adminDB = newConn.getDB("admin");
|
|
assert.eq(1, adminDB.auth(user, password));
|
|
return adminDB.getSiblingDB(dbName);
|
|
};
|
|
|
|
const commandToRun = setup(conn, getConn);
|
|
runBypassTests(getConn, commandToRun);
|
|
|
|
st.stop();
|
|
}
|
|
|
|
// Multi-tenant test.
|
|
{
|
|
const vtsKey = "secret";
|
|
const rstWithTenants = new ReplSetTest({
|
|
nodes: 1,
|
|
nodeOptions: {
|
|
setParameter: {
|
|
multitenancySupport: true,
|
|
testOnlyValidatedTenancyScopeKey: vtsKey,
|
|
}
|
|
},
|
|
keyFile: keyFile
|
|
});
|
|
rstWithTenants.startSet();
|
|
rstWithTenants.initiate();
|
|
|
|
const conn = rstWithTenants.getPrimary();
|
|
|
|
const tenantId1 = ObjectId();
|
|
const unsignedToken1 = _createTenantToken({tenant: tenantId1});
|
|
const getConnWithGlobalUser = (dbName, user, password) => {
|
|
const newConn = new Mongo(conn.host);
|
|
newConn._setSecurityToken(unsignedToken1);
|
|
const newConnDB = newConn.getDB(dbName);
|
|
assert.eq(1, newConnDB.auth(user, password));
|
|
return newConnDB;
|
|
};
|
|
const getConn = (dbName, user, password) => {
|
|
// setClusterParameter is only possible with a global user with useTenant.
|
|
if (user == 'admin') {
|
|
return getConnWithGlobalUser(dbName, user, password);
|
|
}
|
|
|
|
const newConn = new Mongo(conn.host);
|
|
const securityToken =
|
|
_createSecurityToken({user: user, db: 'admin', tenant: tenantId1}, vtsKey);
|
|
newConn._setSecurityToken(securityToken);
|
|
return newConn.getDB(dbName);
|
|
};
|
|
|
|
const commandToRun = setup(conn, getConnWithGlobalUser, true);
|
|
runBypassTests(getConn, commandToRun, "admin");
|
|
|
|
rstWithTenants.stopSet();
|
|
}
|