Skip to content

Commit

Permalink
feat: change authentication to only happen when "enable" is "true"
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
Authentication now is only created when "{ enable: true }" is given
also "disable" has been removed
  • Loading branch information
hasezoey committed Jun 9, 2023
1 parent f689bdd commit 66a5ad3
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 99 deletions.
3 changes: 1 addition & 2 deletions README.md
Expand Up @@ -124,7 +124,6 @@ const mongod = new MongoMemoryServer({
dbPath?: string, // by default create in temp directory
storageEngine?: string, // by default `ephemeralForTest`, available engines: [ 'ephemeralForTest', 'wiredTiger' ]
replSet?: string, // by default no replica set, replica set name
auth?: boolean, // by default `mongod` is started with '--noauth', start `mongod` with '--auth'
args?: string[], // by default no additional arguments, any additional command line arguments for `mongod` `mongod` (ex. ['--notablescan'])
},
binary: {
Expand Down Expand Up @@ -173,7 +172,7 @@ const replSet = new MongoMemoryReplSet({
// unless otherwise noted below these values will be in common with all instances spawned:
replSet: {
name, // replica set name (default: 'testset')
auth, // enable auth support? (default: false)
auth, // enable auth support? (default: undefined / disabled)
args, // any args specified here will be combined with any per instance args from `instanceOpts`
count, // number of additional `mongod` processes to start (will not start any extra if instanceOpts.length > replSet.count); (default: 1)
dbName, // default database for db URI strings. (default: uuid.v4())
Expand Down
7 changes: 3 additions & 4 deletions docs/api/interfaces/mongo-memory-server-automaticauth.md
Expand Up @@ -7,13 +7,12 @@ API Documentation of `AutomaticAuth`-Interface

## Values

### disable
### enable

Typings: `disable?: boolean`
Typings: `enable?: boolean`
Default: `false`

Disable Authentication creation.
Normally authentication is enabled when the `auth` field a object, but with this option it can be explicitly disabled.
Enable or disable Authentication creation.

### extraUsers

Expand Down
10 changes: 9 additions & 1 deletion docs/guides/migration/migrate9.md
Expand Up @@ -34,7 +34,15 @@ With 9.0.0 the option `instance.auth` option is going to be ignored, because its

Example:

`new MongoMemoryServer({ instance: { auth: true } })` is going to be ignored, use `new MongoMemoryServer({ auth: {} })`
`new MongoMemoryServer({ instance: { auth: true } })` is going to be ignored, use `new MongoMemoryServer({ auth: { enable: true } })`

### `AutomaticAuth` changes

`AutomaticAuth` has been changed to **not** be enabled anymore by just having a empty object. Also property `disable` has been removed and `enable` has been added.

Replace `auth: {}` with `auth: { enable: true }`.
Replace `auth: { disable: true }` with `auth: { enable: false }`.
Replace `auth: { disable: false }` with `auth: { enable: true }`.

### MongoMemoryServer and MongoReplSet `.cleanup(boolean)` and `.stop(boolean)` have been removed

Expand Down
12 changes: 6 additions & 6 deletions packages/mongodb-memory-server-core/src/MongoMemoryReplSet.ts
Expand Up @@ -256,11 +256,11 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced

// setting this for sanity
if (typeof this._replSetOpts.auth === 'boolean') {
this._replSetOpts.auth = { disable: !this._replSetOpts.auth };
this._replSetOpts.auth = { enable: this._replSetOpts.auth };
}

// do not set default when "disable" is "true" to save execution and memory
if (!this._replSetOpts.auth.disable) {
// only set default is enabled
if (this._replSetOpts.auth.enable) {
this._replSetOpts.auth = authDefault(this._replSetOpts.auth);
}
}
Expand All @@ -277,9 +277,9 @@ export class MongoMemoryReplSet extends EventEmitter implements ManagerAdvanced

assertion(typeof this._replSetOpts.auth === 'object', new AuthNotObjectError());

return typeof this._replSetOpts.auth.disable === 'boolean' // if "this._replSetOpts.auth.disable" is defined, use that
? !this._replSetOpts.auth.disable // invert the disable boolean, because "auth" should only be disabled if "disabled = true"
: true; // if "this._replSetOpts.auth.disable" is not defined, default to true because "this._replSetOpts.auth" is defined
return typeof this._replSetOpts.auth.enable === 'boolean' // if "this._replSetOpts.auth.enable" is defined, use that
? this._replSetOpts.auth.enable
: false; // if "this._replSetOpts.auth.enable" is not defined, default to false
}

/**
Expand Down
21 changes: 7 additions & 14 deletions packages/mongodb-memory-server-core/src/MongoMemoryServer.ts
Expand Up @@ -44,10 +44,10 @@ export interface MongoMemoryServerOpts {

export interface AutomaticAuth {
/**
* Disable Automatic User creation
* @default false because when defining this object it usually means that AutomaticAuth is wanted
* Enable Automatic User creation
* @default false
*/
disable?: boolean;
enable?: boolean;
/**
* Extra Users to create besides the root user
* @default []
Expand Down Expand Up @@ -246,7 +246,7 @@ export class MongoMemoryServer extends EventEmitter implements ManagerAdvanced {
delete (this.opts.instance as MongoMemoryInstanceOpts | undefined)?.auth;
}

if (!isNullOrUndefined(this.opts.auth)) {
if (this.opts.auth?.enable === true) {
// assign defaults
this.auth = authDefault(this.opts.auth);
}
Expand Down Expand Up @@ -484,13 +484,6 @@ export class MongoMemoryServer extends EventEmitter implements ManagerAdvanced {
if (!isNullOrUndefined(this.auth) && createAuth) {
this.debug(`_startUpInstance: Running "createAuth" (force: "${this.auth.force}")`);
await this.createAuth(data);
} else {
// extra "if" to log when "disable" is set to "true"
if (this.opts.auth?.disable) {
this.debug(
'_startUpInstance: AutomaticAuth.disable is set to "true" skipping "createAuth"'
);
}
}
}

Expand Down Expand Up @@ -809,9 +802,9 @@ export class MongoMemoryServer extends EventEmitter implements ManagerAdvanced {
return false;
}

return typeof this.auth.disable === 'boolean' // if "this._replSetOpts.auth.disable" is defined, use that
? !this.auth.disable // invert the disable boolean, because "auth" should only be disabled if "disabled = true"
: true; // if "this._replSetOpts.auth.disable" is not defined, default to true because "this._replSetOpts.auth" is defined
return typeof this.auth.enable === 'boolean' // if "this._replSetOpts.auth.enable" is defined, use that
? this.auth.enable
: false; // if "this._replSetOpts.auth.enable" is not defined, default to false
}
}

Expand Down
Expand Up @@ -238,7 +238,7 @@ describe('single server replset', () => {
jest.spyOn(MongoMemoryReplSet.prototype, 'initAllServers');
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
const replSet = await MongoMemoryReplSet.create({
replSet: { auth: {}, count: 3, storageEngine: 'ephemeralForTest' },
replSet: { auth: { enable: true }, count: 3, storageEngine: 'ephemeralForTest' },
});

utils.assertion(!utils.isNullOrUndefined(replSet.replSetOpts.auth));
Expand Down Expand Up @@ -309,7 +309,7 @@ describe('single server replset', () => {
jest.spyOn(MongoMemoryReplSet.prototype, 'initAllServers');
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
const replSet = await MongoMemoryReplSet.create({
replSet: { auth: {}, count: 3, storageEngine: 'wiredTiger' },
replSet: { auth: { enable: true }, count: 3, storageEngine: 'wiredTiger' },
});

async function testConnections() {
Expand Down Expand Up @@ -429,7 +429,7 @@ describe('MongoMemoryReplSet', () => {
// @ts-expect-error because "_replSetOpts" is protected
expect(replSet.replSetOpts).toEqual(replSet._replSetOpts);
expect(replSet.replSetOpts).toEqual({
auth: { disable: true },
auth: { enable: false },
args: [],
name: 'testset',
count: 1,
Expand All @@ -444,7 +444,7 @@ describe('MongoMemoryReplSet', () => {
expect(replSet.replSetOpts).toEqual(replSet._replSetOpts);
const authDefault = utils.authDefault(replSet.replSetOpts.auth as AutomaticAuth);
expect(replSet.replSetOpts).toEqual({
auth: { ...authDefault, disable: false },
auth: { ...authDefault, enable: true },
args: [],
name: 'testset',
count: 1,
Expand Down
Expand Up @@ -71,7 +71,7 @@ describe('MongoMemoryServer', () => {
jest.spyOn(MongoInstance.prototype, 'start');
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
const mongoServer = await MongoMemoryServer.create({
auth: {},
auth: { enable: true },
instance: {
storageEngine: 'ephemeralForTest',
},
Expand Down Expand Up @@ -133,8 +133,8 @@ describe('MongoMemoryServer', () => {
await mongoServer.stop();
});

it('should make use of "AutomaticAuth" even when "instance.auth" is not set (wiredTiger)', async () => {
jest.spyOn(MongoInstance.prototype, 'start');
it('should not start auth when "instance.auth" is not set (wiredTiger)', async () => {
jest.spyOn(MongoInstance.prototype, 'start').mockResolvedValueOnce();
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
const mongoServer = await MongoMemoryServer.create({
auth: {},
Expand All @@ -143,67 +143,22 @@ describe('MongoMemoryServer', () => {
},
});

utils.assertion(!utils.isNullOrUndefined(mongoServer.instanceInfo));
utils.assertion(!utils.isNullOrUndefined(mongoServer.auth));

// test unpriviliged connection
{
const con = await MongoClient.connect(mongoServer.getUri());

const db = con.db('somedb');
const col = db.collection('somecol');

try {
await col.insertOne({ test: 1 });
fail('Expected insertion to fail');
} catch (err) {
expect(err).toBeInstanceOf(MongoServerError);
expect((err as MongoServerError).codeName).toEqual('Unauthorized');
} finally {
await con.close();
}
}

// test priviliged connection
{
const con: MongoClient = await MongoClient.connect(
utils.uriTemplate(mongoServer.instanceInfo.ip, mongoServer.instanceInfo.port, 'admin'),
{
authSource: 'admin',
authMechanism: 'SCRAM-SHA-256',
auth: {
username: mongoServer.auth.customRootName,
password: mongoServer.auth.customRootPwd,
},
}
);

const admindb = con.db('admin');
const users: { users?: { user: string }[] } = await admindb.command({
usersInfo: mongoServer.auth.customRootName,
});
expect(users.users).toHaveLength(1);
expect(users.users?.[0].user).toEqual(mongoServer.auth.customRootName);

const db = con.db('somedb');
const col = db.collection('somecol');

expect(await col.insertOne({ test: 1 })).toHaveProperty('acknowledged', true);

await con.close();
}
const args =
// @ts-expect-error "_instanceInfo" is protected
mongoServer._instanceInfo?.instance
// separator comment
.prepareCommandArgs();

expect(MongoInstance.prototype.start).toHaveBeenCalledTimes(1);
expect(console.warn).toHaveBeenCalledTimes(0);
utils.assertion(!utils.isNullOrUndefined(args));

await mongoServer.stop();
expect(args.includes('--noauth')).toBeTruthy();
});

it('should make use of "AutomaticAuth" (wiredTiger)', async () => {
jest.spyOn(MongoInstance.prototype, 'start');
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
const mongoServer = await MongoMemoryServer.create({
auth: {},
auth: { enable: true },
instance: {
storageEngine: 'wiredTiger',
},
Expand Down Expand Up @@ -276,6 +231,7 @@ describe('MongoMemoryServer', () => {
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
const mongoServer = await MongoMemoryServer.create({
auth: {
enable: true,
extraUsers: [
readOnlyUser,
{
Expand Down Expand Up @@ -402,21 +358,23 @@ describe('MongoMemoryServer', () => {
await mongoServer.stop();
});

it('"createAuth" should not be called if "disabled" is true', async () => {
it('"createAuth" should not be called if "enabled" is false', async () => {
jest.spyOn(MongoInstance.prototype, 'start');
jest.spyOn(MongoMemoryServer.prototype, 'createAuth');
const mongoServer = await MongoMemoryServer.create({
auth: {
disable: true,
enable: false,
},
instance: {
storageEngine: 'ephemeralForTest',
},
});

utils.assertion(!utils.isNullOrUndefined(mongoServer.instanceInfo));
utils.assertion(!utils.isNullOrUndefined(mongoServer.auth));
expect(mongoServer.instanceInfo.instance.prepareCommandArgs().includes('--noauth'));
utils.assertion(utils.isNullOrUndefined(mongoServer.auth));
expect(
mongoServer.instanceInfo.instance.prepareCommandArgs().includes('--noauth')
).toBeTruthy();

const con: MongoClient = await MongoClient.connect(
utils.uriTemplate(mongoServer.instanceInfo.ip, mongoServer.instanceInfo.port, 'admin'),
Expand All @@ -439,7 +397,7 @@ describe('MongoMemoryServer', () => {
jest.spyOn(MongoInstance.prototype, 'start');
jest.spyOn(MongoMemoryServer.prototype, 'createAuth');
const mongoServer = new MongoMemoryServer({
auth: {},
auth: { enable: true },
instance: {
// @ts-expect-error "auth" is removed from the type
auth: false,
Expand Down Expand Up @@ -940,7 +898,7 @@ describe('MongoMemoryServer', () => {

const mongoServer = new MongoMemoryServer({
instance: { dbPath: tmpDbPath },
auth: {},
auth: { enable: true },
});

// @ts-expect-error "getStartOptions" is protected
Expand Down Expand Up @@ -968,7 +926,7 @@ describe('MongoMemoryServer', () => {

const mongoServer = new MongoMemoryServer({
instance: { dbPath: tmpDbPath },
auth: {},
auth: { enable: true },
});

// @ts-expect-error "getStartOptions" is protected
Expand Down Expand Up @@ -1101,22 +1059,22 @@ describe('MongoMemoryServer', () => {
).toStrictEqual(false);
});

it('should with defaults return "true" if empty object OR "disable: false"', () => {
it('should with defaults return "false" if empty object OR "enable: false"', () => {
{
const mongoServer = new MongoMemoryServer({ auth: {} });

expect(
// @ts-expect-error "authObjectEnable" is protected
mongoServer.authObjectEnable()
).toStrictEqual(true);
).toStrictEqual(false);
}
{
const mongoServer = new MongoMemoryServer({ auth: { disable: false } });
const mongoServer = new MongoMemoryServer({ auth: { enable: false } });

expect(
// @ts-expect-error "authObjectEnable" is protected
mongoServer.authObjectEnable()
).toStrictEqual(true);
).toStrictEqual(false);
}
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/mongodb-memory-server-core/src/util/utils.ts
Expand Up @@ -181,7 +181,7 @@ export async function ensureAsync(): Promise<void> {
export function authDefault(opts: AutomaticAuth): Required<AutomaticAuth> {
return {
force: false,
disable: false,
enable: true,
customRootName: 'mongodb-memory-server-root',
customRootPwd: 'rootuser',
extraUsers: [],
Expand Down

0 comments on commit 66a5ad3

Please sign in to comment.