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.
- package/index.d.ts +1 -0
- package/index.js +3 -0
- package/lib/api/controllers/adminController.js +9 -0
- package/lib/api/controllers/authController.js +8 -2
- package/lib/api/controllers/debugController.d.ts +59 -0
- package/lib/api/controllers/debugController.js +285 -0
- package/lib/api/controllers/documentController.js +49 -31
- package/lib/api/controllers/index.js +1 -0
- package/lib/api/controllers/indexController.js +10 -6
- package/lib/api/controllers/securityController.js +81 -43
- package/lib/api/controllers/serverController.js +2 -1
- package/lib/api/documentExtractor.js +51 -9
- package/lib/api/funnel.js +30 -8
- package/lib/api/httpRoutes.js +8 -1
- package/lib/api/openapi/OpenApiManager.js +3 -0
- package/lib/api/openapi/components/document/get.yaml +1 -1
- package/lib/api/openapi/components/document/update.yaml +1 -1
- package/lib/api/openapi/components/index.d.ts +1 -0
- package/lib/api/openapi/components/index.js +1 -0
- package/lib/api/openapi/components/security/index.d.ts +2 -0
- package/lib/api/openapi/components/security/index.js +10 -0
- package/lib/api/openapi/components/security/upsertUser.yaml +59 -0
- package/lib/api/request/kuzzleRequest.d.ts +58 -4
- package/lib/api/request/kuzzleRequest.js +163 -31
- package/lib/api/request/requestResponse.js +25 -0
- package/lib/cluster/idCardHandler.js +1 -1
- package/lib/config/default.config.js +3 -0
- package/lib/config/documentEventAliases.d.ts +7 -0
- package/lib/config/documentEventAliases.js +26 -12
- package/lib/core/backend/backend.d.ts +4 -0
- package/lib/core/backend/backend.js +9 -0
- package/lib/core/backend/backendController.d.ts +7 -1
- package/lib/core/backend/backendController.js +15 -3
- package/lib/core/network/protocols/httpwsProtocol.js +14 -6
- package/lib/core/plugin/plugin.js +7 -0
- package/lib/core/shared/sdk/embeddedSdk.d.ts +3 -3
- package/lib/core/shared/sdk/embeddedSdk.js +33 -0
- package/lib/core/shared/sdk/funnelProtocol.d.ts +1 -2
- package/lib/kerror/codes/0-core.json +35 -0
- package/lib/kerror/codes/1-services.json +1 -1
- package/lib/kerror/codes/2-api.json +6 -0
- package/lib/kerror/errors/kuzzleError.d.ts +1 -1
- package/lib/kuzzle/kuzzle.d.ts +1 -1
- package/lib/kuzzle/kuzzle.js +27 -8
- package/lib/model/security/user.js +5 -6
- package/lib/model/storage/apiKey.js +1 -6
- package/lib/service/storage/elasticsearch.js +143 -47
- 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/types/index.d.ts +0 -1
- package/lib/types/index.js +0 -1
- package/lib/util/crypto.d.ts +1 -0
- package/lib/util/crypto.js +12 -0
- package/lib/util/dump-collection.d.ts +35 -0
- package/lib/util/dump-collection.js +11 -8
- package/lib/util/name-generator.d.ts +79 -0
- package/lib/util/name-generator.js +1409 -1345
- package/lib/util/time.d.ts +1 -0
- package/lib/util/time.js +9 -0
- package/package.json +19 -21
- package/lib/core/security/README.md +0 -224
- package/lib/core/shared/README.md +0 -3
- 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
|
-
|
|
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 {
|
|
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/
|
|
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
|
|
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/
|
|
13
|
+
* @see https://docs.kuzzle.io/core/2/api/errors/error-codes/
|
|
14
14
|
*/
|
|
15
15
|
code: number;
|
|
16
16
|
/**
|
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();
|
|
@@ -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
|
|
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
|
|
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
|
|
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);
|