Skip to content

Commit e61a741

Browse files
syz99nbbeeken
andauthored
test(NODE-2556): add integration testing for listDatabases' authorizedDatabases flag (#3101)
Co-authored-by: Neal Beeken <neal.beeken@mongodb.com>
1 parent fb38a56 commit e61a741

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { expect } from 'chai';
2+
3+
import type { MongoClient } from '../../../src';
4+
5+
const REQUIRED_DBS = ['admin', 'local', 'config'];
6+
const DB_NAME = 'listDatabasesTest';
7+
8+
describe('listDatabases() spec prose', function () {
9+
/**
10+
* Execute the method to enumerate full database information (e.g. listDatabases())
11+
* - Verify that the method returns an Iterable of Document types
12+
* - Verify that all databases on the server are present in the result set
13+
* - Verify that the result set does not contain duplicates
14+
*/
15+
let client: MongoClient;
16+
let ENTIRE_DB_LIST: string[];
17+
18+
beforeEach(async function () {
19+
client = this.configuration.newClient();
20+
await client.connect();
21+
22+
const dbInfoFromCommand = await client.db('admin').command({ listDatabases: 1 });
23+
const databasesToDrop = dbInfoFromCommand.databases
24+
.map(({ name }) => name)
25+
.filter(name => !REQUIRED_DBS.includes(name));
26+
27+
for (const dbToDrop of databasesToDrop) {
28+
await client.db(dbToDrop).dropDatabase();
29+
}
30+
31+
await client.db(DB_NAME).createCollection(DB_NAME);
32+
33+
ENTIRE_DB_LIST = (await client.db('admin').command({ listDatabases: 1 })).databases.map(
34+
({ name }) => name
35+
);
36+
ENTIRE_DB_LIST.sort();
37+
expect(ENTIRE_DB_LIST).to.have.lengthOf.at.least(1);
38+
});
39+
40+
afterEach(async function () {
41+
await client.db(DB_NAME).dropDatabase();
42+
await client?.close();
43+
});
44+
45+
it('Verify that the method returns an Iterable of Document types', async () => {
46+
const dbInfo = await client.db().admin().listDatabases();
47+
expect(dbInfo).to.have.property('databases');
48+
expect(dbInfo.databases).to.be.an('array');
49+
expect(dbInfo.databases).to.have.lengthOf(ENTIRE_DB_LIST.length);
50+
for (const db of dbInfo.databases) {
51+
expect(db).to.be.a('object');
52+
}
53+
});
54+
55+
it('Verify that all databases on the server are present in the result set', async () => {
56+
const dbInfo = await client.db().admin().listDatabases();
57+
58+
const namesFromHelper = dbInfo.databases.map(({ name }) => name);
59+
namesFromHelper.sort();
60+
61+
expect(namesFromHelper).to.have.lengthOf(ENTIRE_DB_LIST.length);
62+
expect(namesFromHelper).to.deep.equal(ENTIRE_DB_LIST);
63+
expect(namesFromHelper).to.include(DB_NAME);
64+
});
65+
66+
it('Verify that the result set does not contain duplicates', async () => {
67+
const dbInfo = await client.db().admin().listDatabases();
68+
const databaseNames = dbInfo.databases.map(({ name }) => name);
69+
const databaseNamesSet = new Set(databaseNames);
70+
expect(databaseNames).to.have.lengthOf(databaseNamesSet.size);
71+
});
72+
});
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { expect } from 'chai';
2+
3+
import { AddUserOptions, MongoClient, MongoServerError } from '../../../src';
4+
5+
describe('listDatabases() authorizedDatabases flag', function () {
6+
// TODO(NODE-3860): Create driver test variants that require AUTH enabled
7+
const username = 'a';
8+
const password = 'b';
9+
const mockAuthorizedDb = 'enumerate_databases';
10+
const mockAuthorizedCollection = 'enumerate_databases_collection';
11+
12+
let adminClient: MongoClient;
13+
let authorizedClient: MongoClient;
14+
15+
const authorizedUserOptions: AddUserOptions = {
16+
roles: [{ role: 'read', db: mockAuthorizedDb }]
17+
};
18+
19+
beforeEach(function () {
20+
if (process.env.AUTH !== 'auth') {
21+
this.currentTest.skipReason =
22+
'TODO(NODE-3860): Create driver test variants that require AUTH enabled';
23+
this.skip();
24+
}
25+
});
26+
27+
beforeEach(async function () {
28+
// pass credentials from cluster_setup's mlaunch defaults
29+
// TODO(NODE-3860): pass credentials instead based on environment variable
30+
adminClient = this.configuration.newClient({
31+
auth: { username: 'user', password: 'password' }
32+
});
33+
await adminClient.connect();
34+
35+
await adminClient
36+
.db(mockAuthorizedDb)
37+
.createCollection(mockAuthorizedCollection)
38+
.catch(() => null);
39+
40+
await adminClient.db('admin').addUser(username, password, authorizedUserOptions);
41+
42+
authorizedClient = this.configuration.newClient({
43+
auth: { username: username, password: password }
44+
});
45+
await authorizedClient.connect();
46+
});
47+
48+
afterEach(async function () {
49+
await adminClient?.db('admin').removeUser(username);
50+
await adminClient?.db(mockAuthorizedDb).dropDatabase();
51+
await adminClient?.close();
52+
await authorizedClient?.close();
53+
});
54+
55+
it('should list all databases when admin client sets authorizedDatabases to true', async function () {
56+
const adminListDbs = await adminClient
57+
.db()
58+
.admin()
59+
.listDatabases({ authorizedDatabases: true });
60+
const adminDbs = adminListDbs.databases.map(({ name }) => name);
61+
62+
// no change in the dbs listed since we're using the admin user
63+
expect(adminDbs).to.have.length.greaterThan(1);
64+
expect(adminDbs.filter(db => db === mockAuthorizedDb)).to.have.lengthOf(1);
65+
expect(adminDbs.filter(db => db !== mockAuthorizedDb)).to.have.length.greaterThan(1);
66+
});
67+
68+
it('should list all databases when admin client sets authorizedDatabases to false', async function () {
69+
const adminListDbs = await adminClient
70+
.db()
71+
.admin()
72+
.listDatabases({ authorizedDatabases: false });
73+
const adminDbs = adminListDbs.databases.map(({ name }) => name);
74+
75+
// no change in the dbs listed since we're using the admin user
76+
expect(adminDbs).to.have.length.greaterThan(1);
77+
expect(adminDbs.filter(db => db === mockAuthorizedDb)).to.have.lengthOf(1);
78+
expect(adminDbs.filter(db => db !== mockAuthorizedDb)).to.have.length.greaterThan(1);
79+
});
80+
81+
it('should list authorized databases with authorizedDatabases set to true', async function () {
82+
const adminListDbs = await adminClient.db().admin().listDatabases();
83+
const authorizedListDbs = await authorizedClient
84+
.db()
85+
.admin()
86+
.listDatabases({ authorizedDatabases: true });
87+
const adminDbs = adminListDbs.databases;
88+
const authorizedDbs = authorizedListDbs.databases;
89+
90+
expect(adminDbs).to.have.length.greaterThan(1);
91+
expect(authorizedDbs).to.have.lengthOf(1);
92+
93+
expect(adminDbs.filter(db => db.name === mockAuthorizedDb)).to.have.lengthOf(1);
94+
expect(adminDbs.filter(db => db.name !== mockAuthorizedDb)).to.have.length.greaterThan(1);
95+
expect(authorizedDbs.filter(db => db.name === mockAuthorizedDb)).to.have.lengthOf(1);
96+
});
97+
98+
it('should list authorized databases by default with authorizedDatabases unspecified', async function () {
99+
const adminListDbs = await adminClient.db().admin().listDatabases();
100+
const authorizedListDbs = await authorizedClient.db().admin().listDatabases();
101+
const adminDbs = adminListDbs.databases;
102+
const authorizedDbs = authorizedListDbs.databases;
103+
104+
expect(adminDbs).to.have.length.greaterThan(1);
105+
expect(authorizedDbs).to.have.lengthOf(1);
106+
107+
expect(adminDbs.filter(db => db.name === mockAuthorizedDb)).to.have.lengthOf(1);
108+
expect(adminDbs.filter(db => db.name !== mockAuthorizedDb)).to.have.length.greaterThan(1);
109+
expect(authorizedDbs.filter(db => db.name === mockAuthorizedDb)).to.have.lengthOf(1);
110+
});
111+
112+
it('should not show authorized databases with authorizedDatabases set to false', async function () {
113+
let thrownError;
114+
try {
115+
await authorizedClient.db().admin().listDatabases({ authorizedDatabases: false });
116+
} catch (error) {
117+
thrownError = error;
118+
}
119+
120+
// check correctly produces an 'Insufficient permissions to list all databases' error
121+
expect(thrownError).to.be.instanceOf(MongoServerError);
122+
expect(thrownError).to.have.property('message').that.includes('list');
123+
});
124+
});

test/integration/node-specific/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)