// Auth test the listDatabases command. export function runTest(mongod) { const admin = mongod.getDB("admin"); admin.createUser({user: "admin", pwd: "pass", roles: jsTest.adminUserRoles}); assert(admin.auth("admin", "pass")); // Establish db0..db7 for (let i = 0; i < 8; ++i) { mongod.getDB("db" + i).foo.insert({bar: "baz"}); } mongod.getDB("db0").baz.insert({x: "y"}); mongod.getDB("db2").baz.insert({x: "y"}); admin.createRole({ role: "dbLister", privileges: [{resource: {cluster: true}, actions: ["listDatabases"]}], roles: [], }); admin.createRole({ role: "specificCollection", privileges: [{resource: {db: "db0", collection: "baz"}, actions: ["find"]}], roles: [], }); admin.createRole({ role: "sharedNameCollections", privileges: [{resource: {db: "", collection: "baz"}, actions: ["find"]}], roles: [], }); // Make db0, db2, db4, db6 readable to user1 abd user3. // Make db0, db1, db2, db3 read/writable to user 2 and user3. function makeRole(perm, dbNum) { return {role: perm, db: "db" + dbNum}; } const readEven = [0, 2, 4, 6].map(function (i) { return makeRole("read", i); }); const readWriteLow = [0, 1, 2, 3].map(function (i) { return makeRole("readWrite", i); }); admin.createUser({user: "user1", pwd: "pass", roles: readEven}); admin.createUser({user: "user2", pwd: "pass", roles: readWriteLow}); admin.createUser({user: "user3", pwd: "pass", roles: readEven.concat(readWriteLow)}); // Make db4 readable by user 4, and let them list all dbs. // Make db5 readable by user 5, and let them list all dbs. // Make collection baz in db0 findable by user6, and let them list db0. // Make all baz collections findable by user7, and let them list all dbs. admin.createUser({user: "user4", pwd: "pass", roles: [makeRole("read", 4), "dbLister"]}); admin.createUser({user: "user5", pwd: "pass", roles: [makeRole("read", 5), "dbLister"]}); admin.createUser({user: "user6", pwd: "pass", roles: ["specificCollection"]}); admin.createUser({user: "user7", pwd: "pass", roles: ["sharedNameCollections"]}); admin.logout(); const admin_dbs = ["admin", "db0", "db1", "db2", "db3", "db4", "db5", "db6", "db7"]; [ {user: "user1", dbs: ["db0", "db2", "db4", "db6"]}, {user: "user2", dbs: ["db0", "db1", "db2", "db3"]}, {user: "user3", dbs: ["db0", "db1", "db2", "db3", "db4", "db6"]}, {user: "user4", dbs: admin_dbs, authDbs: ["db4"]}, {user: "user5", dbs: admin_dbs, authDbs: ["db5"]}, {user: "user6", dbs: ["db0"]}, {user: "user7", dbs: admin_dbs}, {user: "admin", dbs: admin_dbs, authDbs: admin_dbs}, ].forEach(function (test) { function filterSpecial(db) { // Returning of local/config varies with sharding, etc.. // Ignore these for simplicity. return db !== "local" && db !== "config"; } // Invoking {listDatabases: 1} directly. function tryList(cmd, expect_dbs) { const dbs = assert.commandWorked(admin.runCommand(cmd)); assert.eq( dbs.databases .map(function (db) { return db.name; }) .filter(filterSpecial) .sort(), expect_dbs, test.user + " permissions", ); } admin.auth(test.user, "pass"); tryList({listDatabases: 1}, test.dbs); tryList({listDatabases: 1, authorizedDatabases: true}, test.authDbs || test.dbs); if (test.authDbs) { tryList({listDatabases: 1, authorizedDatabases: false}, test.dbs); } else { // Users without listDatabases cluster perm may not // request authorizedDatabases: false. assert.throws(tryList, [{listDatabases: 1, authorizedDatabases: false}, test.dbs]); } // Test using shell helper Mongo.getDBs(). assert.eq( mongod.getDBs(undefined, {}, true).filter(filterSpecial), test.dbs, "Shell helper speaking to same version", ); if (test.user !== "admin" && test.user !== "user7") { // Admin and user7 don't have an explicit list of DBs to parse. assert.eq(mongod._getDatabaseNamesFromPrivileges(), test.authDbs || test.dbs); // Test (non-admin) call to Mongo.getDBs() on a < 4.0 MongoD // by injecting a command failure into Mongo.adminCommand(). // This will allow us to resemble a < 4.0 server. const adminCommandFunction = mongod.adminCommand; const adminCommandMethod = adminCommandFunction.bind(mongod); try { mongod.adminCommand = function (cmd) { if (cmd.hasOwnProperty("listDatabases")) { return { ok: 0, errmsg: "Stubbed command failure: " + tojson(cmd), code: ErrorCodes.Unauthorized, codeName: "Unauthorized", }; } return adminCommandMethod(cmd); }; // Command fails, but we dispatch via _getDatabaseNamesFromPrivileges(). assert.eq( mongod.getDBs().databases.map(function (x) { return x.name; }), test.authDbs || test.dbs, ); // Still dispatches with explicit nameOnly===true, returns only names. assert.eq(mongod.getDBs(undefined, undefined, true), test.authDbs || test.dbs); // Command fails and unable to dispatch because nameOnly !== true. assert.throws(() => mongod.getDBs(undefined, undefined, false)); // Command fails and unable to dispatch because filter is not empty. assert.throws(() => mongod.getDBs(undefined, {name: "foo"})); } finally { mongod.adminCommand = adminCommandFunction; } } admin.logout(); }); }