Skip to content

Commit 550ea60

Browse files
chore: make prepare() async
1 parent 14e1e38 commit 550ea60

File tree

7 files changed

+121
-143
lines changed

7 files changed

+121
-143
lines changed

src/cmap/auth/auth_provider.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ export class AuthProvider {
4545
* @param handshakeDoc - The document used for the initial handshake on a connection
4646
* @param authContext - Context for authentication flow
4747
*/
48-
prepare(
48+
async prepare(
4949
handshakeDoc: HandshakeDocument,
50-
authContext: AuthContext,
51-
callback: Callback<HandshakeDocument>
52-
): void {
53-
callback(undefined, handshakeDoc);
50+
/* eslint @typescript-eslint/no-unused-vars : 0 */
51+
authContext: AuthContext
52+
): Promise<HandshakeDocument> {
53+
return handshakeDoc;
5454
}
5555

5656
/**

src/cmap/auth/mongodb_oidc.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -106,37 +106,24 @@ export class MongoDBOIDC extends AuthProvider {
106106
/**
107107
* Add the speculative auth for the initial handshake.
108108
*/
109-
override prepare(
109+
override async prepare(
110110
handshakeDoc: HandshakeDocument,
111-
authContext: AuthContext,
112-
callback: Callback<HandshakeDocument>
113-
): void {
111+
authContext: AuthContext
112+
): Promise<HandshakeDocument> {
114113
const { credentials } = authContext;
115114

116115
if (!credentials) {
117-
return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
116+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
118117
}
119118

120-
getWorkflow(credentials, (error, workflow) => {
121-
if (error) {
122-
return callback(error);
123-
}
124-
if (!workflow) {
125-
return callback(
126-
new MongoRuntimeError(
127-
`Could not load workflow for provider ${credentials.mechanismProperties.PROVIDER_NAME}`
128-
)
129-
);
130-
}
131-
workflow.speculativeAuth().then(
132-
result => {
133-
return callback(undefined, { ...handshakeDoc, ...result });
134-
},
135-
error => {
136-
callback(error);
137-
}
119+
const workflow = getWorkflow(credentials);
120+
if (!workflow) {
121+
throw new MongoRuntimeError(
122+
`Could not load workflow for provider ${credentials.mechanismProperties.PROVIDER_NAME}`
138123
);
139-
});
124+
}
125+
const result = await workflow.speculativeAuth();
126+
return { ...handshakeDoc, ...result };
140127
}
141128
}
142129

src/cmap/auth/scram.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as crypto from 'crypto';
2+
import { promisify } from 'util';
23

34
import { Binary, Document } from '../../bson';
45
import { saslprep } from '../../deps';
@@ -19,37 +20,37 @@ type CryptoMethod = 'sha1' | 'sha256';
1920

2021
class ScramSHA extends AuthProvider {
2122
cryptoMethod: CryptoMethod;
23+
randomBytesAsync: (size: number) => Promise<Buffer>;
2224
constructor(cryptoMethod: CryptoMethod) {
2325
super();
2426
this.cryptoMethod = cryptoMethod || 'sha1';
27+
this.randomBytesAsync = promisify(crypto.randomBytes);
2528
}
2629

27-
override prepare(handshakeDoc: HandshakeDocument, authContext: AuthContext, callback: Callback) {
30+
override async prepare(
31+
handshakeDoc: HandshakeDocument,
32+
authContext: AuthContext
33+
): Promise<HandshakeDocument> {
2834
const cryptoMethod = this.cryptoMethod;
2935
const credentials = authContext.credentials;
3036
if (!credentials) {
31-
return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
37+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
3238
}
3339
if (cryptoMethod === 'sha256' && saslprep == null) {
3440
emitWarning('Warning: no saslprep library specified. Passwords will not be sanitized');
3541
}
3642

37-
crypto.randomBytes(24, (err, nonce) => {
38-
if (err) {
39-
return callback(err);
40-
}
43+
const nonce = await this.randomBytesAsync(24);
44+
// store the nonce for later use
45+
Object.assign(authContext, { nonce });
4146

42-
// store the nonce for later use
43-
Object.assign(authContext, { nonce });
44-
45-
const request = Object.assign({}, handshakeDoc, {
46-
speculativeAuthenticate: Object.assign(makeFirstMessage(cryptoMethod, credentials, nonce), {
47-
db: credentials.source
48-
})
49-
});
50-
51-
callback(undefined, request);
47+
const request = Object.assign({}, handshakeDoc, {
48+
speculativeAuthenticate: Object.assign(makeFirstMessage(cryptoMethod, credentials, nonce), {
49+
db: credentials.source
50+
})
5251
});
52+
53+
return request;
5354
}
5455

5556
override auth(authContext: AuthContext, callback: Callback) {

src/cmap/auth/x509.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@ import { AuthContext, AuthProvider } from './auth_provider';
66
import type { MongoCredentials } from './mongo_credentials';
77

88
export class X509 extends AuthProvider {
9-
override prepare(
9+
override async prepare(
1010
handshakeDoc: HandshakeDocument,
11-
authContext: AuthContext,
12-
callback: Callback
13-
): void {
11+
authContext: AuthContext
12+
): Promise<HandshakeDocument> {
1413
const { credentials } = authContext;
1514
if (!credentials) {
16-
return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
15+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
1716
}
1817
Object.assign(handshakeDoc, {
1918
speculativeAuthenticate: x509AuthenticateCommand(credentials)
2019
});
2120

22-
callback(undefined, handshakeDoc);
21+
return handshakeDoc;
2322
}
2423

2524
override auth(authContext: AuthContext, callback: Callback): void {

src/cmap/connect.ts

Lines changed: 82 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -119,93 +119,92 @@ function performInitialHandshake(
119119

120120
const authContext = new AuthContext(conn, credentials, options);
121121
conn.authContext = authContext;
122-
prepareHandshakeDocument(authContext, (err, handshakeDoc) => {
123-
if (err || !handshakeDoc) {
124-
return callback(err);
125-
}
126-
127-
const handshakeOptions: Document = Object.assign({}, options);
128-
if (typeof options.connectTimeoutMS === 'number') {
129-
// The handshake technically is a monitoring check, so its socket timeout should be connectTimeoutMS
130-
handshakeOptions.socketTimeoutMS = options.connectTimeoutMS;
131-
}
132-
133-
const start = new Date().getTime();
134-
conn.command(ns('admin.$cmd'), handshakeDoc, handshakeOptions, (err, response) => {
135-
if (err) {
136-
callback(err);
137-
return;
122+
prepareHandshakeDocument(authContext).then(
123+
handshakeDoc => {
124+
const handshakeOptions: Document = Object.assign({}, options);
125+
if (typeof options.connectTimeoutMS === 'number') {
126+
// The handshake technically is a monitoring check, so its socket timeout should be connectTimeoutMS
127+
handshakeOptions.socketTimeoutMS = options.connectTimeoutMS;
138128
}
139129

140-
if (response?.ok === 0) {
141-
callback(new MongoServerError(response));
142-
return;
143-
}
130+
const start = new Date().getTime();
131+
conn.command(ns('admin.$cmd'), handshakeDoc, handshakeOptions, (err, response) => {
132+
if (err) {
133+
callback(err);
134+
return;
135+
}
144136

145-
if (!('isWritablePrimary' in response)) {
146-
// Provide hello-style response document.
147-
response.isWritablePrimary = response[LEGACY_HELLO_COMMAND];
148-
}
137+
if (response?.ok === 0) {
138+
callback(new MongoServerError(response));
139+
return;
140+
}
149141

150-
if (response.helloOk) {
151-
conn.helloOk = true;
152-
}
142+
if (!('isWritablePrimary' in response)) {
143+
// Provide hello-style response document.
144+
response.isWritablePrimary = response[LEGACY_HELLO_COMMAND];
145+
}
153146

154-
const supportedServerErr = checkSupportedServer(response, options);
155-
if (supportedServerErr) {
156-
callback(supportedServerErr);
157-
return;
158-
}
147+
if (response.helloOk) {
148+
conn.helloOk = true;
149+
}
159150

160-
if (options.loadBalanced) {
161-
if (!response.serviceId) {
162-
return callback(
163-
new MongoCompatibilityError(
164-
'Driver attempted to initialize in load balancing mode, ' +
165-
'but the server does not support this mode.'
166-
)
167-
);
151+
const supportedServerErr = checkSupportedServer(response, options);
152+
if (supportedServerErr) {
153+
callback(supportedServerErr);
154+
return;
168155
}
169-
}
170156

171-
// NOTE: This is metadata attached to the connection while porting away from
172-
// handshake being done in the `Server` class. Likely, it should be
173-
// relocated, or at very least restructured.
174-
conn.hello = response;
175-
conn.lastHelloMS = new Date().getTime() - start;
176-
177-
if (!response.arbiterOnly && credentials) {
178-
// store the response on auth context
179-
authContext.response = response;
180-
181-
const resolvedCredentials = credentials.resolveAuthMechanism(response);
182-
const provider = AUTH_PROVIDERS.get(resolvedCredentials.mechanism);
183-
if (!provider) {
184-
return callback(
185-
new MongoInvalidArgumentError(
186-
`No AuthProvider for ${resolvedCredentials.mechanism} defined.`
187-
)
188-
);
157+
if (options.loadBalanced) {
158+
if (!response.serviceId) {
159+
return callback(
160+
new MongoCompatibilityError(
161+
'Driver attempted to initialize in load balancing mode, ' +
162+
'but the server does not support this mode.'
163+
)
164+
);
165+
}
189166
}
190-
provider.auth(authContext, err => {
191-
if (err) {
192-
if (err instanceof MongoError) {
193-
err.addErrorLabel(MongoErrorLabel.HandshakeError);
194-
if (needsRetryableWriteLabel(err, response.maxWireVersion)) {
195-
err.addErrorLabel(MongoErrorLabel.RetryableWriteError);
167+
168+
// NOTE: This is metadata attached to the connection while porting away from
169+
// handshake being done in the `Server` class. Likely, it should be
170+
// relocated, or at very least restructured.
171+
conn.hello = response;
172+
conn.lastHelloMS = new Date().getTime() - start;
173+
174+
if (!response.arbiterOnly && credentials) {
175+
// store the response on auth context
176+
authContext.response = response;
177+
178+
const resolvedCredentials = credentials.resolveAuthMechanism(response);
179+
const provider = AUTH_PROVIDERS.get(resolvedCredentials.mechanism);
180+
if (!provider) {
181+
return callback(
182+
new MongoInvalidArgumentError(
183+
`No AuthProvider for ${resolvedCredentials.mechanism} defined.`
184+
)
185+
);
186+
}
187+
provider.auth(authContext, err => {
188+
if (err) {
189+
if (err instanceof MongoError) {
190+
err.addErrorLabel(MongoErrorLabel.HandshakeError);
191+
if (needsRetryableWriteLabel(err, response.maxWireVersion)) {
192+
err.addErrorLabel(MongoErrorLabel.RetryableWriteError);
193+
}
196194
}
195+
return callback(err);
197196
}
198-
return callback(err);
199-
}
200-
callback(undefined, conn);
201-
});
197+
callback(undefined, conn);
198+
});
202199

203-
return;
204-
}
200+
return;
201+
}
205202

206-
callback(undefined, conn);
207-
});
208-
});
203+
callback(undefined, conn);
204+
});
205+
},
206+
error => callback(error)
207+
);
209208
}
210209

211210
export interface HandshakeDocument extends Document {
@@ -226,10 +225,9 @@ export interface HandshakeDocument extends Document {
226225
*
227226
* This function is only exposed for testing purposes.
228227
*/
229-
export function prepareHandshakeDocument(
230-
authContext: AuthContext,
231-
callback: Callback<HandshakeDocument>
232-
) {
228+
export async function prepareHandshakeDocument(
229+
authContext: AuthContext
230+
): Promise<HandshakeDocument> {
233231
const options = authContext.options;
234232
const compressors = options.compressors ? options.compressors : [];
235233
const { serverApi } = authContext.connection;
@@ -253,23 +251,19 @@ export function prepareHandshakeDocument(
253251
const provider = AUTH_PROVIDERS.get(AuthMechanism.MONGODB_SCRAM_SHA256);
254252
if (!provider) {
255253
// This auth mechanism is always present.
256-
return callback(
257-
new MongoInvalidArgumentError(
258-
`No AuthProvider for ${AuthMechanism.MONGODB_SCRAM_SHA256} defined.`
259-
)
254+
throw new MongoInvalidArgumentError(
255+
`No AuthProvider for ${AuthMechanism.MONGODB_SCRAM_SHA256} defined.`
260256
);
261257
}
262-
return provider.prepare(handshakeDoc, authContext, callback);
258+
return provider.prepare(handshakeDoc, authContext);
263259
}
264260
const provider = AUTH_PROVIDERS.get(credentials.mechanism);
265261
if (!provider) {
266-
return callback(
267-
new MongoInvalidArgumentError(`No AuthProvider for ${credentials.mechanism} defined.`)
268-
);
262+
throw new MongoInvalidArgumentError(`No AuthProvider for ${credentials.mechanism} defined.`);
269263
}
270-
return provider.prepare(handshakeDoc, authContext, callback);
264+
return provider.prepare(handshakeDoc, authContext);
271265
}
272-
callback(undefined, handshakeDoc);
266+
return handshakeDoc;
273267
}
274268

275269
/** @public */

test/tools/uri_spec_runner.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ interface UriTest extends UriTestBase {
2424
};
2525
options: Record<string, any>;
2626
}
27-
2827
interface AuthTest extends UriTestBase {
2928
credential: {
3029
username: string;

test/unit/cmap/connect.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
LEGACY_HELLO_COMMAND,
1313
MongoCredentials,
1414
MongoNetworkError,
15-
prepareHandshakeDocument as prepareHandshakeDocumentCb
15+
prepareHandshakeDocument
1616
} from '../../mongodb';
1717
import { genClusterTime } from '../../tools/common';
1818
import * as mock from '../../tools/mongodb-mock/index';
@@ -206,8 +206,6 @@ describe('Connect Tests', function () {
206206
});
207207

208208
context('prepareHandshakeDocument', () => {
209-
const prepareHandshakeDocument = promisify(prepareHandshakeDocumentCb);
210-
211209
context('when serverApi.version is present', () => {
212210
const options = {};
213211
const authContext = {

0 commit comments

Comments
 (0)