kuzzle 2.14.0 → 2.14.5
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/lib/api/controllers/securityController.js +49 -13
- package/lib/api/funnel.js +13 -24
- package/lib/api/openApiGenerator.js +6 -0
- package/lib/api/request/kuzzleRequest.js +9 -9
- package/lib/config/default.config.js +10 -1
- package/lib/core/auth/tokenManager.d.ts +65 -0
- package/lib/core/auth/tokenManager.js +222 -256
- package/lib/core/backend/backend.js +3 -4
- package/lib/core/backend/backendController.d.ts +27 -0
- package/lib/core/backend/backendController.js +27 -0
- package/lib/core/backend/backendImport.js +6 -6
- package/lib/core/backend/backendPlugin.js +1 -1
- package/lib/core/plugin/plugin.js +42 -2
- package/lib/core/plugin/pluginContext.js +2 -2
- package/lib/core/plugin/pluginsManager.js +1 -0
- package/lib/core/realtime/hotelClerk.js +30 -16
- package/lib/core/security/profileRepository.js +5 -22
- package/lib/core/security/roleRepository.js +11 -5
- package/lib/core/security/tokenRepository.js +1 -1
- package/lib/core/security/userRepository.js +2 -2
- package/lib/core/shared/KoncordeWrapper.d.ts +1 -2
- package/lib/core/shared/KoncordeWrapper.js +10 -9
- package/lib/core/shared/repository.js +3 -3
- package/lib/core/shared/sdk/embeddedSdk.d.ts +2 -2
- package/lib/core/shared/sdk/embeddedSdk.js +2 -2
- package/lib/kerror/codes/4-plugin.json +6 -0
- package/lib/kuzzle/kuzzle.js +1 -1
- package/lib/model/security/token.d.ts +29 -0
- package/lib/model/security/token.js +25 -24
- package/lib/service/storage/elasticsearch.js +23 -5
- package/lib/types/ControllerDefinition.d.ts +51 -1
- package/lib/types/Plugin.js +1 -1
- package/lib/util/koncordeCompat.js +1 -1
- package/lib/util/mutex.js +2 -2
- package/package-lock.json +559 -1889
- package/package.json +27 -34
|
@@ -25,7 +25,7 @@ const { isEmpty, isNil } = require('lodash');
|
|
|
25
25
|
const Bluebird = require('bluebird');
|
|
26
26
|
const { v4: uuidv4 } = require('uuid');
|
|
27
27
|
|
|
28
|
-
const { KuzzleError } = require('../../kerror/errors');
|
|
28
|
+
const { KuzzleError, BadRequestError } = require('../../kerror/errors');
|
|
29
29
|
const { Request } = require('../request');
|
|
30
30
|
const { NativeController } = require('./baseController');
|
|
31
31
|
const formatProcessing = require('../../core/auth/formatProcessing');
|
|
@@ -320,19 +320,33 @@ class SecurityController extends NativeController {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
/**
|
|
323
|
-
*
|
|
323
|
+
* Search for roles
|
|
324
324
|
*
|
|
325
325
|
* @param {Request} request
|
|
326
326
|
* @returns {Promise<Object>}
|
|
327
327
|
*/
|
|
328
|
-
async searchRoles(request) {
|
|
328
|
+
async searchRoles (request) {
|
|
329
329
|
const from = request.getInteger('from', 0);
|
|
330
330
|
const size = this._getSearchPageSize(request);
|
|
331
|
-
const
|
|
331
|
+
const lang = request.getLangParam();
|
|
332
|
+
const body = request.getBody({});
|
|
333
|
+
|
|
334
|
+
if (body.controllers && body.query) {
|
|
335
|
+
throw new BadRequestError('You cannot specifify both "controllers" and "query". Prefer the usage of "query" property with a search query.');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (body.controllers) {
|
|
339
|
+
// Type checking
|
|
340
|
+
request.getBodyArray('controllers');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (lang === 'koncorde') {
|
|
344
|
+
body.query = await this.translateKoncorde(body.query || {});
|
|
345
|
+
}
|
|
332
346
|
|
|
333
347
|
const response = await this.ask(
|
|
334
348
|
'core:security:role:search',
|
|
335
|
-
|
|
349
|
+
body,
|
|
336
350
|
{ from, size });
|
|
337
351
|
|
|
338
352
|
response.hits = response.hits.map(formatProcessing.serializeRole);
|
|
@@ -367,7 +381,7 @@ class SecurityController extends NativeController {
|
|
|
367
381
|
* @param {Request} request
|
|
368
382
|
* @returns {Promise<Object>}
|
|
369
383
|
*/
|
|
370
|
-
async createRole(request) {
|
|
384
|
+
async createRole (request) {
|
|
371
385
|
const id = request.getId();
|
|
372
386
|
const body = request.getBody();
|
|
373
387
|
const userId = request.getKuid();
|
|
@@ -515,20 +529,42 @@ class SecurityController extends NativeController {
|
|
|
515
529
|
}
|
|
516
530
|
|
|
517
531
|
/**
|
|
518
|
-
*
|
|
532
|
+
* Search for profiles
|
|
519
533
|
*
|
|
520
534
|
* @param {Request} request
|
|
521
535
|
* @returns {Promise<Object>}
|
|
522
536
|
*/
|
|
523
|
-
async searchProfiles(request) {
|
|
524
|
-
const roles = request.getBodyArray('roles', []);
|
|
525
|
-
const from = request.getInteger('from', 0);
|
|
537
|
+
async searchProfiles (request) {
|
|
526
538
|
const size = this._getSearchPageSize(request);
|
|
527
|
-
const
|
|
539
|
+
const { from, scrollTTL, searchBody } = request.getSearchParams();
|
|
540
|
+
const lang = request.getLangParam();
|
|
541
|
+
const body = request.getBody({});
|
|
542
|
+
|
|
543
|
+
if (body.roles && body.query) {
|
|
544
|
+
throw new BadRequestError('You cannot specifify both "roles" and "query". Prefer the usage of "query" property with a search query.');
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (body.roles) {
|
|
548
|
+
const roles = request.getBodyArray('roles');
|
|
549
|
+
|
|
550
|
+
request.addDeprecation('auto-version', 'Usage of the "roles" property is deprecated. Prefer the usage of "query" property with a search query.');
|
|
551
|
+
|
|
552
|
+
if (roles.length > 0) {
|
|
553
|
+
searchBody.query = { terms: { 'policies.roleId': roles } };
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
searchBody.query = { match_all: {} };
|
|
557
|
+
}
|
|
558
|
+
delete body.roles;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (lang === 'koncorde') {
|
|
562
|
+
searchBody.query = await this.translateKoncorde(searchBody.query || {});
|
|
563
|
+
}
|
|
528
564
|
|
|
529
|
-
const response = await this.ask('core:security:profile:search',
|
|
565
|
+
const response = await this.ask('core:security:profile:search', searchBody, {
|
|
530
566
|
from,
|
|
531
|
-
scroll,
|
|
567
|
+
scroll: scrollTTL,
|
|
532
568
|
size,
|
|
533
569
|
});
|
|
534
570
|
|
package/lib/api/funnel.js
CHANGED
|
@@ -269,10 +269,11 @@ class Funnel {
|
|
|
269
269
|
&& !this._isOriginAuthorized(request.input.headers.origin)
|
|
270
270
|
) {
|
|
271
271
|
debug('Reject request, unauthorized origin %s', request.input.headers.origin);
|
|
272
|
-
|
|
272
|
+
return this._executeError(
|
|
273
273
|
kerror.get('api', 'process', 'unauthorized_origin', request.input.headers.origin),
|
|
274
|
-
request
|
|
275
|
-
|
|
274
|
+
request,
|
|
275
|
+
true,
|
|
276
|
+
callback);
|
|
276
277
|
}
|
|
277
278
|
|
|
278
279
|
try {
|
|
@@ -288,7 +289,7 @@ class Funnel {
|
|
|
288
289
|
debug('Starting request %s:%s [%s]: %j', req.input.controller, req.input.action, req.id, req.input);
|
|
289
290
|
|
|
290
291
|
global.kuzzle.asyncStore.run(() => {
|
|
291
|
-
|
|
292
|
+
|
|
292
293
|
global.kuzzle.asyncStore.set('REQUEST', req);
|
|
293
294
|
global.kuzzle.pipe('request:beforeExecution', req)
|
|
294
295
|
.then(modifiedRequest => {
|
|
@@ -325,7 +326,7 @@ class Funnel {
|
|
|
325
326
|
.catch(err => {
|
|
326
327
|
debug('Error processing request %s: %a', req.id, err);
|
|
327
328
|
return this._executeError(err, req, true, callback);
|
|
328
|
-
});
|
|
329
|
+
});
|
|
329
330
|
});
|
|
330
331
|
}, this, request);
|
|
331
332
|
|
|
@@ -410,31 +411,19 @@ class Funnel {
|
|
|
410
411
|
// When a request is made with cookieAuth set to true
|
|
411
412
|
// We try to use the auth token cookie if present as auth token
|
|
412
413
|
// otherwise check for auth token as input
|
|
413
|
-
if (request.input.headers
|
|
414
|
-
&& has(request.input.headers, 'cookie')
|
|
415
|
-
) {
|
|
414
|
+
if (request.input.headers && has(request.input.headers, 'cookie')) {
|
|
416
415
|
let cookie;
|
|
417
416
|
try {
|
|
418
417
|
cookie = Cookie.parse(request.input.headers.cookie);
|
|
419
418
|
}
|
|
420
419
|
catch (error) {
|
|
421
|
-
throw kerror.get(
|
|
422
|
-
'security',
|
|
423
|
-
'cookie',
|
|
424
|
-
'invalid'
|
|
425
|
-
);
|
|
420
|
+
throw kerror.get('security','cookie', 'invalid');
|
|
426
421
|
}
|
|
427
422
|
|
|
428
423
|
// if cookie is present and not null, and a token is present we should throw because we don't know which one to use
|
|
429
|
-
if (cookie.authToken
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if (!global.kuzzle.config.http.cookieAuthentication) {
|
|
433
|
-
throw kerror.get(
|
|
434
|
-
'security',
|
|
435
|
-
'cookie',
|
|
436
|
-
'unsupported'
|
|
437
|
-
);
|
|
424
|
+
if (cookie.authToken && cookie.authToken !== 'null') {
|
|
425
|
+
if (! global.kuzzle.config.http.cookieAuthentication) {
|
|
426
|
+
throw kerror.get('security', 'cookie', 'unsupported');
|
|
438
427
|
}
|
|
439
428
|
|
|
440
429
|
if (request.input.jwt) {
|
|
@@ -840,8 +829,8 @@ class Funnel {
|
|
|
840
829
|
|
|
841
830
|
/**
|
|
842
831
|
* Verifies if requests sent from a specific origin are allowed
|
|
843
|
-
* @param {string} origin
|
|
844
|
-
* @returns
|
|
832
|
+
* @param {string} origin
|
|
833
|
+
* @returns
|
|
845
834
|
*/
|
|
846
835
|
_isOriginAuthorized (origin) {
|
|
847
836
|
const httpConfig = global.kuzzle.config.http;
|
|
@@ -94,6 +94,12 @@ module.exports = function generateOpenApi () {
|
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// If custom specification, return as it is
|
|
98
|
+
if (route.openapi) {
|
|
99
|
+
response.paths[route.formattedPath][route.verb] = route.openapi;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
97
103
|
if (route.info === undefined) {
|
|
98
104
|
route.info = {};
|
|
99
105
|
}
|
|
@@ -73,7 +73,7 @@ const _deprecations = 'deprecations\u200b';
|
|
|
73
73
|
*/
|
|
74
74
|
class KuzzleRequest {
|
|
75
75
|
constructor(data, options) {
|
|
76
|
-
this[_internalId] = nanoid_1.nanoid();
|
|
76
|
+
this[_internalId] = (0, nanoid_1.nanoid)();
|
|
77
77
|
this[_status] = 102;
|
|
78
78
|
this[_input] = new requestInput_1.RequestInput(data);
|
|
79
79
|
this[_context] = new requestContext_1.RequestContext(options);
|
|
@@ -86,7 +86,7 @@ class KuzzleRequest {
|
|
|
86
86
|
this[_input].headers = this[_context].connection.misc.headers;
|
|
87
87
|
this.id = data.requestId
|
|
88
88
|
? assert.assertString('requestId', data.requestId)
|
|
89
|
-
: nanoid_1.nanoid();
|
|
89
|
+
: (0, nanoid_1.nanoid)();
|
|
90
90
|
this[_timestamp] = data.timestamp || Date.now();
|
|
91
91
|
// handling provided options
|
|
92
92
|
if (options !== undefined && options !== null) {
|
|
@@ -651,7 +651,7 @@ class KuzzleRequest {
|
|
|
651
651
|
* @param errorName name to use in error messages
|
|
652
652
|
*/
|
|
653
653
|
_getBoolean(obj, name, errorName) {
|
|
654
|
-
let value = safeObject_1.get(obj, name);
|
|
654
|
+
let value = (0, safeObject_1.get)(obj, name);
|
|
655
655
|
// In HTTP, booleans are flags: if it's in the querystring, it's set,
|
|
656
656
|
// whatever its value.
|
|
657
657
|
// If a user needs to unset the option, they need to remove it from the
|
|
@@ -680,7 +680,7 @@ class KuzzleRequest {
|
|
|
680
680
|
* @param def default value
|
|
681
681
|
*/
|
|
682
682
|
_getNumber(obj, name, errorName, def = null) {
|
|
683
|
-
let value = safeObject_1.get(obj, name);
|
|
683
|
+
let value = (0, safeObject_1.get)(obj, name);
|
|
684
684
|
if (value === undefined || value === null) {
|
|
685
685
|
if (def !== null) {
|
|
686
686
|
return def;
|
|
@@ -702,7 +702,7 @@ class KuzzleRequest {
|
|
|
702
702
|
* @param def default value
|
|
703
703
|
*/
|
|
704
704
|
_getInteger(obj, name, errorName, def = null) {
|
|
705
|
-
let value = safeObject_1.get(obj, name);
|
|
705
|
+
let value = (0, safeObject_1.get)(obj, name);
|
|
706
706
|
if (value === undefined || value === null) {
|
|
707
707
|
if (def !== null) {
|
|
708
708
|
return def;
|
|
@@ -724,7 +724,7 @@ class KuzzleRequest {
|
|
|
724
724
|
* @param def default value
|
|
725
725
|
*/
|
|
726
726
|
_getString(obj, name, errorName, def = null) {
|
|
727
|
-
const value = safeObject_1.get(obj, name);
|
|
727
|
+
const value = (0, safeObject_1.get)(obj, name);
|
|
728
728
|
if (value === undefined || value === null) {
|
|
729
729
|
if (def !== null) {
|
|
730
730
|
return def;
|
|
@@ -745,7 +745,7 @@ class KuzzleRequest {
|
|
|
745
745
|
* @param def default value
|
|
746
746
|
*/
|
|
747
747
|
_getArray(obj, name, errorName, def = null) {
|
|
748
|
-
const value = safeObject_1.get(obj, name);
|
|
748
|
+
const value = (0, safeObject_1.get)(obj, name);
|
|
749
749
|
if (value === undefined || value === null) {
|
|
750
750
|
if (def !== null) {
|
|
751
751
|
return def;
|
|
@@ -766,14 +766,14 @@ class KuzzleRequest {
|
|
|
766
766
|
* @param def default value
|
|
767
767
|
*/
|
|
768
768
|
_getObject(obj, name, errorName, def = null) {
|
|
769
|
-
const value = safeObject_1.get(obj, name);
|
|
769
|
+
const value = (0, safeObject_1.get)(obj, name);
|
|
770
770
|
if (value === undefined || value === null) {
|
|
771
771
|
if (def !== null) {
|
|
772
772
|
return def;
|
|
773
773
|
}
|
|
774
774
|
throw assertionError.get('missing_argument', errorName);
|
|
775
775
|
}
|
|
776
|
-
if (!safeObject_1.isPlainObject(value)) {
|
|
776
|
+
if (!(0, safeObject_1.isPlainObject)(value)) {
|
|
777
777
|
throw assertionError.get('invalid_type', errorName, 'object');
|
|
778
778
|
}
|
|
779
779
|
return value;
|
|
@@ -301,9 +301,17 @@ module.exports = {
|
|
|
301
301
|
profiles: {
|
|
302
302
|
dynamic: 'false',
|
|
303
303
|
properties: {
|
|
304
|
+
tags: { type: 'keyword' },
|
|
304
305
|
policies: {
|
|
305
306
|
properties: {
|
|
306
|
-
roleId: { type: 'keyword' }
|
|
307
|
+
roleId: { type: 'keyword' },
|
|
308
|
+
restrictedTo: {
|
|
309
|
+
type: 'nested',
|
|
310
|
+
properties: {
|
|
311
|
+
index: { type: 'keyword' },
|
|
312
|
+
collections: { type: 'keyword' }
|
|
313
|
+
}
|
|
314
|
+
}
|
|
307
315
|
}
|
|
308
316
|
}
|
|
309
317
|
}
|
|
@@ -311,6 +319,7 @@ module.exports = {
|
|
|
311
319
|
roles: {
|
|
312
320
|
dynamic: 'false',
|
|
313
321
|
properties: {
|
|
322
|
+
tags: { type: 'keyword' },
|
|
314
323
|
controllers: {
|
|
315
324
|
dynamic: 'false',
|
|
316
325
|
properties: {}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import '../../types/Global';
|
|
2
|
+
import { Token } from '../../model/security/token';
|
|
3
|
+
/**
|
|
4
|
+
* Maintains a list of valid tokens used by connected protocols.
|
|
5
|
+
*
|
|
6
|
+
* When a token expires, this module cleans up the corresponding connection's
|
|
7
|
+
* subscriptions if any, and notify the user
|
|
8
|
+
*/
|
|
9
|
+
export declare class TokenManager {
|
|
10
|
+
private tokens;
|
|
11
|
+
private anonymousUserId;
|
|
12
|
+
private tokensByConnection;
|
|
13
|
+
private timer;
|
|
14
|
+
constructor();
|
|
15
|
+
init(): Promise<void>;
|
|
16
|
+
runTimer(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Link a connection and a token.
|
|
19
|
+
* If one or another expires, associated subscriptions are cleaned up
|
|
20
|
+
* @param token
|
|
21
|
+
* @param connectionId
|
|
22
|
+
*/
|
|
23
|
+
link(token: Token, connectionId: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Unlink a connection from its associated token
|
|
26
|
+
*
|
|
27
|
+
* @param token
|
|
28
|
+
* @param connectionId
|
|
29
|
+
*/
|
|
30
|
+
unlink(token: Token, connectionId: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Called when a token expires before its time (e.g. following a
|
|
33
|
+
* auth:logout action)
|
|
34
|
+
* This method removes all maintained links and cleans up the
|
|
35
|
+
* hotel clerk
|
|
36
|
+
*
|
|
37
|
+
* @param token
|
|
38
|
+
*/
|
|
39
|
+
expire(token: Token): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Refresh an existing token with a new one
|
|
42
|
+
*
|
|
43
|
+
* @param oldToken
|
|
44
|
+
* @param newToken
|
|
45
|
+
*/
|
|
46
|
+
refresh(oldToken: Token, newToken: Token): void;
|
|
47
|
+
checkTokensValidity(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the token matching user & connection if any
|
|
50
|
+
*/
|
|
51
|
+
getConnectedUserToken(userId: string, connectionId: string): Token | null;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the kuid associated to a connection
|
|
54
|
+
*/
|
|
55
|
+
getKuidFromConnection(connectionId: string): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Adds token to internal collections
|
|
58
|
+
*
|
|
59
|
+
* @param token
|
|
60
|
+
* @param connectionId
|
|
61
|
+
*/
|
|
62
|
+
private add;
|
|
63
|
+
private removeConnectionLinkedToToken;
|
|
64
|
+
private deleteByIndex;
|
|
65
|
+
}
|