kuzzle 2.14.14 → 2.15.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.
@@ -88,6 +88,7 @@ class ClusterSubscriber {
88
88
  NewRealtimeRoom: this.handleNewRealtimeRoom,
89
89
  NodeEvicted: this.handleNodeEviction,
90
90
  NodeShutdown: this.handleNodeShutdown,
91
+ RefreshIndexCache: this.handleRefreshIndexCache,
91
92
  RefreshValidators: this.handleRefreshValidators,
92
93
  RemoveAuthStrategy: this.handleAuthStrategyRemoval,
93
94
  RemoveCollection: this.handleCollectionRemoval,
@@ -210,7 +211,7 @@ class ClusterSubscriber {
210
211
 
211
212
  const message = decoder.toObject(decoder.decode(data));
212
213
 
213
- if (!await this.validateMessage(message)) {
214
+ if (! await this.validateMessage(message)) {
214
215
  return;
215
216
  }
216
217
 
@@ -497,6 +498,14 @@ class ClusterSubscriber {
497
498
  await global.kuzzle.validation.curateSpecification();
498
499
  }
499
500
 
501
+ /**
502
+ * Handles manual refresh of the index cache
503
+ */
504
+ async handleRefreshIndexCache () {
505
+ debug('Index cache manually refresh received from node %s', this.remoteNodeId);
506
+ await global.kuzzle.ask('core:storage:public:cache:refresh', { from: 'cluster' });
507
+ }
508
+
500
509
  /**
501
510
  * Invalidates a profile to force reloading it from the storage space
502
511
  *
@@ -15,14 +15,15 @@ class IDCardRenewer {
15
15
  }
16
16
 
17
17
  async init (config) {
18
- if (!this.disposed) {
19
- return; // Already intialized
18
+ if (! this.disposed) {
19
+ return; // Already initialized
20
20
  }
21
21
 
22
22
  this.disposed = false;
23
23
  this.nodeIdKey = config.nodeIdKey;
24
24
  this.refreshDelay = config.refreshDelay || 2000;
25
-
25
+ this.refreshMultiplier = config.refreshMultiplier;
26
+
26
27
  /**
27
28
  * Since we do not have access to the Kuzzle Context we can't use kuzzle.ask('core:cache:internal:*',...),
28
29
  * so we need to have an instance of Redis similar to the one used in the Cache Engine
@@ -48,6 +49,9 @@ class IDCardRenewer {
48
49
  this.renewIDCard.bind(this),
49
50
  this.refreshDelay);
50
51
  }
52
+
53
+ // Notify that the worker is running and updating the ID Card
54
+ this.parentPort.postMessage({ initialized: true });
51
55
  }
52
56
 
53
57
  async initRedis (config, name) {
@@ -63,7 +67,7 @@ class IDCardRenewer {
63
67
  try {
64
68
  const refreshed = await this.redis.commands.pexpire(
65
69
  this.nodeIdKey,
66
- this.refreshDelay * 3);
70
+ this.refreshDelay * this.refreshMultiplier);
67
71
  // Unable to refresh the key in time before it expires
68
72
  // => this node is too slow, we need to remove it from the cluster
69
73
  if (refreshed === 0) {
@@ -99,7 +103,8 @@ class IDCardRenewer {
99
103
 
100
104
  if (!isMainThread) {
101
105
  const idCardRenewer = new IDCardRenewer();
102
- parentPort.on('message', async (message) => {
106
+
107
+ parentPort.on('message', async message => {
103
108
  if (message.action === 'start') {
104
109
  // Simulate basic global Kuzzle Context
105
110
  global.kuzzle = { ...message.kuzzle };
@@ -111,11 +116,12 @@ if (!isMainThread) {
111
116
  };
112
117
  // Should never throw
113
118
  await idCardRenewer.init(message);
114
- } else if (message.action === 'dispose') {
119
+ }
120
+ else if (message.action === 'dispose') {
115
121
  // Should never throw
116
122
  await idCardRenewer.dispose();
117
123
  }
118
- });
124
+ });
119
125
  }
120
126
 
121
127
  module.exports = { IDCardRenewer };
@@ -46,6 +46,7 @@ const routes = [
46
46
  { verb: 'get', path: '/validations/_scroll/:scrollId', controller: 'collection', action: 'scrollSpecifications' },
47
47
  { verb: 'get', path: '/:index/_list', controller: 'collection', action: 'list' },
48
48
 
49
+ { verb: 'post', path: '/admin/_refreshIndexCache', controller: 'admin', action: 'refreshIndexCache' },
49
50
  { verb: 'post', path: '/admin/_resetCache', controller: 'admin', action: 'resetCache' },
50
51
  { verb: 'post', path: '/admin/_resetSecurity', controller: 'admin', action: 'resetSecurity' },
51
52
  { verb: 'post', path: '/admin/_resetDatabase', controller: 'admin', action: 'resetDatabase' },
@@ -180,6 +180,15 @@ class CacheEngine {
180
180
  global.kuzzle.onAsk(
181
181
  'core:cache:internal:store',
182
182
  (key, value, opts) => this.internal.store(key, value, opts));
183
+
184
+ /**
185
+ * Executes an arbitrary NATIVE cache command directly
186
+ * @param {string} command
187
+ * @param {Array} args -- command arguments
188
+ */
189
+ global.kuzzle.onAsk(
190
+ 'core:cache:internal:execute',
191
+ (command, ...args) => this.internal.exec(command, ...args));
183
192
  }
184
193
 
185
194
  registerPublicEvents () {
@@ -381,7 +381,7 @@ class HotelClerk {
381
381
  }, requestContext);
382
382
  await this.module.notifier.notifyUser(roomId, request, 'out', { count: room.size });
383
383
  // Do not send an unsubscription notification if the room has been destroyed
384
- // @aschen Why ?
384
+ // because the other nodes already had destroyed it in the full state
385
385
  if (notify
386
386
  && this.rooms.has(roomId)
387
387
  && room.channels.size > 0) {
@@ -417,6 +417,11 @@ class HotelClerk {
417
417
  async removeRoom(roomId) {
418
418
  this.roomsCount--;
419
419
  this.rooms.delete(roomId);
420
+ // We have to ask the cluster to dispatch the room removal event.
421
+ // The cluster will also remove the room from Koncorde if no other node
422
+ // uses it.
423
+ // (this node may have no subscribers on it, but other nodes might)
424
+ await global.kuzzle.ask('cluster:realtime:room:remove', roomId);
420
425
  // @deprecated -- to be removed in the next major version
421
426
  try {
422
427
  await global.kuzzle.pipe('room:remove', roomId);
@@ -424,11 +429,6 @@ class HotelClerk {
424
429
  catch (e) {
425
430
  return;
426
431
  }
427
- // We have to ask the cluster to dispatch the room removal event.
428
- // The cluster will also remove the room from Koncorde if no other node
429
- // uses it.
430
- // (this node may have no subscribers on it, but other nodes might)
431
- await global.kuzzle.ask('cluster:realtime:room:remove', roomId);
432
432
  }
433
433
  /**
434
434
  * Subscribes a connection to an existing room.
@@ -249,9 +249,7 @@ class NotifierController {
249
249
  if (cache !== null) {
250
250
  const stopListening = difference(JSON.parse(cache), rooms);
251
251
 
252
- await this.notifyDocument(stopListening, request, 'out', 'replace', {
253
- _id: document._id,
254
- });
252
+ await this.notifyDocument(stopListening, request, 'out', 'replace', document);
255
253
  }
256
254
 
257
255
  return rooms;
@@ -281,25 +279,29 @@ class NotifierController {
281
279
  : [];
282
280
 
283
281
  const result = await Bluebird.map(documents, (doc, index) => {
284
- switch(action) {
282
+ switch (action) {
285
283
  case actionEnum.CREATE:
286
284
  return this.notifyDocumentCreate(request, doc);
285
+
287
286
  case actionEnum.DELETE:
288
287
  return this.notifyDocumentDelete(request, doc);
288
+
289
289
  case actionEnum.REPLACE:
290
290
  return this.notifyDocumentReplace(request, doc, cache[index]);
291
+
291
292
  case actionEnum.UPDATE:
292
293
  return this.notifyDocumentUpdate(request, doc, cache[index]);
294
+
293
295
  case actionEnum.UPSERT:
294
- if (doc.created) {
295
- return this.notifyDocumentCreate(request, doc);
296
- }
297
- return this.notifyDocumentUpdate(request, doc, cache[index]);
296
+ return doc.created
297
+ ? this.notifyDocumentCreate(request, doc)
298
+ : this.notifyDocumentUpdate(request, doc, cache[index]);
299
+
298
300
  case actionEnum.WRITE:
299
- if (doc.created) {
300
- return this.notifyDocumentCreate(request, doc);
301
- }
302
- return this.notifyDocumentReplace(request, doc, cache[index]);
301
+ return doc.created
302
+ ? this.notifyDocumentCreate(request, doc)
303
+ : this.notifyDocumentReplace(request, doc, cache[index]);
304
+
303
305
  default:
304
306
  throw kerror.get('core', 'fatal', 'assertion_failed', `unknown notify action "${doc.action}"`);
305
307
  }
@@ -342,9 +344,7 @@ class NotifierController {
342
344
  if (cache !== null) {
343
345
  const stopListening = difference(JSON.parse(cache), rooms);
344
346
 
345
- await this.notifyDocument(stopListening, request, 'out', 'update', {
346
- _id: document._id,
347
- });
347
+ await this.notifyDocument(stopListening, request, 'out', 'update', document);
348
348
  }
349
349
 
350
350
  return rooms;
@@ -361,9 +361,7 @@ class NotifierController {
361
361
  const rooms = this._test(request, document._source, document._id);
362
362
 
363
363
  if (rooms.length > 0) {
364
- await this.notifyDocument(rooms, request, 'out', 'delete', {
365
- _id: document._id,
366
- });
364
+ await this.notifyDocument(rooms, request, 'out', 'delete', document);
367
365
  }
368
366
 
369
367
  return [];
@@ -22,7 +22,7 @@
22
22
  'use strict';
23
23
 
24
24
  const Elasticsearch = require('../../service/storage/elasticsearch');
25
- const IndexCache = require('./indexCache');
25
+ const { IndexCache } = require('./indexCache');
26
26
  const { isPlainObject } = require('../../util/safeObject');
27
27
  const kerror = require('../../kerror');
28
28
  const { Mutex } = require('../../util/mutex');
@@ -42,7 +42,7 @@ class ClientAdapter {
42
42
  global.kuzzle.config.services.storageEngine,
43
43
  scope);
44
44
  this.scope = scope;
45
- this.cache = new IndexCache(scope);
45
+ this.cache = new IndexCache();
46
46
  }
47
47
 
48
48
  async init () {
@@ -57,6 +57,13 @@ class ClientAdapter {
57
57
 
58
58
  // Global store events registration
59
59
 
60
+ /**
61
+ * Manually refresh the index cache (e.g. after alias creation)
62
+ */
63
+ global.kuzzle.onAsk(
64
+ `core:storage:${this.scope}:cache:refresh`,
65
+ () => this.populateCache());
66
+
60
67
  /**
61
68
  * Return information about the instantiated ES service
62
69
  * @returns {Promise.<Object>}
@@ -77,7 +84,6 @@ class ClientAdapter {
77
84
  }
78
85
 
79
86
  async createIndex (index, { indexCacheOnly=false, propagate=true } = {}) {
80
-
81
87
  if (this.cache.hasIndex(index)) {
82
88
  throw servicesError.get('index_already_exists', this.scope, index);
83
89
  }
@@ -264,7 +270,7 @@ class ClientAdapter {
264
270
  `core:storage:${this.scope}:collection:update`,
265
271
  (index, collection, changes) => {
266
272
  this.cache.assertCollectionExists(index, collection);
267
- this.client.updateCollection(index, collection, changes);
273
+ return this.client.updateCollection(index, collection, changes);
268
274
  });
269
275
  }
270
276
 
@@ -856,7 +862,7 @@ class ClientAdapter {
856
862
  * @returns {Promise}
857
863
  */
858
864
  async loadMappings (
859
- fixtures = {},
865
+ fixtures = {},
860
866
  options = {
861
867
  indexCacheOnly: false,
862
868
  propagate: true,
@@ -0,0 +1,55 @@
1
+ export declare class IndexCache {
2
+ /**
3
+ * Index map: each entry holds a set of collection names
4
+ *
5
+ * Map<index, Set<collection>>
6
+ */
7
+ private indexes;
8
+ constructor();
9
+ /**
10
+ * Cache a new index
11
+ *
12
+ * @return true if an index was added, false if there is no modification
13
+ */
14
+ addIndex(index: string): boolean;
15
+ /**
16
+ * Cache a new collection
17
+ */
18
+ addCollection(index: string, collection: string): void;
19
+ /**
20
+ * Check an index existence
21
+ */
22
+ hasIndex(index: string): boolean;
23
+ /**
24
+ * Check a collection existence
25
+ */
26
+ hasCollection(index: string, collection: string): boolean;
27
+ /**
28
+ * Return the list of cached indexes
29
+ */
30
+ listIndexes(): string[];
31
+ /**
32
+ * Return the list of an index collections
33
+ *
34
+ * @throws If the provided index does not exist
35
+ */
36
+ listCollections(index: string): string[];
37
+ /**
38
+ * Remove an index from the cache
39
+ */
40
+ removeIndex(index: string): void;
41
+ /**
42
+ * Remove a collection from the cache
43
+ */
44
+ removeCollection(index: string, collection: string): void;
45
+ /**
46
+ * Assert that the provided index exists
47
+ *
48
+ * @throws If the index does not exist
49
+ */
50
+ assertIndexExists(index: string): void;
51
+ /**
52
+ * Assert that the provided index and collection exist
53
+ */
54
+ assertCollectionExists(index: string, collection: string): void;
55
+ }
@@ -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,142 +19,108 @@
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
- const kerror = require('../../kerror').wrap('services', 'storage');
25
-
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.IndexCache = void 0;
27
+ const kerror_1 = __importDefault(require("../../kerror"));
28
+ const storageError = kerror_1.default.wrap('services', 'storage');
26
29
  class IndexCache {
27
- /**
28
- * @param {storeScopeEnum} scope
29
- */
30
- constructor (scope) {
31
- this.scope = scope;
32
-
30
+ constructor() {
31
+ /**
32
+ * Index map: each entry holds a set of collection names
33
+ *
34
+ * Map<index, Set<collection>>
35
+ */
36
+ this.indexes = new Map();
37
+ this.indexes = new Map();
38
+ }
39
+ /**
40
+ * Cache a new index
41
+ *
42
+ * @return true if an index was added, false if there is no modification
43
+ */
44
+ addIndex(index) {
45
+ if (this.indexes.has(index)) {
46
+ return false;
47
+ }
48
+ this.indexes.set(index, new Set());
49
+ return true;
50
+ }
51
+ /**
52
+ * Cache a new collection
53
+ */
54
+ addCollection(index, collection) {
55
+ this.addIndex(index);
56
+ const collections = this.indexes.get(index);
57
+ collections.add(collection);
58
+ }
59
+ /**
60
+ * Check an index existence
61
+ */
62
+ hasIndex(index) {
63
+ return this.indexes.has(index);
64
+ }
65
+ /**
66
+ * Check a collection existence
67
+ */
68
+ hasCollection(index, collection) {
69
+ const collections = this.indexes.get(index);
70
+ if (!collections) {
71
+ return false;
72
+ }
73
+ return collections.has(collection);
74
+ }
75
+ /**
76
+ * Return the list of cached indexes
77
+ */
78
+ listIndexes() {
79
+ return Array.from(this.indexes.keys());
80
+ }
33
81
  /**
34
- * Index map: each entry holds a set of collection names
35
- * @type {Map.<String, Set>}
82
+ * Return the list of an index collections
83
+ *
84
+ * @throws If the provided index does not exist
36
85
  */
37
- this.indexes = new Map();
38
- }
39
-
40
- /**
41
- * Cache a new index
42
- * @param {string} index
43
- * @return {boolean} true if an index was added, false if there is no
44
- * modification
45
- */
46
- addIndex (index) {
47
- if (this.indexes.has(index)) {
48
- return false;
86
+ listCollections(index) {
87
+ this.assertIndexExists(index);
88
+ return Array.from(this.indexes.get(index));
49
89
  }
50
-
51
- this.indexes.set(index, new Set());
52
-
53
- return true;
54
- }
55
-
56
- /**
57
- * Cache a new collection
58
- * @param {string} index
59
- * @param {string} collection
60
- */
61
- addCollection (index, collection) {
62
- this.addIndex(index);
63
- const collections = this.indexes.get(index);
64
-
65
- collections.add(collection);
66
- }
67
-
68
- /**
69
- * Check an index existence
70
- * @param {string} index
71
- * @returns {boolean}
72
- */
73
- hasIndex (index) {
74
- return this.indexes.has(index);
75
- }
76
-
77
- /**
78
- * Check a collection existence
79
- * @param {string} index
80
- * @param {string} collection
81
- * @returns {boolean}
82
- */
83
- hasCollection (index, collection) {
84
- const collections = this.indexes.get(index);
85
-
86
- if (!collections) {
87
- return false;
90
+ /**
91
+ * Remove an index from the cache
92
+ */
93
+ removeIndex(index) {
94
+ this.indexes.delete(index);
88
95
  }
89
-
90
- return collections.has(collection);
91
- }
92
-
93
- /**
94
- * Return the list of cached indexes
95
- * @returns {string[]}
96
- */
97
- listIndexes () {
98
- return Array.from(this.indexes.keys());
99
- }
100
-
101
- /**
102
- * Return the list of an index' collections
103
- * @param {string} index
104
- * @returns {string[]}
105
- * @throws If the provided index does not exist
106
- */
107
- listCollections (index) {
108
- this.assertIndexExists(index);
109
-
110
- return Array.from(this.indexes.get(index));
111
- }
112
-
113
- /**
114
- * Remove an index from the cache
115
- * @param {string} index
116
- */
117
- removeIndex (index) {
118
- this.indexes.delete(index);
119
- }
120
-
121
- /**
122
- * Remove a collection from the cache
123
- * @param {string} index
124
- * @param {string} collection
125
- */
126
- removeCollection (index, collection) {
127
- const collections = this.indexes.get(index);
128
-
129
- if (collections) {
130
- collections.delete(collection);
96
+ /**
97
+ * Remove a collection from the cache
98
+ */
99
+ removeCollection(index, collection) {
100
+ const collections = this.indexes.get(index);
101
+ if (collections) {
102
+ collections.delete(collection);
103
+ }
131
104
  }
132
- }
133
-
134
- /**
135
- * Assert that the provided index exists
136
- * @param {string} index
137
- * @throws If the index does not exist
138
- */
139
- assertIndexExists (index) {
140
- if (!this.indexes.has(index)) {
141
- throw kerror.get('unknown_index', index);
105
+ /**
106
+ * Assert that the provided index exists
107
+ *
108
+ * @throws If the index does not exist
109
+ */
110
+ assertIndexExists(index) {
111
+ if (!this.indexes.has(index)) {
112
+ throw storageError.get('unknown_index', index);
113
+ }
142
114
  }
143
- }
144
-
145
- /**
146
- * Assert that the provided index and collection exist
147
- * @param {string} index
148
- * @param {string} collection
149
- */
150
- assertCollectionExists (index, collection) {
151
- this.assertIndexExists(index);
152
-
153
- if (!this.indexes.get(index).has(collection)) {
154
- throw kerror.get('unknown_collection', index, collection);
115
+ /**
116
+ * Assert that the provided index and collection exist
117
+ */
118
+ assertCollectionExists(index, collection) {
119
+ this.assertIndexExists(index);
120
+ if (!this.indexes.get(index).has(collection)) {
121
+ throw storageError.get('unknown_collection', index, collection);
122
+ }
155
123
  }
156
- }
157
124
  }
158
-
159
- module.exports = IndexCache;
125
+ exports.IndexCache = IndexCache;
126
+ //# sourceMappingURL=indexCache.js.map
@@ -28,7 +28,7 @@ const murmurhash_native_1 = require("murmurhash-native");
28
28
  const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
29
29
  const koncorde_1 = require("koncorde");
30
30
  const bluebird_1 = __importDefault(require("bluebird"));
31
- const segfault_handler_1 = __importDefault(require("segfault-handler"));
31
+ const node_segfault_handler_1 = __importDefault(require("node-segfault-handler"));
32
32
  const lodash_1 = __importDefault(require("lodash"));
33
33
  const kuzzleStateEnum_1 = __importDefault(require("./kuzzleStateEnum"));
34
34
  const kuzzleEventEmitter_1 = __importDefault(require("./event/kuzzleEventEmitter"));
@@ -444,7 +444,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
444
444
  this.shutdown();
445
445
  });
446
446
  }
447
- segfault_handler_1.default.registerHandler();
447
+ node_segfault_handler_1.default.registerHandler();
448
448
  }
449
449
  async dumpAndExit(suffix) {
450
450
  if (this.config.dump.enabled) {
@@ -128,12 +128,11 @@ class ElasticSearch extends Service {
128
128
  '_source_includes'
129
129
  ];
130
130
 
131
- // Forbidden keys in a query
132
- this.scriptKeys = [
133
- 'script',
134
- '_script'
135
- ];
136
-
131
+ /**
132
+ * Only allow stored-scripts in queries
133
+ */
134
+ this.scriptKeys = ['script', '_script'];
135
+ this.scriptAllowedArgs = ['id', 'params'];
137
136
 
138
137
  this.maxScrollDuration = this._loadMsConfig('maxScrollDuration');
139
138
 
@@ -2979,14 +2978,20 @@ class ElasticSearch extends Service {
2979
2978
  }
2980
2979
 
2981
2980
  /**
2982
- * Throw if any script keyword is contained in the object
2981
+ * Throw if a script is used in the query.
2982
+ *
2983
+ * Only Stored Scripts are accepted
2983
2984
  *
2984
2985
  * @param {Object} object
2985
2986
  */
2986
- _scriptCheck(object) {
2987
+ _scriptCheck (object) {
2987
2988
  for (const [key, value] of Object.entries(object)) {
2988
2989
  if (this.scriptKeys.includes(key)) {
2989
- throw kerror.get('invalid_query_keyword', key);
2990
+ for (const scriptArg of Object.keys(value)) {
2991
+ if (! this.scriptAllowedArgs.includes(scriptArg)) {
2992
+ throw kerror.get('invalid_query_keyword', `${key}.${scriptArg}`);
2993
+ }
2994
+ }
2990
2995
  }
2991
2996
  // Every object must be checked here, even the ones nested into an array
2992
2997
  else if (typeof value === 'object' && value !== null) {