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.
Files changed (36) hide show
  1. package/lib/api/controllers/securityController.js +49 -13
  2. package/lib/api/funnel.js +13 -24
  3. package/lib/api/openApiGenerator.js +6 -0
  4. package/lib/api/request/kuzzleRequest.js +9 -9
  5. package/lib/config/default.config.js +10 -1
  6. package/lib/core/auth/tokenManager.d.ts +65 -0
  7. package/lib/core/auth/tokenManager.js +222 -256
  8. package/lib/core/backend/backend.js +3 -4
  9. package/lib/core/backend/backendController.d.ts +27 -0
  10. package/lib/core/backend/backendController.js +27 -0
  11. package/lib/core/backend/backendImport.js +6 -6
  12. package/lib/core/backend/backendPlugin.js +1 -1
  13. package/lib/core/plugin/plugin.js +42 -2
  14. package/lib/core/plugin/pluginContext.js +2 -2
  15. package/lib/core/plugin/pluginsManager.js +1 -0
  16. package/lib/core/realtime/hotelClerk.js +30 -16
  17. package/lib/core/security/profileRepository.js +5 -22
  18. package/lib/core/security/roleRepository.js +11 -5
  19. package/lib/core/security/tokenRepository.js +1 -1
  20. package/lib/core/security/userRepository.js +2 -2
  21. package/lib/core/shared/KoncordeWrapper.d.ts +1 -2
  22. package/lib/core/shared/KoncordeWrapper.js +10 -9
  23. package/lib/core/shared/repository.js +3 -3
  24. package/lib/core/shared/sdk/embeddedSdk.d.ts +2 -2
  25. package/lib/core/shared/sdk/embeddedSdk.js +2 -2
  26. package/lib/kerror/codes/4-plugin.json +6 -0
  27. package/lib/kuzzle/kuzzle.js +1 -1
  28. package/lib/model/security/token.d.ts +29 -0
  29. package/lib/model/security/token.js +25 -24
  30. package/lib/service/storage/elasticsearch.js +23 -5
  31. package/lib/types/ControllerDefinition.d.ts +51 -1
  32. package/lib/types/Plugin.js +1 -1
  33. package/lib/util/koncordeCompat.js +1 -1
  34. package/lib/util/mutex.js +2 -2
  35. package/package-lock.json +559 -1889
  36. 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
- * Return a list of roles that specify a right for the given indexes
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 controllers = request.getBodyArray('controllers', []);
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
- controllers,
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
- * Returns a list of profiles that contain a given set of roles
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 scroll = request.getScrollTTLParam();
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', roles, {
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
- callback(
272
+ return this._executeError(
273
273
  kerror.get('api', 'process', 'unauthorized_origin', request.input.headers.origin),
274
- request);
275
- return 1;
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
- && cookie.authToken !== 'null'
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
+ }