kuzzle 2.18.0 → 2.18.1
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/lib/api/controllers/authController.js +2 -1
- package/lib/api/controllers/documentController.js +6 -2
- package/lib/api/controllers/securityController.js +26 -20
- package/lib/api/controllers/serverController.js +2 -1
- package/lib/api/documentExtractor.js +2 -1
- package/lib/api/request/kuzzleRequest.d.ts +25 -0
- package/lib/api/request/kuzzleRequest.js +98 -16
- package/lib/api/request/requestResponse.js +25 -0
- package/lib/core/backend/backendController.d.ts +7 -1
- package/lib/core/backend/backendController.js +15 -3
- package/lib/core/network/protocols/httpwsProtocol.js +8 -6
- package/lib/core/plugin/plugin.js +7 -0
- package/package-lock.json +1 -1
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ const { NativeController } = require('./baseController');
|
|
|
35
35
|
const formatProcessing = require('../../core/auth/formatProcessing');
|
|
36
36
|
const { User } = require('../../model/security/user');
|
|
37
37
|
const ApiKey = require('../../model/storage/apiKey');
|
|
38
|
+
const SecurityController = require('./securityController');
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* @class AuthController
|
|
@@ -471,7 +472,7 @@ class AuthController extends NativeController {
|
|
|
471
472
|
userId,
|
|
472
473
|
});
|
|
473
474
|
|
|
474
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
475
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on user "${userId}."`);
|
|
475
476
|
|
|
476
477
|
return formatProcessing.serializeUser(user);
|
|
477
478
|
}
|
|
@@ -228,8 +228,12 @@ class DocumentController extends NativeController {
|
|
|
228
228
|
mimeType = 'text/csv';
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
request.response.
|
|
232
|
-
|
|
231
|
+
request.response.configure({
|
|
232
|
+
headers: {
|
|
233
|
+
'Content-Disposition': `attachment; filename="${index}-${collection}.${format}"`,
|
|
234
|
+
'Content-Type': mimeType,
|
|
235
|
+
}
|
|
236
|
+
});
|
|
233
237
|
|
|
234
238
|
return dumpCollectionDocuments(
|
|
235
239
|
index,
|
|
@@ -38,6 +38,11 @@ const { generateRandomName } = require('../../util/name-generator');
|
|
|
38
38
|
* @class SecurityController
|
|
39
39
|
*/
|
|
40
40
|
class SecurityController extends NativeController {
|
|
41
|
+
|
|
42
|
+
static userOrSdk (userId) {
|
|
43
|
+
return userId === null ? 'EmbeddedSDK' : `User "${userId}"`;
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
constructor () {
|
|
42
47
|
super([
|
|
43
48
|
'checkRights',
|
|
@@ -150,7 +155,7 @@ class SecurityController extends NativeController {
|
|
|
150
155
|
refresh,
|
|
151
156
|
});
|
|
152
157
|
|
|
153
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
158
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(creatorId)} applied action "${request.input.action}" on user "${userId}."`);
|
|
154
159
|
return apiKey.serialize({ includeToken: true });
|
|
155
160
|
}
|
|
156
161
|
|
|
@@ -372,7 +377,7 @@ class SecurityController extends NativeController {
|
|
|
372
377
|
userId,
|
|
373
378
|
});
|
|
374
379
|
|
|
375
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
380
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on role "${role._id}."`);
|
|
376
381
|
return formatProcessing.serializeRole(role);
|
|
377
382
|
}
|
|
378
383
|
|
|
@@ -393,7 +398,7 @@ class SecurityController extends NativeController {
|
|
|
393
398
|
userId,
|
|
394
399
|
});
|
|
395
400
|
|
|
396
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
401
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on role "${role._id}."`);
|
|
397
402
|
return formatProcessing.serializeRole(role);
|
|
398
403
|
}
|
|
399
404
|
|
|
@@ -410,7 +415,7 @@ class SecurityController extends NativeController {
|
|
|
410
415
|
refresh: request.getRefresh('wait_for')
|
|
411
416
|
});
|
|
412
417
|
|
|
413
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
418
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} applied action "${request.input.action} on role "${id}."`);
|
|
414
419
|
|
|
415
420
|
// @todo This avoids a breaking change... but we should really return
|
|
416
421
|
// an acknowledgment.
|
|
@@ -473,7 +478,7 @@ class SecurityController extends NativeController {
|
|
|
473
478
|
userId,
|
|
474
479
|
});
|
|
475
480
|
|
|
476
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
481
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on profile "${profile._id}."`);
|
|
477
482
|
|
|
478
483
|
return formatProcessing.serializeProfile(profile);
|
|
479
484
|
}
|
|
@@ -502,7 +507,7 @@ class SecurityController extends NativeController {
|
|
|
502
507
|
userId,
|
|
503
508
|
});
|
|
504
509
|
|
|
505
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
510
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on profile "${profile._id}."`);
|
|
506
511
|
|
|
507
512
|
return formatProcessing.serializeProfile(profile);
|
|
508
513
|
}
|
|
@@ -523,7 +528,7 @@ class SecurityController extends NativeController {
|
|
|
523
528
|
|
|
524
529
|
await this.ask('core:security:profile:delete', id, options);
|
|
525
530
|
|
|
526
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
531
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on profile "${id}."`);
|
|
527
532
|
|
|
528
533
|
// @todo - replace by an acknowledgement
|
|
529
534
|
return { _id: id };
|
|
@@ -603,7 +608,8 @@ class SecurityController extends NativeController {
|
|
|
603
608
|
ids = request.getBodyArray('ids');
|
|
604
609
|
}
|
|
605
610
|
else {
|
|
606
|
-
|
|
611
|
+
// @deprecated Should be replaced with request.getArray('ids')
|
|
612
|
+
ids = request.getArrayLegacy('ids');
|
|
607
613
|
}
|
|
608
614
|
|
|
609
615
|
const users = await this.ask('core:security:user:mGet', ids);
|
|
@@ -758,7 +764,7 @@ class SecurityController extends NativeController {
|
|
|
758
764
|
|
|
759
765
|
await this.ask('core:security:user:delete', id, options);
|
|
760
766
|
|
|
761
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
767
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} applied action "${request.input.action}" on user "${id}."`);
|
|
762
768
|
|
|
763
769
|
return { _id: id };
|
|
764
770
|
}
|
|
@@ -865,7 +871,7 @@ class SecurityController extends NativeController {
|
|
|
865
871
|
content,
|
|
866
872
|
{ refresh: request.getRefresh('wait_for'), userId });
|
|
867
873
|
|
|
868
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
874
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on user "${id}."`);
|
|
869
875
|
|
|
870
876
|
return formatProcessing.serializeUser(user);
|
|
871
877
|
}
|
|
@@ -888,7 +894,7 @@ class SecurityController extends NativeController {
|
|
|
888
894
|
userId,
|
|
889
895
|
});
|
|
890
896
|
|
|
891
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
897
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on profile "${id}."`);
|
|
892
898
|
return formatProcessing.serializeProfile(updated);
|
|
893
899
|
}
|
|
894
900
|
|
|
@@ -910,7 +916,7 @@ class SecurityController extends NativeController {
|
|
|
910
916
|
userId,
|
|
911
917
|
});
|
|
912
918
|
|
|
913
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
919
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on role "${id}."`);
|
|
914
920
|
|
|
915
921
|
return formatProcessing.serializeRole(updated);
|
|
916
922
|
}
|
|
@@ -951,7 +957,7 @@ class SecurityController extends NativeController {
|
|
|
951
957
|
}
|
|
952
958
|
}
|
|
953
959
|
|
|
954
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
960
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}".`);
|
|
955
961
|
|
|
956
962
|
return user;
|
|
957
963
|
}
|
|
@@ -1040,7 +1046,7 @@ class SecurityController extends NativeController {
|
|
|
1040
1046
|
|
|
1041
1047
|
const createMethod = this.getStrategyMethod(strategy, 'create');
|
|
1042
1048
|
|
|
1043
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1049
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} applied action "${request.input.action}" on user "${id}."`);
|
|
1044
1050
|
return createMethod(request, body, id, strategy);
|
|
1045
1051
|
}
|
|
1046
1052
|
|
|
@@ -1064,7 +1070,7 @@ class SecurityController extends NativeController {
|
|
|
1064
1070
|
|
|
1065
1071
|
const updateMethod = this.getStrategyMethod(strategy, 'update');
|
|
1066
1072
|
|
|
1067
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1073
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} applied action "${request.input.action}" on user "${id}."`);
|
|
1068
1074
|
|
|
1069
1075
|
return updateMethod(request, body, id, strategy);
|
|
1070
1076
|
}
|
|
@@ -1117,7 +1123,7 @@ class SecurityController extends NativeController {
|
|
|
1117
1123
|
|
|
1118
1124
|
await deleteMethod(request, id, strategy);
|
|
1119
1125
|
|
|
1120
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1126
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} applied action "${request.input.action}" on user "${id}."`);
|
|
1121
1127
|
|
|
1122
1128
|
return { acknowledged: true };
|
|
1123
1129
|
}
|
|
@@ -1228,10 +1234,10 @@ class SecurityController extends NativeController {
|
|
|
1228
1234
|
}
|
|
1229
1235
|
|
|
1230
1236
|
if (successes.length > 1000) {
|
|
1231
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1237
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} deleted the following ${type}s: ${successes.slice(0, 1000).join(', ')}... (${successes.length - 1000} more users deleted)."`);
|
|
1232
1238
|
}
|
|
1233
1239
|
else {
|
|
1234
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1240
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} deleted the following ${type}s: ${successes.join(', ')}."`);
|
|
1235
1241
|
}
|
|
1236
1242
|
|
|
1237
1243
|
return successes;
|
|
@@ -1253,7 +1259,7 @@ class SecurityController extends NativeController {
|
|
|
1253
1259
|
userId,
|
|
1254
1260
|
});
|
|
1255
1261
|
|
|
1256
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1262
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${request.input.action}" on user "${id}."`);
|
|
1257
1263
|
|
|
1258
1264
|
return formatProcessing.serializeUser(updated);
|
|
1259
1265
|
}
|
|
@@ -1347,7 +1353,7 @@ class SecurityController extends NativeController {
|
|
|
1347
1353
|
}
|
|
1348
1354
|
|
|
1349
1355
|
if (creationFailure === null) {
|
|
1350
|
-
global.kuzzle.log.info(`[SECURITY]
|
|
1356
|
+
global.kuzzle.log.info(`[SECURITY] ${SecurityController.userOrSdk(request.getKuid())} applied action "${request.input.action}" on user "${id}."`);
|
|
1351
1357
|
return createdUser;
|
|
1352
1358
|
}
|
|
1353
1359
|
|
|
@@ -188,7 +188,8 @@ class ServerController extends NativeController {
|
|
|
188
188
|
|
|
189
189
|
let services;
|
|
190
190
|
if (typeof request.input.args.services === 'string') {
|
|
191
|
-
|
|
191
|
+
// @deprecated Should be replaced with request.getArray('services')
|
|
192
|
+
services = request.getArrayLegacy('services');
|
|
192
193
|
}
|
|
193
194
|
if (! services || services.includes('internalCache')) {
|
|
194
195
|
try {
|
|
@@ -56,7 +56,8 @@ const extractors = ([
|
|
|
56
56
|
ids = request.input.args.ids;
|
|
57
57
|
}
|
|
58
58
|
else if (typeof request.input.args.ids === 'string') {
|
|
59
|
-
|
|
59
|
+
// @deprecated Should be replaced with request.getArray('ids')
|
|
60
|
+
ids = request.getArrayLegacy('ids');
|
|
60
61
|
}
|
|
61
62
|
else {
|
|
62
63
|
throw assertionError.get(
|
|
@@ -258,6 +258,9 @@ export declare class KuzzleRequest {
|
|
|
258
258
|
/**
|
|
259
259
|
* Gets a parameter from a request arguments and checks that it is an array
|
|
260
260
|
*
|
|
261
|
+
* If the request argument is a JSON String instead of an array, it will be parsed
|
|
262
|
+
* and returned if it is a valid JSON array, otherwise it will @throws {api.assert.invalid_type}.
|
|
263
|
+
*
|
|
261
264
|
* @param name parameter name
|
|
262
265
|
* @param def default value to return if the parameter is not set
|
|
263
266
|
*
|
|
@@ -266,9 +269,29 @@ export declare class KuzzleRequest {
|
|
|
266
269
|
* @throws {api.assert.invalid_type} If the fetched parameter is not an array
|
|
267
270
|
*/
|
|
268
271
|
getArray(name: string, def?: [] | undefined): any[];
|
|
272
|
+
/**
|
|
273
|
+
* @deprecated do not use, Use getArray instead
|
|
274
|
+
*
|
|
275
|
+
* Gets a parameter from a request arguments and checks that it is an array
|
|
276
|
+
*
|
|
277
|
+
* If the request argument is a String instead of an array, it will be JSON parsed
|
|
278
|
+
* and returned if it is a valid JSON array, otherwise it will return the string splitted on `,`.
|
|
279
|
+
*
|
|
280
|
+
*
|
|
281
|
+
* @param name parameter name
|
|
282
|
+
* @param def default value to return if the parameter is not set
|
|
283
|
+
*
|
|
284
|
+
* @throws {api.assert.missing_argument} If parameter not found and no default
|
|
285
|
+
* value provided
|
|
286
|
+
* @throws {api.assert.invalid_type} If the fetched parameter is not an array or a string
|
|
287
|
+
*/
|
|
288
|
+
getArrayLegacy(name: string, def?: [] | undefined): any[];
|
|
269
289
|
/**
|
|
270
290
|
* Gets a parameter from a request arguments and checks that it is an object
|
|
271
291
|
*
|
|
292
|
+
* If the request argument is a JSON String instead of an object, it will be parsed
|
|
293
|
+
* and returned if it is a valid JSON object, otherwise it will @throws {api.assert.invalid_type}.
|
|
294
|
+
*
|
|
272
295
|
* @param name parameter name
|
|
273
296
|
* @param def default value to return if the parameter is not set
|
|
274
297
|
*
|
|
@@ -374,6 +397,7 @@ export declare class KuzzleRequest {
|
|
|
374
397
|
* @param obj container object
|
|
375
398
|
* @param name parameter name
|
|
376
399
|
* @param errorName name to use in error messages
|
|
400
|
+
* @param querystring if true, the object is expected to be found in a querystring
|
|
377
401
|
*/
|
|
378
402
|
private _getBoolean;
|
|
379
403
|
/**
|
|
@@ -419,6 +443,7 @@ export declare class KuzzleRequest {
|
|
|
419
443
|
* @param name parameter name
|
|
420
444
|
* @param errorName name to use in error messages
|
|
421
445
|
* @param def default value
|
|
446
|
+
* @param querystring if true, the object is expected to be found in a querystring
|
|
422
447
|
*/
|
|
423
448
|
private _getObject;
|
|
424
449
|
}
|
|
@@ -228,7 +228,9 @@ class KuzzleRequest {
|
|
|
228
228
|
}
|
|
229
229
|
this.status = options.status || 200;
|
|
230
230
|
if (options.headers) {
|
|
231
|
-
this.response.
|
|
231
|
+
this.response.configure({
|
|
232
|
+
headers: options.headers
|
|
233
|
+
});
|
|
232
234
|
}
|
|
233
235
|
if (options.raw !== undefined) {
|
|
234
236
|
this.response.raw = options.raw;
|
|
@@ -465,7 +467,7 @@ class KuzzleRequest {
|
|
|
465
467
|
* @param name parameter name
|
|
466
468
|
*/
|
|
467
469
|
getBoolean(name) {
|
|
468
|
-
return this._getBoolean(this.input.args, name, name);
|
|
470
|
+
return this._getBoolean(this.input.args, name, name, true);
|
|
469
471
|
}
|
|
470
472
|
/**
|
|
471
473
|
* Gets a parameter from a request arguments and checks that it is a number
|
|
@@ -509,6 +511,9 @@ class KuzzleRequest {
|
|
|
509
511
|
/**
|
|
510
512
|
* Gets a parameter from a request arguments and checks that it is an array
|
|
511
513
|
*
|
|
514
|
+
* If the request argument is a JSON String instead of an array, it will be parsed
|
|
515
|
+
* and returned if it is a valid JSON array, otherwise it will @throws {api.assert.invalid_type}.
|
|
516
|
+
*
|
|
512
517
|
* @param name parameter name
|
|
513
518
|
* @param def default value to return if the parameter is not set
|
|
514
519
|
*
|
|
@@ -517,11 +522,56 @@ class KuzzleRequest {
|
|
|
517
522
|
* @throws {api.assert.invalid_type} If the fetched parameter is not an array
|
|
518
523
|
*/
|
|
519
524
|
getArray(name, def = undefined) {
|
|
520
|
-
return this._getArray(this.input.args, name, name, def);
|
|
525
|
+
return this._getArray(this.input.args, name, name, def, true);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* @deprecated do not use, Use getArray instead
|
|
529
|
+
*
|
|
530
|
+
* Gets a parameter from a request arguments and checks that it is an array
|
|
531
|
+
*
|
|
532
|
+
* If the request argument is a String instead of an array, it will be JSON parsed
|
|
533
|
+
* and returned if it is a valid JSON array, otherwise it will return the string splitted on `,`.
|
|
534
|
+
*
|
|
535
|
+
*
|
|
536
|
+
* @param name parameter name
|
|
537
|
+
* @param def default value to return if the parameter is not set
|
|
538
|
+
*
|
|
539
|
+
* @throws {api.assert.missing_argument} If parameter not found and no default
|
|
540
|
+
* value provided
|
|
541
|
+
* @throws {api.assert.invalid_type} If the fetched parameter is not an array or a string
|
|
542
|
+
*/
|
|
543
|
+
getArrayLegacy(name, def = undefined) {
|
|
544
|
+
const value = (0, lodash_1.get)(this.input.args, name, def);
|
|
545
|
+
if (value === undefined) {
|
|
546
|
+
throw assertionError.get('missing_argument', name);
|
|
547
|
+
}
|
|
548
|
+
if (Array.isArray(value)) {
|
|
549
|
+
return value;
|
|
550
|
+
}
|
|
551
|
+
if (typeof value !== 'string') {
|
|
552
|
+
throw assertionError.get('invalid_type', name, 'array');
|
|
553
|
+
}
|
|
554
|
+
// If we are using the HTTP protocol and we have a string instead of an Array
|
|
555
|
+
// we try to parse it as JSON
|
|
556
|
+
if (this.context.connection.protocol === 'http') {
|
|
557
|
+
try {
|
|
558
|
+
const parsedValue = JSON.parse(value);
|
|
559
|
+
if (Array.isArray(parsedValue)) {
|
|
560
|
+
return parsedValue;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
catch (e) {
|
|
564
|
+
// Do nothing, let the code continue
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return value.split(',');
|
|
521
568
|
}
|
|
522
569
|
/**
|
|
523
570
|
* Gets a parameter from a request arguments and checks that it is an object
|
|
524
571
|
*
|
|
572
|
+
* If the request argument is a JSON String instead of an object, it will be parsed
|
|
573
|
+
* and returned if it is a valid JSON object, otherwise it will @throws {api.assert.invalid_type}.
|
|
574
|
+
*
|
|
525
575
|
* @param name parameter name
|
|
526
576
|
* @param def default value to return if the parameter is not set
|
|
527
577
|
*
|
|
@@ -530,7 +580,7 @@ class KuzzleRequest {
|
|
|
530
580
|
* @throws {api.assert.invalid_type} If the fetched parameter is not an object
|
|
531
581
|
*/
|
|
532
582
|
getObject(name, def = undefined) {
|
|
533
|
-
return this._getObject(this.input.args, name, name, def);
|
|
583
|
+
return this._getObject(this.input.args, name, name, def, true);
|
|
534
584
|
}
|
|
535
585
|
/**
|
|
536
586
|
* Gets a parameter from a request arguments and check with moment.js if the date is an ISO8601 format date
|
|
@@ -677,13 +727,7 @@ class KuzzleRequest {
|
|
|
677
727
|
|| this.context.connection.misc.verb !== 'GET') {
|
|
678
728
|
return this.getBody({});
|
|
679
729
|
}
|
|
680
|
-
|
|
681
|
-
try {
|
|
682
|
-
return JSON.parse(searchBody);
|
|
683
|
-
}
|
|
684
|
-
catch (err) {
|
|
685
|
-
throw assertionError.get('invalid_argument', err.message);
|
|
686
|
-
}
|
|
730
|
+
return this.getObject('searchBody', {});
|
|
687
731
|
}
|
|
688
732
|
/**
|
|
689
733
|
* Returns the search params.
|
|
@@ -736,16 +780,17 @@ class KuzzleRequest {
|
|
|
736
780
|
* @param obj container object
|
|
737
781
|
* @param name parameter name
|
|
738
782
|
* @param errorName name to use in error messages
|
|
783
|
+
* @param querystring if true, the object is expected to be found in a querystring
|
|
739
784
|
*/
|
|
740
|
-
_getBoolean(obj, name, errorName) {
|
|
785
|
+
_getBoolean(obj, name, errorName, querystring = false) {
|
|
741
786
|
let value = (0, lodash_1.get)(obj, name);
|
|
742
787
|
// In HTTP, booleans are flags: if it's in the querystring, it's set,
|
|
743
788
|
// whatever its value.
|
|
744
789
|
// If a user needs to unset the option, they need to remove it from the
|
|
745
790
|
// querystring.
|
|
746
|
-
if (this.context.connection.protocol === 'http') {
|
|
791
|
+
if (this.context.connection.protocol === 'http' && querystring) {
|
|
747
792
|
value = value !== undefined;
|
|
748
|
-
obj
|
|
793
|
+
(0, lodash_1.set)(obj, name, value);
|
|
749
794
|
}
|
|
750
795
|
else if (value === undefined || value === null) {
|
|
751
796
|
value = false;
|
|
@@ -822,12 +867,30 @@ class KuzzleRequest {
|
|
|
822
867
|
* @param errorName name to use in error messages
|
|
823
868
|
* @param def default value
|
|
824
869
|
*/
|
|
825
|
-
_getArray(obj, name, errorName, def = undefined) {
|
|
870
|
+
_getArray(obj, name, errorName, def = undefined, querystring = false) {
|
|
826
871
|
const value = (0, lodash_1.get)(obj, name, def);
|
|
827
872
|
if (value === undefined) {
|
|
828
873
|
throw assertionError.get('missing_argument', errorName);
|
|
829
874
|
}
|
|
830
875
|
if (!Array.isArray(value)) {
|
|
876
|
+
// If we are using the HTTP protocol and we have a string instead of an Array
|
|
877
|
+
// we try to parse it as JSON
|
|
878
|
+
if (this.context.connection.protocol === 'http'
|
|
879
|
+
&& querystring
|
|
880
|
+
&& typeof value === 'string') {
|
|
881
|
+
try {
|
|
882
|
+
const parsedValue = JSON.parse(value);
|
|
883
|
+
if (Array.isArray(parsedValue)) {
|
|
884
|
+
// Replace the value with the parsed value
|
|
885
|
+
// This way subsequent calls to this function will return the parsed value directly
|
|
886
|
+
(0, lodash_1.set)(obj, name, parsedValue);
|
|
887
|
+
return parsedValue;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
catch (e) {
|
|
891
|
+
// Do nothing, let the error be thrown below
|
|
892
|
+
}
|
|
893
|
+
}
|
|
831
894
|
throw assertionError.get('invalid_type', errorName, 'array');
|
|
832
895
|
}
|
|
833
896
|
return value;
|
|
@@ -839,13 +902,32 @@ class KuzzleRequest {
|
|
|
839
902
|
* @param name parameter name
|
|
840
903
|
* @param errorName name to use in error messages
|
|
841
904
|
* @param def default value
|
|
905
|
+
* @param querystring if true, the object is expected to be found in a querystring
|
|
842
906
|
*/
|
|
843
|
-
_getObject(obj, name, errorName, def = undefined) {
|
|
907
|
+
_getObject(obj, name, errorName, def = undefined, querystring = false) {
|
|
844
908
|
const value = (0, lodash_1.get)(obj, name, def);
|
|
845
909
|
if (value === undefined) {
|
|
846
910
|
throw assertionError.get('missing_argument', errorName);
|
|
847
911
|
}
|
|
848
912
|
if (!(0, safeObject_1.isPlainObject)(value)) {
|
|
913
|
+
// If we are using the HTTP protocol and we have a string instead of an Array
|
|
914
|
+
// we try to parse it as JSON
|
|
915
|
+
if (this.context.connection.protocol === 'http'
|
|
916
|
+
&& querystring
|
|
917
|
+
&& typeof value === 'string') {
|
|
918
|
+
try {
|
|
919
|
+
const parsedValue = JSON.parse(value);
|
|
920
|
+
if ((0, safeObject_1.isPlainObject)(parsedValue)) {
|
|
921
|
+
// Replace the value with the parsed value
|
|
922
|
+
// This way subsequent calls to this function will return the parsed value directly
|
|
923
|
+
(0, lodash_1.set)(obj, name, parsedValue);
|
|
924
|
+
return parsedValue;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
catch (e) {
|
|
928
|
+
// Do nothing, let the error be thrown below
|
|
929
|
+
}
|
|
930
|
+
}
|
|
849
931
|
throw assertionError.get('invalid_type', errorName, 'object');
|
|
850
932
|
}
|
|
851
933
|
return value;
|
|
@@ -49,6 +49,11 @@ const assert = __importStar(require("../../util/assertType"));
|
|
|
49
49
|
// \u200b is a zero width space, used to masquerade console.log output
|
|
50
50
|
const _request = 'request\u200b';
|
|
51
51
|
const _headers = 'headers\u200b';
|
|
52
|
+
const _userHeaders = 'userHeaders\u200b'; // List of headers to be sent in the response
|
|
53
|
+
// List of headers that should not be present in the body of the response
|
|
54
|
+
const restrictedHeaders = [
|
|
55
|
+
'set-cookie',
|
|
56
|
+
];
|
|
52
57
|
class Headers {
|
|
53
58
|
constructor() {
|
|
54
59
|
this.namesMap = new Map();
|
|
@@ -150,6 +155,7 @@ class RequestResponse {
|
|
|
150
155
|
this.raw = false;
|
|
151
156
|
this[_request] = request;
|
|
152
157
|
this[_headers] = new Headers();
|
|
158
|
+
this[_userHeaders] = new Set();
|
|
153
159
|
Object.seal(this);
|
|
154
160
|
}
|
|
155
161
|
/**
|
|
@@ -254,6 +260,9 @@ class RequestResponse {
|
|
|
254
260
|
configure(options = {}) {
|
|
255
261
|
if (options.headers) {
|
|
256
262
|
this.setHeaders(options.headers);
|
|
263
|
+
for (const key of Object.keys(options.headers)) {
|
|
264
|
+
this[_userHeaders].add(key.toLowerCase());
|
|
265
|
+
}
|
|
257
266
|
}
|
|
258
267
|
if (options.status) {
|
|
259
268
|
this.status = options.status;
|
|
@@ -316,6 +325,21 @@ class RequestResponse {
|
|
|
316
325
|
status: this.status,
|
|
317
326
|
};
|
|
318
327
|
}
|
|
328
|
+
const filteredHeaders = {};
|
|
329
|
+
for (const name of this[_userHeaders]) {
|
|
330
|
+
filteredHeaders[name] = this.getHeader(name);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Remove headers that are not allowed to be sent to the client in the response's body
|
|
334
|
+
* For example "set-cookie" headers should only be visible by the browser,
|
|
335
|
+
* otherwise they may leak information about the server's cookies, since the browser will
|
|
336
|
+
* not be able to restrict them to the domain of the request.
|
|
337
|
+
*/
|
|
338
|
+
for (const header of restrictedHeaders) {
|
|
339
|
+
if (filteredHeaders[header] !== undefined) {
|
|
340
|
+
filteredHeaders[header] = undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
319
343
|
return {
|
|
320
344
|
content: {
|
|
321
345
|
action: this.action,
|
|
@@ -323,6 +347,7 @@ class RequestResponse {
|
|
|
323
347
|
controller: this.controller,
|
|
324
348
|
deprecations: this.deprecations,
|
|
325
349
|
error: this.error,
|
|
350
|
+
headers: filteredHeaders,
|
|
326
351
|
index: this.index,
|
|
327
352
|
node: this.node,
|
|
328
353
|
requestId: this.requestId,
|
|
@@ -86,5 +86,11 @@ export declare class BackendController extends ApplicationManager {
|
|
|
86
86
|
* @param controller Controller class
|
|
87
87
|
*/
|
|
88
88
|
use(controller: Controller): void;
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Adds the controller definition to the list of application controllers.
|
|
91
|
+
*
|
|
92
|
+
* This method also check if the definition is valid to throw with a stacktrace
|
|
93
|
+
* beginning on the user code adding the controller.
|
|
94
|
+
*/
|
|
95
|
+
private add;
|
|
90
96
|
}
|
|
@@ -42,11 +42,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
42
42
|
__setModuleDefault(result, mod);
|
|
43
43
|
return result;
|
|
44
44
|
};
|
|
45
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
|
+
};
|
|
45
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
49
|
exports.BackendController = void 0;
|
|
47
50
|
const inflector_1 = require("../../util/inflector");
|
|
48
51
|
const kerror = __importStar(require("../../kerror"));
|
|
49
52
|
const index_1 = require("./index");
|
|
53
|
+
const plugin_1 = __importDefault(require("../plugin/plugin"));
|
|
50
54
|
const assertionError = kerror.wrap('plugin', 'assert');
|
|
51
55
|
const runtimeError = kerror.wrap('plugin', 'runtime');
|
|
52
56
|
class BackendController extends index_1.ApplicationManager {
|
|
@@ -74,7 +78,8 @@ class BackendController extends index_1.ApplicationManager {
|
|
|
74
78
|
if (this._application.started) {
|
|
75
79
|
throw runtimeError.get('already_started', 'controller');
|
|
76
80
|
}
|
|
77
|
-
|
|
81
|
+
plugin_1.default.checkControllerDefinition(name, definition);
|
|
82
|
+
this.add(name, definition);
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
80
85
|
* Uses a new controller class.
|
|
@@ -147,6 +152,7 @@ class BackendController extends index_1.ApplicationManager {
|
|
|
147
152
|
controller.name = inflector_1.Inflector.kebabCase(controller.constructor.name)
|
|
148
153
|
.replace('-controller', '');
|
|
149
154
|
}
|
|
155
|
+
plugin_1.default.checkControllerDefinition(controller.name, controller.definition);
|
|
150
156
|
for (const [action, definition] of Object.entries(controller.definition.actions)) {
|
|
151
157
|
if (typeof definition.handler !== 'function') {
|
|
152
158
|
throw assertionError.get('invalid_controller_definition', controller.name, `Handler for action "${action}" is not a function.`);
|
|
@@ -158,9 +164,15 @@ class BackendController extends index_1.ApplicationManager {
|
|
|
158
164
|
definition.handler = definition.handler.bind(controller);
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
|
-
this.
|
|
167
|
+
this.add(controller.name, controller.definition);
|
|
162
168
|
}
|
|
163
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Adds the controller definition to the list of application controllers.
|
|
171
|
+
*
|
|
172
|
+
* This method also check if the definition is valid to throw with a stacktrace
|
|
173
|
+
* beginning on the user code adding the controller.
|
|
174
|
+
*/
|
|
175
|
+
add(name, definition) {
|
|
164
176
|
if (this._application._controllers[name]) {
|
|
165
177
|
throw assertionError.get('invalid_controller_definition', name, 'A controller with this name already exists');
|
|
166
178
|
}
|
|
@@ -256,10 +256,15 @@ class HttpWsProtocol extends Protocol {
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
wsOnUpgradeHandler (res, req, context) {
|
|
259
|
+
const headers = {};
|
|
260
|
+
// Extract headers from uWS request
|
|
261
|
+
req.forEach((header, value) => {
|
|
262
|
+
headers[header] = value;
|
|
263
|
+
});
|
|
264
|
+
|
|
259
265
|
res.upgrade(
|
|
260
266
|
{
|
|
261
|
-
|
|
262
|
-
origin: req.getHeader('origin'),
|
|
267
|
+
headers,
|
|
263
268
|
},
|
|
264
269
|
req.getHeader('sec-websocket-key'),
|
|
265
270
|
req.getHeader('sec-websocket-protocol'),
|
|
@@ -273,10 +278,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
273
278
|
const connection = new ClientConnection(
|
|
274
279
|
this.name,
|
|
275
280
|
[ip],
|
|
276
|
-
|
|
277
|
-
cookie: socket.cookie,
|
|
278
|
-
origin: socket.origin
|
|
279
|
-
}
|
|
281
|
+
socket.headers
|
|
280
282
|
);
|
|
281
283
|
|
|
282
284
|
this.entryPoint.newConnection(connection);
|
|
@@ -300,6 +300,13 @@ class Plugin {
|
|
|
300
300
|
'Controller definition must be an object');
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
if (! isPlainObject(definition.actions)) {
|
|
304
|
+
throw assertionError.get(
|
|
305
|
+
'invalid_controller_definition',
|
|
306
|
+
name,
|
|
307
|
+
'Controller definition "actions" property must be an object');
|
|
308
|
+
}
|
|
309
|
+
|
|
303
310
|
for (const [action, actionDefinition] of Object.entries(definition.actions)) {
|
|
304
311
|
const actionProperties = Object.keys(actionDefinition);
|
|
305
312
|
|
package/package-lock.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kuzzle",
|
|
3
3
|
"author": "The Kuzzle Team <support@kuzzle.io>",
|
|
4
|
-
"version": "2.18.
|
|
4
|
+
"version": "2.18.1",
|
|
5
5
|
"description": "Kuzzle is an open-source solution that handles all the data management through a secured API, with a large choice of protocols.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kuzzle": "bin/start-kuzzle-server"
|