@@ -48,7 +48,7 @@ export interface ReplSetOpts {
4848 * enable auth ("--auth" / "--noauth")
4949 * @default false
5050 */
51- auth ?: boolean | AutomaticAuth ;
51+ auth ?: boolean | AutomaticAuth ; // TODO: remove "boolean" option next major version
5252 /**
5353 * additional command line arguments passed to `mongod`
5454 * @default []
@@ -255,29 +255,58 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced
255255
256256 assertion ( this . _replSetOpts . count > 0 , new ReplsetCountLowError ( this . _replSetOpts . count ) ) ;
257257
258- if ( typeof this . _replSetOpts . auth === 'object' ) {
258+ // setting this for sanity
259+ if ( typeof this . _replSetOpts . auth === 'boolean' ) {
260+ this . _replSetOpts . auth = { disable : ! this . _replSetOpts . auth } ;
261+ }
262+
263+ // do not set default when "disable" is "true" to save execution and memory
264+ if ( ! this . _replSetOpts . auth . disable ) {
259265 this . _replSetOpts . auth = authDefault ( this . _replSetOpts . auth ) ;
260266 }
261267 }
262268
269+ /**
270+ * Helper function to determine if "auth" should be enabled
271+ * This function expectes to be run after the auth object has been transformed to a object
272+ * @returns "true" when "auth" should be enabled
273+ */
274+ protected enableAuth ( ) : boolean {
275+ if ( isNullOrUndefined ( this . _replSetOpts . auth ) ) {
276+ return false ;
277+ }
278+
279+ assertion ( typeof this . _replSetOpts . auth === 'object' , new AuthNotObjectError ( ) ) ;
280+
281+ return typeof this . _replSetOpts . auth . disable === 'boolean' // if "this._replSetOpts.auth.disable" is defined, use that
282+ ? ! this . _replSetOpts . auth . disable // invert the disable boolean, because "auth" should only be disabled if "disabled = true"
283+ : true ; // if "this._replSetOpts.auth.disable" is not defined, default to true because "this._replSetOpts.auth" is defined
284+ }
285+
263286 /**
264287 * Returns instance options suitable for a MongoMemoryServer.
265288 * @param baseOpts Options to merge with
289+ * @param keyfileLocation The Keyfile location if "auth" is used
266290 */
267- protected getInstanceOpts ( baseOpts : MongoMemoryInstanceOptsBase = { } ) : MongoMemoryInstanceOpts {
291+ protected getInstanceOpts (
292+ baseOpts : MongoMemoryInstanceOptsBase = { } ,
293+ keyfileLocation ?: string
294+ ) : MongoMemoryInstanceOpts {
295+ const enableAuth : boolean = this . enableAuth ( ) ;
296+
268297 const opts : MongoMemoryInstanceOpts = {
269- // disable "auth" if replsetopts has an object-auth
270- auth :
271- typeof this . _replSetOpts . auth === 'object' && ! this . _ranCreateAuth
272- ? false
273- : ! ! this . _replSetOpts . auth ,
298+ auth : enableAuth ,
274299 args : this . _replSetOpts . args ,
275300 dbName : this . _replSetOpts . dbName ,
276301 ip : this . _replSetOpts . ip ,
277302 replSet : this . _replSetOpts . name ,
278303 storageEngine : this . _replSetOpts . storageEngine ,
279304 } ;
280305
306+ if ( ! isNullOrUndefined ( keyfileLocation ) ) {
307+ opts . keyfileLocation = keyfileLocation ;
308+ }
309+
281310 if ( baseOpts . args ) {
282311 opts . args = this . _replSetOpts . args . concat ( baseOpts . args ) ;
283312 }
@@ -410,6 +439,12 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced
410439 return ;
411440 }
412441
442+ let keyfilePath : string | undefined = undefined ;
443+
444+ if ( this . enableAuth ( ) ) {
445+ keyfilePath = resolve ( ( await this . ensureKeyFile ( ) ) . name , 'keyfile' ) ;
446+ }
447+
413448 // Any servers defined within `_instanceOpts` should be started first as
414449 // the user could have specified a `dbPath` in which case we would want to perform
415450 // the `replSetInitiate` command against that server.
@@ -420,15 +455,15 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced
420455 } " from instanceOpts (count: ${ this . servers . length + 1 } ):`,
421456 opts
422457 ) ;
423- this . servers . push ( this . _initServer ( this . getInstanceOpts ( opts ) ) ) ;
458+ this . servers . push ( this . _initServer ( this . getInstanceOpts ( opts , keyfilePath ) ) ) ;
424459 } ) ;
425460 while ( this . servers . length < this . _replSetOpts . count ) {
426461 log (
427462 `initAllServers: starting extra server "${ this . servers . length + 1 } " of "${
428463 this . _replSetOpts . count
429464 } " (count: ${ this . servers . length + 1 } )`
430465 ) ;
431- this . servers . push ( this . _initServer ( this . getInstanceOpts ( ) ) ) ;
466+ this . servers . push ( this . _initServer ( this . getInstanceOpts ( undefined , keyfilePath ) ) ) ;
432467 }
433468
434469 log ( 'initAllServers: waiting for all servers to finish starting' ) ;
@@ -638,15 +673,15 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced
638673 const uris = this . servers . map ( ( server ) => server . getUri ( ) ) ;
639674 const isInMemory = this . servers [ 0 ] . instanceInfo ?. storageEngine === 'ephemeralForTest' ;
640675
641- let con : MongoClient = await MongoClient . connect ( uris [ 0 ] , {
676+ const con : MongoClient = await MongoClient . connect ( uris [ 0 ] , {
642677 // somehow since mongodb-nodejs 4.0, this option is needed when the server is set to be in a replset
643678 directConnection : true ,
644679 } ) ;
645680 log ( '_initReplSet: connected' ) ;
646681
647682 // try-finally to close connection in any case
648683 try {
649- let adminDb = con . db ( 'admin' ) ;
684+ const adminDb = con . db ( 'admin' ) ;
650685
651686 const members = uris . map ( ( uri , index ) => ( {
652687 _id : index ,
@@ -682,34 +717,9 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced
682717 new InstanceInfoError ( '_initReplSet authIsObject primary' )
683718 ) ;
684719
720+ await con . close ( ) ; // just ensuring that no timeouts happen or conflicts happen
685721 await primary . createAuth ( primary . instanceInfo ) ;
686722 this . _ranCreateAuth = true ;
687-
688- // TODO: maybe change the static "isInMemory" to be for each server individually, based on "storageEngine", not just the first one
689- if ( ! isInMemory ) {
690- log ( '_initReplSet: closing connection for restart' ) ;
691- await con . close ( ) ; // close connection in preparation for "stop"
692- await this . stop ( { doCleanup : false , force : false } ) ; // stop all servers for enabling auth
693- log ( '_initReplSet: starting all server again with auth' ) ;
694- await this . initAllServers ( ) ; // start all servers again with "auth" enabled
695- await this . _waitForPrimary ( ) ; // wait for a primary to come around, because otherwise server selection may time out, this may take more than 30s
696-
697- con = await MongoClient . connect ( this . getUri ( ) , {
698- authSource : 'admin' ,
699- authMechanism : 'SCRAM-SHA-256' ,
700- auth : {
701- username : this . _replSetOpts . auth . customRootName as string , // cast because these are existing
702- password : this . _replSetOpts . auth . customRootPwd as string ,
703- } ,
704- } ) ;
705- adminDb = con . db ( 'admin' ) ;
706- log ( '_initReplSet: auth restart finished' ) ;
707- } else {
708- console . warn (
709- 'Not Restarting ReplSet for Auth\n' +
710- 'Storage engine of current PRIMARY is ephemeralForTest, which does not write data on shutdown, and mongodb does not allow changing "auth" runtime'
711- ) ;
712- }
713723 }
714724 } catch ( e ) {
715725 if ( e instanceof MongoError && e . errmsg == 'already initialized' ) {
0 commit comments