mongodb 3.2.5 → 3.3.0-beta2
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/HISTORY.md +0 -10
- package/index.js +4 -4
- package/lib/admin.js +56 -56
- package/lib/aggregation_cursor.js +7 -3
- package/lib/bulk/common.js +18 -13
- package/lib/change_stream.js +196 -89
- package/lib/collection.js +217 -169
- package/lib/command_cursor.js +17 -7
- package/lib/core/auth/auth_provider.js +158 -0
- package/lib/core/auth/defaultAuthProviders.js +29 -0
- package/lib/core/auth/gssapi.js +241 -0
- package/lib/core/auth/mongo_credentials.js +81 -0
- package/lib/core/auth/mongocr.js +51 -0
- package/lib/core/auth/plain.js +35 -0
- package/lib/core/auth/scram.js +293 -0
- package/lib/core/auth/sspi.js +131 -0
- package/lib/core/auth/x509.js +26 -0
- package/lib/core/connection/apm.js +236 -0
- package/lib/core/connection/command_result.js +36 -0
- package/lib/core/connection/commands.js +507 -0
- package/lib/core/connection/connect.js +370 -0
- package/lib/core/connection/connection.js +624 -0
- package/lib/core/connection/logger.js +246 -0
- package/lib/core/connection/msg.js +219 -0
- package/lib/core/connection/pool.js +1285 -0
- package/lib/core/connection/utils.js +57 -0
- package/lib/core/cursor.js +752 -0
- package/lib/core/error.js +186 -0
- package/lib/core/index.js +50 -0
- package/lib/core/sdam/monitoring.js +228 -0
- package/lib/core/sdam/server.js +467 -0
- package/lib/core/sdam/server_description.js +163 -0
- package/lib/core/sdam/server_selectors.js +244 -0
- package/lib/core/sdam/srv_polling.js +135 -0
- package/lib/core/sdam/topology.js +1151 -0
- package/lib/core/sdam/topology_description.js +408 -0
- package/lib/core/sessions.js +711 -0
- package/lib/core/tools/smoke_plugin.js +61 -0
- package/lib/core/topologies/mongos.js +1337 -0
- package/lib/core/topologies/read_preference.js +202 -0
- package/lib/core/topologies/replset.js +1507 -0
- package/lib/core/topologies/replset_state.js +1121 -0
- package/lib/core/topologies/server.js +984 -0
- package/lib/core/topologies/shared.js +453 -0
- package/lib/core/transactions.js +167 -0
- package/lib/core/uri_parser.js +631 -0
- package/lib/core/utils.js +165 -0
- package/lib/core/wireprotocol/command.js +170 -0
- package/lib/core/wireprotocol/compression.js +73 -0
- package/lib/core/wireprotocol/constants.js +13 -0
- package/lib/core/wireprotocol/get_more.js +86 -0
- package/lib/core/wireprotocol/index.js +18 -0
- package/lib/core/wireprotocol/kill_cursors.js +70 -0
- package/lib/core/wireprotocol/query.js +224 -0
- package/lib/core/wireprotocol/shared.js +115 -0
- package/lib/core/wireprotocol/write_command.js +50 -0
- package/lib/cursor.js +40 -46
- package/lib/db.js +141 -95
- package/lib/dynamic_loaders.js +32 -0
- package/lib/error.js +12 -10
- package/lib/gridfs/chunk.js +2 -2
- package/lib/gridfs/grid_store.js +31 -25
- package/lib/gridfs-stream/index.js +4 -4
- package/lib/gridfs-stream/upload.js +1 -1
- package/lib/mongo_client.js +37 -15
- package/lib/operations/add_user.js +96 -0
- package/lib/operations/aggregate.js +24 -13
- package/lib/operations/aggregate_operation.js +127 -0
- package/lib/operations/bulk_write.js +104 -0
- package/lib/operations/close.js +47 -0
- package/lib/operations/collection_ops.js +28 -287
- package/lib/operations/collections.js +55 -0
- package/lib/operations/command.js +120 -0
- package/lib/operations/command_v2.js +43 -0
- package/lib/operations/common_functions.js +372 -0
- package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
- package/lib/operations/count.js +72 -0
- package/lib/operations/count_documents.js +46 -0
- package/lib/operations/create_collection.js +118 -0
- package/lib/operations/create_index.js +92 -0
- package/lib/operations/create_indexes.js +61 -0
- package/lib/operations/cursor_ops.js +3 -4
- package/lib/operations/db_ops.js +15 -12
- package/lib/operations/delete_many.js +25 -0
- package/lib/operations/delete_one.js +25 -0
- package/lib/operations/distinct.js +85 -0
- package/lib/operations/drop.js +53 -0
- package/lib/operations/drop_index.js +42 -0
- package/lib/operations/drop_indexes.js +23 -0
- package/lib/operations/estimated_document_count.js +33 -0
- package/lib/operations/execute_db_admin_command.js +34 -0
- package/lib/operations/execute_operation.js +165 -0
- package/lib/operations/explain.js +23 -0
- package/lib/operations/find_and_modify.js +98 -0
- package/lib/operations/find_one.js +33 -0
- package/lib/operations/find_one_and_delete.js +16 -0
- package/lib/operations/find_one_and_replace.js +18 -0
- package/lib/operations/find_one_and_update.js +19 -0
- package/lib/operations/geo_haystack_search.js +79 -0
- package/lib/operations/has_next.js +40 -0
- package/lib/operations/index_exists.js +39 -0
- package/lib/operations/index_information.js +23 -0
- package/lib/operations/indexes.js +22 -0
- package/lib/operations/insert_many.js +63 -0
- package/lib/operations/insert_one.js +75 -0
- package/lib/operations/is_capped.js +19 -0
- package/lib/operations/list_indexes.js +66 -0
- package/lib/operations/map_reduce.js +189 -0
- package/lib/operations/next.js +32 -0
- package/lib/operations/operation.js +63 -0
- package/lib/operations/options_operation.js +32 -0
- package/lib/operations/profiling_level.js +31 -0
- package/lib/operations/re_index.js +28 -0
- package/lib/operations/remove_user.js +52 -0
- package/lib/operations/rename.js +61 -0
- package/lib/operations/replace_one.js +47 -0
- package/lib/operations/set_profiling_level.js +48 -0
- package/lib/operations/stats.js +45 -0
- package/lib/operations/to_array.js +68 -0
- package/lib/operations/update_many.js +29 -0
- package/lib/operations/update_one.js +44 -0
- package/lib/operations/validate_collection.js +40 -0
- package/lib/read_concern.js +55 -0
- package/lib/topologies/mongos.js +3 -3
- package/lib/topologies/native_topology.js +22 -2
- package/lib/topologies/replset.js +3 -3
- package/lib/topologies/server.js +4 -4
- package/lib/topologies/topology_base.js +6 -6
- package/lib/url_parser.js +4 -3
- package/lib/utils.js +46 -59
- package/lib/write_concern.js +66 -0
- package/package.json +15 -6
- package/lib/.DS_Store +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
5
|
+
const retrieveBSON = require('../connection/utils').retrieveBSON;
|
|
6
|
+
const MongoError = require('../error').MongoError;
|
|
7
|
+
const AuthProvider = require('./auth_provider').AuthProvider;
|
|
8
|
+
|
|
9
|
+
const BSON = retrieveBSON();
|
|
10
|
+
const Binary = BSON.Binary;
|
|
11
|
+
|
|
12
|
+
let saslprep;
|
|
13
|
+
try {
|
|
14
|
+
saslprep = require('saslprep');
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// don't do anything;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
var parsePayload = function(payload) {
|
|
20
|
+
var dict = {};
|
|
21
|
+
var parts = payload.split(',');
|
|
22
|
+
|
|
23
|
+
for (var i = 0; i < parts.length; i++) {
|
|
24
|
+
var valueParts = parts[i].split('=');
|
|
25
|
+
dict[valueParts[0]] = valueParts[1];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return dict;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
var passwordDigest = function(username, password) {
|
|
32
|
+
if (typeof username !== 'string') throw new MongoError('username must be a string');
|
|
33
|
+
if (typeof password !== 'string') throw new MongoError('password must be a string');
|
|
34
|
+
if (password.length === 0) throw new MongoError('password cannot be empty');
|
|
35
|
+
// Use node md5 generator
|
|
36
|
+
var md5 = crypto.createHash('md5');
|
|
37
|
+
// Generate keys used for authentication
|
|
38
|
+
md5.update(username + ':mongo:' + password, 'utf8');
|
|
39
|
+
return md5.digest('hex');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// XOR two buffers
|
|
43
|
+
function xor(a, b) {
|
|
44
|
+
if (!Buffer.isBuffer(a)) a = Buffer.from(a);
|
|
45
|
+
if (!Buffer.isBuffer(b)) b = Buffer.from(b);
|
|
46
|
+
const length = Math.max(a.length, b.length);
|
|
47
|
+
const res = [];
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < length; i += 1) {
|
|
50
|
+
res.push(a[i] ^ b[i]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return Buffer.from(res).toString('base64');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function H(method, text) {
|
|
57
|
+
return crypto
|
|
58
|
+
.createHash(method)
|
|
59
|
+
.update(text)
|
|
60
|
+
.digest();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function HMAC(method, key, text) {
|
|
64
|
+
return crypto
|
|
65
|
+
.createHmac(method, key)
|
|
66
|
+
.update(text)
|
|
67
|
+
.digest();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var _hiCache = {};
|
|
71
|
+
var _hiCacheCount = 0;
|
|
72
|
+
var _hiCachePurge = function() {
|
|
73
|
+
_hiCache = {};
|
|
74
|
+
_hiCacheCount = 0;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const hiLengthMap = {
|
|
78
|
+
sha256: 32,
|
|
79
|
+
sha1: 20
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function HI(data, salt, iterations, cryptoMethod) {
|
|
83
|
+
// omit the work if already generated
|
|
84
|
+
const key = [data, salt.toString('base64'), iterations].join('_');
|
|
85
|
+
if (_hiCache[key] !== undefined) {
|
|
86
|
+
return _hiCache[key];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// generate the salt
|
|
90
|
+
const saltedData = crypto.pbkdf2Sync(
|
|
91
|
+
data,
|
|
92
|
+
salt,
|
|
93
|
+
iterations,
|
|
94
|
+
hiLengthMap[cryptoMethod],
|
|
95
|
+
cryptoMethod
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// cache a copy to speed up the next lookup, but prevent unbounded cache growth
|
|
99
|
+
if (_hiCacheCount >= 200) {
|
|
100
|
+
_hiCachePurge();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_hiCache[key] = saltedData;
|
|
104
|
+
_hiCacheCount += 1;
|
|
105
|
+
return saltedData;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Creates a new ScramSHA authentication mechanism
|
|
110
|
+
* @class
|
|
111
|
+
* @extends AuthProvider
|
|
112
|
+
*/
|
|
113
|
+
class ScramSHA extends AuthProvider {
|
|
114
|
+
constructor(bson, cryptoMethod) {
|
|
115
|
+
super(bson);
|
|
116
|
+
this.cryptoMethod = cryptoMethod || 'sha1';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static _getError(err, r) {
|
|
120
|
+
if (err) {
|
|
121
|
+
return err;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (r.$err || r.errmsg) {
|
|
125
|
+
return new MongoError(r);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @ignore
|
|
131
|
+
*/
|
|
132
|
+
_executeScram(sendAuthCommand, connection, credentials, nonce, callback) {
|
|
133
|
+
let username = credentials.username;
|
|
134
|
+
const password = credentials.password;
|
|
135
|
+
const db = credentials.source;
|
|
136
|
+
|
|
137
|
+
const cryptoMethod = this.cryptoMethod;
|
|
138
|
+
let mechanism = 'SCRAM-SHA-1';
|
|
139
|
+
let processedPassword;
|
|
140
|
+
|
|
141
|
+
if (cryptoMethod === 'sha256') {
|
|
142
|
+
mechanism = 'SCRAM-SHA-256';
|
|
143
|
+
|
|
144
|
+
processedPassword = saslprep ? saslprep(password) : password;
|
|
145
|
+
} else {
|
|
146
|
+
try {
|
|
147
|
+
processedPassword = passwordDigest(username, password);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return callback(e);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Clean up the user
|
|
154
|
+
username = username.replace('=', '=3D').replace(',', '=2C');
|
|
155
|
+
|
|
156
|
+
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
|
|
157
|
+
// Since the username is not sasl-prep-d, we need to do this here.
|
|
158
|
+
const firstBare = Buffer.concat([
|
|
159
|
+
Buffer.from('n=', 'utf8'),
|
|
160
|
+
Buffer.from(username, 'utf8'),
|
|
161
|
+
Buffer.from(',r=', 'utf8'),
|
|
162
|
+
Buffer.from(nonce, 'utf8')
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
// Build command structure
|
|
166
|
+
const saslStartCmd = {
|
|
167
|
+
saslStart: 1,
|
|
168
|
+
mechanism,
|
|
169
|
+
payload: new Binary(Buffer.concat([Buffer.from('n,,', 'utf8'), firstBare])),
|
|
170
|
+
autoAuthorize: 1
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Write the commmand on the connection
|
|
174
|
+
sendAuthCommand(connection, `${db}.$cmd`, saslStartCmd, (err, r) => {
|
|
175
|
+
let tmpError = ScramSHA._getError(err, r);
|
|
176
|
+
if (tmpError) {
|
|
177
|
+
return callback(tmpError, null);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const payload = Buffer.isBuffer(r.payload) ? new Binary(r.payload) : r.payload;
|
|
181
|
+
const dict = parsePayload(payload.value());
|
|
182
|
+
const iterations = parseInt(dict.i, 10);
|
|
183
|
+
const salt = dict.s;
|
|
184
|
+
const rnonce = dict.r;
|
|
185
|
+
|
|
186
|
+
// Set up start of proof
|
|
187
|
+
const withoutProof = `c=biws,r=${rnonce}`;
|
|
188
|
+
const saltedPassword = HI(
|
|
189
|
+
processedPassword,
|
|
190
|
+
Buffer.from(salt, 'base64'),
|
|
191
|
+
iterations,
|
|
192
|
+
cryptoMethod
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (iterations && iterations < 4096) {
|
|
196
|
+
const error = new MongoError(`Server returned an invalid iteration count ${iterations}`);
|
|
197
|
+
return callback(error, false);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const clientKey = HMAC(cryptoMethod, saltedPassword, 'Client Key');
|
|
201
|
+
const storedKey = H(cryptoMethod, clientKey);
|
|
202
|
+
const authMessage = [firstBare, payload.value().toString('base64'), withoutProof].join(',');
|
|
203
|
+
|
|
204
|
+
const clientSignature = HMAC(cryptoMethod, storedKey, authMessage);
|
|
205
|
+
const clientProof = `p=${xor(clientKey, clientSignature)}`;
|
|
206
|
+
const clientFinal = [withoutProof, clientProof].join(',');
|
|
207
|
+
const saslContinueCmd = {
|
|
208
|
+
saslContinue: 1,
|
|
209
|
+
conversationId: r.conversationId,
|
|
210
|
+
payload: new Binary(Buffer.from(clientFinal))
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
sendAuthCommand(connection, `${db}.$cmd`, saslContinueCmd, (err, r) => {
|
|
214
|
+
if (!r || r.done !== false) {
|
|
215
|
+
return callback(err, r);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const retrySaslContinueCmd = {
|
|
219
|
+
saslContinue: 1,
|
|
220
|
+
conversationId: r.conversationId,
|
|
221
|
+
payload: Buffer.alloc(0)
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
sendAuthCommand(connection, `${db}.$cmd`, retrySaslContinueCmd, callback);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Implementation of authentication for a single connection
|
|
231
|
+
* @override
|
|
232
|
+
*/
|
|
233
|
+
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
|
234
|
+
// Create a random nonce
|
|
235
|
+
crypto.randomBytes(24, (err, buff) => {
|
|
236
|
+
if (err) {
|
|
237
|
+
return callback(err, null);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return this._executeScram(
|
|
241
|
+
sendAuthCommand,
|
|
242
|
+
connection,
|
|
243
|
+
credentials,
|
|
244
|
+
buff.toString('base64'),
|
|
245
|
+
callback
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Authenticate
|
|
252
|
+
* @override
|
|
253
|
+
* @method
|
|
254
|
+
*/
|
|
255
|
+
auth(sendAuthCommand, connections, credentials, callback) {
|
|
256
|
+
this._checkSaslprep();
|
|
257
|
+
super.auth(sendAuthCommand, connections, credentials, callback);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
_checkSaslprep() {
|
|
261
|
+
const cryptoMethod = this.cryptoMethod;
|
|
262
|
+
|
|
263
|
+
if (cryptoMethod === 'sha256') {
|
|
264
|
+
if (!saslprep) {
|
|
265
|
+
console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Creates a new ScramSHA1 authentication mechanism
|
|
273
|
+
* @class
|
|
274
|
+
* @extends ScramSHA
|
|
275
|
+
*/
|
|
276
|
+
class ScramSHA1 extends ScramSHA {
|
|
277
|
+
constructor(bson) {
|
|
278
|
+
super(bson, 'sha1');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Creates a new ScramSHA256 authentication mechanism
|
|
284
|
+
* @class
|
|
285
|
+
* @extends ScramSHA
|
|
286
|
+
*/
|
|
287
|
+
class ScramSHA256 extends ScramSHA {
|
|
288
|
+
constructor(bson) {
|
|
289
|
+
super(bson, 'sha256');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = { ScramSHA1, ScramSHA256 };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AuthProvider = require('./auth_provider').AuthProvider;
|
|
4
|
+
const retrieveKerberos = require('../utils').retrieveKerberos;
|
|
5
|
+
let kerberos;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new SSPI authentication mechanism
|
|
9
|
+
* @class
|
|
10
|
+
* @extends AuthProvider
|
|
11
|
+
*/
|
|
12
|
+
class SSPI extends AuthProvider {
|
|
13
|
+
/**
|
|
14
|
+
* Implementation of authentication for a single connection
|
|
15
|
+
* @override
|
|
16
|
+
*/
|
|
17
|
+
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
|
18
|
+
// TODO: Destructure this
|
|
19
|
+
const username = credentials.username;
|
|
20
|
+
const password = credentials.password;
|
|
21
|
+
const mechanismProperties = credentials.mechanismProperties;
|
|
22
|
+
const gssapiServiceName =
|
|
23
|
+
mechanismProperties['gssapiservicename'] ||
|
|
24
|
+
mechanismProperties['gssapiServiceName'] ||
|
|
25
|
+
'mongodb';
|
|
26
|
+
|
|
27
|
+
SSIPAuthenticate(
|
|
28
|
+
this,
|
|
29
|
+
kerberos.processes.MongoAuthProcess,
|
|
30
|
+
username,
|
|
31
|
+
password,
|
|
32
|
+
gssapiServiceName,
|
|
33
|
+
sendAuthCommand,
|
|
34
|
+
connection,
|
|
35
|
+
mechanismProperties,
|
|
36
|
+
callback
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Authenticate
|
|
42
|
+
* @override
|
|
43
|
+
* @method
|
|
44
|
+
*/
|
|
45
|
+
auth(sendAuthCommand, connections, credentials, callback) {
|
|
46
|
+
if (kerberos == null) {
|
|
47
|
+
try {
|
|
48
|
+
kerberos = retrieveKerberos();
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return callback(e, null);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
super.auth(sendAuthCommand, connections, credentials, callback);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function SSIPAuthenticate(
|
|
59
|
+
self,
|
|
60
|
+
MongoAuthProcess,
|
|
61
|
+
username,
|
|
62
|
+
password,
|
|
63
|
+
gssapiServiceName,
|
|
64
|
+
sendAuthCommand,
|
|
65
|
+
connection,
|
|
66
|
+
options,
|
|
67
|
+
callback
|
|
68
|
+
) {
|
|
69
|
+
const authProcess = new MongoAuthProcess(
|
|
70
|
+
connection.host,
|
|
71
|
+
connection.port,
|
|
72
|
+
gssapiServiceName,
|
|
73
|
+
options
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
function authCommand(command, authCb) {
|
|
77
|
+
sendAuthCommand(connection, '$external.$cmd', command, authCb);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
authProcess.init(username, password, err => {
|
|
81
|
+
if (err) return callback(err, false);
|
|
82
|
+
|
|
83
|
+
authProcess.transition('', (err, payload) => {
|
|
84
|
+
if (err) return callback(err, false);
|
|
85
|
+
|
|
86
|
+
const command = {
|
|
87
|
+
saslStart: 1,
|
|
88
|
+
mechanism: 'GSSAPI',
|
|
89
|
+
payload,
|
|
90
|
+
autoAuthorize: 1
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
authCommand(command, (err, doc) => {
|
|
94
|
+
if (err) return callback(err, false);
|
|
95
|
+
|
|
96
|
+
authProcess.transition(doc.payload, (err, payload) => {
|
|
97
|
+
if (err) return callback(err, false);
|
|
98
|
+
const command = {
|
|
99
|
+
saslContinue: 1,
|
|
100
|
+
conversationId: doc.conversationId,
|
|
101
|
+
payload
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
authCommand(command, (err, doc) => {
|
|
105
|
+
if (err) return callback(err, false);
|
|
106
|
+
|
|
107
|
+
authProcess.transition(doc.payload, (err, payload) => {
|
|
108
|
+
if (err) return callback(err, false);
|
|
109
|
+
const command = {
|
|
110
|
+
saslContinue: 1,
|
|
111
|
+
conversationId: doc.conversationId,
|
|
112
|
+
payload
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
authCommand(command, (err, response) => {
|
|
116
|
+
if (err) return callback(err, false);
|
|
117
|
+
|
|
118
|
+
authProcess.transition(null, err => {
|
|
119
|
+
if (err) return callback(err, null);
|
|
120
|
+
callback(null, response);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = SSPI;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AuthProvider = require('./auth_provider').AuthProvider;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new X509 authentication mechanism
|
|
7
|
+
* @class
|
|
8
|
+
* @extends AuthProvider
|
|
9
|
+
*/
|
|
10
|
+
class X509 extends AuthProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Implementation of authentication for a single connection
|
|
13
|
+
* @override
|
|
14
|
+
*/
|
|
15
|
+
_authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
|
|
16
|
+
const username = credentials.username;
|
|
17
|
+
const command = { authenticate: 1, mechanism: 'MONGODB-X509' };
|
|
18
|
+
if (username) {
|
|
19
|
+
command.user = username;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
sendAuthCommand(connection, '$external.$cmd', command, callback);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = X509;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Msg = require('../connection/msg').Msg;
|
|
3
|
+
const KillCursor = require('../connection/commands').KillCursor;
|
|
4
|
+
const GetMore = require('../connection/commands').GetMore;
|
|
5
|
+
const calculateDurationInMs = require('../utils').calculateDurationInMs;
|
|
6
|
+
|
|
7
|
+
/** Commands that we want to redact because of the sensitive nature of their contents */
|
|
8
|
+
const SENSITIVE_COMMANDS = new Set([
|
|
9
|
+
'authenticate',
|
|
10
|
+
'saslStart',
|
|
11
|
+
'saslContinue',
|
|
12
|
+
'getnonce',
|
|
13
|
+
'createUser',
|
|
14
|
+
'updateUser',
|
|
15
|
+
'copydbgetnonce',
|
|
16
|
+
'copydbsaslstart',
|
|
17
|
+
'copydb'
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
// helper methods
|
|
21
|
+
const extractCommandName = commandDoc => Object.keys(commandDoc)[0];
|
|
22
|
+
const namespace = command => command.ns;
|
|
23
|
+
const databaseName = command => command.ns.split('.')[0];
|
|
24
|
+
const collectionName = command => command.ns.split('.')[1];
|
|
25
|
+
const generateConnectionId = pool => `${pool.options.host}:${pool.options.port}`;
|
|
26
|
+
const maybeRedact = (commandName, result) => (SENSITIVE_COMMANDS.has(commandName) ? {} : result);
|
|
27
|
+
|
|
28
|
+
const LEGACY_FIND_QUERY_MAP = {
|
|
29
|
+
$query: 'filter',
|
|
30
|
+
$orderby: 'sort',
|
|
31
|
+
$hint: 'hint',
|
|
32
|
+
$comment: 'comment',
|
|
33
|
+
$maxScan: 'maxScan',
|
|
34
|
+
$max: 'max',
|
|
35
|
+
$min: 'min',
|
|
36
|
+
$returnKey: 'returnKey',
|
|
37
|
+
$showDiskLoc: 'showRecordId',
|
|
38
|
+
$maxTimeMS: 'maxTimeMS',
|
|
39
|
+
$snapshot: 'snapshot'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const LEGACY_FIND_OPTIONS_MAP = {
|
|
43
|
+
numberToSkip: 'skip',
|
|
44
|
+
numberToReturn: 'batchSize',
|
|
45
|
+
returnFieldsSelector: 'projection'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const OP_QUERY_KEYS = [
|
|
49
|
+
'tailable',
|
|
50
|
+
'oplogReplay',
|
|
51
|
+
'noCursorTimeout',
|
|
52
|
+
'awaitData',
|
|
53
|
+
'partial',
|
|
54
|
+
'exhaust'
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extract the actual command from the query, possibly upconverting if it's a legacy
|
|
59
|
+
* format
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} command the command
|
|
62
|
+
*/
|
|
63
|
+
const extractCommand = command => {
|
|
64
|
+
if (command instanceof GetMore) {
|
|
65
|
+
return {
|
|
66
|
+
getMore: command.cursorId,
|
|
67
|
+
collection: collectionName(command),
|
|
68
|
+
batchSize: command.numberToReturn
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (command instanceof KillCursor) {
|
|
73
|
+
return {
|
|
74
|
+
killCursors: collectionName(command),
|
|
75
|
+
cursors: command.cursorIds
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (command instanceof Msg) {
|
|
80
|
+
return command.command;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (command.query && command.query.$query) {
|
|
84
|
+
let result;
|
|
85
|
+
if (command.ns === 'admin.$cmd') {
|
|
86
|
+
// upconvert legacy command
|
|
87
|
+
result = Object.assign({}, command.query.$query);
|
|
88
|
+
} else {
|
|
89
|
+
// upconvert legacy find command
|
|
90
|
+
result = { find: collectionName(command) };
|
|
91
|
+
Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
|
|
92
|
+
if (typeof command.query[key] !== 'undefined')
|
|
93
|
+
result[LEGACY_FIND_QUERY_MAP[key]] = command.query[key];
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
|
|
98
|
+
if (typeof command[key] !== 'undefined') result[LEGACY_FIND_OPTIONS_MAP[key]] = command[key];
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
OP_QUERY_KEYS.forEach(key => {
|
|
102
|
+
if (command[key]) result[key] = command[key];
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (typeof command.pre32Limit !== 'undefined') {
|
|
106
|
+
result.limit = command.pre32Limit;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (command.query.$explain) {
|
|
110
|
+
return { explain: result };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return command.query ? command.query : command;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const extractReply = (command, reply) => {
|
|
120
|
+
if (command instanceof GetMore) {
|
|
121
|
+
return {
|
|
122
|
+
ok: 1,
|
|
123
|
+
cursor: {
|
|
124
|
+
id: reply.message.cursorId,
|
|
125
|
+
ns: namespace(command),
|
|
126
|
+
nextBatch: reply.message.documents
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (command instanceof KillCursor) {
|
|
132
|
+
return {
|
|
133
|
+
ok: 1,
|
|
134
|
+
cursorsUnknown: command.cursorIds
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// is this a legacy find command?
|
|
139
|
+
if (command.query && typeof command.query.$query !== 'undefined') {
|
|
140
|
+
return {
|
|
141
|
+
ok: 1,
|
|
142
|
+
cursor: {
|
|
143
|
+
id: reply.message.cursorId,
|
|
144
|
+
ns: namespace(command),
|
|
145
|
+
firstBatch: reply.message.documents
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// in the event of a `noResponse` command, just return
|
|
151
|
+
if (reply === null) return reply;
|
|
152
|
+
|
|
153
|
+
return reply.result;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/** An event indicating the start of a given command */
|
|
157
|
+
class CommandStartedEvent {
|
|
158
|
+
/**
|
|
159
|
+
* Create a started event
|
|
160
|
+
*
|
|
161
|
+
* @param {Pool} pool the pool that originated the command
|
|
162
|
+
* @param {Object} command the command
|
|
163
|
+
*/
|
|
164
|
+
constructor(pool, command) {
|
|
165
|
+
const cmd = extractCommand(command);
|
|
166
|
+
const commandName = extractCommandName(cmd);
|
|
167
|
+
|
|
168
|
+
// NOTE: remove in major revision, this is not spec behavior
|
|
169
|
+
if (SENSITIVE_COMMANDS.has(commandName)) {
|
|
170
|
+
this.commandObj = {};
|
|
171
|
+
this.commandObj[commandName] = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Object.assign(this, {
|
|
175
|
+
command: cmd,
|
|
176
|
+
databaseName: databaseName(command),
|
|
177
|
+
commandName,
|
|
178
|
+
requestId: command.requestId,
|
|
179
|
+
connectionId: generateConnectionId(pool)
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** An event indicating the success of a given command */
|
|
185
|
+
class CommandSucceededEvent {
|
|
186
|
+
/**
|
|
187
|
+
* Create a succeeded event
|
|
188
|
+
*
|
|
189
|
+
* @param {Pool} pool the pool that originated the command
|
|
190
|
+
* @param {Object} command the command
|
|
191
|
+
* @param {Object} reply the reply for this command from the server
|
|
192
|
+
* @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration
|
|
193
|
+
*/
|
|
194
|
+
constructor(pool, command, reply, started) {
|
|
195
|
+
const cmd = extractCommand(command);
|
|
196
|
+
const commandName = extractCommandName(cmd);
|
|
197
|
+
|
|
198
|
+
Object.assign(this, {
|
|
199
|
+
duration: calculateDurationInMs(started),
|
|
200
|
+
commandName,
|
|
201
|
+
reply: maybeRedact(commandName, extractReply(command, reply)),
|
|
202
|
+
requestId: command.requestId,
|
|
203
|
+
connectionId: generateConnectionId(pool)
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** An event indicating the failure of a given command */
|
|
209
|
+
class CommandFailedEvent {
|
|
210
|
+
/**
|
|
211
|
+
* Create a failure event
|
|
212
|
+
*
|
|
213
|
+
* @param {Pool} pool the pool that originated the command
|
|
214
|
+
* @param {Object} command the command
|
|
215
|
+
* @param {MongoError|Object} error the generated error or a server error response
|
|
216
|
+
* @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration
|
|
217
|
+
*/
|
|
218
|
+
constructor(pool, command, error, started) {
|
|
219
|
+
const cmd = extractCommand(command);
|
|
220
|
+
const commandName = extractCommandName(cmd);
|
|
221
|
+
|
|
222
|
+
Object.assign(this, {
|
|
223
|
+
duration: calculateDurationInMs(started),
|
|
224
|
+
commandName,
|
|
225
|
+
failure: maybeRedact(commandName, error),
|
|
226
|
+
requestId: command.requestId,
|
|
227
|
+
connectionId: generateConnectionId(pool)
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = {
|
|
233
|
+
CommandStartedEvent,
|
|
234
|
+
CommandSucceededEvent,
|
|
235
|
+
CommandFailedEvent
|
|
236
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new CommandResult instance
|
|
5
|
+
* @class
|
|
6
|
+
* @param {object} result CommandResult object
|
|
7
|
+
* @param {Connection} connection A connection instance associated with this result
|
|
8
|
+
* @return {CommandResult} A cursor instance
|
|
9
|
+
*/
|
|
10
|
+
var CommandResult = function(result, connection, message) {
|
|
11
|
+
this.result = result;
|
|
12
|
+
this.connection = connection;
|
|
13
|
+
this.message = message;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert CommandResult to JSON
|
|
18
|
+
* @method
|
|
19
|
+
* @return {object}
|
|
20
|
+
*/
|
|
21
|
+
CommandResult.prototype.toJSON = function() {
|
|
22
|
+
let result = Object.assign({}, this, this.result);
|
|
23
|
+
delete result.message;
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Convert CommandResult to String representation
|
|
29
|
+
* @method
|
|
30
|
+
* @return {string}
|
|
31
|
+
*/
|
|
32
|
+
CommandResult.prototype.toString = function() {
|
|
33
|
+
return JSON.stringify(this.toJSON());
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
module.exports = CommandResult;
|