kuzzle 2.17.8 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/index.d.ts +1 -0
  2. package/index.js +3 -0
  3. package/lib/api/controllers/adminController.js +9 -0
  4. package/lib/api/controllers/authController.js +8 -2
  5. package/lib/api/controllers/debugController.d.ts +59 -0
  6. package/lib/api/controllers/debugController.js +285 -0
  7. package/lib/api/controllers/documentController.js +49 -31
  8. package/lib/api/controllers/index.js +1 -0
  9. package/lib/api/controllers/indexController.js +10 -6
  10. package/lib/api/controllers/securityController.js +81 -43
  11. package/lib/api/controllers/serverController.js +2 -1
  12. package/lib/api/documentExtractor.js +51 -9
  13. package/lib/api/funnel.js +30 -8
  14. package/lib/api/httpRoutes.js +8 -1
  15. package/lib/api/openapi/OpenApiManager.js +3 -0
  16. package/lib/api/openapi/components/document/get.yaml +1 -1
  17. package/lib/api/openapi/components/document/update.yaml +1 -1
  18. package/lib/api/openapi/components/index.d.ts +1 -0
  19. package/lib/api/openapi/components/index.js +1 -0
  20. package/lib/api/openapi/components/security/index.d.ts +2 -0
  21. package/lib/api/openapi/components/security/index.js +10 -0
  22. package/lib/api/openapi/components/security/upsertUser.yaml +59 -0
  23. package/lib/api/request/kuzzleRequest.d.ts +58 -4
  24. package/lib/api/request/kuzzleRequest.js +163 -31
  25. package/lib/api/request/requestResponse.js +25 -0
  26. package/lib/cluster/idCardHandler.js +1 -1
  27. package/lib/config/default.config.js +3 -0
  28. package/lib/config/documentEventAliases.d.ts +7 -0
  29. package/lib/config/documentEventAliases.js +26 -12
  30. package/lib/core/backend/backend.d.ts +4 -0
  31. package/lib/core/backend/backend.js +9 -0
  32. package/lib/core/backend/backendController.d.ts +7 -1
  33. package/lib/core/backend/backendController.js +15 -3
  34. package/lib/core/network/protocols/httpwsProtocol.js +14 -6
  35. package/lib/core/plugin/plugin.js +7 -0
  36. package/lib/core/shared/sdk/embeddedSdk.d.ts +3 -3
  37. package/lib/core/shared/sdk/embeddedSdk.js +33 -0
  38. package/lib/core/shared/sdk/funnelProtocol.d.ts +1 -2
  39. package/lib/kerror/codes/0-core.json +35 -0
  40. package/lib/kerror/codes/1-services.json +1 -1
  41. package/lib/kerror/codes/2-api.json +6 -0
  42. package/lib/kerror/errors/kuzzleError.d.ts +1 -1
  43. package/lib/kuzzle/kuzzle.d.ts +1 -1
  44. package/lib/kuzzle/kuzzle.js +27 -8
  45. package/lib/model/security/user.js +5 -6
  46. package/lib/model/storage/apiKey.js +1 -6
  47. package/lib/service/storage/elasticsearch.js +143 -47
  48. package/lib/types/DebugModule.d.ts +23 -0
  49. package/lib/types/DebugModule.js +39 -0
  50. package/lib/types/config/SecurityConfiguration.d.ts +10 -0
  51. package/lib/types/index.d.ts +0 -1
  52. package/lib/types/index.js +0 -1
  53. package/lib/util/crypto.d.ts +1 -0
  54. package/lib/util/crypto.js +12 -0
  55. package/lib/util/dump-collection.d.ts +35 -0
  56. package/lib/util/dump-collection.js +11 -8
  57. package/lib/util/name-generator.d.ts +79 -0
  58. package/lib/util/name-generator.js +1409 -1345
  59. package/lib/util/time.d.ts +1 -0
  60. package/lib/util/time.js +9 -0
  61. package/package.json +19 -21
  62. package/lib/core/security/README.md +0 -224
  63. package/lib/core/shared/README.md +0 -3
  64. package/package-lock.json +0 -8403
@@ -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
- cookie: req.getHeader('cookie'),
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
 
@@ -1,12 +1,12 @@
1
- import { RealtimeController, Notification, JSONObject, ScopeOption, UserOption, Kuzzle } from 'kuzzle-sdk';
2
- import { RequestPayload, ResponsePayload } from '../../../types';
1
+ import { RealtimeController, Notification, JSONObject, ScopeOption, UserOption, Kuzzle, RequestPayload } from 'kuzzle-sdk';
2
+ import { ResponsePayload } from '../../../types';
3
3
  interface EmbeddedRealtime extends RealtimeController {
4
4
  /**
5
5
  * Subscribes by providing a set of filters: messages, document changes
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/6-realtime-engine/
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
  }
@@ -1,5 +1,4 @@
1
- import { KuzzleEventEmitter } from 'kuzzle-sdk';
2
- import { RequestPayload } from '../../../types';
1
+ import { KuzzleEventEmitter, RequestPayload } from 'kuzzle-sdk';
3
2
  export declare class FunnelProtocol extends KuzzleEventEmitter {
4
3
  private id;
5
4
  private connectionId;
@@ -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
  }
@@ -273,7 +273,7 @@
273
273
  "multiple_indice_alias": {
274
274
  "description": "The unique association between an indice and its alias has not been respected",
275
275
  "code": 48,
276
- "message": "An \"%s\" is not allowed to be associated with multiple \"%\". This is probably not linked to this request but rather the consequence of previous actions on ElasticSearch.",
276
+ "message": "An %s is not allowed to be associated with multiple %s. This is probably not linked to this request but rather the consequence of previous actions on ElasticSearch.",
277
277
  "class": "PreconditionError"
278
278
  },
279
279
  "invalid_multi_index_collection_usage": {
@@ -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
  }
@@ -10,7 +10,7 @@ export declare class KuzzleError extends Error {
10
10
  status: number;
11
11
  /**
12
12
  * Error unique code
13
- * @see https://docs.kuzzle.io/core/2/core/2/api/essentials/error-codes/
13
+ * @see https://docs.kuzzle.io/core/2/api/errors/error-codes/
14
14
  */
15
15
  code: number;
16
16
  /**
@@ -1 +1 @@
1
- export {};
1
+ export declare const BACKEND_IMPORT_KEY = "backend:init:import";
@@ -46,6 +46,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
46
46
  return (mod && mod.__esModule) ? mod : { "default": mod };
47
47
  };
48
48
  Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.BACKEND_IMPORT_KEY = void 0;
49
50
  const path_1 = __importDefault(require("path"));
50
51
  const murmurhash_native_1 = require("murmurhash-native");
51
52
  const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
@@ -78,7 +79,8 @@ const cluster_1 = __importDefault(require("../cluster"));
78
79
  const package_json_1 = require("../../package.json");
79
80
  const name_generator_1 = require("../util/name-generator");
80
81
  const openapi_1 = require("../api/openapi");
81
- const BACKEND_IMPORT_KEY = 'backend:init:import';
82
+ const crypto_1 = require("../util/crypto");
83
+ exports.BACKEND_IMPORT_KEY = 'backend:init:import';
82
84
  let _kuzzle = null;
83
85
  Reflect.defineProperty(global, 'kuzzle', {
84
86
  configurable: true,
@@ -201,7 +203,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
201
203
  this.log.info('[✔] Cluster initialized');
202
204
  }
203
205
  else {
204
- id = (0, name_generator_1.generateRandomName)('knode');
206
+ id = name_generator_1.NameGenerator.generateRandomName({ prefix: 'knode' });
205
207
  this.log.info('[X] Cluster disabled: single node mode.');
206
208
  }
207
209
  return id;
@@ -368,11 +370,10 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
368
370
  */
369
371
  async _waitForImportToFinish() {
370
372
  const importTypes = Object.keys(this.importTypes);
371
- while (importTypes.length) {
373
+ for (const importType of importTypes) {
372
374
  // If the import is done, we pop it from the queue to check the next one
373
- if (await this.ask('core:cache:internal:get', `${BACKEND_IMPORT_KEY}:${importTypes[0]}`)) {
374
- importTypes.shift();
375
- continue;
375
+ if (await this.ask('core:cache:internal:get', `${exports.BACKEND_IMPORT_KEY}:${importType}`)) {
376
+ return;
376
377
  }
377
378
  await bluebird_1.default.delay(1000);
378
379
  }
@@ -399,8 +400,26 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
399
400
  const lockedMutex = [];
400
401
  try {
401
402
  for (const [type, importMethod] of Object.entries(this.importTypes)) {
403
+ const importPayload = {};
404
+ switch (type) {
405
+ case 'fixtures':
406
+ lodash_1.default.set(importPayload, 'toSupport.fixtures', toSupport.fixtures);
407
+ break;
408
+ case 'mappings':
409
+ lodash_1.default.set(importPayload, 'toSupport.mappings', toSupport.mappings);
410
+ lodash_1.default.set(importPayload, 'toImport.mappings', toImport.mappings);
411
+ break;
412
+ case 'permissions':
413
+ lodash_1.default.set(importPayload, 'toSupport.securities', toSupport.securities);
414
+ lodash_1.default.set(importPayload, 'toImport.profiles', toImport.profiles);
415
+ lodash_1.default.set(importPayload, 'toImport.roles', toImport.roles);
416
+ lodash_1.default.set(importPayload, 'toImport.users', toImport.users);
417
+ break;
418
+ }
419
+ const importPayloadHash = (0, crypto_1.sha256)((0, json_stable_stringify_1.default)(importPayload));
402
420
  const mutex = new mutex_1.Mutex(`backend:import:${type}`, { timeout: 0 });
403
- const initialized = await this.ask('core:cache:internal:get', `${BACKEND_IMPORT_KEY}:${type}`) === '1';
421
+ const existingHash = await this.ask('core:cache:internal:get', `${exports.BACKEND_IMPORT_KEY}:${type}`);
422
+ const initialized = existingHash === importPayloadHash;
404
423
  const locked = await mutex.lock();
405
424
  await importMethod({ toImport, toSupport }, {
406
425
  firstCall: !initialized && locked,
@@ -409,7 +428,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
409
428
  });
410
429
  if (!initialized && locked) {
411
430
  lockedMutex.push(mutex);
412
- await this.ask('core:cache:internal:store', `${BACKEND_IMPORT_KEY}:${type}`, 1, { ttl: 5 * 60 * 1000 });
431
+ await this.ask('core:cache:internal:store', `${exports.BACKEND_IMPORT_KEY}:${type}`, importPayloadHash);
413
432
  }
414
433
  }
415
434
  await this._waitForImportToFinish();
@@ -47,9 +47,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
47
47
  };
48
48
  Object.defineProperty(exports, "__esModule", { value: true });
49
49
  exports.User = void 0;
50
- const rights_1 = __importDefault(require("./rights"));
51
- const bluebird_1 = __importDefault(require("bluebird"));
52
50
  const lodash_1 = __importDefault(require("lodash"));
51
+ const rights_1 = __importDefault(require("./rights"));
53
52
  const kerror = __importStar(require("../../kerror"));
54
53
  /**
55
54
  * @class User
@@ -73,7 +72,7 @@ class User {
73
72
  */
74
73
  async getRights() {
75
74
  const profiles = await this.getProfiles();
76
- const results = await bluebird_1.default.map(profiles, p => p.getRights());
75
+ const results = await Promise.all(profiles.map(p => p.getRights()));
77
76
  const rights = {};
78
77
  results.forEach(right => lodash_1.default.assignWith(rights, right, rights_1.default.merge));
79
78
  return rights;
@@ -97,15 +96,15 @@ class User {
97
96
  return false;
98
97
  }
99
98
  // Every target must be allowed by at least one profile
100
- return this.areTargetsAllowed(profiles, targets);
99
+ return this.areTargetsAllowed(request, profiles, targets);
101
100
  }
102
101
  /**
103
102
  * Verifies that every targets are allowed by at least one profile,
104
103
  * while skipping the ones that includes a wildcard since they will be expanded
105
104
  * later on, based on index and collections authorized for the given user.
106
105
  */
107
- async areTargetsAllowed(profiles, targets) {
108
- const profilesPolicies = await bluebird_1.default.map(profiles, profile => profile.getAllowedPolicies());
106
+ async areTargetsAllowed(request, profiles, targets) {
107
+ const profilesPolicies = await Promise.all(profiles.map(profile => profile.getAllowedPolicies(request)));
109
108
  // Every target must be allowed by at least one profile
110
109
  for (const target of targets) {
111
110
  // Skip targets with no Index or Collection
@@ -21,16 +21,11 @@
21
21
 
22
22
  'use strict';
23
23
 
24
- const crypto = require('crypto');
25
-
24
+ const { sha256 } = require('../../util/crypto');
26
25
  const debug = require('../../util/debug')('models:storage:apiKey');
27
26
  const kerror = require('../../kerror');
28
27
  const BaseModel = require('./baseModel');
29
28
 
30
- function sha256 (string) {
31
- return crypto.createHash('sha256').update(string).digest('hex');
32
- }
33
-
34
29
  class ApiKey extends BaseModel {
35
30
  constructor (_source, _id = null) {
36
31
  super(_source, _id);