diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 4ba93ea82c3..15e6d1cd95b 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -15,6 +15,8 @@ const LIST_COLLECTIONS_WIRE_VERSION = 3; export interface ListCollectionsOptions extends CommandOperationOptions { /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */ nameOnly?: boolean; + /** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */ + authorizedCollections?: boolean; /** The batchSize for the returned command cursor or if pre 2.8 the systems batch collection */ batchSize?: number; } @@ -25,6 +27,7 @@ export class ListCollectionsOperation extends CommandOperation { db: Db; filter: Document; nameOnly: boolean; + authorizedCollections: boolean; batchSize?: number; constructor(db: Db, filter: Document, options?: ListCollectionsOptions) { @@ -34,6 +37,7 @@ export class ListCollectionsOperation extends CommandOperation { this.db = db; this.filter = filter; this.nameOnly = !!this.options.nameOnly; + this.authorizedCollections = !!this.options.authorizedCollections; if (typeof this.options.batchSize === 'number') { this.batchSize = this.options.batchSize; @@ -94,7 +98,8 @@ export class ListCollectionsOperation extends CommandOperation { listCollections: 1, filter: this.filter, cursor: this.batchSize ? { batchSize: this.batchSize } : {}, - nameOnly: this.nameOnly + nameOnly: this.nameOnly, + authorizedCollections: this.authorizedCollections }; return super.executeCommand(server, session, command, callback); diff --git a/test/functional/unit_db_list_collections.test.js b/test/functional/unit_db_list_collections.test.js index 9baa658ad48..d930dde2d45 100644 --- a/test/functional/unit_db_list_collections.test.js +++ b/test/functional/unit_db_list_collections.test.js @@ -34,17 +34,27 @@ describe('db.listCollections', function () { { description: 'should always send nameOnly option, defaulting to false', command: db => db.listCollections().toArray(() => {}), - listCollectionsValue: false + listCollectionsOptions: { nameOnly: false } }, { description: 'should propagate the nameOnly option', command: db => db.listCollections({}, { nameOnly: true }).toArray(() => {}), - listCollectionsValue: true + listCollectionsOptions: { nameOnly: true } + }, + { + description: 'should always send authorizedCollections option, defaulting to false', + command: db => db.listCollections().toArray(() => {}), + listCollectionsOptions: { authorizedCollections: false } + }, + { + description: 'should propagate the authorizedCollections option', + command: db => db.listCollections({}, { authorizedCollections: true }).toArray(() => {}), + listCollectionsOptions: { authorizedCollections: true } }, { description: 'should send nameOnly: true for db.collections', command: db => db.collections(() => {}), - listCollectionsValue: true + listCollectionsOptions: { nameOnly: true } } ].forEach(config => { function testFn(done) { @@ -59,7 +69,9 @@ describe('db.listCollections', function () { client.on('commandStarted', e => { if (e.commandName === 'listCollections') { try { - expect(e).to.have.nested.property('command.nameOnly', config.listCollectionsValue); + for (const [name, value] of Object.entries(config.listCollectionsOptions)) { + expect(e).to.have.nested.property(`command.${name}`, value); + } client.close(done); } catch (err) { client.close(() => done(err));