kuzzle 2.18.1 → 2.19.2

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 (42) 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/debugController.d.ts +59 -0
  5. package/lib/api/controllers/debugController.js +285 -0
  6. package/lib/api/controllers/documentController.js +43 -29
  7. package/lib/api/controllers/index.js +1 -0
  8. package/lib/api/controllers/securityController.js +2 -2
  9. package/lib/api/documentExtractor.js +49 -8
  10. package/lib/api/funnel.js +30 -8
  11. package/lib/api/httpRoutes.js +6 -0
  12. package/lib/api/request/kuzzleRequest.d.ts +12 -4
  13. package/lib/api/request/kuzzleRequest.js +17 -13
  14. package/lib/cluster/idCardHandler.js +1 -1
  15. package/lib/config/default.config.js +3 -0
  16. package/lib/config/documentEventAliases.d.ts +7 -0
  17. package/lib/config/documentEventAliases.js +26 -12
  18. package/lib/core/backend/backend.d.ts +4 -0
  19. package/lib/core/backend/backend.js +9 -0
  20. package/lib/core/network/protocols/httpwsProtocol.js +6 -0
  21. package/lib/core/shared/sdk/embeddedSdk.d.ts +1 -1
  22. package/lib/core/shared/sdk/embeddedSdk.js +33 -0
  23. package/lib/kerror/codes/0-core.json +35 -0
  24. package/lib/kerror/codes/2-api.json +6 -0
  25. package/lib/kuzzle/kuzzle.d.ts +1 -1
  26. package/lib/kuzzle/kuzzle.js +27 -8
  27. package/lib/model/storage/apiKey.js +1 -6
  28. package/lib/service/storage/elasticsearch.js +40 -13
  29. package/lib/types/DebugModule.d.ts +23 -0
  30. package/lib/types/DebugModule.js +39 -0
  31. package/lib/types/config/SecurityConfiguration.d.ts +10 -0
  32. package/lib/util/crypto.d.ts +1 -0
  33. package/lib/util/crypto.js +12 -0
  34. package/lib/util/name-generator.d.ts +79 -0
  35. package/lib/util/name-generator.js +1409 -1345
  36. package/lib/util/time.d.ts +1 -0
  37. package/lib/util/time.js +9 -0
  38. package/npm-shrinkwrap.json +19422 -0
  39. package/package.json +4 -6
  40. package/lib/core/security/README.md +0 -224
  41. package/lib/core/shared/README.md +0 -3
  42. package/package-lock.json +0 -8422
package/lib/api/funnel.js CHANGED
@@ -33,6 +33,7 @@ const {
33
33
  BulkController,
34
34
  ClusterController,
35
35
  CollectionController,
36
+ DebugController,
36
37
  DocumentController,
37
38
  IndexController,
38
39
  MemoryStorageController,
@@ -40,7 +41,7 @@ const {
40
41
  SecurityController,
41
42
  ServerController,
42
43
  } = require('./controllers');
43
- const documentEventAliases = require('../config/documentEventAliases');
44
+ const { documentEventAliases } = require('../config/documentEventAliases');
44
45
  const DocumentExtractor = require('./documentExtractor');
45
46
  const sdkCompatibility = require('../config/sdkCompatibility');
46
47
  const RateLimiter = require('./rateLimiter');
@@ -115,6 +116,7 @@ class Funnel {
115
116
  this.controllers.set('bulk', new BulkController());
116
117
  this.controllers.set('cluster', new ClusterController());
117
118
  this.controllers.set('collection', new CollectionController());
119
+ this.controllers.set('debug', new DebugController());
118
120
  this.controllers.set('document', new DocumentController());
119
121
  this.controllers.set('index', new IndexController());
120
122
  this.controllers.set('realtime', new RealtimeController());
@@ -344,19 +346,39 @@ class Funnel {
344
346
  modifiedRequest.id,
345
347
  processResult);
346
348
 
347
- callback(null, processResult);
348
-
349
- // disables a bluebird warning in dev. mode triggered when
350
- // a promise is created and not returned
351
- return null;
349
+ return global.kuzzle.pipe(
350
+ 'request:afterExecution',
351
+ {
352
+ request: _request,
353
+ result: processResult,
354
+ success: true,
355
+ })
356
+ .then(pipeEvent => {
357
+ callback(null, pipeEvent.result);
358
+
359
+ // disables a bluebird warning in dev. mode triggered when
360
+ // a promise is created and not returned
361
+ return null;
362
+ });
352
363
  }).catch(err => {
353
364
  debug('Error processing request %s: %a', modifiedRequest.id, err);
354
- return this._executeError(err, modifiedRequest, true, callback);
365
+ return global.kuzzle.pipe(
366
+ 'request:afterExecution',
367
+ {
368
+ error: err,
369
+ request: modifiedRequest,
370
+ success: false,
371
+ }).then(pipeEvent => this._executeError(pipeEvent.error, pipeEvent.request, true, callback));
355
372
  });
356
373
  })
357
374
  .catch(err => {
358
375
  debug('Error processing request %s: %a', req.id, err);
359
- return this._executeError(err, req, true, callback);
376
+ return global.kuzzle.pipe('request:afterExecution',
377
+ {
378
+ error: err,
379
+ request: req,
380
+ success: false,
381
+ }).then(pipeEvent => this._executeError(pipeEvent.error, pipeEvent.request, true, callback));
360
382
  });
361
383
  });
362
384
  }, this, request);
@@ -52,6 +52,12 @@ const routes = [
52
52
  { verb: 'get', path: '/credentials/:strategy/_me', controller: 'auth', action: 'getMyCredentials', deprecated: { since: '2.4.0', message: 'Use this route instead: http://kuzzle:7512/_me/credentials/:strategy' } }, // @deprecated
53
53
  { verb: 'get', path: '/credentials/:strategy/_me/_exists', controller: 'auth', action: 'credentialsExist', deprecated: { since: '2.4.0', message: 'Use this route instead: http://kuzzle:7512/_me/credentials/:strategy/_exists' } }, // @deprecated
54
54
 
55
+ { verb: 'get', path: '/debug/_nodeVersion', controller: 'debug', action: 'nodeVersion' },
56
+ { verb: 'post', path: '/debug/_post', controller: 'debug', action: 'post' },
57
+ { verb: 'post', path: '/debug/_enable', controller: 'debug', action: 'enable' },
58
+ { verb: 'post', path: '/debug/_disable', controller: 'debug', action: 'disable' },
59
+
60
+
55
61
  // We need to expose a GET method for "login" action in order to make authentication protocol like Oauth2 or CAS work:
56
62
  { verb: 'get', path: '/_login/:strategy', controller: 'auth', action: 'login' },
57
63
 
@@ -324,11 +324,15 @@ export declare class KuzzleRequest {
324
324
  /**
325
325
  * Returns the index specified in the request
326
326
  */
327
- getIndex(): string;
327
+ getIndex({ required }?: {
328
+ required?: boolean;
329
+ }): string;
328
330
  /**
329
331
  * Returns the collection specified in the request
330
332
  */
331
- getCollection(): string;
333
+ getCollection({ required }?: {
334
+ required?: boolean;
335
+ }): string;
332
336
  /**
333
337
  * Returns the index and collection specified in the request
334
338
  */
@@ -366,8 +370,8 @@ export declare class KuzzleRequest {
366
370
  */
367
371
  getUser(): User | null;
368
372
  /**
369
- * Returns the search body query according to the http method
370
- */
373
+ * Returns the search body query according to the http method
374
+ */
371
375
  getSearchBody(): JSONObject;
372
376
  /**
373
377
  * Returns the search params.
@@ -446,6 +450,10 @@ export declare class KuzzleRequest {
446
450
  * @param querystring if true, the object is expected to be found in a querystring
447
451
  */
448
452
  private _getObject;
453
+ /**
454
+ * Throw `missing_argument` when this one is required
455
+ */
456
+ private checkRequired;
449
457
  }
450
458
  export declare class Request extends KuzzleRequest {
451
459
  }
@@ -627,22 +627,18 @@ class KuzzleRequest {
627
627
  /**
628
628
  * Returns the index specified in the request
629
629
  */
630
- getIndex() {
630
+ getIndex({ required = true } = {}) {
631
631
  const index = this.input.args.index;
632
- if (!index) {
633
- throw assertionError.get('missing_argument', 'index');
634
- }
635
- return index;
632
+ this.checkRequired(index, 'index', required);
633
+ return index ? String(index) : null;
636
634
  }
637
635
  /**
638
636
  * Returns the collection specified in the request
639
637
  */
640
- getCollection() {
638
+ getCollection({ required = true } = {}) {
641
639
  const collection = this.input.args.collection;
642
- if (!collection) {
643
- throw assertionError.get('missing_argument', 'collection');
644
- }
645
- return collection;
640
+ this.checkRequired(collection, 'collection', required);
641
+ return collection ? String(collection) : null;
646
642
  }
647
643
  /**
648
644
  * Returns the index and collection specified in the request
@@ -699,7 +695,7 @@ class KuzzleRequest {
699
695
  if (typeof id !== 'string') {
700
696
  throw assertionError.get('invalid_type', '_id', 'string');
701
697
  }
702
- return id;
698
+ return String(id);
703
699
  }
704
700
  /**
705
701
  * Returns the current user kuid
@@ -720,8 +716,8 @@ class KuzzleRequest {
720
716
  return null;
721
717
  }
722
718
  /**
723
- * Returns the search body query according to the http method
724
- */
719
+ * Returns the search body query according to the http method
720
+ */
725
721
  getSearchBody() {
726
722
  if (this.context.connection.protocol !== 'http'
727
723
  || this.context.connection.misc.verb !== 'GET') {
@@ -932,6 +928,14 @@ class KuzzleRequest {
932
928
  }
933
929
  return value;
934
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
+ }
935
939
  }
936
940
  exports.KuzzleRequest = KuzzleRequest;
937
941
  class Request extends KuzzleRequest {
@@ -98,7 +98,7 @@ class ClusterIdCardHandler {
98
98
  async createIdCard() {
99
99
  let reserved = false;
100
100
  do {
101
- this.nodeId = (0, name_generator_1.generateRandomName)('knode');
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(),
@@ -105,6 +105,9 @@ const defaultConfig = {
105
105
  }
106
106
  },
107
107
  security: {
108
+ debug: {
109
+ native_debug_protocol: false
110
+ },
108
111
  restrictedProfileIds: ['default'],
109
112
  jwt: {
110
113
  algorithm: 'HS256',
@@ -0,0 +1,7 @@
1
+ interface EventAliases {
2
+ list: Record<string, unknown>;
3
+ namespace: string;
4
+ notBefore: string[];
5
+ }
6
+ export declare const documentEventAliases: EventAliases;
7
+ export {};
@@ -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
- 'use strict';
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
@@ -168,6 +168,10 @@ export declare class Backend {
168
168
  * EmbeddedSDK instance
169
169
  */
170
170
  get sdk(): EmbeddedSDK;
171
+ /**
172
+ * Cluster node ID
173
+ */
174
+ get nodeId(): string;
171
175
  private get _instanceProxy();
172
176
  /**
173
177
  * Try to read the current commit hash.
@@ -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,
@@ -555,10 +555,16 @@ class HttpWsProtocol extends Protocol {
555
555
  if (type.includes('multipart/form-data')) {
556
556
  const parts = uWS.getParts(content, message.headers['content-type']);
557
557
  message.content = {};
558
+
559
+ if (! parts) {
560
+ cb();
561
+ return;
562
+ }
558
563
 
559
564
  for (const part of parts) {
560
565
  if (part.data.byteLength > this.maxFormFileSize) {
561
566
  cb(HTTP_FILE_TOO_LARGE_ERROR);
567
+ return;
562
568
  }
563
569
 
564
570
  if (part.filename) {
@@ -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/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
  }
@@ -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
  }
@@ -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();
@@ -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);
@@ -182,7 +182,7 @@ class ElasticSearch extends Service {
182
182
  /**
183
183
  * Translate Koncorde filters to Elasticsearch query
184
184
  *
185
- * @param {Object} koncordeFilters - Set of valid Koncorde filters
185
+ * @param {Object} filters - Set of valid Koncorde filters
186
186
  * @returns {Object} Equivalent Elasticsearch query
187
187
  */
188
188
  translateKoncordeFilters (filters) {
@@ -1435,10 +1435,9 @@ class ElasticSearch extends Service {
1435
1435
  // rollback the whole operation. Since mappings can't be rollback, we try to
1436
1436
  // update the settings first, then the mappings and we rollback the settings
1437
1437
  // if putMappings fail.
1438
- let indiceSettings;
1438
+ let indexSettings;
1439
1439
  try {
1440
- const response = await this._client.indices.getSettings(esRequest);
1441
- indiceSettings = response.body[esRequest.index].settings;
1440
+ indexSettings = await this._getSettings(esRequest);
1442
1441
  }
1443
1442
  catch (error) {
1444
1443
  throw this._esWrapper.formatESError(error);
@@ -1460,11 +1459,7 @@ class ElasticSearch extends Service {
1460
1459
  }
1461
1460
  }
1462
1461
  catch (error) {
1463
- const allowedSettings = {
1464
- index: _.omit(
1465
- indiceSettings.index,
1466
- ['creation_date', 'provided_name', 'uuid', 'version'])
1467
- };
1462
+ const allowedSettings = this.getAllowedIndexSettings(indexSettings);
1468
1463
 
1469
1464
  // Rollback to previous settings
1470
1465
  if (! _.isEmpty(settings)) {
@@ -1477,6 +1472,20 @@ class ElasticSearch extends Service {
1477
1472
  return null;
1478
1473
  }
1479
1474
 
1475
+ /**
1476
+ * Given index settings we return a new version of index settings
1477
+ * only with allowed settings that can be set (during update or create index).
1478
+ * @param indexSettings the index settings
1479
+ * @returns {{index: *}} a new index settings with only allowed settings.
1480
+ */
1481
+ getAllowedIndexSettings (indexSettings) {
1482
+ return {
1483
+ index: _.omit(
1484
+ indexSettings.index,
1485
+ ['creation_date', 'provided_name', 'uuid', 'version'])
1486
+ };
1487
+ }
1488
+
1480
1489
  /**
1481
1490
  * Sends an empty UpdateByQuery request to update the search index
1482
1491
  *
@@ -1583,7 +1592,7 @@ class ElasticSearch extends Service {
1583
1592
  }
1584
1593
 
1585
1594
  /**
1586
- * Empties the content of a collection. Keep the existing mapping.
1595
+ * Empties the content of a collection. Keep the existing mapping and settings.
1587
1596
  *
1588
1597
  * @param {String} index - Index name
1589
1598
  * @param {String} collection - Collection name
@@ -1592,6 +1601,7 @@ class ElasticSearch extends Service {
1592
1601
  */
1593
1602
  async truncateCollection (index, collection) {
1594
1603
  let mappings;
1604
+ let settings;
1595
1605
 
1596
1606
  const esRequest = {
1597
1607
  index: await this._getIndice(index, collection)
@@ -1599,7 +1609,11 @@ class ElasticSearch extends Service {
1599
1609
 
1600
1610
  try {
1601
1611
  mappings = await this.getMapping(index, collection, { includeKuzzleMeta: true });
1602
-
1612
+ settings = await this._getSettings(esRequest);
1613
+ settings = {
1614
+ ...settings,
1615
+ ...this.getAllowedIndexSettings(settings)
1616
+ };
1603
1617
  await this._client.indices.delete(esRequest);
1604
1618
 
1605
1619
  await this._client.indices.create({
@@ -1608,7 +1622,8 @@ class ElasticSearch extends Service {
1608
1622
  aliases: {
1609
1623
  [this._getAlias(index, collection)]: {}
1610
1624
  },
1611
- mappings
1625
+ mappings,
1626
+ settings
1612
1627
  }
1613
1628
  });
1614
1629
 
@@ -2535,7 +2550,7 @@ class ElasticSearch extends Service {
2535
2550
  *
2536
2551
  * @param {String} index - Index name
2537
2552
  * @param {String} collection - Collection name
2538
- * @param {Array.<String>} documents - Documents IDs
2553
+ * @param {Array.<String>} ids - Documents IDs
2539
2554
  * @param {Object} options - timeout (undefined), refresh (undefined)
2540
2555
  *
2541
2556
  * @returns {Promise.<{ documents, errors }>
@@ -2857,6 +2872,18 @@ class ElasticSearch extends Service {
2857
2872
  return body[0].index;
2858
2873
  }
2859
2874
 
2875
+ /**
2876
+ * Given an ES Request returns the settings of the corresponding indice.
2877
+ *
2878
+ * @param esRequest the ES Request with wanted settings.
2879
+ * @return {Promise<*>} the settings of the indice.
2880
+ * @private
2881
+ */
2882
+ async _getSettings (esRequest) {
2883
+ const response = await this._client.indices.getSettings(esRequest);
2884
+ return response.body[esRequest.index].settings;
2885
+ }
2886
+
2860
2887
  /**
2861
2888
  * Given index + collection, returns an available indice name.
2862
2889
  * Use this function when creating the associated indice. Otherwise use `_getAlias`.