Skip to content

Commit 464a587

Browse files
committed
test(NODE-5035): add caching prose test
1 parent 642c906 commit 464a587

File tree

4 files changed

+153
-34
lines changed

4 files changed

+153
-34
lines changed

src/cmap/auth/mongodb_oidc.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,21 @@ export interface OIDCRequestTokenResult {
2727
/** @public */
2828
export type OIDCRequestFunction = (
2929
principalName: string,
30-
idl: OIDCMechanismServerStep1,
30+
serverResult: OIDCMechanismServerStep1,
3131
timeout: AbortSignal | number
3232
) => Promise<OIDCRequestTokenResult>;
3333

3434
/** @public */
3535
export type OIDCRefreshFunction = (
3636
principalName: string,
37-
idl: OIDCMechanismServerStep1,
37+
serverResult: OIDCMechanismServerStep1,
3838
result: OIDCRequestTokenResult,
3939
timeout: AbortSignal | number
4040
) => Promise<OIDCRequestTokenResult>;
4141

4242
/** @internal */
43-
const DEVICE_WORKFLOWS = {
43+
export const OIDC_WORKFLOWS = {
44+
callback: new CallbackWorkflow(),
4445
aws: new AwsDeviceWorkflow(),
4546
azure: undefined,
4647
gcp: undefined
@@ -102,8 +103,5 @@ export class MongoDBOIDC extends AuthProvider {
102103
*/
103104
function getWorkflow(credentials: MongoCredentials): Workflow | undefined {
104105
const deviceName = credentials.mechanismProperties.DEVICE_NAME;
105-
if (deviceName) {
106-
return DEVICE_WORKFLOWS[deviceName];
107-
}
108-
return new CallbackWorkflow();
106+
return OIDC_WORKFLOWS[deviceName || 'callback'];
109107
}

src/cmap/auth/mongodb_oidc/token_entry_cache.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ export class TokenEntryCache {
6262
return entry;
6363
}
6464

65+
/**
66+
* Clear the cache.
67+
*/
68+
clear(): void {
69+
this.entries.clear();
70+
}
71+
6572
/**
6673
* Delete an entry from the cache.
6774
*/

test/manual/mongodb_oidc.test.ts

Lines changed: 128 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises';
22

33
import { expect } from 'chai';
44

5-
import { MongoClient } from '../mongodb';
5+
import { MongoClient, OIDC_WORKFLOWS } from '../mongodb';
66

77
describe('MONGODB-OIDC', function () {
88
context('when running in the environment', function () {
@@ -319,33 +319,134 @@ describe('MONGODB-OIDC', function () {
319319
// the authorization code workflow with and without a provided refresh callback.
320320
// If desired, the caching tests can be done using mock server responses.
321321
describe('5. Caching', function () {
322-
// - Clear the cache.
323-
// - Create a new client with a request callback and a refresh callback. Both callbacks will read the contents of the ``AWS_WEB_IDENTITY_TOKEN_FILE`` location to obtain a valid access token.
324-
// - Validate the request callback inputs, including the timeout parameter if
325-
// -ssible.
322+
let requestInvokations = 0;
323+
let refreshInvokations = 0;
324+
const cache = OIDC_WORKFLOWS.callback.cache;
326325
// - Give a callback response with a valid accessToken and an expiresInSeconds
327-
// -at is within one minute.
328-
// - Ensure that a ``find`` operation adds credentials to the cache.
329-
// - Create a new client with the same request callback and a refresh callback.
330-
// - Ensure that a ``find`` operation results in a call to the refresh callback.
331-
// - Validate the refresh callback inputs, including the timeout parameter if
332-
// -ssible.
333-
// - Ensure there is a cache with credentials that will expire in less than 5 minutes, using a client with an appropriate request callback.
334-
// - Create a new client with the a request callback but no refresh callback.
335-
// - Ensure that a ``find`` operation results in a call to the request callback.
336-
// - the driver does not supports using callback hashes as part of the cache key,
337-
// -ip the next test.
338-
// -Create a new client with a different request callback.
339-
// -Ensure that a ``find`` operation adds a new entry to the cache.
340-
// - Clear the cache.
341-
// - Ensure there is a cache with credentials that will expire in less than 5 minutes, using a client with an appropriate request callback.
342-
// - Ensure there is a cache with credentials that will expire in less than 5 minutes.
343-
// - Create a new client with a valid request callback and a refresh callback that gives invalid credentials.
344-
// - Ensure that a ``find`` operation results in an error.
345-
// - Ensure that the cache has been cleared.
346-
// - Clear the cache.
347-
// - Create a new client using the AWS device workflow.
348-
// - Ensure that a ``find`` operation does not add credentials to the cache.
326+
// that is within one minute.
327+
// - Validate the request callback inputs, including the timeout parameter if possible.
328+
const requestCallback = async (principalName, serverResult, timeout) => {
329+
const token = await readFile(`${process.env.OIDC_TOKEN_DIR}/test_user1`, {
330+
encoding: 'utf8'
331+
});
332+
333+
expect(principalName).to.equal('test_user1');
334+
expect(serverResult).to.have.property('clientId');
335+
expect(timeout).to.equal(300000);
336+
requestInvokations++;
337+
338+
return { accessToken: token, expiresInSeconds: 30 };
339+
};
340+
341+
const refreshCallback = async (principalName, serverResult, tokenResult, timeout) => {
342+
const token = await readFile(`${process.env.OIDC_TOKEN_DIR}/test_user1`, {
343+
encoding: 'utf8'
344+
});
345+
346+
expect(principalName).to.equal('test_user1');
347+
expect(serverResult).to.have.property('clientId');
348+
expect(tokenResult.accessToken).to.equal(token);
349+
expect(timeout).to.equal(300000);
350+
refreshInvokations++;
351+
352+
return { accessToken: token, expiresInSeconds: 30 };
353+
};
354+
355+
beforeEach(() => {
356+
requestInvokations = 0;
357+
refreshInvokations = 0;
358+
});
359+
360+
context('when calling the request callback', function () {
361+
let client;
362+
let collection;
363+
364+
// - Clear the cache.
365+
before(function () {
366+
cache.clear();
367+
368+
// - Create a new client with a request callback and a refresh callback.
369+
// Both callbacks will read the contents of the AWS_WEB_IDENTITY_TOKEN_FILE
370+
// location to obtain a valid access token.
371+
client = new MongoClient('mongodb://test_user1@localhost/?authMechanism=MONGODB-OIDC', {
372+
authMechanismProperties: {
373+
REQUEST_TOKEN_CALLBACK: requestCallback,
374+
REFRESH_TOKEN_CALLBACK: refreshCallback
375+
}
376+
});
377+
collection = client.db('test').collection('test');
378+
});
379+
380+
after(async () => {
381+
client?.close();
382+
});
383+
384+
// - Ensure that a find operation adds credentials to the cache.
385+
it('adds credentials to the cache', async function () {
386+
await collection.findOne();
387+
expect(cache.entries.size).to.equal(1);
388+
});
389+
});
390+
391+
context('when calling the refresh callback', function () {
392+
let client;
393+
let collection;
394+
395+
before(function () {
396+
// - Create a new client with the same request callback and a refresh callback.
397+
client = new MongoClient('mongodb://test_user1@localhost/?authMechanism=MONGODB-OIDC', {
398+
authMechanismProperties: {
399+
REQUEST_TOKEN_CALLBACK: requestCallback,
400+
REFRESH_TOKEN_CALLBACK: refreshCallback
401+
}
402+
});
403+
collection = client.db('test').collection('test');
404+
});
405+
406+
after(async () => {
407+
client?.close();
408+
});
409+
410+
// - Ensure that a find operation results in a call to the refresh callback.
411+
// - Validate the refresh callback inputs, including the timeout parameter if possible.
412+
// - Ensure there is a cache with credentials that will expire in less than 5 minutes,
413+
// using a client with an appropriate request callback.
414+
it('adds credentials to the cache', async function () {
415+
await collection.findOne();
416+
expect(requestInvokations).to.equal(0);
417+
expect(refreshInvokations).to.equal(1);
418+
expect(cache.entries.values().next().value.expiration).to.be.below(
419+
Date.now() + 300000
420+
);
421+
});
422+
});
423+
424+
context('when providing no refresh callback', function () {
425+
let client;
426+
let collection;
427+
428+
before(function () {
429+
// - Create a new client with the a request callback but no refresh callback.
430+
client = new MongoClient('mongodb://test_user1@localhost/?authMechanism=MONGODB-OIDC', {
431+
authMechanismProperties: {
432+
REQUEST_TOKEN_CALLBACK: requestCallback
433+
}
434+
});
435+
collection = client.db('test').collection('test');
436+
});
437+
438+
after(async () => {
439+
client?.close();
440+
cache.clear();
441+
});
442+
443+
// - Ensure that a find operation results in a call to the request callback.
444+
it('adds credentials to the cache', async function () {
445+
await collection.findOne();
446+
expect(requestInvokations).to.equal(1);
447+
expect(refreshInvokations).to.equal(0);
448+
});
449+
});
349450
});
350451
});
351452
});

test/unit/cmap/auth/mongodb_oidc/token_entry_cache.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ describe('TokenEntryCache', function () {
5151
});
5252
});
5353

54+
describe('#clear', function () {
55+
const cache = new TokenEntryCache();
56+
57+
before(function () {
58+
cache.addEntry(tokenResult, serverResult, 'localhost', 'user');
59+
cache.clear();
60+
});
61+
62+
it('clears the cache', function () {
63+
expect(cache.entries.size).to.equal(0);
64+
});
65+
});
66+
5467
describe('#deleteEntry', function () {
5568
const cache = new TokenEntryCache();
5669

0 commit comments

Comments
 (0)