kuzzle 2.18.0 → 2.19.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/index.d.ts +1 -0
- package/index.js +3 -0
- package/lib/api/controllers/adminController.js +9 -0
- package/lib/api/controllers/authController.js +2 -1
- package/lib/api/controllers/debugController.d.ts +59 -0
- package/lib/api/controllers/debugController.js +285 -0
- package/lib/api/controllers/documentController.js +49 -31
- package/lib/api/controllers/index.js +1 -0
- package/lib/api/controllers/securityController.js +28 -22
- package/lib/api/controllers/serverController.js +2 -1
- package/lib/api/documentExtractor.js +51 -9
- package/lib/api/funnel.js +30 -8
- package/lib/api/httpRoutes.js +6 -0
- package/lib/api/request/kuzzleRequest.d.ts +37 -4
- package/lib/api/request/kuzzleRequest.js +115 -29
- package/lib/api/request/requestResponse.js +25 -0
- package/lib/cluster/idCardHandler.js +1 -1
- package/lib/config/default.config.js +3 -0
- package/lib/config/documentEventAliases.d.ts +7 -0
- package/lib/config/documentEventAliases.js +26 -12
- package/lib/core/backend/backend.d.ts +4 -0
- package/lib/core/backend/backend.js +9 -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 +14 -6
- package/lib/core/plugin/plugin.js +7 -0
- package/lib/core/shared/sdk/embeddedSdk.d.ts +1 -1
- package/lib/core/shared/sdk/embeddedSdk.js +33 -0
- package/lib/kerror/codes/0-core.json +35 -0
- package/lib/kerror/codes/2-api.json +6 -0
- package/lib/kuzzle/kuzzle.d.ts +1 -1
- package/lib/kuzzle/kuzzle.js +27 -8
- package/lib/model/storage/apiKey.js +1 -6
- package/lib/service/storage/elasticsearch.js +40 -13
- package/lib/types/DebugModule.d.ts +23 -0
- package/lib/types/DebugModule.js +39 -0
- package/lib/types/config/SecurityConfiguration.d.ts +10 -0
- package/lib/util/crypto.d.ts +1 -0
- package/lib/util/crypto.js +12 -0
- package/lib/util/name-generator.d.ts +79 -0
- package/lib/util/name-generator.js +1409 -1345
- package/lib/util/time.d.ts +1 -0
- package/lib/util/time.js +9 -0
- package/package.json +4 -6
- package/lib/core/security/README.md +0 -224
- package/lib/core/shared/README.md +0 -3
- package/package-lock.json +0 -8422
|
@@ -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
|
|
@@ -577,22 +627,18 @@ class KuzzleRequest {
|
|
|
577
627
|
/**
|
|
578
628
|
* Returns the index specified in the request
|
|
579
629
|
*/
|
|
580
|
-
getIndex() {
|
|
630
|
+
getIndex({ required = true } = {}) {
|
|
581
631
|
const index = this.input.args.index;
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
return index;
|
|
632
|
+
this.checkRequired(index, 'index', required);
|
|
633
|
+
return index ? String(index) : null;
|
|
586
634
|
}
|
|
587
635
|
/**
|
|
588
636
|
* Returns the collection specified in the request
|
|
589
637
|
*/
|
|
590
|
-
getCollection() {
|
|
638
|
+
getCollection({ required = true } = {}) {
|
|
591
639
|
const collection = this.input.args.collection;
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
return collection;
|
|
640
|
+
this.checkRequired(collection, 'collection', required);
|
|
641
|
+
return collection ? String(collection) : null;
|
|
596
642
|
}
|
|
597
643
|
/**
|
|
598
644
|
* Returns the index and collection specified in the request
|
|
@@ -649,7 +695,7 @@ class KuzzleRequest {
|
|
|
649
695
|
if (typeof id !== 'string') {
|
|
650
696
|
throw assertionError.get('invalid_type', '_id', 'string');
|
|
651
697
|
}
|
|
652
|
-
return id;
|
|
698
|
+
return String(id);
|
|
653
699
|
}
|
|
654
700
|
/**
|
|
655
701
|
* Returns the current user kuid
|
|
@@ -670,20 +716,14 @@ class KuzzleRequest {
|
|
|
670
716
|
return null;
|
|
671
717
|
}
|
|
672
718
|
/**
|
|
673
|
-
|
|
674
|
-
|
|
719
|
+
* Returns the search body query according to the http method
|
|
720
|
+
*/
|
|
675
721
|
getSearchBody() {
|
|
676
722
|
if (this.context.connection.protocol !== 'http'
|
|
677
723
|
|| this.context.connection.misc.verb !== 'GET') {
|
|
678
724
|
return this.getBody({});
|
|
679
725
|
}
|
|
680
|
-
|
|
681
|
-
try {
|
|
682
|
-
return JSON.parse(searchBody);
|
|
683
|
-
}
|
|
684
|
-
catch (err) {
|
|
685
|
-
throw assertionError.get('invalid_argument', err.message);
|
|
686
|
-
}
|
|
726
|
+
return this.getObject('searchBody', {});
|
|
687
727
|
}
|
|
688
728
|
/**
|
|
689
729
|
* Returns the search params.
|
|
@@ -736,16 +776,17 @@ class KuzzleRequest {
|
|
|
736
776
|
* @param obj container object
|
|
737
777
|
* @param name parameter name
|
|
738
778
|
* @param errorName name to use in error messages
|
|
779
|
+
* @param querystring if true, the object is expected to be found in a querystring
|
|
739
780
|
*/
|
|
740
|
-
_getBoolean(obj, name, errorName) {
|
|
781
|
+
_getBoolean(obj, name, errorName, querystring = false) {
|
|
741
782
|
let value = (0, lodash_1.get)(obj, name);
|
|
742
783
|
// In HTTP, booleans are flags: if it's in the querystring, it's set,
|
|
743
784
|
// whatever its value.
|
|
744
785
|
// If a user needs to unset the option, they need to remove it from the
|
|
745
786
|
// querystring.
|
|
746
|
-
if (this.context.connection.protocol === 'http') {
|
|
787
|
+
if (this.context.connection.protocol === 'http' && querystring) {
|
|
747
788
|
value = value !== undefined;
|
|
748
|
-
obj
|
|
789
|
+
(0, lodash_1.set)(obj, name, value);
|
|
749
790
|
}
|
|
750
791
|
else if (value === undefined || value === null) {
|
|
751
792
|
value = false;
|
|
@@ -822,12 +863,30 @@ class KuzzleRequest {
|
|
|
822
863
|
* @param errorName name to use in error messages
|
|
823
864
|
* @param def default value
|
|
824
865
|
*/
|
|
825
|
-
_getArray(obj, name, errorName, def = undefined) {
|
|
866
|
+
_getArray(obj, name, errorName, def = undefined, querystring = false) {
|
|
826
867
|
const value = (0, lodash_1.get)(obj, name, def);
|
|
827
868
|
if (value === undefined) {
|
|
828
869
|
throw assertionError.get('missing_argument', errorName);
|
|
829
870
|
}
|
|
830
871
|
if (!Array.isArray(value)) {
|
|
872
|
+
// If we are using the HTTP protocol and we have a string instead of an Array
|
|
873
|
+
// we try to parse it as JSON
|
|
874
|
+
if (this.context.connection.protocol === 'http'
|
|
875
|
+
&& querystring
|
|
876
|
+
&& typeof value === 'string') {
|
|
877
|
+
try {
|
|
878
|
+
const parsedValue = JSON.parse(value);
|
|
879
|
+
if (Array.isArray(parsedValue)) {
|
|
880
|
+
// Replace the value with the parsed value
|
|
881
|
+
// This way subsequent calls to this function will return the parsed value directly
|
|
882
|
+
(0, lodash_1.set)(obj, name, parsedValue);
|
|
883
|
+
return parsedValue;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
catch (e) {
|
|
887
|
+
// Do nothing, let the error be thrown below
|
|
888
|
+
}
|
|
889
|
+
}
|
|
831
890
|
throw assertionError.get('invalid_type', errorName, 'array');
|
|
832
891
|
}
|
|
833
892
|
return value;
|
|
@@ -839,17 +898,44 @@ class KuzzleRequest {
|
|
|
839
898
|
* @param name parameter name
|
|
840
899
|
* @param errorName name to use in error messages
|
|
841
900
|
* @param def default value
|
|
901
|
+
* @param querystring if true, the object is expected to be found in a querystring
|
|
842
902
|
*/
|
|
843
|
-
_getObject(obj, name, errorName, def = undefined) {
|
|
903
|
+
_getObject(obj, name, errorName, def = undefined, querystring = false) {
|
|
844
904
|
const value = (0, lodash_1.get)(obj, name, def);
|
|
845
905
|
if (value === undefined) {
|
|
846
906
|
throw assertionError.get('missing_argument', errorName);
|
|
847
907
|
}
|
|
848
908
|
if (!(0, safeObject_1.isPlainObject)(value)) {
|
|
909
|
+
// If we are using the HTTP protocol and we have a string instead of an Array
|
|
910
|
+
// we try to parse it as JSON
|
|
911
|
+
if (this.context.connection.protocol === 'http'
|
|
912
|
+
&& querystring
|
|
913
|
+
&& typeof value === 'string') {
|
|
914
|
+
try {
|
|
915
|
+
const parsedValue = JSON.parse(value);
|
|
916
|
+
if ((0, safeObject_1.isPlainObject)(parsedValue)) {
|
|
917
|
+
// Replace the value with the parsed value
|
|
918
|
+
// This way subsequent calls to this function will return the parsed value directly
|
|
919
|
+
(0, lodash_1.set)(obj, name, parsedValue);
|
|
920
|
+
return parsedValue;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
catch (e) {
|
|
924
|
+
// Do nothing, let the error be thrown below
|
|
925
|
+
}
|
|
926
|
+
}
|
|
849
927
|
throw assertionError.get('invalid_type', errorName, 'object');
|
|
850
928
|
}
|
|
851
929
|
return value;
|
|
852
930
|
}
|
|
931
|
+
/**
|
|
932
|
+
* Throw `missing_argument` when this one is required
|
|
933
|
+
*/
|
|
934
|
+
checkRequired(arg, argName, required) {
|
|
935
|
+
if (required && !arg) {
|
|
936
|
+
throw assertionError.get('missing_argument', argName);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
853
939
|
}
|
|
854
940
|
exports.KuzzleRequest = KuzzleRequest;
|
|
855
941
|
class Request extends KuzzleRequest {
|
|
@@ -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,
|
|
@@ -98,7 +98,7 @@ class ClusterIdCardHandler {
|
|
|
98
98
|
async createIdCard() {
|
|
99
99
|
let reserved = false;
|
|
100
100
|
do {
|
|
101
|
-
this.nodeId =
|
|
101
|
+
this.nodeId = name_generator_1.NameGenerator.generateRandomName({ prefix: 'knode' });
|
|
102
102
|
this.nodeIdKey = `${REDIS_PREFIX}${this.nodeId}`;
|
|
103
103
|
this.idCard = new IdCard({
|
|
104
104
|
birthdate: Date.now(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
* Kuzzle, a backend software, self-hostable and ready to use
|
|
3
4
|
* to power modern apps
|
|
@@ -18,16 +19,29 @@
|
|
|
18
19
|
* See the License for the specific language governing permissions and
|
|
19
20
|
* limitations under the License.
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
list: {
|
|
26
|
-
'delete': ['delete', 'deleteByQuery', 'mDelete'],
|
|
27
|
-
'get': ['get', 'mGet', 'search'],
|
|
28
|
-
'update': ['update', 'mUpdate', 'updateByQuery', 'upsert'],
|
|
29
|
-
'write': ['create', 'createOrReplace', 'mCreate', 'mCreateOrReplace', 'mReplace', 'replace']
|
|
30
|
-
},
|
|
31
|
-
namespace: 'generic:document',
|
|
32
|
-
notBefore: ['search', 'deleteByQuery', 'updateByQuery'],
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
33
24
|
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.documentEventAliases = void 0;
|
|
27
|
+
const documentController_1 = __importDefault(require("../api/controllers/documentController"));
|
|
28
|
+
function filter(obj, expectValue) {
|
|
29
|
+
const result = [];
|
|
30
|
+
for (const [action, event] of Object.entries(obj)) {
|
|
31
|
+
if (event === expectValue) {
|
|
32
|
+
result.push(action);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
exports.documentEventAliases = {
|
|
38
|
+
list: {
|
|
39
|
+
delete: filter(documentController_1.default.actions, 'delete'),
|
|
40
|
+
get: filter(documentController_1.default.actions, 'get'),
|
|
41
|
+
update: filter(documentController_1.default.actions, 'update'),
|
|
42
|
+
write: filter(documentController_1.default.actions, 'write'),
|
|
43
|
+
},
|
|
44
|
+
namespace: 'generic:document',
|
|
45
|
+
notBefore: ['search', 'deleteByQuery', 'updateByQuery', 'export'],
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=documentEventAliases.js.map
|
|
@@ -243,6 +243,15 @@ class Backend {
|
|
|
243
243
|
}
|
|
244
244
|
return this._sdk;
|
|
245
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Cluster node ID
|
|
248
|
+
*/
|
|
249
|
+
get nodeId() {
|
|
250
|
+
if (!this.started) {
|
|
251
|
+
throw runtimeError.get('unavailable_before_start', 'nodeId');
|
|
252
|
+
}
|
|
253
|
+
return this._kuzzle.id;
|
|
254
|
+
}
|
|
246
255
|
get _instanceProxy() {
|
|
247
256
|
return {
|
|
248
257
|
api: this._controllers,
|
|
@@ -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);
|
|
@@ -553,10 +555,16 @@ class HttpWsProtocol extends Protocol {
|
|
|
553
555
|
if (type.includes('multipart/form-data')) {
|
|
554
556
|
const parts = uWS.getParts(content, message.headers['content-type']);
|
|
555
557
|
message.content = {};
|
|
558
|
+
|
|
559
|
+
if (! parts) {
|
|
560
|
+
cb();
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
556
563
|
|
|
557
564
|
for (const part of parts) {
|
|
558
565
|
if (part.data.byteLength > this.maxFormFileSize) {
|
|
559
566
|
cb(HTTP_FILE_TOO_LARGE_ERROR);
|
|
567
|
+
return;
|
|
560
568
|
}
|
|
561
569
|
|
|
562
570
|
if (part.filename) {
|
|
@@ -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
|
|
|
@@ -6,7 +6,7 @@ interface EmbeddedRealtime extends RealtimeController {
|
|
|
6
6
|
* and, optionally, user events matching the provided filters will generate
|
|
7
7
|
* real-time notifications.
|
|
8
8
|
*
|
|
9
|
-
* @see https://docs.kuzzle.io/core/2/guides/main-concepts/
|
|
9
|
+
* @see https://docs.kuzzle.io/core/2/guides/main-concepts/realtime-engine/
|
|
10
10
|
*
|
|
11
11
|
* @param index Index name
|
|
12
12
|
* @param collection Collection name
|
|
@@ -48,11 +48,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
49
|
exports.EmbeddedSDK = void 0;
|
|
50
50
|
const kuzzle_sdk_1 = require("kuzzle-sdk");
|
|
51
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
51
52
|
const funnelProtocol_1 = require("./funnelProtocol");
|
|
52
53
|
const safeObject_1 = require("../../../util/safeObject");
|
|
53
54
|
const kerror = __importStar(require("../../../kerror"));
|
|
54
55
|
const impersonatedSdk_1 = __importDefault(require("./impersonatedSdk"));
|
|
55
56
|
const contextError = kerror.wrap('plugin', 'context');
|
|
57
|
+
const forbiddenEmbeddedActions = {
|
|
58
|
+
'auth': new Set([
|
|
59
|
+
'checkRights',
|
|
60
|
+
'createApiKey',
|
|
61
|
+
'createMyCredentials',
|
|
62
|
+
'credentialsExist',
|
|
63
|
+
'deleteApiKey',
|
|
64
|
+
'getCurrentUser',
|
|
65
|
+
'getMyCredentials',
|
|
66
|
+
'getMyRights',
|
|
67
|
+
'getStrategies',
|
|
68
|
+
'logout',
|
|
69
|
+
'refreshToken',
|
|
70
|
+
'searchApiKeys',
|
|
71
|
+
'updateMyCredentials',
|
|
72
|
+
'updateSelf',
|
|
73
|
+
'validateMyCredentials',
|
|
74
|
+
]),
|
|
75
|
+
};
|
|
76
|
+
const warnEmbeddedActions = {
|
|
77
|
+
'auth': {
|
|
78
|
+
'login': 'EmbeddedSDK.login is deprecated, use user impersonation instead',
|
|
79
|
+
}
|
|
80
|
+
};
|
|
56
81
|
/**
|
|
57
82
|
* Kuzzle embedded SDK to make API calls inside applications or plugins.
|
|
58
83
|
*/
|
|
@@ -91,6 +116,14 @@ class EmbeddedSDK extends kuzzle_sdk_1.Kuzzle {
|
|
|
91
116
|
? false
|
|
92
117
|
: options.propagate;
|
|
93
118
|
}
|
|
119
|
+
if (forbiddenEmbeddedActions[request.controller] !== undefined
|
|
120
|
+
&& forbiddenEmbeddedActions[request.controller].has(request.action)) {
|
|
121
|
+
throw kerror.get('api', 'process', 'forbidden_embedded_sdk_action', request.controller, request.action, ', use user impersonation or security controller instead');
|
|
122
|
+
}
|
|
123
|
+
const warning = lodash_1.default.get(warnEmbeddedActions, [request.controller, request.action]);
|
|
124
|
+
if (warning) {
|
|
125
|
+
global.kuzzle.log.warn(warning);
|
|
126
|
+
}
|
|
94
127
|
return super.query(request, options);
|
|
95
128
|
}
|
|
96
129
|
}
|
|
@@ -154,6 +154,41 @@
|
|
|
154
154
|
"class": "GatewayTimeoutError"
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
+
},
|
|
158
|
+
"debugger": {
|
|
159
|
+
"code": 5,
|
|
160
|
+
"errors": {
|
|
161
|
+
"not_enabled": {
|
|
162
|
+
"description": "The debugger is not enabled",
|
|
163
|
+
"code": 1,
|
|
164
|
+
"message": "Debugger is not enabled",
|
|
165
|
+
"class": "PreconditionError"
|
|
166
|
+
},
|
|
167
|
+
"monitor_already_running": {
|
|
168
|
+
"description": "The monitor is already running",
|
|
169
|
+
"code": 2,
|
|
170
|
+
"message": "The monitoring of \"%s\" is already running",
|
|
171
|
+
"class": "PreconditionError"
|
|
172
|
+
},
|
|
173
|
+
"monitor_not_running": {
|
|
174
|
+
"description": "The monitor is not running",
|
|
175
|
+
"code": 3,
|
|
176
|
+
"message": "The monitoring of \"%s\" is not running",
|
|
177
|
+
"class": "PreconditionError"
|
|
178
|
+
},
|
|
179
|
+
"native_debug_protocol_usage_denied": {
|
|
180
|
+
"description": "Usage of the native debug protocol is not allowed",
|
|
181
|
+
"code": 4,
|
|
182
|
+
"message": "Usage of the native debug protocol is not allowed",
|
|
183
|
+
"class": "PreconditionError"
|
|
184
|
+
},
|
|
185
|
+
"method_not_found": {
|
|
186
|
+
"description": "Debugger method not found",
|
|
187
|
+
"code": 5,
|
|
188
|
+
"message": "Debugger method \"%s\" not found.",
|
|
189
|
+
"class": "PreconditionError"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
157
192
|
}
|
|
158
193
|
}
|
|
159
194
|
}
|
|
@@ -182,6 +182,12 @@
|
|
|
182
182
|
"code": 13,
|
|
183
183
|
"message": "Rejected: Too many login attempts per second",
|
|
184
184
|
"class": "TooManyRequestsError"
|
|
185
|
+
},
|
|
186
|
+
"forbidden_embedded_sdk_action": {
|
|
187
|
+
"description": "A forbidden EmbdeddedSDK action has been called",
|
|
188
|
+
"code": 14,
|
|
189
|
+
"message": "The action %s:%s has been called while it is forbidden in the EmbeddedSDK%s.",
|
|
190
|
+
"class": "PluginImplementationError"
|
|
185
191
|
}
|
|
186
192
|
}
|
|
187
193
|
}
|
package/lib/kuzzle/kuzzle.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare const BACKEND_IMPORT_KEY = "backend:init:import";
|