Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NODE-5233)!: prevent session from one client from being used on another #3790

Merged
merged 6 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,14 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
return client.connect();
}

/** Starts a new session on the server */
/**
* Creates a new ClientSession. When using the returned session in an operation
* a corresponding ServerSession will be created.
*
* @remarks
* A ClientSession instance may only be passed to operations being performed on the same
* MongoClient it was started from.
*/
startSession(options?: ClientSessionOptions): ClientSession {
const session = new ClientSession(
this,
Expand Down
2 changes: 2 additions & 0 deletions src/operations/execute_operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ async function executeOperationAsync<
throw new MongoExpiredSessionError('Use of expired sessions is not permitted');
} else if (session.snapshotEnabled && !topology.capabilities.supportsSnapshotReads) {
throw new MongoCompatibilityError('Snapshot reads require MongoDB 5.0 or later');
} else if (session.client !== client) {
throw new MongoRuntimeError('ClientSession must be from the same MongoClient');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a MongoInvalidArgument error? We this error should be thrown when the user provides an invalid session as an argument.

}

const readPreference = operation.readPreference ?? ReadPreference.primary;
Expand Down
37 changes: 36 additions & 1 deletion test/integration/sessions/sessions.prose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,46 @@ import {
type Collection,
type CommandStartedEvent,
MongoClient,
MongoDriverError
MongoDriverError,
MongoRuntimeError
} from '../../mongodb';
import { sleep } from '../../tools/utils';

describe('Sessions Prose Tests', () => {
describe('5. Session argument is for the right client', () => {
let client1: MongoClient;
let client2: MongoClient;
beforeEach(async function () {
client1 = this.configuration.newClient();
client2 = this.configuration.newClient();
});

afterEach(async function () {
await client1?.close();
await client2?.close();
});

/**
* Steps:
* - Create client1 and client2
* - Get database from client1
* - Get collection from database
* - Start session from client2
* - Call collection.insertOne(session,...)
* - Assert that an error was reported because session was not started from client1
*/
context('when session is started from a different client than operation is being run on', () =>
it('the operation throws a MongoRuntimeError', async () => {
const db = client1.db();
const collection = db.collection('test');
const session = client2.startSession();
const error = await collection.insertOne({}, { session }).catch(error => error);
expect(error).to.be.instanceOf(MongoRuntimeError);
expect(error).to.match(/ClientSession must be from the same MongoClient/i);
})
);
});

describe('14. Implicit sessions only allocate their server session after a successful connection checkout', () => {
let client: MongoClient;
let testCollection: Collection<{ _id: number; a?: number }>;
Expand Down