kuzzle 2.60.0-beta.4 → 2.60.0-beta.6

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 (38) hide show
  1. package/dist/lib/api/controllers/adminController.d.ts +7 -1
  2. package/dist/lib/api/controllers/adminController.js +9 -2
  3. package/dist/lib/api/funnel.js +12 -1
  4. package/dist/lib/api/httpRoutes.js +52 -20
  5. package/dist/lib/api/request/kuzzleRequest.d.ts +7 -2
  6. package/dist/lib/api/request/kuzzleRequest.js +20 -10
  7. package/dist/lib/api/request/requestContext.d.ts +1 -1
  8. package/dist/lib/api/request/requestResponse.js +2 -1
  9. package/dist/lib/config/index.js +60 -3
  10. package/dist/lib/core/backend/backend.d.ts +3 -2
  11. package/dist/lib/core/backend/internalLogger.d.ts +6 -0
  12. package/dist/lib/core/backend/internalLogger.js +6 -0
  13. package/dist/lib/core/network/router.d.ts +18 -0
  14. package/dist/lib/core/network/router.js +44 -10
  15. package/dist/lib/core/security/authTokenSettings.d.ts +21 -0
  16. package/dist/lib/core/security/authTokenSettings.js +61 -0
  17. package/dist/lib/core/security/tokenRepository.js +8 -14
  18. package/dist/lib/core/shared/KoncordeWrapper.d.ts +9 -0
  19. package/dist/lib/core/shared/KoncordeWrapper.js +8 -8
  20. package/dist/lib/core/storage/clientAdapter.js +7 -2
  21. package/dist/lib/kuzzle/Logger.d.ts +4 -0
  22. package/dist/lib/kuzzle/Logger.js +9 -2
  23. package/dist/lib/kuzzle/internalIndexHandler.js +4 -5
  24. package/dist/lib/kuzzle/kuzzle.d.ts +17 -0
  25. package/dist/lib/kuzzle/kuzzle.js +62 -0
  26. package/dist/lib/kuzzle/vault.d.ts +13 -0
  27. package/dist/lib/kuzzle/vault.js +25 -7
  28. package/dist/lib/service/storage/7/elasticsearch.d.ts +12 -0
  29. package/dist/lib/service/storage/7/elasticsearch.js +37 -15
  30. package/dist/lib/service/storage/8/elasticsearch.d.ts +12 -0
  31. package/dist/lib/service/storage/8/elasticsearch.js +37 -15
  32. package/dist/lib/types/config/KuzzleConfiguration.d.ts +7 -0
  33. package/dist/lib/types/config/SecurityConfiguration.d.ts +4 -3
  34. package/dist/lib/types/controllers/ControllerDefinition.d.ts +4 -4
  35. package/dist/lib/util/mutex.d.ts +3 -3
  36. package/dist/lib/util/mutex.js +1 -1
  37. package/dist/package.json +1 -1
  38. package/package.json +1 -1
@@ -44,7 +44,13 @@ declare class AdminController extends NativeController {
44
44
  shutdown(): Promise<{
45
45
  acknowledge: boolean;
46
46
  }>;
47
- loadFixtures(request: KuzzleRequest): Promise<any>;
47
+ /**
48
+ * Always waits for the import, so that a failure is reported to the caller:
49
+ * `refresh=false` only skips waiting for the documents to be indexed.
50
+ */
51
+ loadFixtures(request: KuzzleRequest): Promise<{
52
+ acknowledge: boolean;
53
+ }>;
48
54
  loadMappings(request: KuzzleRequest): Promise<{
49
55
  acknowledge: boolean;
50
56
  }>;
@@ -161,15 +161,22 @@ class AdminController extends baseController_1.NativeController {
161
161
  if (this.shuttingDown) {
162
162
  throw kerror.get("api", "process", "action_locked", "Kuzzle is already shutting down.");
163
163
  }
164
+ this.shuttingDown = true;
164
165
  global.kuzzle.shutdown();
165
166
  return { acknowledge: true };
166
167
  }
167
- loadFixtures(request) {
168
+ /**
169
+ * Always waits for the import, so that a failure is reported to the caller:
170
+ * `refresh=false` only skips waiting for the documents to be indexed.
171
+ */
172
+ async loadFixtures(request) {
168
173
  const fixtures = request.getBody();
169
174
  const refresh = request.getRefresh("wait_for");
170
- return global.kuzzle.ask("core:storage:public:document:import", fixtures, {
175
+ await global.kuzzle.ask("core:storage:public:document:import", fixtures, {
171
176
  refresh,
172
177
  });
178
+ // The documented answer. 2.17.6 to 2.59 answered `null` instead.
179
+ return { acknowledge: true };
173
180
  }
174
181
  loadMappings(request) {
175
182
  const mappings = request.getBody();
@@ -664,13 +664,24 @@ class Funnel {
664
664
  catch (e) {
665
665
  error = e;
666
666
  debug("Error processing request %s: %a", request.id, error);
667
+ // processRequest does not dump: do it as the event-less path does.
668
+ // Rethrown, never falling through to the event-less execution below,
669
+ // which ran a failed (or pipe-rejected) request a second time.
670
+ this.handleErrorDump((0, thrown_1.causeOf)(e));
671
+ throw e;
667
672
  }
668
673
  finally {
669
- global.kuzzle.pipe("request:afterExecution", {
674
+ // Not awaited, as it always was: the caller's result does not depend
675
+ // on it. Its rejection is logged instead of left unhandled.
676
+ global.kuzzle
677
+ .pipe("request:afterExecution", {
670
678
  error: error,
671
679
  request: request,
672
680
  result: res,
673
681
  success: error === undefined,
682
+ })
683
+ ?.catch((pipeError) => {
684
+ this.logger.error(pipeError, `request:afterExecution pipe failed for plugin request ${request.id}`);
674
685
  });
675
686
  }
676
687
  }
@@ -184,12 +184,6 @@ const routes = [
184
184
  controller: "admin",
185
185
  action: "resetDatabase",
186
186
  },
187
- {
188
- verb: "post",
189
- path: "/admin/_resetKuzzleData",
190
- controller: "admin",
191
- action: "resetKuzzleData",
192
- },
193
187
  { verb: "post", path: "/admin/_dump", controller: "admin", action: "dump" },
194
188
  {
195
189
  verb: "post",
@@ -251,12 +245,6 @@ const routes = [
251
245
  controller: "index",
252
246
  action: "exists",
253
247
  },
254
- {
255
- verb: "get",
256
- path: "/:index/_autoRefresh",
257
- controller: "index",
258
- action: "getAutoRefresh",
259
- },
260
248
  { verb: "get", path: "/_list", controller: "index", action: "list" },
261
249
  { verb: "get", path: "/_storageStats", controller: "index", action: "stats" },
262
250
  {
@@ -1133,13 +1121,6 @@ const routes = [
1133
1121
  controller: "ms",
1134
1122
  action: "append",
1135
1123
  },
1136
- {
1137
- verb: "post",
1138
- path: "/ms/_bgrewriteaof",
1139
- controller: "ms",
1140
- action: "bgrewriteaof",
1141
- },
1142
- { verb: "post", path: "/ms/_bgsave", controller: "ms", action: "bgsave" },
1143
1124
  { verb: "post", path: "/ms/_bitop/:_id", controller: "ms", action: "bitop" },
1144
1125
  { verb: "post", path: "/ms/_decr/:_id", controller: "ms", action: "decr" },
1145
1126
  {
@@ -1514,7 +1495,7 @@ const routes = [
1514
1495
  controller: "security",
1515
1496
  action: "deleteApiKey",
1516
1497
  openapi: {
1517
- description: "Deletes a user API key, identified by its clear-text `key` or its `fingerprint` instead of its `_id`. Exactly one of `key` or `fingerprint` must be provided. If you are looking to manage your own API keys, use the `auth` controller instead.",
1498
+ description: "Deletes a user API key, identified by its clear-text `key` or its `fingerprint` instead of its `_id`. One of them must be provided; if several are given, `_id` is used first, then `key`, then `fingerprint`, and the others are ignored. A `key` in the query string is a secret written in the URL: prefer the `fingerprint`. If you are looking to manage your own API keys, use the `auth` controller instead.",
1518
1499
  parameters: [
1519
1500
  {
1520
1501
  in: "path",
@@ -1572,6 +1553,57 @@ const routes = [
1572
1553
  controller: "auth",
1573
1554
  action: "deleteApiKey",
1574
1555
  },
1556
+ {
1557
+ verb: "delete",
1558
+ path: "/api-keys",
1559
+ controller: "auth",
1560
+ action: "deleteApiKey",
1561
+ openapi: {
1562
+ description: "Deletes an API key of the current user, identified by its clear-text `key` or its `fingerprint` instead of its `_id`. One of them must be provided; if several are given, `_id` is used first, then `key`, then `fingerprint`, and the others are ignored. A `key` in the query string is a secret written in the URL: prefer the `fingerprint`.",
1563
+ parameters: [
1564
+ {
1565
+ in: "query",
1566
+ name: "key",
1567
+ schema: { type: "string" },
1568
+ required: false,
1569
+ description: "Clear-text API key to delete",
1570
+ },
1571
+ {
1572
+ in: "query",
1573
+ name: "fingerprint",
1574
+ schema: { type: "string" },
1575
+ required: false,
1576
+ description: "SHA256 fingerprint of the API key to delete",
1577
+ },
1578
+ {
1579
+ in: "query",
1580
+ name: "refresh",
1581
+ schema: {
1582
+ type: "string",
1583
+ enum: ["wait_for", "false"],
1584
+ default: "wait_for",
1585
+ },
1586
+ required: false,
1587
+ description: "If set to `wait_for`, waits for the deletion to be indexed before responding",
1588
+ },
1589
+ ],
1590
+ responses: {
1591
+ 200: {
1592
+ description: "The deleted API key ID",
1593
+ content: {
1594
+ "application/json": {
1595
+ schema: {
1596
+ type: "object",
1597
+ properties: {
1598
+ _id: { type: "string" },
1599
+ },
1600
+ },
1601
+ },
1602
+ },
1603
+ },
1604
+ },
1605
+ },
1606
+ },
1575
1607
  // PUT (idempotent)
1576
1608
  { verb: "put", path: "/_me", controller: "auth", action: "updateSelf" },
1577
1609
  {
@@ -385,12 +385,17 @@ export declare class KuzzleRequest {
385
385
  */
386
386
  getDate(name: string, format?: string): string;
387
387
  /**
388
- * Gets a parameter from a request arguments and returns it to timestamp format.
388
+ * Gets a parameter from a request arguments and checks that it is an integer
389
+ * timestamp (e.g. milliseconds since Epoch).
390
+ *
391
+ * An integer string, such as an HTTP query string value, is accepted and
392
+ * converted. A date string (e.g. `"2026-10-27"`) is rejected.
389
393
  *
390
394
  * @param name parameter name.
391
395
  * @throws {api.assert.missing_argument} If parameter not found and no default
392
396
  * value provided
393
- * @throws {api.assert.invalid_type} If parameter value is not a valid date.
397
+ * @throws {api.assert.invalid_type} If parameter value is not an integer
398
+ * timestamp.
394
399
  */
395
400
  getTimestamp(name: string): number;
396
401
  /**
@@ -676,31 +676,41 @@ class KuzzleRequest {
676
676
  if (args[name] === undefined) {
677
677
  throw assertionError.get("missing_argument", name);
678
678
  }
679
- if (format && !(0, moment_1.default)(args[name], format, true).isValid()) {
680
- throw assertionError.get("invalid_type", name, "date");
681
- }
682
- if (!(0, moment_1.default)(args[name], moment_1.default.ISO_8601).isValid()) {
679
+ // A custom format replaces the ISO 8601 check: applying both rejected
680
+ // every value of a format that is not ISO 8601.
681
+ const isValid = format
682
+ ? (0, moment_1.default)(args[name], format, true).isValid()
683
+ : (0, moment_1.default)(args[name], moment_1.default.ISO_8601).isValid();
684
+ if (!isValid) {
683
685
  throw assertionError.get("invalid_type", name, "date");
684
686
  }
685
687
  return this.getString(name);
686
688
  }
687
689
  /**
688
- * Gets a parameter from a request arguments and returns it to timestamp format.
690
+ * Gets a parameter from a request arguments and checks that it is an integer
691
+ * timestamp (e.g. milliseconds since Epoch).
692
+ *
693
+ * An integer string, such as an HTTP query string value, is accepted and
694
+ * converted. A date string (e.g. `"2026-10-27"`) is rejected.
689
695
  *
690
696
  * @param name parameter name.
691
697
  * @throws {api.assert.missing_argument} If parameter not found and no default
692
698
  * value provided
693
- * @throws {api.assert.invalid_type} If parameter value is not a valid date.
699
+ * @throws {api.assert.invalid_type} If parameter value is not an integer
700
+ * timestamp.
694
701
  */
695
702
  getTimestamp(name) {
696
- const args = this.input.args;
697
- if (args[name] === undefined) {
703
+ const value = this.input.args[name];
704
+ if (value === undefined) {
698
705
  throw assertionError.get("missing_argument", name);
699
706
  }
700
- if ((0, moment_1.default)(args[name], true).isValid() === false) {
707
+ const timestamp = typeof value === "string" && /^-?\d+$/.test(value)
708
+ ? Number(value)
709
+ : value;
710
+ if (!Number.isSafeInteger(timestamp)) {
701
711
  throw assertionError.get("invalid_type", name, "date");
702
712
  }
703
- return this.getInteger(name);
713
+ return timestamp;
704
714
  }
705
715
  /**
706
716
  * Returns the index specified in the request
@@ -19,7 +19,7 @@ export type ContextMisc = {
19
19
  */
20
20
  path?: string;
21
21
  /**
22
- * HTTP headers
22
+ * HTTP method, uppercase (e.g. `GET`)
23
23
  */
24
24
  verb?: string;
25
25
  /**
@@ -127,7 +127,8 @@ class Headers {
127
127
  case "host":
128
128
  case "if-modified-since":
129
129
  case "if-unmodified-since":
130
- case "last-modified, location":
130
+ case "last-modified":
131
+ case "location":
131
132
  case "max-forwards":
132
133
  case "proxy-authorization":
133
134
  case "referer":
@@ -28,21 +28,27 @@ const assert_1 = __importDefault(require("assert"));
28
28
  const node_util_1 = require("node:util");
29
29
  const rc_1 = __importDefault(require("rc"));
30
30
  const default_config_1 = __importDefault(require("./default.config"));
31
+ const authTokenSettings_1 = require("../core/security/authTokenSettings");
31
32
  const package_json_1 = __importDefault(require("../../package.json"));
32
33
  const kerror_1 = require("../kerror");
33
34
  const safeObject_1 = require("../util/safeObject");
34
35
  const bytes_1 = __importDefault(require("../util/bytes"));
35
36
  const wrapped = (0, kerror_1.wrap)("core", "configuration");
36
37
  function loadConfig() {
37
- let loaded;
38
+ let raw;
38
39
  try {
39
- loaded = (0, rc_1.default)("kuzzle", default_config_1.default);
40
+ raw = (0, rc_1.default)("kuzzle", withoutTokenDefaults(default_config_1.default));
40
41
  }
41
42
  catch (e) {
42
43
  throw wrapped.get("cannot_parse", e instanceof Error ? e.message : (0, node_util_1.inspect)(e));
43
44
  }
44
45
  // In place: what `unstringify` answers is the object it was given.
45
- unstringify(loaded);
46
+ unstringify(raw);
47
+ const { authToken, deprecatedSettings, jwt } = resolveTokenSettings(raw.security);
48
+ const loaded = {
49
+ ...raw,
50
+ security: { ...raw.security, authToken, jwt },
51
+ };
46
52
  checkLimitsConfig(loaded);
47
53
  checkHttpOptions(loaded);
48
54
  checkWebSocketOptions(loaded);
@@ -56,6 +62,7 @@ function loadConfig() {
56
62
  hash: {
57
63
  seed: Buffer.from("^m&mOISKBvb1xpl1mRsrylaQXpjb&IJX"),
58
64
  },
65
+ deprecatedSettings,
59
66
  notifiableProtocols: [],
60
67
  },
61
68
  version: package_json_1.default.version,
@@ -66,6 +73,56 @@ function loadConfig() {
66
73
  preprocessRedisOptions(config.services.memoryStorage);
67
74
  return config;
68
75
  }
76
+ /**
77
+ * The packaged defaults, minus the values of `security.authToken` and of its
78
+ * deprecated spelling `security.jwt`: both sections ship the same non-null
79
+ * defaults, so once `rc` has merged them nothing tells a user's
80
+ * `jwt.expiresIn` from the packaged one. Left empty here, they hold only what
81
+ * the user set, and {@link resolveTokenSettings} applies the defaults after.
82
+ *
83
+ * A shallow copy: the other sections stay the packaged objects `rc` merges
84
+ * into, as before.
85
+ */
86
+ function withoutTokenDefaults(defaults) {
87
+ const authToken = {};
88
+ const jwt = {};
89
+ return { ...defaults, security: { ...defaults.security, authToken, jwt } };
90
+ }
91
+ /**
92
+ * Resolves the authentication token settings: for each one, the value set
93
+ * under `security.authToken` wins, then the one set under the deprecated
94
+ * `security.jwt`, then the packaged default. A `null` counts as unset, as the
95
+ * readers' `authToken.x ?? jwt.x` always did. Exception: `gracePeriod` set in
96
+ * both sections keeps the `jwt` one, as it always did (see
97
+ * {@link LEGACY_FIRST}).
98
+ *
99
+ * Both sections get the resolved values, so a reader of either — a plugin
100
+ * may still read `jwt` — sees the settings in effect. `deprecatedSettings`
101
+ * lists the `security.jwt.*` keys the user set, for the deprecation warning.
102
+ */
103
+ function resolveTokenSettings(security) {
104
+ const packaged = default_config_1.default.security.authToken;
105
+ const fromPackage = packaged;
106
+ const fromAuthToken = (0, safeObject_1.isPlainObject)(security.authToken)
107
+ ? security.authToken
108
+ : {};
109
+ const fromJwt = (0, safeObject_1.isPlainObject)(security.jwt) ? security.jwt : {};
110
+ const resolved = {};
111
+ for (const key of new Set([
112
+ ...Object.keys(packaged),
113
+ ...Object.keys(fromJwt),
114
+ ...Object.keys(fromAuthToken),
115
+ ])) {
116
+ resolved[key] = authTokenSettings_1.LEGACY_FIRST.has(key)
117
+ ? (fromJwt[key] ?? fromAuthToken[key] ?? fromPackage[key])
118
+ : (fromAuthToken[key] ?? fromJwt[key] ?? fromPackage[key]);
119
+ }
120
+ return {
121
+ authToken: { ...packaged, ...resolved },
122
+ deprecatedSettings: Object.keys(fromJwt).map((key) => `security.jwt.${key}`),
123
+ jwt: { ...resolved },
124
+ };
125
+ }
69
126
  /**
70
127
  * RC params can be overriden using environment variables,
71
128
  * in which case all values are passed as strings.
@@ -99,13 +99,14 @@ export declare class Backend {
99
99
  */
100
100
  plugin: BackendPlugin;
101
101
  /**
102
- * InternalLogger
102
+ * Application logger
103
103
  *
104
+ * @method trace
104
105
  * @method debug
105
106
  * @method info
106
107
  * @method warn
107
108
  * @method error
108
- * @method verbose
109
+ * @method fatal
109
110
  */
110
111
  log: Logger;
111
112
  /**
@@ -1,4 +1,10 @@
1
1
  import { ApplicationManager } from "./index";
2
+ /**
3
+ * Kuzzle no longer creates instances of this class: `Backend.log` is a
4
+ * `Logger` since 2.45.0.
5
+ *
6
+ * @deprecated Use `Logger` (`app.log`) instead.
7
+ */
2
8
  export declare class InternalLogger extends ApplicationManager {
3
9
  /**
4
10
  * Logs a trace message
@@ -26,6 +26,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.InternalLogger = void 0;
27
27
  const util_1 = __importDefault(require("util"));
28
28
  const index_1 = require("./index");
29
+ /**
30
+ * Kuzzle no longer creates instances of this class: `Backend.log` is a
31
+ * `Logger` since 2.45.0.
32
+ *
33
+ * @deprecated Use `Logger` (`app.log`) instead.
34
+ */
29
35
  class InternalLogger extends index_1.ApplicationManager {
30
36
  /**
31
37
  * Logs a trace message
@@ -32,9 +32,27 @@ declare class Router {
32
32
  * Transmit HTTP requests to the funnel controller and forward its response
33
33
  * back to the client
34
34
  *
35
+ * Triggers the `http:<verb>` pipe with the verb lowercased, as documented.
36
+ * A route declared with another case (e.g. `verb: 'GET'`) also triggers the
37
+ * event under the declared case (`http:GET`), which is the one it used to
38
+ * trigger: see docs/v3-breaking-changes.md.
39
+ *
40
+ * @param verb - the route's HTTP verb, as declared
35
41
  * @param request - includes URL and POST query data
36
42
  * @param cb - callback to invoke with the result
37
43
  */
38
44
  _executeFromHttp(verb: string, request: Request, cb: RouteCallback): void;
45
+ /**
46
+ * Triggers an `http:<verb>` pipe, and answers its error through `cb` or
47
+ * hands the piped request over to `next`.
48
+ *
49
+ * Callbacks rather than promises: this runs before the funnel.
50
+ */
51
+ private _pipeHttpEvent;
52
+ /**
53
+ * Executes the piped request in the funnel and answers its result through
54
+ * `cb`, falling back on the original request when there is none.
55
+ */
56
+ private _executeInFunnel;
39
57
  }
40
58
  export = Router;
@@ -190,25 +190,59 @@ class Router {
190
190
  * Transmit HTTP requests to the funnel controller and forward its response
191
191
  * back to the client
192
192
  *
193
+ * Triggers the `http:<verb>` pipe with the verb lowercased, as documented.
194
+ * A route declared with another case (e.g. `verb: 'GET'`) also triggers the
195
+ * event under the declared case (`http:GET`), which is the one it used to
196
+ * trigger: see docs/v3-breaking-changes.md.
197
+ *
198
+ * @param verb - the route's HTTP verb, as declared
193
199
  * @param request - includes URL and POST query data
194
200
  * @param cb - callback to invoke with the result
195
201
  */
196
202
  _executeFromHttp(verb, request, cb) {
197
- global.kuzzle.pipe(`http:${verb}`, request, (error, mutatedRequest) => {
203
+ const event = `http:${verb.toLowerCase()}`;
204
+ const declaredEvent = `http:${verb}`;
205
+ this._pipeHttpEvent(event, request, cb, (pipedRequest) => {
206
+ if (declaredEvent === event) {
207
+ this._executeInFunnel(pipedRequest, request, cb);
208
+ return;
209
+ }
210
+ // @deprecated - declared-case event, to drop in v3
211
+ this._pipeHttpEvent(declaredEvent, pipedRequest, cb, (legacyRequest) => {
212
+ this._executeInFunnel(legacyRequest, request, cb);
213
+ });
214
+ });
215
+ }
216
+ /**
217
+ * Triggers an `http:<verb>` pipe, and answers its error through `cb` or
218
+ * hands the piped request over to `next`.
219
+ *
220
+ * Callbacks rather than promises: this runs before the funnel.
221
+ */
222
+ _pipeHttpEvent(event, request, cb, next) {
223
+ // callback form: the outcome, error included, goes through the callback
224
+ void global.kuzzle.pipe(event, request, (error, mutatedRequest) => {
198
225
  if (error) {
199
226
  request.setError(error);
200
227
  cb(request);
201
228
  return;
202
229
  }
203
- global.kuzzle.funnel.execute(mutatedRequest, (err, result) => {
204
- const _res = result || request;
205
- if (err && !_res.error) {
206
- _res.setError(err);
207
- }
208
- // no sanitisation here: `_res` is a KuzzleRequest, and the stack
209
- // is stripped by the protocols, on the serialized response
210
- cb(_res);
211
- });
230
+ next(mutatedRequest);
231
+ });
232
+ }
233
+ /**
234
+ * Executes the piped request in the funnel and answers its result through
235
+ * `cb`, falling back on the original request when there is none.
236
+ */
237
+ _executeInFunnel(pipedRequest, request, cb) {
238
+ global.kuzzle.funnel.execute(pipedRequest, (err, result) => {
239
+ const _res = result || request;
240
+ if (err && !_res.error) {
241
+ _res.setError(err);
242
+ }
243
+ // no sanitisation here: `_res` is a KuzzleRequest, and the stack
244
+ // is stripped by the protocols, on the serialized response
245
+ cb(_res);
212
246
  });
213
247
  }
214
248
  }
@@ -0,0 +1,21 @@
1
+ import type { SecurityConfiguration } from "../../types/config/SecurityConfiguration";
2
+ type AuthTokenSettings = SecurityConfiguration["authToken"];
3
+ /**
4
+ * The settings for which a value set under the deprecated `security.jwt`
5
+ * wins over one set under `security.authToken`: `gracePeriod` used to be
6
+ * read from `jwt` only, so a configuration setting both keeps working as it
7
+ * did. To drop in v3 (docs/v3-breaking-changes.md).
8
+ */
9
+ export declare const LEGACY_FIRST: ReadonlySet<string>;
10
+ /**
11
+ * Reads one authentication token setting: the value set under
12
+ * `security.authToken` wins, then the one set under its deprecated spelling
13
+ * `security.jwt`, then the packaged default (except for {@link LEGACY_FIRST}).
14
+ *
15
+ * `loadConfig()` already resolves both sections from what the configuration
16
+ * files and the environment set. This covers a change made afterwards, e.g.
17
+ * `app.config.set("security.jwt.expiresIn", "2h")` before `app.start()`: a
18
+ * value that differs from the packaged default is one somebody set.
19
+ */
20
+ export declare function authTokenSetting<K extends keyof AuthTokenSettings>(key: K): AuthTokenSettings[K];
21
+ export {};
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /*
3
+ * Kuzzle, a backend software, self-hostable and ready to use
4
+ * to power modern apps
5
+ *
6
+ * Copyright 2015-2022 Kuzzle
7
+ * mailto: support AT kuzzle.io
8
+ * website: http://kuzzle.io
9
+ *
10
+ * Licensed under the Apache License, Version 2.0 (the "License");
11
+ * you may not use this file except in compliance with the License.
12
+ * You may obtain a copy of the License at
13
+ *
14
+ * https://www.apache.org/licenses/LICENSE-2.0
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
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.LEGACY_FIRST = void 0;
27
+ exports.authTokenSetting = authTokenSetting;
28
+ const default_config_1 = __importDefault(require("../../config/default.config"));
29
+ /**
30
+ * The settings for which a value set under the deprecated `security.jwt`
31
+ * wins over one set under `security.authToken`: `gracePeriod` used to be
32
+ * read from `jwt` only, so a configuration setting both keeps working as it
33
+ * did. To drop in v3 (docs/v3-breaking-changes.md).
34
+ */
35
+ exports.LEGACY_FIRST = new Set(["gracePeriod"]);
36
+ function isSet(value) {
37
+ return value !== undefined && value !== null;
38
+ }
39
+ /**
40
+ * Reads one authentication token setting: the value set under
41
+ * `security.authToken` wins, then the one set under its deprecated spelling
42
+ * `security.jwt`, then the packaged default (except for {@link LEGACY_FIRST}).
43
+ *
44
+ * `loadConfig()` already resolves both sections from what the configuration
45
+ * files and the environment set. This covers a change made afterwards, e.g.
46
+ * `app.config.set("security.jwt.expiresIn", "2h")` before `app.start()`: a
47
+ * value that differs from the packaged default is one somebody set.
48
+ */
49
+ function authTokenSetting(key) {
50
+ const { authToken, jwt } = global.kuzzle.config.security; // NOSONAR: the deprecated section is read on purpose, as the documented fallback
51
+ const packaged = default_config_1.default.security.authToken[key];
52
+ const current = authToken?.[key];
53
+ const legacy = jwt?.[key]; // NOSONAR
54
+ const candidates = exports.LEGACY_FIRST.has(key)
55
+ ? [legacy, current]
56
+ : [current, legacy];
57
+ return (candidates.find((value) => isSet(value) && value !== packaged) ??
58
+ candidates.find(isSet) ??
59
+ packaged);
60
+ }
61
+ //# sourceMappingURL=authTokenSettings.js.map
@@ -66,15 +66,8 @@ const token_1 = require("../../model/security/token");
66
66
  const apiKey_1 = __importDefault(require("../../model/storage/apiKey"));
67
67
  const ObjectRepository_1 = require("../shared/ObjectRepository");
68
68
  const crypto_1 = require("../../util/crypto");
69
+ const authTokenSettings_1 = require("./authTokenSettings");
69
70
  const securityError = kerror.wrap("security", "token");
70
- /**
71
- * The deprecated `security.jwt` section, which the `authToken` settings fall
72
- * back to. The packaged defaults always ship it; the type says optional only
73
- * because v2.56.0's did (see SecurityConfiguration.jwt).
74
- */
75
- function legacyJwtConfig() {
76
- return global.kuzzle.config.security.jwt; // NOSONAR: the deprecated section is read on purpose, as the documented fallback
77
- }
78
71
  class TokenRepository extends ObjectRepository_1.ObjectRepository {
79
72
  constructor(opts = {}) {
80
73
  super();
@@ -83,7 +76,11 @@ class TokenRepository extends ObjectRepository_1.ObjectRepository {
83
76
  if (opts.ttl !== undefined) {
84
77
  this.ttl = opts.ttl;
85
78
  }
86
- this.tokenGracePeriod = Math.floor(legacyJwtConfig().gracePeriod);
79
+ this.tokenGracePeriod = Math.floor((0, authTokenSettings_1.authTokenSetting)("gracePeriod"));
80
+ const deprecated = global.kuzzle.config.internal?.deprecatedSettings;
81
+ if (deprecated?.length) {
82
+ global.kuzzle.log.warn(`[DEPRECATED] The configuration section "security.jwt" is deprecated, use "security.authToken" instead (set: ${deprecated.join(", ")}). A value set under "security.authToken" takes precedence.`);
83
+ }
87
84
  this.anonymousToken = new token_1.Token({ userId: "-1" });
88
85
  }
89
86
  async init() {
@@ -194,17 +191,14 @@ class TokenRepository extends ObjectRepository_1.ObjectRepository {
194
191
  *
195
192
  * @returns {Promise.<Object>} { _id, jwt, userId, ttl, expiresAt }
196
193
  */
197
- async generateToken(user, { algorithm = global.kuzzle.config.security.authToken.algorithm ??
198
- legacyJwtConfig().algorithm, expiresIn = global.kuzzle.config.security.authToken.expiresIn ??
199
- legacyJwtConfig().expiresIn, bypassMaxTTL = false, type = "authToken", singleUse = false, } = {}) {
194
+ async generateToken(user, { algorithm = (0, authTokenSettings_1.authTokenSetting)("algorithm"), expiresIn = (0, authTokenSettings_1.authTokenSetting)("expiresIn"), bypassMaxTTL = false, type = "authToken", singleUse = false, } = {}) {
200
195
  if (!user || user._id === null) {
201
196
  throw securityError.get("unknown_user");
202
197
  }
203
198
  const parsedExpiresIn = parseTimespan(expiresIn);
204
199
  const maxTTL = type === "apiKey"
205
200
  ? global.kuzzle.config.security.apiKey.maxTTL
206
- : (global.kuzzle.config.security.authToken.maxTTL ??
207
- legacyJwtConfig().maxTTL);
201
+ : (0, authTokenSettings_1.authTokenSetting)("maxTTL");
208
202
  if (!bypassMaxTTL &&
209
203
  maxTTL > -1 &&
210
204
  (parsedExpiresIn > maxTTL || parsedExpiresIn === -1)) {
@@ -29,6 +29,15 @@ export declare class NormalizedFilterV3 {
29
29
  normalized: JSONObject[][];
30
30
  constructor(index: string, collection: string, normalized: JSONObject);
31
31
  }
32
+ /**
33
+ * Exposes Koncorde v4 through an interface emulating Koncorde v3, to prevent
34
+ * breaking changes in Kuzzle.
35
+ *
36
+ * This class should either be removed entirely in the next major version, or
37
+ * perhaps proposed in a compatibility mode to help clients migrate smoothly.
38
+ *
39
+ * @deprecated Use the `koncorde` package (v4) directly.
40
+ */
32
41
  export declare class Koncorde {
33
42
  private filterIdIndexMap;
34
43
  private koncorde;