mongodb 5.0.1 → 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/bson.js +3 -1
- package/lib/bson.js.map +1 -1
- package/lib/change_stream.js +3 -2
- package/lib/change_stream.js.map +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 +17 -0
- 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 +126 -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 +62 -0
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -0
- package/lib/cmap/auth/plain.js +4 -5
- package/lib/cmap/auth/plain.js.map +1 -1
- package/lib/cmap/auth/providers.js +4 -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 +9 -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 +68 -74
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +51 -13
- 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 +47 -33
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +13 -7
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +1 -1
- package/lib/db.js +3 -2
- package/lib/db.js.map +1 -1
- 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/aggregate.js +4 -1
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/create_collection.js +1 -0
- package/lib/operations/create_collection.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 +3 -2
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/indexes.js +2 -1
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/list_collections.js +2 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/read_concern.js +1 -1
- package/lib/read_preference.js +2 -2
- package/lib/sdam/monitor.js +1 -0
- package/lib/sdam/monitor.js.map +1 -1
- package/lib/sdam/server.js +4 -2
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/topology.js +3 -26
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +2 -1
- package/lib/sessions.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 +137 -68
- package/package.json +30 -30
- package/src/bson.ts +3 -1
- package/src/bulk/common.ts +1 -1
- package/src/change_stream.ts +16 -8
- package/src/cmap/auth/auth_provider.ts +29 -16
- package/src/cmap/auth/gssapi.ts +102 -149
- package/src/cmap/auth/mongo_credentials.ts +47 -1
- package/src/cmap/auth/mongocr.ts +31 -36
- package/src/cmap/auth/mongodb_aws.ts +167 -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 +123 -0
- package/src/cmap/auth/plain.ts +6 -6
- package/src/cmap/auth/providers.ts +5 -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 +11 -1
- package/src/cmap/connect.ts +90 -114
- package/src/cmap/connection.ts +92 -90
- package/src/cmap/connection_pool.ts +77 -16
- 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 +11 -11
- package/src/connection_string.ts +52 -35
- package/src/cursor/abstract_cursor.ts +13 -6
- package/src/cursor/change_stream_cursor.ts +5 -5
- package/src/cursor/find_cursor.ts +1 -1
- package/src/db.ts +3 -2
- package/src/deps.ts +56 -38
- package/src/error.ts +3 -2
- package/src/index.ts +7 -0
- package/src/mongo_client.ts +35 -10
- package/src/mongo_logger.ts +20 -2
- package/src/mongo_types.ts +4 -3
- package/src/operations/aggregate.ts +4 -2
- package/src/operations/create_collection.ts +2 -1
- package/src/operations/execute_operation.ts +8 -25
- package/src/operations/find.ts +13 -4
- package/src/operations/find_and_modify.ts +4 -4
- package/src/operations/indexes.ts +12 -4
- package/src/operations/list_collections.ts +11 -3
- 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/monitor.ts +1 -0
- package/src/sdam/server.ts +4 -1
- package/src/sdam/topology.ts +4 -33
- package/src/sessions.ts +2 -1
- package/src/transactions.ts +1 -1
- package/src/utils.ts +24 -98
- package/src/write_concern.ts +1 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Resolves the default auth mechanism according to
|
|
2
2
|
import type { Document } from '../../bson';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
MongoAPIError,
|
|
5
|
+
MongoInvalidArgumentError,
|
|
6
|
+
MongoMissingCredentialsError
|
|
7
|
+
} from '../../error';
|
|
4
8
|
import { GSSAPICanonicalizationValue } from './gssapi';
|
|
9
|
+
import type { OIDCRefreshFunction, OIDCRequestFunction } from './mongodb_oidc';
|
|
5
10
|
import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './providers';
|
|
6
11
|
|
|
7
12
|
// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
|
|
@@ -32,6 +37,12 @@ export interface AuthMechanismProperties extends Document {
|
|
|
32
37
|
SERVICE_REALM?: string;
|
|
33
38
|
CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue;
|
|
34
39
|
AWS_SESSION_TOKEN?: string;
|
|
40
|
+
/** @experimental */
|
|
41
|
+
REQUEST_TOKEN_CALLBACK?: OIDCRequestFunction;
|
|
42
|
+
/** @experimental */
|
|
43
|
+
REFRESH_TOKEN_CALLBACK?: OIDCRefreshFunction;
|
|
44
|
+
/** @experimental */
|
|
45
|
+
PROVIDER_NAME?: 'aws';
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
/** @public */
|
|
@@ -137,6 +148,41 @@ export class MongoCredentials {
|
|
|
137
148
|
throw new MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`);
|
|
138
149
|
}
|
|
139
150
|
|
|
151
|
+
if (this.mechanism === AuthMechanism.MONGODB_OIDC) {
|
|
152
|
+
if (this.username && this.mechanismProperties.PROVIDER_NAME) {
|
|
153
|
+
throw new MongoInvalidArgumentError(
|
|
154
|
+
`username and PROVIDER_NAME may not be used together for mechanism '${this.mechanism}'.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
this.mechanismProperties.PROVIDER_NAME &&
|
|
160
|
+
this.mechanismProperties.PROVIDER_NAME !== 'aws'
|
|
161
|
+
) {
|
|
162
|
+
throw new MongoInvalidArgumentError(
|
|
163
|
+
`Currently only a PROVIDER_NAME of 'aws' is supported for mechanism '${this.mechanism}'.`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (
|
|
168
|
+
this.mechanismProperties.REFRESH_TOKEN_CALLBACK &&
|
|
169
|
+
!this.mechanismProperties.REQUEST_TOKEN_CALLBACK
|
|
170
|
+
) {
|
|
171
|
+
throw new MongoInvalidArgumentError(
|
|
172
|
+
`A REQUEST_TOKEN_CALLBACK must be provided when using a REFRESH_TOKEN_CALLBACK for mechanism '${this.mechanism}'`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
!this.mechanismProperties.PROVIDER_NAME &&
|
|
178
|
+
!this.mechanismProperties.REQUEST_TOKEN_CALLBACK
|
|
179
|
+
) {
|
|
180
|
+
throw new MongoInvalidArgumentError(
|
|
181
|
+
`Either a PROVIDER_NAME or a REQUEST_TOKEN_CALLBACK must be specified for mechanism '${this.mechanism}'.`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
140
186
|
if (AUTH_MECHS_AUTH_SRC_EXTERNAL.has(this.mechanism)) {
|
|
141
187
|
if (this.source != null && this.source !== '$external') {
|
|
142
188
|
// TODO(NODE-3485): Replace this with a MongoAuthValidationError
|
package/src/cmap/auth/mongocr.ts
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
2
|
|
|
3
3
|
import { MongoMissingCredentialsError } from '../../error';
|
|
4
|
-
import {
|
|
4
|
+
import { ns } from '../../utils';
|
|
5
5
|
import { AuthContext, AuthProvider } from './auth_provider';
|
|
6
6
|
|
|
7
7
|
export class MongoCR extends AuthProvider {
|
|
8
|
-
override auth(authContext: AuthContext
|
|
8
|
+
override async auth(authContext: AuthContext): Promise<void> {
|
|
9
9
|
const { connection, credentials } = authContext;
|
|
10
10
|
if (!credentials) {
|
|
11
|
-
|
|
11
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
const password = credentials
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
key
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
connection.command(ns(`${source}.$cmd`), authenticateCommand, undefined, callback);
|
|
45
|
-
});
|
|
13
|
+
|
|
14
|
+
const { username, password, source } = credentials;
|
|
15
|
+
|
|
16
|
+
const { nonce } = await connection.commandAsync(
|
|
17
|
+
ns(`${source}.$cmd`),
|
|
18
|
+
{ getnonce: 1 },
|
|
19
|
+
undefined
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const hashPassword = crypto
|
|
23
|
+
.createHash('md5')
|
|
24
|
+
.update(`${username}:mongo:${password}`, 'utf8')
|
|
25
|
+
.digest('hex');
|
|
26
|
+
|
|
27
|
+
// Final key
|
|
28
|
+
const key = crypto
|
|
29
|
+
.createHash('md5')
|
|
30
|
+
.update(`${nonce}${username}${hashPassword}`, 'utf8')
|
|
31
|
+
.digest('hex');
|
|
32
|
+
|
|
33
|
+
const authenticateCommand = {
|
|
34
|
+
authenticate: 1,
|
|
35
|
+
user: username,
|
|
36
|
+
nonce,
|
|
37
|
+
key
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
await connection.commandAsync(ns(`${source}.$cmd`), authenticateCommand, undefined);
|
|
46
41
|
}
|
|
47
42
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
2
|
import * as http from 'http';
|
|
3
3
|
import * as url from 'url';
|
|
4
|
+
import { promisify } from 'util';
|
|
4
5
|
|
|
5
6
|
import type { Binary, BSONSerializeOptions } from '../../bson';
|
|
6
7
|
import * as BSON from '../../bson';
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
MongoMissingCredentialsError,
|
|
12
13
|
MongoRuntimeError
|
|
13
14
|
} from '../../error';
|
|
14
|
-
import { ByteUtils,
|
|
15
|
+
import { ByteUtils, maxWireVersion, ns } from '../../utils';
|
|
15
16
|
import { AuthContext, AuthProvider } from './auth_provider';
|
|
16
17
|
import { MongoCredentials } from './mongo_credentials';
|
|
17
18
|
import { AuthMechanism } from './providers';
|
|
@@ -21,6 +22,7 @@ const AWS_RELATIVE_URI = 'http://169.254.170.2';
|
|
|
21
22
|
const AWS_EC2_URI = 'http://169.254.169.254';
|
|
22
23
|
const AWS_EC2_PATH = '/latest/meta-data/iam/security-credentials';
|
|
23
24
|
const bsonOptions: BSONSerializeOptions = {
|
|
25
|
+
useBigInt64: false,
|
|
24
26
|
promoteLongs: true,
|
|
25
27
|
promoteValues: true,
|
|
26
28
|
promoteBuffers: false,
|
|
@@ -34,37 +36,36 @@ interface AWSSaslContinuePayload {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export class MongoDBAWS extends AuthProvider {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
randomBytesAsync: (size: number) => Promise<Buffer>;
|
|
40
|
+
|
|
41
|
+
constructor() {
|
|
42
|
+
super();
|
|
43
|
+
this.randomBytesAsync = promisify(crypto.randomBytes);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override async auth(authContext: AuthContext): Promise<void> {
|
|
47
|
+
const { connection } = authContext;
|
|
48
|
+
if (!authContext.credentials) {
|
|
49
|
+
throw new MongoMissingCredentialsError('AuthContext must provide credentials.');
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
if ('kModuleError' in aws4) {
|
|
44
|
-
|
|
53
|
+
throw aws4['kModuleError'];
|
|
45
54
|
}
|
|
46
55
|
const { sign } = aws4;
|
|
47
56
|
|
|
48
57
|
if (maxWireVersion(connection) < 9) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'MONGODB-AWS authentication requires MongoDB version 4.4 or later'
|
|
52
|
-
)
|
|
58
|
+
throw new MongoCompatibilityError(
|
|
59
|
+
'MONGODB-AWS authentication requires MongoDB version 4.4 or later'
|
|
53
60
|
);
|
|
54
|
-
return;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
if (!credentials.username) {
|
|
58
|
-
makeTempCredentials(credentials
|
|
59
|
-
if (err || !tempCredentials) return callback(err);
|
|
60
|
-
|
|
61
|
-
authContext.credentials = tempCredentials;
|
|
62
|
-
this.auth(authContext, callback);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return;
|
|
63
|
+
if (!authContext.credentials.username) {
|
|
64
|
+
authContext.credentials = await makeTempCredentials(authContext.credentials);
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
const { credentials } = authContext;
|
|
68
|
+
|
|
68
69
|
const accessKeyId = credentials.username;
|
|
69
70
|
const secretAccessKey = credentials.password;
|
|
70
71
|
const sessionToken = credentials.mechanismProperties.AWS_SESSION_TOKEN;
|
|
@@ -78,87 +79,75 @@ export class MongoDBAWS extends AuthProvider {
|
|
|
78
79
|
: undefined;
|
|
79
80
|
|
|
80
81
|
const db = credentials.source;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
const nonce = await this.randomBytesAsync(32);
|
|
83
|
+
|
|
84
|
+
const saslStart = {
|
|
85
|
+
saslStart: 1,
|
|
86
|
+
mechanism: 'MONGODB-AWS',
|
|
87
|
+
payload: BSON.serialize({ r: nonce, p: ASCII_N }, bsonOptions)
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const saslStartResponse = await connection.commandAsync(ns(`${db}.$cmd`), saslStart, undefined);
|
|
91
|
+
|
|
92
|
+
const serverResponse = BSON.deserialize(saslStartResponse.payload.buffer, bsonOptions) as {
|
|
93
|
+
s: Binary;
|
|
94
|
+
h: string;
|
|
95
|
+
};
|
|
96
|
+
const host = serverResponse.h;
|
|
97
|
+
const serverNonce = serverResponse.s.buffer;
|
|
98
|
+
if (serverNonce.length !== 64) {
|
|
99
|
+
// TODO(NODE-3483)
|
|
100
|
+
throw new MongoRuntimeError(`Invalid server nonce length ${serverNonce.length}, expected 64`);
|
|
101
|
+
}
|
|
86
102
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
payload: BSON.serialize({ r: nonce, p: ASCII_N }, bsonOptions)
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
connection.command(ns(`${db}.$cmd`), saslStart, undefined, (err, res) => {
|
|
94
|
-
if (err) return callback(err);
|
|
95
|
-
|
|
96
|
-
const serverResponse = BSON.deserialize(res.payload.buffer, bsonOptions) as {
|
|
97
|
-
s: Binary;
|
|
98
|
-
h: string;
|
|
99
|
-
};
|
|
100
|
-
const host = serverResponse.h;
|
|
101
|
-
const serverNonce = serverResponse.s.buffer;
|
|
102
|
-
if (serverNonce.length !== 64) {
|
|
103
|
-
callback(
|
|
104
|
-
// TODO(NODE-3483)
|
|
105
|
-
new MongoRuntimeError(`Invalid server nonce length ${serverNonce.length}, expected 64`)
|
|
106
|
-
);
|
|
103
|
+
if (!ByteUtils.equals(serverNonce.subarray(0, nonce.byteLength), nonce)) {
|
|
104
|
+
// throw because the serverNonce's leading 32 bytes must equal the client nonce's 32 bytes
|
|
105
|
+
// https://github.com/mongodb/specifications/blob/875446db44aade414011731840831f38a6c668df/source/auth/auth.rst#id11
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
// TODO(NODE-3483)
|
|
108
|
+
throw new MongoRuntimeError('Server nonce does not begin with client nonce');
|
|
109
|
+
}
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
if (host.length < 1 || host.length > 255 || host.indexOf('..') !== -1) {
|
|
112
|
+
// TODO(NODE-3483)
|
|
113
|
+
throw new MongoRuntimeError(`Server returned an invalid host: "${host}"`);
|
|
114
|
+
}
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
const body = 'Action=GetCallerIdentity&Version=2011-06-15';
|
|
117
|
+
const options = sign(
|
|
118
|
+
{
|
|
119
|
+
method: 'POST',
|
|
120
|
+
host,
|
|
121
|
+
region: deriveRegion(serverResponse.h),
|
|
122
|
+
service: 'sts',
|
|
123
|
+
headers: {
|
|
124
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
125
|
+
'Content-Length': body.length,
|
|
126
|
+
'X-MongoDB-Server-Nonce': ByteUtils.toBase64(serverNonce),
|
|
127
|
+
'X-MongoDB-GS2-CB-Flag': 'n'
|
|
128
|
+
},
|
|
129
|
+
path: '/',
|
|
130
|
+
body
|
|
131
|
+
},
|
|
132
|
+
awsCredentials
|
|
133
|
+
);
|
|
119
134
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
135
|
+
const payload: AWSSaslContinuePayload = {
|
|
136
|
+
a: options.headers.Authorization,
|
|
137
|
+
d: options.headers['X-Amz-Date']
|
|
138
|
+
};
|
|
125
139
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
method: 'POST',
|
|
130
|
-
host,
|
|
131
|
-
region: deriveRegion(serverResponse.h),
|
|
132
|
-
service: 'sts',
|
|
133
|
-
headers: {
|
|
134
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
135
|
-
'Content-Length': body.length,
|
|
136
|
-
'X-MongoDB-Server-Nonce': ByteUtils.toBase64(serverNonce),
|
|
137
|
-
'X-MongoDB-GS2-CB-Flag': 'n'
|
|
138
|
-
},
|
|
139
|
-
path: '/',
|
|
140
|
-
body
|
|
141
|
-
},
|
|
142
|
-
awsCredentials
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
const payload: AWSSaslContinuePayload = {
|
|
146
|
-
a: options.headers.Authorization,
|
|
147
|
-
d: options.headers['X-Amz-Date']
|
|
148
|
-
};
|
|
149
|
-
if (sessionToken) {
|
|
150
|
-
payload.t = sessionToken;
|
|
151
|
-
}
|
|
140
|
+
if (sessionToken) {
|
|
141
|
+
payload.t = sessionToken;
|
|
142
|
+
}
|
|
152
143
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
144
|
+
const saslContinue = {
|
|
145
|
+
saslContinue: 1,
|
|
146
|
+
conversationId: 1,
|
|
147
|
+
payload: BSON.serialize(payload, bsonOptions)
|
|
148
|
+
};
|
|
158
149
|
|
|
159
|
-
|
|
160
|
-
});
|
|
161
|
-
});
|
|
150
|
+
await connection.commandAsync(ns(`${db}.$cmd`), saslContinue, undefined);
|
|
162
151
|
}
|
|
163
152
|
}
|
|
164
153
|
|
|
@@ -178,27 +167,21 @@ export interface AWSCredentials {
|
|
|
178
167
|
expiration?: Date;
|
|
179
168
|
}
|
|
180
169
|
|
|
181
|
-
function makeTempCredentials(credentials: MongoCredentials
|
|
182
|
-
function
|
|
170
|
+
async function makeTempCredentials(credentials: MongoCredentials): Promise<MongoCredentials> {
|
|
171
|
+
function makeMongoCredentialsFromAWSTemp(creds: AWSTempCredentials) {
|
|
183
172
|
if (!creds.AccessKeyId || !creds.SecretAccessKey || !creds.Token) {
|
|
184
|
-
|
|
185
|
-
new MongoMissingCredentialsError('Could not obtain temporary MONGODB-AWS credentials')
|
|
186
|
-
);
|
|
187
|
-
return;
|
|
173
|
+
throw new MongoMissingCredentialsError('Could not obtain temporary MONGODB-AWS credentials');
|
|
188
174
|
}
|
|
189
175
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
);
|
|
176
|
+
return new MongoCredentials({
|
|
177
|
+
username: creds.AccessKeyId,
|
|
178
|
+
password: creds.SecretAccessKey,
|
|
179
|
+
source: credentials.source,
|
|
180
|
+
mechanism: AuthMechanism.MONGODB_AWS,
|
|
181
|
+
mechanismProperties: {
|
|
182
|
+
AWS_SESSION_TOKEN: creds.Token
|
|
183
|
+
}
|
|
184
|
+
});
|
|
202
185
|
}
|
|
203
186
|
|
|
204
187
|
const credentialProvider = getAwsCredentialProvider();
|
|
@@ -209,47 +192,32 @@ function makeTempCredentials(credentials: MongoCredentials, callback: Callback<M
|
|
|
209
192
|
// If the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
|
|
210
193
|
// is set then drivers MUST assume that it was set by an AWS ECS agent
|
|
211
194
|
if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
|
|
212
|
-
|
|
213
|
-
`${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}
|
|
214
|
-
undefined,
|
|
215
|
-
(err, res) => {
|
|
216
|
-
if (err) return callback(err);
|
|
217
|
-
done(res);
|
|
218
|
-
}
|
|
195
|
+
return makeMongoCredentialsFromAWSTemp(
|
|
196
|
+
await request(`${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`)
|
|
219
197
|
);
|
|
220
|
-
|
|
221
|
-
return;
|
|
222
198
|
}
|
|
223
199
|
|
|
224
200
|
// Otherwise assume we are on an EC2 instance
|
|
225
201
|
|
|
226
202
|
// get a token
|
|
227
|
-
request(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (err) return callback(err);
|
|
246
|
-
done(creds);
|
|
247
|
-
}
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
);
|
|
203
|
+
const token = await request(`${AWS_EC2_URI}/latest/api/token`, {
|
|
204
|
+
method: 'PUT',
|
|
205
|
+
json: false,
|
|
206
|
+
headers: { 'X-aws-ec2-metadata-token-ttl-seconds': 30 }
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// get role name
|
|
210
|
+
const roleName = await request(`${AWS_EC2_URI}/${AWS_EC2_PATH}`, {
|
|
211
|
+
json: false,
|
|
212
|
+
headers: { 'X-aws-ec2-metadata-token': token }
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// get temp credentials
|
|
216
|
+
const creds = await request(`${AWS_EC2_URI}/${AWS_EC2_PATH}/${roleName}`, {
|
|
217
|
+
headers: { 'X-aws-ec2-metadata-token': token }
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return makeMongoCredentialsFromAWSTemp(creds);
|
|
253
221
|
} else {
|
|
254
222
|
/*
|
|
255
223
|
* Creates a credential provider that will attempt to find credentials from the
|
|
@@ -263,18 +231,17 @@ function makeTempCredentials(credentials: MongoCredentials, callback: Callback<M
|
|
|
263
231
|
*/
|
|
264
232
|
const { fromNodeProviderChain } = credentialProvider;
|
|
265
233
|
const provider = fromNodeProviderChain();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
});
|
|
274
|
-
})
|
|
275
|
-
.catch((error: Error) => {
|
|
276
|
-
callback(new MongoAWSError(error.message));
|
|
234
|
+
try {
|
|
235
|
+
const creds = await provider();
|
|
236
|
+
return makeMongoCredentialsFromAWSTemp({
|
|
237
|
+
AccessKeyId: creds.accessKeyId,
|
|
238
|
+
SecretAccessKey: creds.secretAccessKey,
|
|
239
|
+
Token: creds.sessionToken,
|
|
240
|
+
Expiration: creds.expiration
|
|
277
241
|
});
|
|
242
|
+
} catch (error) {
|
|
243
|
+
throw new MongoAWSError(error.message);
|
|
244
|
+
}
|
|
278
245
|
}
|
|
279
246
|
}
|
|
280
247
|
|
|
@@ -294,42 +261,53 @@ interface RequestOptions {
|
|
|
294
261
|
headers?: http.OutgoingHttpHeaders;
|
|
295
262
|
}
|
|
296
263
|
|
|
297
|
-
function request(uri: string
|
|
298
|
-
|
|
299
|
-
|
|
264
|
+
async function request(uri: string): Promise<Record<string, any>>;
|
|
265
|
+
async function request(
|
|
266
|
+
uri: string,
|
|
267
|
+
options?: { json?: true } & RequestOptions
|
|
268
|
+
): Promise<Record<string, any>>;
|
|
269
|
+
async function request(uri: string, options?: { json: false } & RequestOptions): Promise<string>;
|
|
270
|
+
async function request(
|
|
271
|
+
uri: string,
|
|
272
|
+
options: RequestOptions = {}
|
|
273
|
+
): Promise<string | Record<string, any>> {
|
|
274
|
+
return new Promise<string | Record<string, any>>((resolve, reject) => {
|
|
275
|
+
const requestOptions = {
|
|
300
276
|
method: 'GET',
|
|
301
277
|
timeout: 10000,
|
|
302
|
-
json: true
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
const req = http.request(options, res => {
|
|
309
|
-
res.setEncoding('utf8');
|
|
310
|
-
|
|
311
|
-
let data = '';
|
|
312
|
-
res.on('data', d => (data += d));
|
|
313
|
-
res.on('end', () => {
|
|
314
|
-
if (options.json === false) {
|
|
315
|
-
callback(undefined, data);
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
278
|
+
json: true,
|
|
279
|
+
...url.parse(uri),
|
|
280
|
+
...options
|
|
281
|
+
};
|
|
318
282
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
283
|
+
const req = http.request(requestOptions, res => {
|
|
284
|
+
res.setEncoding('utf8');
|
|
285
|
+
|
|
286
|
+
let data = '';
|
|
287
|
+
res.on('data', d => {
|
|
288
|
+
data += d;
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
res.once('end', () => {
|
|
292
|
+
if (options.json === false) {
|
|
293
|
+
resolve(data);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const parsed = JSON.parse(data);
|
|
299
|
+
resolve(parsed);
|
|
300
|
+
} catch {
|
|
301
|
+
// TODO(NODE-3483)
|
|
302
|
+
reject(new MongoRuntimeError(`Invalid JSON response: "${data}"`));
|
|
303
|
+
}
|
|
304
|
+
});
|
|
326
305
|
});
|
|
327
|
-
});
|
|
328
306
|
|
|
329
|
-
|
|
330
|
-
|
|
307
|
+
req.once('timeout', () =>
|
|
308
|
+
req.destroy(new MongoAWSError(`AWS request to ${uri} timed out after ${options.timeout} ms`))
|
|
309
|
+
);
|
|
310
|
+
req.once('error', error => reject(error));
|
|
311
|
+
req.end();
|
|
331
312
|
});
|
|
332
|
-
|
|
333
|
-
req.on('error', err => callback(err));
|
|
334
|
-
req.end();
|
|
335
313
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
import { MongoAWSError } from '../../../error';
|
|
4
|
+
import { ServiceWorkflow } from './service_workflow';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Device workflow implementation for AWS.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export class AwsServiceWorkflow extends ServiceWorkflow {
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the token from the environment.
|
|
18
|
+
*/
|
|
19
|
+
async getToken(): Promise<string> {
|
|
20
|
+
const tokenFile = process.env.AWS_WEB_IDENTITY_TOKEN_FILE;
|
|
21
|
+
if (!tokenFile) {
|
|
22
|
+
throw new MongoAWSError('AWS_WEB_IDENTITY_TOKEN_FILE must be set in the environment.');
|
|
23
|
+
}
|
|
24
|
+
return readFile(tokenFile, 'utf8');
|
|
25
|
+
}
|
|
26
|
+
}
|