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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/**
* Tests that initial sync works correctly when multitenancySupport is enabled.
*/
(function() {
"use strict";
load('jstests/aggregation/extras/utils.js'); // For arrayEq()
load("jstests/libs/feature_flag_util.js"); // for isEnabled
const rst = new ReplSetTest({
nodes: 1,
nodeOptions: {
auth: '',
setParameter: {
multitenancySupport: true,
featureFlagSecurityToken: true,
}
}
});
rst.startSet({keyFile: 'jstests/libs/key1'});
rst.initiate();
const primary = rst.getPrimary();
const kTenant1 = ObjectId();
const kTenant2 = ObjectId();
const kDbName = "test";
const kCollName = "foo";
const primaryConn = new Mongo(primary.host);
const primaryDB = primaryConn.getDB(kDbName);
// Create users for two different tenants.
const adminDb = primary.getDB('admin');
assert.commandWorked(adminDb.runCommand({createUser: 'admin', pwd: 'pwd', roles: ['root']}));
assert(adminDb.auth('admin', 'pwd'));
const featureFlagRequireTenantId = FeatureFlagUtil.isEnabled(adminDb, "RequireTenantID");
const securityToken1 =
_createSecurityToken({user: "userTenant1", db: '$external', tenant: kTenant1});
assert.commandWorked(primary.getDB('$external').runCommand({
createUser: "userTenant1",
'$tenant': kTenant1,
roles: [{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}]
}));
const securityToken2 =
_createSecurityToken({user: "userTenant2", db: '$external', tenant: kTenant2});
assert.commandWorked(primary.getDB('$external').runCommand({
createUser: "userTenant2",
'$tenant': kTenant2,
roles: [{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}]
}));
// Logout the root user to avoid multiple authentication.
primaryConn.getDB("admin").logout();
// Create a collection, insert some data, and create indexes on the collection for tenant1.
primaryConn._setSecurityToken(securityToken1);
const tenant1Docs = [{_id: 0, a: 1, b: 1}, {_id: 1, a: 2, b: 3}];
assert.commandWorked(primaryDB.runCommand({insert: kCollName, documents: tenant1Docs}));
const tenant1Idxs = [{key: {a: 1}, name: "indexA"}, {key: {b: 1}, name: "indexB"}];
let res =
assert.commandWorked(primaryDB.runCommand({createIndexes: kCollName, indexes: tenant1Idxs}));
assert.eq(3, res.numIndexesAfter);
// Create a collections, insert some data, and create indexes on the collection for tenant2.
primaryConn._setSecurityToken(securityToken2);
const tenant2Docs = [{_id: 10, a: 10, b: 10}, {_id: 11, a: 20, b: 30}];
assert.commandWorked(primaryDB.runCommand({insert: kCollName, documents: tenant2Docs}));
const tenant2Idxs = [{key: {a: -1}, name: "indexA"}, {key: {b: -1}, name: "indexB"}];
res = assert.commandWorked(primaryDB.runCommand({createIndexes: kCollName, indexes: tenant2Idxs}));
assert.eq(3, res.numIndexesAfter);
// Add a new secondary to the replica set and wait for initial sync to finish.
const secondary = rst.add({
setParameter: {
multitenancySupport: true,
featureFlagSecurityToken: true,
}
});
rst.reInitiate();
rst.awaitReplication();
rst.awaitSecondaryNodes();
// Check that we find the data for each tenant on the newly synced secondary.
const secondaryConn = new Mongo(secondary.host);
const secondaryDB = secondaryConn.getDB(kDbName);
secondaryConn.setSecondaryOk();
// Look for tenant1's data and indexes.
secondaryConn._setSecurityToken(securityToken1);
const findTenant1Res = assert.commandWorked(secondaryDB.runCommand({find: kCollName, filter: {}}));
assert(arrayEq(tenant1Docs, findTenant1Res.cursor.firstBatch), tojson(findTenant1Res));
res = assert.commandWorked(secondaryDB.runCommand({listIndexes: kCollName}));
assert.eq(3, res.cursor.firstBatch.length);
assert(arrayEq(tenant1Idxs.concat([
{key: {"_id": 1}, name: "_id_"},
]),
res.cursor.firstBatch.map(function(index) {
return {key: index.key, name: index.name};
})));
// Look for tenant2's data and indexes.
secondaryConn._setSecurityToken(securityToken2);
const findTenant2Res = assert.commandWorked(secondaryDB.runCommand({find: kCollName, filter: {}}));
assert(arrayEq(tenant2Docs, findTenant2Res.cursor.firstBatch), tojson(findTenant2Res));
res = assert.commandWorked(secondaryDB.runCommand({listIndexes: kCollName}));
assert.eq(3, res.cursor.firstBatch.length);
assert(arrayEq(tenant2Idxs.concat([
{key: {"_id": 1}, name: "_id_"},
]),
res.cursor.firstBatch.map(function(index) {
return {key: index.key, name: index.name};
})));
rst.stopSet();
})();
|