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.
- package/index.d.ts +1 -0
- package/index.js +3 -0
- package/lib/api/controllers/adminController.js +9 -0
- package/lib/api/controllers/debugController.d.ts +59 -0
- package/lib/api/controllers/debugController.js +285 -0
- package/lib/api/controllers/documentController.js +43 -29
- package/lib/api/controllers/index.js +1 -0
- package/lib/api/controllers/securityController.js +2 -2
- package/lib/api/documentExtractor.js +49 -8
- package/lib/api/funnel.js +30 -8
- package/lib/api/httpRoutes.js +6 -0
- package/lib/api/request/kuzzleRequest.d.ts +12 -4
- package/lib/api/request/kuzzleRequest.js +17 -13
- 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/network/protocols/httpwsProtocol.js +6 -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/npm-shrinkwrap.json +19422 -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
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
|
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
|
|
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);
|
package/lib/api/httpRoutes.js
CHANGED
|
@@ -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(
|
|
327
|
+
getIndex({ required }?: {
|
|
328
|
+
required?: boolean;
|
|
329
|
+
}): string;
|
|
328
330
|
/**
|
|
329
331
|
* Returns the collection specified in the request
|
|
330
332
|
*/
|
|
331
|
-
getCollection(
|
|
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
|
-
|
|
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
|
-
|
|
633
|
-
|
|
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
|
-
|
|
643
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
@@ -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/
|
|
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";
|
package/lib/kuzzle/kuzzle.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
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}:${
|
|
374
|
-
|
|
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
|
|
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}`,
|
|
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
|
|
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}
|
|
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
|
|
1438
|
+
let indexSettings;
|
|
1439
1439
|
try {
|
|
1440
|
-
|
|
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>}
|
|
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`.
|