-
Notifications
You must be signed in to change notification settings - Fork 5.6k
/
Copy pathnative_tenant_data_isolation_kill_op.js
113 lines (91 loc) · 4.63 KB
/
native_tenant_data_isolation_kill_op.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// 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`.
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {funWithArgs} from "jstests/libs/parallel_shell_helpers.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
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 kVTSKey = 'secret';
const rst = new ReplSetTest({
nodes: 3,
nodeOptions: {
auth: '',
setParameter: {
multitenancySupport: true,
featureFlagSecurityToken: true,
testOnlyValidatedTenancyScopeKey: kVTSKey
}
}
});
rst.startSet({keyFile: 'jstests/libs/key1'});
rst.initiate();
const primary = rst.getPrimary();
const adminDb = primary.getDB('admin');
// Prepare an authenticated user for testing.
// Must be authenticated as a user with ActionType::useTenant in order to use security token
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}, kVTSKey);
primary._setSecurityToken(_createTenantToken({tenant: kTenant}));
assert.commandWorked(primary.getDB('$external').runCommand({
createUser: "userTenant1",
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}, kVTSKey);
primary._setSecurityToken(_createTenantToken({tenant: kOtherTenant}));
assert.commandWorked(primary.getDB('$external').runCommand({
createUser: "userTenant2",
roles:
[{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}]
}));
primary._setSecurityToken(undefined);
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 therefore 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();