summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormathisbessamdb <mathis.bessa@mongodb.com>2022-12-20 23:02:26 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-12-21 00:22:27 +0000
commit7399dc8f481fb056ea36085548da39868c4217b9 (patch)
tree6cc11d306ee94911ec06d134f48bb9d60a2ae16d
parentd1500aed6baa47ca511692c5ca326321c80cd50d (diff)
downloadmongo-7399dc8f481fb056ea36085548da39868c4217b9.tar.gz
SERVER-71861 Allow killOp with security token
-rw-r--r--jstests/core/count10.js2
-rw-r--r--jstests/core/count_plan_summary.js2
-rw-r--r--jstests/core/currentop_shell.js2
-rw-r--r--jstests/core/illegal_cmd_namespace.js2
-rw-r--r--jstests/core/killop_drop_collection.js2
-rw-r--r--jstests/core/mr_killop.js2
-rw-r--r--jstests/core/shellkillop.js2
-rw-r--r--jstests/core/txns/transactions_profiling_with_drops.js2
-rw-r--r--jstests/libs/override_methods/inject_security_token.js2
-rw-r--r--jstests/serverless/native_tenant_data_isolation_kill_op.js107
-rw-r--r--src/mongo/db/commands/kill_op.cpp4
11 files changed, 116 insertions, 13 deletions
diff --git a/jstests/core/count10.js b/jstests/core/count10.js
index 7f5e2957abe..c981e45a9b8 100644
--- a/jstests/core/count10.js
+++ b/jstests/core/count10.js
@@ -1,8 +1,6 @@
// Test that interrupting a count returns an error code.
//
-// The test runs commands that are not allowed with security token: killOp.
// @tags: [
-// not_allowed_with_security_token,
// # This test attempts to perform a count command and find it using the currentOp command. The
// # former operation may be routed to a secondary in the replica set, whereas the latter must be
// # routed to the primary.
diff --git a/jstests/core/count_plan_summary.js b/jstests/core/count_plan_summary.js
index 9db379a1a0a..f38e6f00a11 100644
--- a/jstests/core/count_plan_summary.js
+++ b/jstests/core/count_plan_summary.js
@@ -1,8 +1,6 @@
// Test that the plan summary string appears in db.currentOp() for count operations. SERVER-14064.
//
-// The test runs commands that are not allowed with security token: killOp.
// @tags: [
-// not_allowed_with_security_token,
// # This test attempts to perform a find command and find it using the currentOp command. The
// # former operation may be routed to a secondary in the replica set, whereas the latter must be
// # routed to the primary.
diff --git a/jstests/core/currentop_shell.js b/jstests/core/currentop_shell.js
index 365e1ab3344..72f236658b4 100644
--- a/jstests/core/currentop_shell.js
+++ b/jstests/core/currentop_shell.js
@@ -2,7 +2,7 @@
* Tests that the shell helper db.currentOpCursor isn't constrained by the legacy currentOp server
* command - ie. the result set isn't limited to 16MB and long operations aren't truncated.
*
- * The test runs commands that are not allowed with security token: getLog, killOp.
+ * The test runs commands that are not allowed with security token: getLog.
* @tags: [
* not_allowed_with_security_token,
* uses_parallel_shell,
diff --git a/jstests/core/illegal_cmd_namespace.js b/jstests/core/illegal_cmd_namespace.js
index f3075337abe..3dc26c4e67a 100644
--- a/jstests/core/illegal_cmd_namespace.js
+++ b/jstests/core/illegal_cmd_namespace.js
@@ -2,9 +2,7 @@
* Test that an attempt to run a query over a $cmd namespace is not treated specially by the shell,
* but is rejected by the server.
*
- * The test runs commands that are not allowed with security token: killop.
* @tags: [
- * not_allowed_with_security_token,
* assumes_unsharded_collection,
* ]
*/
diff --git a/jstests/core/killop_drop_collection.js b/jstests/core/killop_drop_collection.js
index b8a4597712a..17e0154fbfb 100644
--- a/jstests/core/killop_drop_collection.js
+++ b/jstests/core/killop_drop_collection.js
@@ -4,7 +4,7 @@
* This test confirms that killOp won't interrupt a collection drop, and that the drop occurs
* successfully.
*
- * The test runs commands that are not allowed with security token: fsyncUnlock, killOp.
+ * The test runs commands that are not allowed with security token: fsyncUnlock.
* @tags: [
* not_allowed_with_security_token,
* assumes_superuser_permissions,
diff --git a/jstests/core/mr_killop.js b/jstests/core/mr_killop.js
index 8ed4d1cc680..de91c72c7af 100644
--- a/jstests/core/mr_killop.js
+++ b/jstests/core/mr_killop.js
@@ -1,5 +1,5 @@
// Test killop applied to m/r operations and child ops of m/r operations.
-// The test runs commands that are not allowed with security token: killOp, mapreduce.
+// The test runs commands that are not allowed with security token: mapreduce.
// @tags: [
// not_allowed_with_security_token,
// # mapReduce does not support afterClusterTime.
diff --git a/jstests/core/shellkillop.js b/jstests/core/shellkillop.js
index 78e757c5726..0ac2ad681ed 100644
--- a/jstests/core/shellkillop.js
+++ b/jstests/core/shellkillop.js
@@ -1,5 +1,3 @@
-// The test runs commands that are not allowed with security token: killop.
-// @tags: [not_allowed_with_security_token]
baseName = "jstests_shellkillop";
// 'retry' should be set to true in contexts where an exception should cause the test to be retried
diff --git a/jstests/core/txns/transactions_profiling_with_drops.js b/jstests/core/txns/transactions_profiling_with_drops.js
index d71328b701f..821070fd51d 100644
--- a/jstests/core/txns/transactions_profiling_with_drops.js
+++ b/jstests/core/txns/transactions_profiling_with_drops.js
@@ -1,5 +1,5 @@
// Tests that locks acquisitions for profiling in a transaction have a 0-second timeout.
-// The test runs commands that are not allowed with security token: endSession, killOp, profile.
+// The test runs commands that are not allowed with security token: endSession, profile.
// @tags: [
// not_allowed_with_security_token,uses_transactions, uses_parallel_shell]
(function() {
diff --git a/jstests/libs/override_methods/inject_security_token.js b/jstests/libs/override_methods/inject_security_token.js
index dcce73b4e5b..b7ef386f330 100644
--- a/jstests/libs/override_methods/inject_security_token.js
+++ b/jstests/libs/override_methods/inject_security_token.js
@@ -148,7 +148,7 @@ const kCmdsAllowedWithSecurityToken = new Set([
// `connectionStatus`,
// `connPoolStats`,
// `top`,
- // `killop`,
+ `killop`,
// `endSessions`,
]);
diff --git a/jstests/serverless/native_tenant_data_isolation_kill_op.js b/jstests/serverless/native_tenant_data_isolation_kill_op.js
new file mode 100644
index 00000000000..445a19a15d6
--- /dev/null
+++ b/jstests/serverless/native_tenant_data_isolation_kill_op.js
@@ -0,0 +1,107 @@
+// Test that we can create and auth a tenant (user) using a security token.
+// Then create an op (insert) for that tenant, find it using `currentOp` and kill it using `killOp`.
+
+(function() {
+"use strict";
+
+load('jstests/aggregation/extras/utils.js'); // For arrayEq()
+load("jstests/libs/fail_point_util.js"); // For configureFailPoint()
+load("jstests/libs/parallel_shell_helpers.js"); // For funWithArgs()
+
+function killCurrentOpTest() {
+ function operationToKillFunc(securityToken, dbName, colName) {
+ db.getMongo()._setSecurityToken(securityToken);
+ const insertCmdObj = {insert: colName, documents: [{_id: 0}]};
+ assert.commandWorked(db.getSiblingDB(dbName).runCommand(insertCmdObj));
+ }
+
+ const rst = new ReplSetTest({
+ nodes: 3,
+ nodeOptions:
+ {auth: '', setParameter: {multitenancySupport: true, featureFlagSecurityToken: true}}
+ });
+ rst.startSet({keyFile: 'jstests/libs/key1'});
+ rst.initiate();
+
+ const primary = rst.getPrimary();
+ const adminDb = primary.getDB('admin');
+
+ // Prepare a user for testing pass tenant via $tenant.
+ // Must be authenticated as a user with ActionType::useTenant in order to use $tenant.
+ assert.commandWorked(adminDb.runCommand({createUser: 'admin', pwd: 'pwd', roles: ['root']}));
+ assert(adminDb.auth('admin', 'pwd'));
+
+ const kTenant = ObjectId();
+ const kOtherTenant = ObjectId();
+ const kDbName = 'myDb';
+ const kCollName = "currOpColl";
+
+ // Create a user for kTenant and its security token.
+ const securityToken =
+ _createSecurityToken({user: "userTenant1", db: '$external', tenant: kTenant});
+ assert.commandWorked(primary.getDB('$external').runCommand({
+ createUser: "userTenant1",
+ '$tenant': kTenant,
+ roles:
+ [{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}]
+ }));
+
+ // Create a different tenant to test that one tenant can't see or kill other tenant's op.
+ const securityTokenOtherTenant =
+ _createSecurityToken({user: "userTenant2", db: '$external', tenant: kOtherTenant});
+ assert.commandWorked(primary.getDB('$external').runCommand({
+ createUser: "userTenant2",
+ '$tenant': kOtherTenant,
+ roles:
+ [{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}]
+ }));
+
+ const tokenConn = new Mongo(primary.host);
+ tokenConn._setSecurityToken(securityToken);
+
+ // test that the current tenant can see and kill his current op.
+ {
+ const findCurrentOpCmd = {
+ aggregate: 1,
+ pipeline: [{$currentOp: {allUsers: false}}, {$match: {op: "insert"}}],
+ cursor: {}
+ };
+
+ const createCollFP = configureFailPoint(primary, "hangBeforeLoggingCreateCollection");
+
+ const parallelShell = startParallelShell(
+ funWithArgs(operationToKillFunc, securityToken, kDbName, kCollName), primary.port);
+
+ createCollFP.wait();
+
+ // find the current insert op that's being blocked due to the failpoint.
+ let findCurrentOpRes = tokenConn.getDB("admin").runCommand(findCurrentOpCmd);
+ assert.eq(findCurrentOpRes.cursor.firstBatch.length, 1, tojson(findCurrentOpRes));
+ const opIdToKill = findCurrentOpRes.cursor.firstBatch[0].opid;
+
+ // Try to kill the op with a different tenant / security token. Fails due to Unauthorized.
+ tokenConn._setSecurityToken(securityTokenOtherTenant);
+ assert.commandFailedWithCode(
+ tokenConn.getDB("admin").runCommand({killOp: 1, op: opIdToKill}),
+ ErrorCodes.Unauthorized);
+
+ // Try to kill the op with a the same tenant / security token. Succeeds.
+ tokenConn._setSecurityToken(securityToken);
+ assert.commandWorked(tokenConn.getDB("admin").runCommand({killOp: 1, op: opIdToKill}));
+
+ createCollFP.off();
+
+ // the current op was killed therefor the thread will throw an exception and wil return
+ // code 252.
+ const exitCode = parallelShell({checkExitSuccess: false});
+ assert.neq(0, exitCode, "Expected shell to exit with failure due to operation kill");
+
+ // we should no longer have an operation for that tenant.
+ findCurrentOpRes = tokenConn.getDB("admin").runCommand(findCurrentOpCmd);
+ assert.eq(findCurrentOpRes.cursor.firstBatch.length, 0, tojson(findCurrentOpRes));
+ }
+
+ rst.stopSet();
+}
+killCurrentOpTest();
+})();
diff --git a/src/mongo/db/commands/kill_op.cpp b/src/mongo/db/commands/kill_op.cpp
index 633b10ef325..b31f45242ff 100644
--- a/src/mongo/db/commands/kill_op.cpp
+++ b/src/mongo/db/commands/kill_op.cpp
@@ -67,6 +67,10 @@ public:
// killOp always reports success once past the auth check.
return true;
}
+
+ bool allowedWithSecurityToken() const final {
+ return true;
+ }
} killOpCmd;
} // namespace mongo