mongodb 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -22
- package/lib/change_stream.js +1 -1
- package/lib/cmap/auth/auth_provider.js +21 -10
- package/lib/cmap/auth/auth_provider.js.map +1 -1
- package/lib/cmap/auth/gssapi.js +71 -116
- package/lib/cmap/auth/gssapi.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +7 -9
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongocr.js +20 -29
- package/lib/cmap/auth/mongocr.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +125 -140
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +28 -0
- package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +178 -0
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc/service_workflow.js +41 -0
- package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js +115 -0
- package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc/workflow.js +3 -0
- package/lib/cmap/auth/mongodb_oidc/workflow.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc.js +59 -0
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/auth/plain.js +4 -5
- package/lib/cmap/auth/plain.js.map +1 -1
- package/lib/cmap/auth/providers.js +1 -1
- package/lib/cmap/auth/providers.js.map +1 -1
- package/lib/cmap/auth/scram.js +45 -73
- package/lib/cmap/auth/scram.js.map +1 -1
- package/lib/cmap/auth/x509.js +8 -11
- package/lib/cmap/auth/x509.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +8 -5
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +1 -1
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connect.js +72 -86
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +19 -23
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +49 -11
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/message_stream.js.map +1 -1
- package/lib/cmap/wire_protocol/shared.js +1 -16
- package/lib/cmap/wire_protocol/shared.js.map +1 -1
- package/lib/collection.js +10 -10
- package/lib/connection_string.js +11 -17
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +1 -1
- package/lib/cursor/find_cursor.js +1 -1
- package/lib/db.js +2 -2
- package/lib/error.js +2 -1
- package/lib/error.js.map +1 -1
- package/lib/mongo_client.js +22 -2
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_logger.js +17 -1
- package/lib/mongo_logger.js.map +1 -1
- package/lib/operations/execute_operation.js +8 -27
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +1 -1
- package/lib/read_concern.js +1 -1
- package/lib/read_preference.js +2 -2
- package/lib/sdam/topology.js +0 -16
- package/lib/sdam/topology.js.map +1 -1
- package/lib/utils.js +15 -70
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js +1 -1
- package/mongodb.d.ts +103 -70
- package/package.json +29 -30
- package/src/bulk/common.ts +1 -1
- package/src/change_stream.ts +5 -5
- package/src/cmap/auth/auth_provider.ts +29 -16
- package/src/cmap/auth/gssapi.ts +102 -149
- package/src/cmap/auth/mongo_credentials.ts +14 -23
- package/src/cmap/auth/mongocr.ts +31 -36
- package/src/cmap/auth/mongodb_aws.ts +166 -189
- package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +26 -0
- package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +259 -0
- package/src/cmap/auth/mongodb_oidc/service_workflow.ts +47 -0
- package/src/cmap/auth/mongodb_oidc/token_entry_cache.ts +166 -0
- package/src/cmap/auth/mongodb_oidc/workflow.ts +21 -0
- package/src/cmap/auth/mongodb_oidc.ts +101 -17
- package/src/cmap/auth/plain.ts +6 -6
- package/src/cmap/auth/providers.ts +2 -2
- package/src/cmap/auth/scram.ts +56 -90
- package/src/cmap/auth/x509.ts +12 -18
- package/src/cmap/command_monitoring_events.ts +5 -2
- package/src/cmap/commands.ts +1 -1
- package/src/cmap/connect.ts +90 -114
- package/src/cmap/connection.ts +36 -24
- package/src/cmap/connection_pool.ts +75 -14
- package/src/cmap/message_stream.ts +0 -2
- package/src/cmap/wire_protocol/compression.ts +1 -1
- package/src/cmap/wire_protocol/shared.ts +1 -23
- package/src/collection.ts +10 -10
- package/src/connection_string.ts +12 -17
- package/src/cursor/abstract_cursor.ts +2 -2
- package/src/cursor/change_stream_cursor.ts +5 -5
- package/src/cursor/find_cursor.ts +1 -1
- package/src/db.ts +2 -2
- package/src/deps.ts +3 -2
- package/src/error.ts +3 -2
- package/src/index.ts +1 -0
- package/src/mongo_client.ts +35 -10
- package/src/mongo_logger.ts +20 -2
- package/src/mongo_types.ts +2 -2
- package/src/operations/aggregate.ts +1 -1
- package/src/operations/create_collection.ts +1 -1
- package/src/operations/execute_operation.ts +8 -25
- package/src/operations/find.ts +1 -1
- package/src/operations/find_and_modify.ts +4 -4
- package/src/operations/set_profiling_level.ts +1 -1
- package/src/operations/stats.ts +1 -1
- package/src/read_concern.ts +2 -2
- package/src/read_preference.ts +3 -3
- package/src/sdam/common.ts +2 -2
- package/src/sdam/topology.ts +0 -20
- package/src/transactions.ts +1 -1
- package/src/utils.ts +24 -98
- package/src/write_concern.ts +1 -1
package/src/cmap/auth/scram.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
|
+
import { promisify } from 'util';
|
|
2
3
|
|
|
3
4
|
import { Binary, Document } from '../../bson';
|
|
4
5
|
import { saslprep } from '../../deps';
|
|
5
6
|
import {
|
|
6
|
-
AnyError,
|
|
7
7
|
MongoInvalidArgumentError,
|
|
8
8
|
MongoMissingCredentialsError,
|
|
9
|
-
MongoRuntimeError
|
|
10
|
-
MongoServerError
|
|
9
|
+
MongoRuntimeError
|
|
11
10
|
} from '../../error';
|
|
12
|
-
import {
|
|
11
|
+
import { emitWarning, ns } from '../../utils';
|
|
13
12
|
import type { HandshakeDocument } from '../connect';
|
|
14
13
|
import { AuthContext, AuthProvider } from './auth_provider';
|
|
15
14
|
import type { MongoCredentials } from './mongo_credentials';
|
|
@@ -19,53 +18,51 @@ type CryptoMethod = 'sha1' | 'sha256';
|
|
|
19
18
|
|
|
20
19
|
class ScramSHA extends AuthProvider {
|
|
21
20
|
cryptoMethod: CryptoMethod;
|
|
21
|
+
randomBytesAsync: (size: number) => Promise<Buffer>;
|
|
22
22
|
constructor(cryptoMethod: CryptoMethod) {
|
|
23
23
|
super();
|
|
24
24
|
this.cryptoMethod = cryptoMethod || 'sha1';
|
|
25
|
+
this.randomBytesAsync = promisify(crypto.randomBytes);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
override prepare(
|
|
28
|
+
override async prepare(
|
|
29
|
+
handshakeDoc: HandshakeDocument,
|
|
30
|
+
authContext: AuthContext
|
|
31
|
+
): Promise<HandshakeDocument> {
|
|
28
32
|
const cryptoMethod = this.cryptoMethod;
|
|
29
33
|
const credentials = authContext.credentials;
|
|
30
34
|
if (!credentials) {
|
|
31
|
-
|
|
35
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
32
36
|
}
|
|
33
37
|
if (cryptoMethod === 'sha256' && saslprep == null) {
|
|
34
38
|
emitWarning('Warning: no saslprep library specified. Passwords will not be sanitized');
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// store the nonce for later use
|
|
43
|
-
Object.assign(authContext, { nonce });
|
|
41
|
+
const nonce = await this.randomBytesAsync(24);
|
|
42
|
+
// store the nonce for later use
|
|
43
|
+
authContext.nonce = nonce;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
const request = {
|
|
46
|
+
...handshakeDoc,
|
|
47
|
+
speculativeAuthenticate: {
|
|
48
|
+
...makeFirstMessage(cryptoMethod, credentials, nonce),
|
|
49
|
+
db: credentials.source
|
|
50
|
+
}
|
|
51
|
+
};
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
});
|
|
53
|
+
return request;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
override auth(authContext: AuthContext
|
|
56
|
-
const response = authContext
|
|
57
|
-
if (response &&
|
|
58
|
-
continueScramConversation(
|
|
56
|
+
override async auth(authContext: AuthContext) {
|
|
57
|
+
const { reauthenticating, response } = authContext;
|
|
58
|
+
if (response?.speculativeAuthenticate && !reauthenticating) {
|
|
59
|
+
return continueScramConversation(
|
|
59
60
|
this.cryptoMethod,
|
|
60
61
|
response.speculativeAuthenticate,
|
|
61
|
-
authContext
|
|
62
|
-
callback
|
|
62
|
+
authContext
|
|
63
63
|
);
|
|
64
|
-
|
|
65
|
-
return;
|
|
66
64
|
}
|
|
67
|
-
|
|
68
|
-
executeScram(this.cryptoMethod, authContext, callback);
|
|
65
|
+
return executeScram(this.cryptoMethod, authContext);
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
|
|
@@ -106,43 +103,34 @@ function makeFirstMessage(
|
|
|
106
103
|
};
|
|
107
104
|
}
|
|
108
105
|
|
|
109
|
-
function executeScram(cryptoMethod: CryptoMethod, authContext: AuthContext
|
|
106
|
+
async function executeScram(cryptoMethod: CryptoMethod, authContext: AuthContext): Promise<void> {
|
|
110
107
|
const { connection, credentials } = authContext;
|
|
111
108
|
if (!credentials) {
|
|
112
|
-
|
|
109
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
113
110
|
}
|
|
114
111
|
if (!authContext.nonce) {
|
|
115
|
-
|
|
116
|
-
new MongoInvalidArgumentError('AuthContext must contain a valid nonce property')
|
|
117
|
-
);
|
|
112
|
+
throw new MongoInvalidArgumentError('AuthContext must contain a valid nonce property');
|
|
118
113
|
}
|
|
119
114
|
const nonce = authContext.nonce;
|
|
120
115
|
const db = credentials.source;
|
|
121
116
|
|
|
122
117
|
const saslStartCmd = makeFirstMessage(cryptoMethod, credentials, nonce);
|
|
123
|
-
connection.
|
|
124
|
-
|
|
125
|
-
if (err) {
|
|
126
|
-
return callback(err);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
continueScramConversation(cryptoMethod, result, authContext, callback);
|
|
130
|
-
});
|
|
118
|
+
const response = await connection.commandAsync(ns(`${db}.$cmd`), saslStartCmd, undefined);
|
|
119
|
+
await continueScramConversation(cryptoMethod, response, authContext);
|
|
131
120
|
}
|
|
132
121
|
|
|
133
|
-
function continueScramConversation(
|
|
122
|
+
async function continueScramConversation(
|
|
134
123
|
cryptoMethod: CryptoMethod,
|
|
135
124
|
response: Document,
|
|
136
|
-
authContext: AuthContext
|
|
137
|
-
|
|
138
|
-
) {
|
|
125
|
+
authContext: AuthContext
|
|
126
|
+
): Promise<void> {
|
|
139
127
|
const connection = authContext.connection;
|
|
140
128
|
const credentials = authContext.credentials;
|
|
141
129
|
if (!credentials) {
|
|
142
|
-
|
|
130
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
143
131
|
}
|
|
144
132
|
if (!authContext.nonce) {
|
|
145
|
-
|
|
133
|
+
throw new MongoInvalidArgumentError('Unable to continue SCRAM without valid nonce');
|
|
146
134
|
}
|
|
147
135
|
const nonce = authContext.nonce;
|
|
148
136
|
|
|
@@ -154,11 +142,7 @@ function continueScramConversation(
|
|
|
154
142
|
if (cryptoMethod === 'sha256') {
|
|
155
143
|
processedPassword = 'kModuleError' in saslprep ? password : saslprep(password);
|
|
156
144
|
} else {
|
|
157
|
-
|
|
158
|
-
processedPassword = passwordDigest(username, password);
|
|
159
|
-
} catch (e) {
|
|
160
|
-
return callback(e);
|
|
161
|
-
}
|
|
145
|
+
processedPassword = passwordDigest(username, password);
|
|
162
146
|
}
|
|
163
147
|
|
|
164
148
|
const payload = Buffer.isBuffer(response.payload)
|
|
@@ -168,20 +152,15 @@ function continueScramConversation(
|
|
|
168
152
|
|
|
169
153
|
const iterations = parseInt(dict.i, 10);
|
|
170
154
|
if (iterations && iterations < 4096) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
new MongoRuntimeError(`Server returned an invalid iteration count ${iterations}`),
|
|
174
|
-
false
|
|
175
|
-
);
|
|
176
|
-
return;
|
|
155
|
+
// TODO(NODE-3483)
|
|
156
|
+
throw new MongoRuntimeError(`Server returned an invalid iteration count ${iterations}`);
|
|
177
157
|
}
|
|
178
158
|
|
|
179
159
|
const salt = dict.s;
|
|
180
160
|
const rnonce = dict.r;
|
|
181
161
|
if (rnonce.startsWith('nonce')) {
|
|
182
162
|
// TODO(NODE-3483)
|
|
183
|
-
|
|
184
|
-
return;
|
|
163
|
+
throw new MongoRuntimeError(`Server returned an invalid nonce: ${rnonce}`);
|
|
185
164
|
}
|
|
186
165
|
|
|
187
166
|
// Set up start of proof
|
|
@@ -211,30 +190,25 @@ function continueScramConversation(
|
|
|
211
190
|
payload: new Binary(Buffer.from(clientFinal))
|
|
212
191
|
};
|
|
213
192
|
|
|
214
|
-
connection.
|
|
215
|
-
|
|
216
|
-
if (err) {
|
|
217
|
-
return callback(err);
|
|
218
|
-
}
|
|
193
|
+
const r = await connection.commandAsync(ns(`${db}.$cmd`), saslContinueCmd, undefined);
|
|
194
|
+
const parsedResponse = parsePayload(r.payload.value());
|
|
219
195
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
196
|
+
if (!compareDigest(Buffer.from(parsedResponse.v, 'base64'), serverSignature)) {
|
|
197
|
+
throw new MongoRuntimeError('Server returned an invalid signature');
|
|
198
|
+
}
|
|
225
199
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
200
|
+
if (r.done !== false) {
|
|
201
|
+
// If the server sends r.done === true we can save one RTT
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
229
204
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
205
|
+
const retrySaslContinueCmd = {
|
|
206
|
+
saslContinue: 1,
|
|
207
|
+
conversationId: r.conversationId,
|
|
208
|
+
payload: Buffer.alloc(0)
|
|
209
|
+
};
|
|
235
210
|
|
|
236
|
-
|
|
237
|
-
});
|
|
211
|
+
await connection.commandAsync(ns(`${db}.$cmd`), retrySaslContinueCmd, undefined);
|
|
238
212
|
}
|
|
239
213
|
|
|
240
214
|
function parsePayload(payload: string) {
|
|
@@ -363,14 +337,6 @@ function compareDigest(lhs: Buffer, rhs: Uint8Array) {
|
|
|
363
337
|
return result === 0;
|
|
364
338
|
}
|
|
365
339
|
|
|
366
|
-
function resolveError(err?: AnyError, result?: Document) {
|
|
367
|
-
if (err) return err;
|
|
368
|
-
if (result) {
|
|
369
|
-
if (result.$err || result.errmsg) return new MongoServerError(result);
|
|
370
|
-
}
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
340
|
export class ScramSHA1 extends ScramSHA {
|
|
375
341
|
constructor() {
|
|
376
342
|
super('sha1');
|
package/src/cmap/auth/x509.ts
CHANGED
|
@@ -1,44 +1,38 @@
|
|
|
1
1
|
import type { Document } from '../../bson';
|
|
2
2
|
import { MongoMissingCredentialsError } from '../../error';
|
|
3
|
-
import {
|
|
3
|
+
import { ns } from '../../utils';
|
|
4
4
|
import type { HandshakeDocument } from '../connect';
|
|
5
5
|
import { AuthContext, AuthProvider } from './auth_provider';
|
|
6
6
|
import type { MongoCredentials } from './mongo_credentials';
|
|
7
7
|
|
|
8
8
|
export class X509 extends AuthProvider {
|
|
9
|
-
override prepare(
|
|
9
|
+
override async prepare(
|
|
10
10
|
handshakeDoc: HandshakeDocument,
|
|
11
|
-
authContext: AuthContext
|
|
12
|
-
|
|
13
|
-
): void {
|
|
11
|
+
authContext: AuthContext
|
|
12
|
+
): Promise<HandshakeDocument> {
|
|
14
13
|
const { credentials } = authContext;
|
|
15
14
|
if (!credentials) {
|
|
16
|
-
|
|
15
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
speculativeAuthenticate: x509AuthenticateCommand(credentials)
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
callback(undefined, handshakeDoc);
|
|
17
|
+
return { ...handshakeDoc, speculativeAuthenticate: x509AuthenticateCommand(credentials) };
|
|
23
18
|
}
|
|
24
19
|
|
|
25
|
-
override auth(authContext: AuthContext
|
|
20
|
+
override async auth(authContext: AuthContext) {
|
|
26
21
|
const connection = authContext.connection;
|
|
27
22
|
const credentials = authContext.credentials;
|
|
28
23
|
if (!credentials) {
|
|
29
|
-
|
|
24
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
30
25
|
}
|
|
31
26
|
const response = authContext.response;
|
|
32
27
|
|
|
33
|
-
if (response
|
|
34
|
-
return
|
|
28
|
+
if (response?.speculativeAuthenticate) {
|
|
29
|
+
return;
|
|
35
30
|
}
|
|
36
31
|
|
|
37
|
-
connection.
|
|
32
|
+
await connection.commandAsync(
|
|
38
33
|
ns('$external.$cmd'),
|
|
39
34
|
x509AuthenticateCommand(credentials),
|
|
40
|
-
undefined
|
|
41
|
-
callback
|
|
35
|
+
undefined
|
|
42
36
|
);
|
|
43
37
|
}
|
|
44
38
|
}
|
|
@@ -149,8 +149,11 @@ export class CommandFailedEvent {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
/**
|
|
153
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Commands that we want to redact because of the sensitive nature of their contents
|
|
154
|
+
* @internal
|
|
155
|
+
*/
|
|
156
|
+
export const SENSITIVE_COMMANDS = new Set([
|
|
154
157
|
'authenticate',
|
|
155
158
|
'saslStart',
|
|
156
159
|
'saslContinue',
|
package/src/cmap/commands.ts
CHANGED
|
@@ -374,7 +374,7 @@ export class Response {
|
|
|
374
374
|
};
|
|
375
375
|
|
|
376
376
|
// Position within OP_REPLY at which documents start
|
|
377
|
-
// (See https://
|
|
377
|
+
// (See https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/#wire-op-reply)
|
|
378
378
|
this.index = 20;
|
|
379
379
|
|
|
380
380
|
// Read the message body
|
package/src/cmap/connect.ts
CHANGED
|
@@ -15,19 +15,19 @@ import {
|
|
|
15
15
|
MongoNetworkError,
|
|
16
16
|
MongoNetworkTimeoutError,
|
|
17
17
|
MongoRuntimeError,
|
|
18
|
-
MongoServerError,
|
|
19
18
|
needsRetryableWriteLabel
|
|
20
19
|
} from '../error';
|
|
21
|
-
import { Callback, ClientMetadata, HostAddress,
|
|
20
|
+
import { Callback, ClientMetadata, HostAddress, ns } from '../utils';
|
|
22
21
|
import { AuthContext, AuthProvider } from './auth/auth_provider';
|
|
23
22
|
import { GSSAPI } from './auth/gssapi';
|
|
24
23
|
import { MongoCR } from './auth/mongocr';
|
|
25
24
|
import { MongoDBAWS } from './auth/mongodb_aws';
|
|
25
|
+
import { MongoDBOIDC } from './auth/mongodb_oidc';
|
|
26
26
|
import { Plain } from './auth/plain';
|
|
27
27
|
import { AuthMechanism } from './auth/providers';
|
|
28
28
|
import { ScramSHA1, ScramSHA256 } from './auth/scram';
|
|
29
29
|
import { X509 } from './auth/x509';
|
|
30
|
-
import { Connection, ConnectionOptions, CryptoConnection } from './connection';
|
|
30
|
+
import { CommandOptions, Connection, ConnectionOptions, CryptoConnection } from './connection';
|
|
31
31
|
import {
|
|
32
32
|
MAX_SUPPORTED_SERVER_VERSION,
|
|
33
33
|
MAX_SUPPORTED_WIRE_VERSION,
|
|
@@ -35,10 +35,12 @@ import {
|
|
|
35
35
|
MIN_SUPPORTED_WIRE_VERSION
|
|
36
36
|
} from './wire_protocol/constants';
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
/** @internal */
|
|
39
|
+
export const AUTH_PROVIDERS = new Map<AuthMechanism | string, AuthProvider>([
|
|
39
40
|
[AuthMechanism.MONGODB_AWS, new MongoDBAWS()],
|
|
40
41
|
[AuthMechanism.MONGODB_CR, new MongoCR()],
|
|
41
42
|
[AuthMechanism.MONGODB_GSSAPI, new GSSAPI()],
|
|
43
|
+
[AuthMechanism.MONGODB_OIDC, new MongoDBOIDC()],
|
|
42
44
|
[AuthMechanism.MONGODB_PLAIN, new Plain()],
|
|
43
45
|
[AuthMechanism.MONGODB_SCRAM_SHA1, new ScramSHA1()],
|
|
44
46
|
[AuthMechanism.MONGODB_SCRAM_SHA256, new ScramSHA256()],
|
|
@@ -58,7 +60,16 @@ export function connect(options: ConnectionOptions, callback: Callback<Connectio
|
|
|
58
60
|
if (options.autoEncrypter) {
|
|
59
61
|
ConnectionType = CryptoConnection;
|
|
60
62
|
}
|
|
61
|
-
|
|
63
|
+
|
|
64
|
+
const connection = new ConnectionType(socket, options);
|
|
65
|
+
|
|
66
|
+
performInitialHandshake(connection, options).then(
|
|
67
|
+
() => callback(undefined, connection),
|
|
68
|
+
error => {
|
|
69
|
+
connection.destroy({ force: false });
|
|
70
|
+
callback(error);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
62
73
|
});
|
|
63
74
|
}
|
|
64
75
|
|
|
@@ -89,119 +100,89 @@ function checkSupportedServer(hello: Document, options: ConnectionOptions) {
|
|
|
89
100
|
return new MongoCompatibilityError(message);
|
|
90
101
|
}
|
|
91
102
|
|
|
92
|
-
function performInitialHandshake(
|
|
103
|
+
async function performInitialHandshake(
|
|
93
104
|
conn: Connection,
|
|
94
|
-
options: ConnectionOptions
|
|
95
|
-
|
|
96
|
-
) {
|
|
97
|
-
const callback: Callback<Document> = function (err, ret) {
|
|
98
|
-
if (err && conn) {
|
|
99
|
-
conn.destroy({ force: false });
|
|
100
|
-
}
|
|
101
|
-
_callback(err, ret);
|
|
102
|
-
};
|
|
103
|
-
|
|
105
|
+
options: ConnectionOptions
|
|
106
|
+
): Promise<void> {
|
|
104
107
|
const credentials = options.credentials;
|
|
108
|
+
|
|
105
109
|
if (credentials) {
|
|
106
110
|
if (
|
|
107
111
|
!(credentials.mechanism === AuthMechanism.MONGODB_DEFAULT) &&
|
|
108
112
|
!AUTH_PROVIDERS.get(credentials.mechanism)
|
|
109
113
|
) {
|
|
110
|
-
|
|
111
|
-
new MongoInvalidArgumentError(`AuthMechanism '${credentials.mechanism}' not supported`)
|
|
112
|
-
);
|
|
113
|
-
return;
|
|
114
|
+
throw new MongoInvalidArgumentError(`AuthMechanism '${credentials.mechanism}' not supported`);
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
const authContext = new AuthContext(conn, credentials, options);
|
|
118
|
-
|
|
119
|
-
if (err || !handshakeDoc) {
|
|
120
|
-
return callback(err);
|
|
121
|
-
}
|
|
119
|
+
conn.authContext = authContext;
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
if (typeof options.connectTimeoutMS === 'number') {
|
|
125
|
-
// The handshake technically is a monitoring check, so its socket timeout should be connectTimeoutMS
|
|
126
|
-
handshakeOptions.socketTimeoutMS = options.connectTimeoutMS;
|
|
127
|
-
}
|
|
121
|
+
const handshakeDoc = await prepareHandshakeDocument(authContext);
|
|
128
122
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
123
|
+
// @ts-expect-error: TODO(NODE-5141): The options need to be filtered properly, Connection options differ from Command options
|
|
124
|
+
const handshakeOptions: CommandOptions = { ...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;
|
|
128
|
+
}
|
|
135
129
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
130
|
+
const start = new Date().getTime();
|
|
131
|
+
const response = await conn.commandAsync(ns('admin.$cmd'), handshakeDoc, handshakeOptions);
|
|
140
132
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
if (!('isWritablePrimary' in response)) {
|
|
134
|
+
// Provide hello-style response document.
|
|
135
|
+
response.isWritablePrimary = response[LEGACY_HELLO_COMMAND];
|
|
136
|
+
}
|
|
145
137
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
138
|
+
if (response.helloOk) {
|
|
139
|
+
conn.helloOk = true;
|
|
140
|
+
}
|
|
149
141
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
142
|
+
const supportedServerErr = checkSupportedServer(response, options);
|
|
143
|
+
if (supportedServerErr) {
|
|
144
|
+
throw supportedServerErr;
|
|
145
|
+
}
|
|
155
146
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
}
|
|
147
|
+
if (options.loadBalanced) {
|
|
148
|
+
if (!response.serviceId) {
|
|
149
|
+
throw new MongoCompatibilityError(
|
|
150
|
+
'Driver attempted to initialize in load balancing mode, ' +
|
|
151
|
+
'but the server does not support this mode.'
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
166
155
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
156
|
+
// NOTE: This is metadata attached to the connection while porting away from
|
|
157
|
+
// handshake being done in the `Server` class. Likely, it should be
|
|
158
|
+
// relocated, or at very least restructured.
|
|
159
|
+
conn.hello = response;
|
|
160
|
+
conn.lastHelloMS = new Date().getTime() - start;
|
|
161
|
+
|
|
162
|
+
if (!response.arbiterOnly && credentials) {
|
|
163
|
+
// store the response on auth context
|
|
164
|
+
authContext.response = response;
|
|
165
|
+
|
|
166
|
+
const resolvedCredentials = credentials.resolveAuthMechanism(response);
|
|
167
|
+
const provider = AUTH_PROVIDERS.get(resolvedCredentials.mechanism);
|
|
168
|
+
if (!provider) {
|
|
169
|
+
throw new MongoInvalidArgumentError(
|
|
170
|
+
`No AuthProvider for ${resolvedCredentials.mechanism} defined.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
await provider.auth(authContext);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (error instanceof MongoError) {
|
|
178
|
+
error.addErrorLabel(MongoErrorLabel.HandshakeError);
|
|
179
|
+
if (needsRetryableWriteLabel(error, response.maxWireVersion)) {
|
|
180
|
+
error.addErrorLabel(MongoErrorLabel.RetryableWriteError);
|
|
185
181
|
}
|
|
186
|
-
provider.auth(authContext, err => {
|
|
187
|
-
if (err) {
|
|
188
|
-
if (err instanceof MongoError) {
|
|
189
|
-
err.addErrorLabel(MongoErrorLabel.HandshakeError);
|
|
190
|
-
if (needsRetryableWriteLabel(err, response.maxWireVersion)) {
|
|
191
|
-
err.addErrorLabel(MongoErrorLabel.RetryableWriteError);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return callback(err);
|
|
195
|
-
}
|
|
196
|
-
callback(undefined, conn);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
return;
|
|
200
182
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
});
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
205
186
|
}
|
|
206
187
|
|
|
207
188
|
export interface HandshakeDocument extends Document {
|
|
@@ -222,10 +203,9 @@ export interface HandshakeDocument extends Document {
|
|
|
222
203
|
*
|
|
223
204
|
* This function is only exposed for testing purposes.
|
|
224
205
|
*/
|
|
225
|
-
export function prepareHandshakeDocument(
|
|
226
|
-
authContext: AuthContext
|
|
227
|
-
|
|
228
|
-
) {
|
|
206
|
+
export async function prepareHandshakeDocument(
|
|
207
|
+
authContext: AuthContext
|
|
208
|
+
): Promise<HandshakeDocument> {
|
|
229
209
|
const options = authContext.options;
|
|
230
210
|
const compressors = options.compressors ? options.compressors : [];
|
|
231
211
|
const { serverApi } = authContext.connection;
|
|
@@ -233,7 +213,7 @@ export function prepareHandshakeDocument(
|
|
|
233
213
|
const handshakeDoc: HandshakeDocument = {
|
|
234
214
|
[serverApi?.version ? 'hello' : LEGACY_HELLO_COMMAND]: 1,
|
|
235
215
|
helloOk: true,
|
|
236
|
-
client: options.metadata
|
|
216
|
+
client: options.metadata,
|
|
237
217
|
compression: compressors
|
|
238
218
|
};
|
|
239
219
|
|
|
@@ -249,23 +229,19 @@ export function prepareHandshakeDocument(
|
|
|
249
229
|
const provider = AUTH_PROVIDERS.get(AuthMechanism.MONGODB_SCRAM_SHA256);
|
|
250
230
|
if (!provider) {
|
|
251
231
|
// This auth mechanism is always present.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
`No AuthProvider for ${AuthMechanism.MONGODB_SCRAM_SHA256} defined.`
|
|
255
|
-
)
|
|
232
|
+
throw new MongoInvalidArgumentError(
|
|
233
|
+
`No AuthProvider for ${AuthMechanism.MONGODB_SCRAM_SHA256} defined.`
|
|
256
234
|
);
|
|
257
235
|
}
|
|
258
|
-
return provider.prepare(handshakeDoc, authContext
|
|
236
|
+
return provider.prepare(handshakeDoc, authContext);
|
|
259
237
|
}
|
|
260
238
|
const provider = AUTH_PROVIDERS.get(credentials.mechanism);
|
|
261
239
|
if (!provider) {
|
|
262
|
-
|
|
263
|
-
new MongoInvalidArgumentError(`No AuthProvider for ${credentials.mechanism} defined.`)
|
|
264
|
-
);
|
|
240
|
+
throw new MongoInvalidArgumentError(`No AuthProvider for ${credentials.mechanism} defined.`);
|
|
265
241
|
}
|
|
266
|
-
return provider.prepare(handshakeDoc, authContext
|
|
242
|
+
return provider.prepare(handshakeDoc, authContext);
|
|
267
243
|
}
|
|
268
|
-
|
|
244
|
+
return handshakeDoc;
|
|
269
245
|
}
|
|
270
246
|
|
|
271
247
|
/** @public */
|
|
@@ -347,7 +323,7 @@ function parseSslOptions(options: MakeConnectionOptions): TLSConnectionOpts {
|
|
|
347
323
|
}
|
|
348
324
|
|
|
349
325
|
const SOCKET_ERROR_EVENT_LIST = ['error', 'close', 'timeout', 'parseError'] as const;
|
|
350
|
-
type ErrorHandlerEventName = typeof SOCKET_ERROR_EVENT_LIST[number] | 'cancel';
|
|
326
|
+
type ErrorHandlerEventName = (typeof SOCKET_ERROR_EVENT_LIST)[number] | 'cancel';
|
|
351
327
|
const SOCKET_ERROR_EVENTS = new Set(SOCKET_ERROR_EVENT_LIST);
|
|
352
328
|
|
|
353
329
|
function makeConnection(options: MakeConnectionOptions, _callback: Callback<Stream>) {
|
|
@@ -428,7 +404,7 @@ function makeConnection(options: MakeConnectionOptions, _callback: Callback<Stre
|
|
|
428
404
|
}
|
|
429
405
|
}
|
|
430
406
|
|
|
431
|
-
socket.setTimeout(
|
|
407
|
+
socket.setTimeout(0);
|
|
432
408
|
callback(undefined, socket);
|
|
433
409
|
}
|
|
434
410
|
|