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.
- package/dist/lib/api/controllers/adminController.d.ts +7 -1
- package/dist/lib/api/controllers/adminController.js +9 -2
- package/dist/lib/api/funnel.js +12 -1
- package/dist/lib/api/httpRoutes.js +52 -20
- package/dist/lib/api/request/kuzzleRequest.d.ts +7 -2
- package/dist/lib/api/request/kuzzleRequest.js +20 -10
- package/dist/lib/api/request/requestContext.d.ts +1 -1
- package/dist/lib/api/request/requestResponse.js +2 -1
- package/dist/lib/config/index.js +60 -3
- package/dist/lib/core/backend/backend.d.ts +3 -2
- package/dist/lib/core/backend/internalLogger.d.ts +6 -0
- package/dist/lib/core/backend/internalLogger.js +6 -0
- package/dist/lib/core/network/router.d.ts +18 -0
- package/dist/lib/core/network/router.js +44 -10
- package/dist/lib/core/security/authTokenSettings.d.ts +21 -0
- package/dist/lib/core/security/authTokenSettings.js +61 -0
- package/dist/lib/core/security/tokenRepository.js +8 -14
- package/dist/lib/core/shared/KoncordeWrapper.d.ts +9 -0
- package/dist/lib/core/shared/KoncordeWrapper.js +8 -8
- package/dist/lib/core/storage/clientAdapter.js +7 -2
- package/dist/lib/kuzzle/Logger.d.ts +4 -0
- package/dist/lib/kuzzle/Logger.js +9 -2
- package/dist/lib/kuzzle/internalIndexHandler.js +4 -5
- package/dist/lib/kuzzle/kuzzle.d.ts +17 -0
- package/dist/lib/kuzzle/kuzzle.js +62 -0
- package/dist/lib/kuzzle/vault.d.ts +13 -0
- package/dist/lib/kuzzle/vault.js +25 -7
- package/dist/lib/service/storage/7/elasticsearch.d.ts +12 -0
- package/dist/lib/service/storage/7/elasticsearch.js +37 -15
- package/dist/lib/service/storage/8/elasticsearch.d.ts +12 -0
- package/dist/lib/service/storage/8/elasticsearch.js +37 -15
- package/dist/lib/types/config/KuzzleConfiguration.d.ts +7 -0
- package/dist/lib/types/config/SecurityConfiguration.d.ts +4 -3
- package/dist/lib/types/controllers/ControllerDefinition.d.ts +4 -4
- package/dist/lib/util/mutex.d.ts +3 -3
- package/dist/lib/util/mutex.js +1 -1
- package/dist/package.json +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|
package/dist/lib/api/funnel.js
CHANGED
|
@@ -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
|
-
|
|
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`.
|
|
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
|
|
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
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
|
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
|
|
699
|
+
* @throws {api.assert.invalid_type} If parameter value is not an integer
|
|
700
|
+
* timestamp.
|
|
694
701
|
*/
|
|
695
702
|
getTimestamp(name) {
|
|
696
|
-
const
|
|
697
|
-
if (
|
|
703
|
+
const value = this.input.args[name];
|
|
704
|
+
if (value === undefined) {
|
|
698
705
|
throw assertionError.get("missing_argument", name);
|
|
699
706
|
}
|
|
700
|
-
|
|
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
|
|
713
|
+
return timestamp;
|
|
704
714
|
}
|
|
705
715
|
/**
|
|
706
716
|
* Returns the index specified in the request
|
|
@@ -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
|
|
130
|
+
case "last-modified":
|
|
131
|
+
case "location":
|
|
131
132
|
case "max-forwards":
|
|
132
133
|
case "proxy-authorization":
|
|
133
134
|
case "referer":
|
package/dist/lib/config/index.js
CHANGED
|
@@ -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
|
|
38
|
+
let raw;
|
|
38
39
|
try {
|
|
39
|
-
|
|
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(
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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(
|
|
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 =
|
|
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
|
-
: (
|
|
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;
|