@interopio/gateway-server 0.4.0-beta → 0.5.0-beta
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/changelog.md +18 -0
- package/dist/gateway-ent.cjs +25 -2
- package/dist/gateway-ent.cjs.map +2 -2
- package/dist/gateway-ent.js +25 -2
- package/dist/gateway-ent.js.map +2 -2
- package/dist/index.cjs +1182 -109
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +1182 -109
- package/dist/index.js.map +4 -4
- package/dist/metrics/publisher/rest.cjs +60 -0
- package/dist/metrics/publisher/rest.cjs.map +7 -0
- package/dist/metrics/publisher/rest.js +23 -0
- package/dist/metrics/publisher/rest.js.map +7 -0
- package/package.json +12 -12
- package/types/gateway-ent.d.ts +2 -1
- package/dist/metrics-rest.cjs +0 -21440
- package/dist/metrics-rest.cjs.map +0 -7
- package/dist/metrics-rest.js +0 -21430
- package/dist/metrics-rest.js.map +0 -7
- package/src/common/compose.ts +0 -40
- package/src/gateway/ent/config.ts +0 -174
- package/src/gateway/ent/index.ts +0 -18
- package/src/gateway/ent/logging.ts +0 -89
- package/src/gateway/ent/server.ts +0 -34
- package/src/gateway/metrics/rest.ts +0 -20
- package/src/gateway/ws/core.ts +0 -90
- package/src/index.ts +0 -3
- package/src/logger.ts +0 -6
- package/src/mesh/connections.ts +0 -101
- package/src/mesh/rest-directory/routes.ts +0 -38
- package/src/mesh/ws/broker/core.ts +0 -163
- package/src/mesh/ws/cluster/core.ts +0 -107
- package/src/mesh/ws/relays/core.ts +0 -159
- package/src/metrics/routes.ts +0 -86
- package/src/server/address.ts +0 -47
- package/src/server/cors.ts +0 -311
- package/src/server/exchange.ts +0 -379
- package/src/server/monitoring.ts +0 -167
- package/src/server/types.ts +0 -69
- package/src/server/ws-client-verify.ts +0 -79
- package/src/server.ts +0 -316
- package/src/utils.ts +0 -10
package/dist/index.cjs
CHANGED
|
@@ -44,7 +44,7 @@ var import_ws = require("ws");
|
|
|
44
44
|
var import_node_http = __toESM(require("node:http"), 1);
|
|
45
45
|
var import_node_https = __toESM(require("node:https"), 1);
|
|
46
46
|
var import_node_fs = require("node:fs");
|
|
47
|
-
var
|
|
47
|
+
var import_node_async_hooks2 = require("node:async_hooks");
|
|
48
48
|
var import_gateway7 = require("@interopio/gateway");
|
|
49
49
|
|
|
50
50
|
// src/logger.ts
|
|
@@ -73,6 +73,7 @@ var WebExchange = class {
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
// src/server/exchange.ts
|
|
76
|
+
var import_tough_cookie = require("tough-cookie");
|
|
76
77
|
function requestToProtocol(request, defaultProtocol) {
|
|
77
78
|
let proto = request.headers.get("x-forwarded-proto");
|
|
78
79
|
if (Array.isArray(proto)) {
|
|
@@ -96,9 +97,7 @@ function requestToHost(request, defaultHost) {
|
|
|
96
97
|
host = `${host}:${port}`;
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
|
-
|
|
100
|
-
host = request.headers.one("host");
|
|
101
|
-
}
|
|
100
|
+
host ??= request.headers.one("host");
|
|
102
101
|
}
|
|
103
102
|
if (Array.isArray(host)) {
|
|
104
103
|
host = host[0];
|
|
@@ -108,14 +107,22 @@ function requestToHost(request, defaultHost) {
|
|
|
108
107
|
}
|
|
109
108
|
return defaultHost;
|
|
110
109
|
}
|
|
110
|
+
function parseCookies(request) {
|
|
111
|
+
return request.headers.list("cookie").map((s) => s.split(";").map((cs) => import_tough_cookie.Cookie.parse(cs))).flat(1).filter((tc) => tc !== void 0).map((tc) => {
|
|
112
|
+
const result = { name: tc.key, value: tc.value };
|
|
113
|
+
return result;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
111
116
|
var HttpServerRequest = class {
|
|
112
|
-
constructor(_req) {
|
|
113
|
-
this._req = _req;
|
|
114
|
-
this._headers = new IncomingMessageHeaders(_req);
|
|
115
|
-
}
|
|
116
117
|
_body;
|
|
117
118
|
_url;
|
|
119
|
+
_cookies;
|
|
118
120
|
_headers;
|
|
121
|
+
_req;
|
|
122
|
+
constructor(req) {
|
|
123
|
+
this._req = req;
|
|
124
|
+
this._headers = new IncomingMessageHeaders(this._req);
|
|
125
|
+
}
|
|
119
126
|
get http2() {
|
|
120
127
|
return this._req.httpVersionMajor >= 2;
|
|
121
128
|
}
|
|
@@ -140,9 +147,7 @@ var HttpServerRequest = class {
|
|
|
140
147
|
if (this._req.httpVersionMajor >= 2) {
|
|
141
148
|
dh = (this._req?.headers)[":authority"];
|
|
142
149
|
}
|
|
143
|
-
|
|
144
|
-
dh = this._req?.socket.remoteAddress;
|
|
145
|
-
}
|
|
150
|
+
dh ??= this._req?.socket.remoteAddress;
|
|
146
151
|
return requestToHost(this, dh);
|
|
147
152
|
}
|
|
148
153
|
get protocol() {
|
|
@@ -150,14 +155,16 @@ var HttpServerRequest = class {
|
|
|
150
155
|
if (this._req.httpVersionMajor > 2) {
|
|
151
156
|
dp = this._req.headers[":scheme"];
|
|
152
157
|
}
|
|
153
|
-
|
|
154
|
-
dp = this._req?.socket["encrypted"] ? "https" : "http";
|
|
155
|
-
}
|
|
158
|
+
dp ??= this._req?.socket["encrypted"] ? "https" : "http";
|
|
156
159
|
return requestToProtocol(this, dp);
|
|
157
160
|
}
|
|
158
161
|
get socket() {
|
|
159
162
|
return this._req.socket;
|
|
160
163
|
}
|
|
164
|
+
get cookies() {
|
|
165
|
+
this._cookies ??= parseCookies(this);
|
|
166
|
+
return this._cookies;
|
|
167
|
+
}
|
|
161
168
|
get body() {
|
|
162
169
|
this._body ??= new Promise((resolve, reject) => {
|
|
163
170
|
const chunks = [];
|
|
@@ -170,6 +177,13 @@ var HttpServerRequest = class {
|
|
|
170
177
|
get text() {
|
|
171
178
|
return this.body.then(async (blob) => await blob.text());
|
|
172
179
|
}
|
|
180
|
+
get formData() {
|
|
181
|
+
return this.body.then(async (blob) => {
|
|
182
|
+
const text = await blob.text();
|
|
183
|
+
const formData = new URLSearchParams(text);
|
|
184
|
+
return formData;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
173
187
|
get json() {
|
|
174
188
|
return this.body.then(async (blob) => {
|
|
175
189
|
const json = JSON.parse(await blob.text());
|
|
@@ -202,8 +216,9 @@ var IncomingMessageHeaders = class {
|
|
|
202
216
|
}
|
|
203
217
|
};
|
|
204
218
|
var OutgoingMessageHeaders = class {
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
_msg;
|
|
220
|
+
constructor(msg) {
|
|
221
|
+
this._msg = msg;
|
|
207
222
|
}
|
|
208
223
|
has(name) {
|
|
209
224
|
return this._msg.hasHeader(name);
|
|
@@ -248,11 +263,12 @@ var OutgoingMessageHeaders = class {
|
|
|
248
263
|
}
|
|
249
264
|
};
|
|
250
265
|
var HttpServerResponse = class {
|
|
251
|
-
constructor(_res) {
|
|
252
|
-
this._res = _res;
|
|
253
|
-
this._headers = new OutgoingMessageHeaders(_res);
|
|
254
|
-
}
|
|
255
266
|
_headers;
|
|
267
|
+
_res;
|
|
268
|
+
constructor(res) {
|
|
269
|
+
this._res = res;
|
|
270
|
+
this._headers = new OutgoingMessageHeaders(res);
|
|
271
|
+
}
|
|
256
272
|
get statusCode() {
|
|
257
273
|
return this._res.statusCode;
|
|
258
274
|
}
|
|
@@ -262,9 +278,57 @@ var HttpServerResponse = class {
|
|
|
262
278
|
}
|
|
263
279
|
this._res.statusCode = value;
|
|
264
280
|
}
|
|
281
|
+
set statusMessage(value) {
|
|
282
|
+
this._res.statusMessage = value;
|
|
283
|
+
}
|
|
265
284
|
get headers() {
|
|
266
285
|
return this._headers;
|
|
267
286
|
}
|
|
287
|
+
get cookies() {
|
|
288
|
+
return this.headers.list("set-cookie").map((cookie) => {
|
|
289
|
+
const parsed = import_tough_cookie.Cookie.parse(cookie);
|
|
290
|
+
if (parsed) {
|
|
291
|
+
const result = { name: parsed.key, value: parsed.value, maxAge: Number(parsed.maxAge ?? -1) };
|
|
292
|
+
if (parsed.httpOnly) result.httpOnly = true;
|
|
293
|
+
if (parsed.domain) result.domain = parsed.domain;
|
|
294
|
+
if (parsed.path) result.path = parsed.path;
|
|
295
|
+
if (parsed.secure) result.secure = true;
|
|
296
|
+
if (parsed.httpOnly) result.httpOnly = true;
|
|
297
|
+
if (parsed.sameSite) result.sameSite = parsed.sameSite;
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
}).filter((cookie) => cookie !== void 0);
|
|
301
|
+
}
|
|
302
|
+
end(chunk) {
|
|
303
|
+
if (!this._res.headersSent) {
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
if (chunk === void 0) {
|
|
306
|
+
this._res.end(() => {
|
|
307
|
+
resolve(true);
|
|
308
|
+
});
|
|
309
|
+
} else {
|
|
310
|
+
this._res.end(chunk, () => {
|
|
311
|
+
resolve(true);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
} else {
|
|
316
|
+
return Promise.resolve(false);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
addCookie(cookie) {
|
|
320
|
+
this.headers.add("set-cookie", new import_tough_cookie.Cookie({
|
|
321
|
+
key: cookie.name,
|
|
322
|
+
value: cookie.value,
|
|
323
|
+
maxAge: cookie.maxAge,
|
|
324
|
+
domain: cookie.domain,
|
|
325
|
+
path: cookie.path,
|
|
326
|
+
secure: cookie.secure,
|
|
327
|
+
httpOnly: cookie.httpOnly,
|
|
328
|
+
sameSite: cookie.sameSite
|
|
329
|
+
}).toString());
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
268
332
|
};
|
|
269
333
|
var DefaultWebExchange = class extends WebExchange {
|
|
270
334
|
constructor(request, response) {
|
|
@@ -272,6 +336,9 @@ var DefaultWebExchange = class extends WebExchange {
|
|
|
272
336
|
this.request = request;
|
|
273
337
|
this.response = response;
|
|
274
338
|
}
|
|
339
|
+
get principal() {
|
|
340
|
+
return Promise.resolve(void 0);
|
|
341
|
+
}
|
|
275
342
|
};
|
|
276
343
|
function toList(values) {
|
|
277
344
|
if (typeof values === "string") {
|
|
@@ -315,17 +382,61 @@ function parseHeader(value) {
|
|
|
315
382
|
}
|
|
316
383
|
return list;
|
|
317
384
|
}
|
|
385
|
+
var MapHttpHeaders = class extends Map {
|
|
386
|
+
get(name) {
|
|
387
|
+
return super.get(name.toLowerCase());
|
|
388
|
+
}
|
|
389
|
+
one(name) {
|
|
390
|
+
return this.get(name)?.[0];
|
|
391
|
+
}
|
|
392
|
+
list(name) {
|
|
393
|
+
const values = super.get(name.toLowerCase());
|
|
394
|
+
return toList(values);
|
|
395
|
+
}
|
|
396
|
+
set(name, value) {
|
|
397
|
+
if (typeof value === "number") {
|
|
398
|
+
value = String(value);
|
|
399
|
+
}
|
|
400
|
+
if (typeof value === "string") {
|
|
401
|
+
value = [value];
|
|
402
|
+
}
|
|
403
|
+
if (value) {
|
|
404
|
+
return super.set(name.toLowerCase(), value);
|
|
405
|
+
} else {
|
|
406
|
+
super.delete(name.toLowerCase());
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
add(name, value) {
|
|
411
|
+
const prev = super.get(name.toLowerCase());
|
|
412
|
+
if (typeof value === "string") {
|
|
413
|
+
value = [value];
|
|
414
|
+
}
|
|
415
|
+
if (prev) {
|
|
416
|
+
value = prev.concat(value);
|
|
417
|
+
}
|
|
418
|
+
this.set(name, value);
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
318
422
|
|
|
319
423
|
// src/gateway/ws/core.ts
|
|
320
424
|
var import_gateway2 = require("@interopio/gateway");
|
|
321
425
|
var GatewayEncoders = import_gateway2.IOGateway.Encoding;
|
|
322
426
|
var log = getLogger("ws");
|
|
323
427
|
var codec = GatewayEncoders.json();
|
|
324
|
-
function initClient(key, socket, host) {
|
|
428
|
+
function initClient(key, socket, host, securityContextPromise) {
|
|
325
429
|
const opts = {
|
|
326
430
|
key,
|
|
327
431
|
host,
|
|
328
432
|
codec,
|
|
433
|
+
onAuthenticate: async () => {
|
|
434
|
+
const authentication = (await securityContextPromise)?.authentication;
|
|
435
|
+
if (authentication?.authenticated) {
|
|
436
|
+
return { type: "success", user: authentication.name };
|
|
437
|
+
}
|
|
438
|
+
throw new Error(`no valid client authentication ${key}`);
|
|
439
|
+
},
|
|
329
440
|
onPing: () => {
|
|
330
441
|
socket.ping((err) => {
|
|
331
442
|
if (err) {
|
|
@@ -362,10 +473,11 @@ async function create(server) {
|
|
|
362
473
|
log.error("error starting the gateway websocket server", err);
|
|
363
474
|
}).on("connection", (socket, req) => {
|
|
364
475
|
const request = new HttpServerRequest(req);
|
|
476
|
+
const securityContextPromise = server.storage?.getStore()?.securityContext;
|
|
365
477
|
const key = socketKey(request.socket);
|
|
366
478
|
const host = request.host;
|
|
367
479
|
log.info(`${key} connected on gw from ${host}`);
|
|
368
|
-
const client = initClient.call(this, key, socket);
|
|
480
|
+
const client = initClient.call(this, key, socket, host, securityContextPromise);
|
|
369
481
|
if (!client) {
|
|
370
482
|
log.error(`${key} gw client init failed`);
|
|
371
483
|
socket.terminate();
|
|
@@ -867,9 +979,8 @@ var logger5 = getLogger("metrics");
|
|
|
867
979
|
var COOKIE_NAME = "GW_LOGIN";
|
|
868
980
|
function loggedIn(auth, ctx) {
|
|
869
981
|
if (auth) {
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
return cookie && parseInt(cookie?.substring(COOKIE_NAME.length + 1)) > Date.now();
|
|
982
|
+
const value = ctx.request.cookies.find((cookie) => cookie.name === COOKIE_NAME)?.value;
|
|
983
|
+
return value && parseInt(value) > Date.now();
|
|
873
984
|
}
|
|
874
985
|
return true;
|
|
875
986
|
}
|
|
@@ -895,7 +1006,7 @@ async function routes2(config) {
|
|
|
895
1006
|
if (ctx.method === "GET" && ctx.path === "/api/login") {
|
|
896
1007
|
const redirectTo = new URLSearchParams(ctx.request.query ?? void 0).get("redirectTo");
|
|
897
1008
|
const expires = Date.now() + 180 * 1e3;
|
|
898
|
-
ctx.response.
|
|
1009
|
+
ctx.response.addCookie({ name: COOKIE_NAME, value: `${expires}`, maxAge: Infinity, path: "/api", sameSite: "strict" });
|
|
899
1010
|
if (redirectTo) {
|
|
900
1011
|
ctx.response.statusCode = 302;
|
|
901
1012
|
ctx.response.headers.set("location", redirectTo);
|
|
@@ -941,30 +1052,38 @@ function compose(...middleware) {
|
|
|
941
1052
|
if (!Array.isArray(middleware)) {
|
|
942
1053
|
throw new Error("middleware must be array!");
|
|
943
1054
|
}
|
|
944
|
-
|
|
1055
|
+
const fns = middleware.flat();
|
|
1056
|
+
for (const fn of fns) {
|
|
945
1057
|
if (typeof fn !== "function") {
|
|
946
1058
|
throw new Error("middleware must be compose of functions!");
|
|
947
1059
|
}
|
|
948
1060
|
}
|
|
949
1061
|
return async function(ctx, next) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (i < index) {
|
|
954
|
-
throw new Error("next() called multiple times");
|
|
955
|
-
}
|
|
956
|
-
index = i;
|
|
957
|
-
let fn;
|
|
958
|
-
if (i === middleware.length) {
|
|
959
|
-
fn = next;
|
|
960
|
-
} else {
|
|
961
|
-
fn = middleware[i];
|
|
962
|
-
}
|
|
963
|
-
if (!fn) {
|
|
1062
|
+
const dispatch = async (i) => {
|
|
1063
|
+
const fn = i === fns.length ? next : fns[i];
|
|
1064
|
+
if (fn === void 0) {
|
|
964
1065
|
return;
|
|
965
1066
|
}
|
|
966
|
-
|
|
967
|
-
|
|
1067
|
+
let nextCalled = false;
|
|
1068
|
+
let nextResolved = false;
|
|
1069
|
+
const nextFn = async () => {
|
|
1070
|
+
if (nextCalled) {
|
|
1071
|
+
throw new Error("next() called multiple times");
|
|
1072
|
+
}
|
|
1073
|
+
nextCalled = true;
|
|
1074
|
+
try {
|
|
1075
|
+
return await dispatch(i + 1);
|
|
1076
|
+
} finally {
|
|
1077
|
+
nextResolved = true;
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
const result = await fn(ctx, nextFn);
|
|
1081
|
+
if (nextCalled && !nextResolved) {
|
|
1082
|
+
throw new Error("middleware resolved before downstream.\n You are probably missing an await or return statement in your middleware function.");
|
|
1083
|
+
}
|
|
1084
|
+
return result;
|
|
1085
|
+
};
|
|
1086
|
+
return dispatch(0);
|
|
968
1087
|
};
|
|
969
1088
|
}
|
|
970
1089
|
|
|
@@ -1283,9 +1402,9 @@ function processRequest(exchange, config) {
|
|
|
1283
1402
|
}
|
|
1284
1403
|
function validateConfig(config) {
|
|
1285
1404
|
if (config) {
|
|
1286
|
-
const
|
|
1287
|
-
if (
|
|
1288
|
-
|
|
1405
|
+
const headers2 = config.headers;
|
|
1406
|
+
if (headers2?.allow && headers2.allow !== ALL) {
|
|
1407
|
+
headers2.allow = headers2.allow.map((header) => header.toLowerCase());
|
|
1289
1408
|
}
|
|
1290
1409
|
const origins = config.origins;
|
|
1291
1410
|
if (origins?.allow && origins.allow !== ALL) {
|
|
@@ -1304,7 +1423,7 @@ var handler = (config) => {
|
|
|
1304
1423
|
return async (ctx, next) => {
|
|
1305
1424
|
const isValid = processRequest(ctx, config);
|
|
1306
1425
|
if (!isValid || isPreFlightRequest(ctx.request)) {
|
|
1307
|
-
ctx.response.
|
|
1426
|
+
await ctx.response.end();
|
|
1308
1427
|
} else {
|
|
1309
1428
|
await next();
|
|
1310
1429
|
}
|
|
@@ -1444,10 +1563,934 @@ function getMethodToUse(request, isPreFlight) {
|
|
|
1444
1563
|
return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
|
|
1445
1564
|
}
|
|
1446
1565
|
function getHeadersToUse(request, isPreFlight) {
|
|
1447
|
-
const
|
|
1448
|
-
return isPreFlight ?
|
|
1566
|
+
const headers2 = request.headers;
|
|
1567
|
+
return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/server/server-header.ts
|
|
1571
|
+
var serverHeader = (server) => {
|
|
1572
|
+
return async ({ response }, next) => {
|
|
1573
|
+
if (!response.headers.has("server")) {
|
|
1574
|
+
response.headers.set("Server", server);
|
|
1575
|
+
}
|
|
1576
|
+
await next();
|
|
1577
|
+
};
|
|
1578
|
+
};
|
|
1579
|
+
var server_header_default = (server = "gateway-server") => serverHeader(server);
|
|
1580
|
+
|
|
1581
|
+
// src/server/security/http-headers.ts
|
|
1582
|
+
var staticServerHttpHeadersWriter = (headers2) => {
|
|
1583
|
+
return async (exchange) => {
|
|
1584
|
+
let containsNoHeaders = true;
|
|
1585
|
+
const { response } = exchange;
|
|
1586
|
+
for (const name of headers2.keys()) {
|
|
1587
|
+
if (response.headers.has(name)) {
|
|
1588
|
+
containsNoHeaders = false;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
if (containsNoHeaders) {
|
|
1592
|
+
for (const [name, value] of headers2) {
|
|
1593
|
+
response.headers.set(name, value);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
};
|
|
1598
|
+
var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
|
|
1599
|
+
new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
|
|
1600
|
+
);
|
|
1601
|
+
var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
|
|
1602
|
+
new MapHttpHeaders().add("x-content-type-options", "nosniff")
|
|
1603
|
+
);
|
|
1604
|
+
var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
|
|
1605
|
+
let headerValue = `max-age=${maxAgeInSeconds}`;
|
|
1606
|
+
if (includeSubDomains) {
|
|
1607
|
+
headerValue += " ; includeSubDomains";
|
|
1608
|
+
}
|
|
1609
|
+
if (preload) {
|
|
1610
|
+
headerValue += " ; preload";
|
|
1611
|
+
}
|
|
1612
|
+
const delegate = staticServerHttpHeadersWriter(
|
|
1613
|
+
new MapHttpHeaders().add("strict-transport-security", headerValue)
|
|
1614
|
+
);
|
|
1615
|
+
const isSecure = (exchange) => {
|
|
1616
|
+
const protocol = exchange.request.URL.protocol;
|
|
1617
|
+
return protocol === "https:";
|
|
1618
|
+
};
|
|
1619
|
+
return async (exchange) => {
|
|
1620
|
+
if (isSecure(exchange)) {
|
|
1621
|
+
await delegate(exchange);
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
};
|
|
1625
|
+
var frameOptionsServerHttpHeadersWriter = (mode) => {
|
|
1626
|
+
return staticServerHttpHeadersWriter(
|
|
1627
|
+
new MapHttpHeaders().add("x-frame-options", mode)
|
|
1628
|
+
);
|
|
1629
|
+
};
|
|
1630
|
+
var xssProtectionServerHttpHeadersWriter = (headerValue) => staticServerHttpHeadersWriter(
|
|
1631
|
+
new MapHttpHeaders().add("x-xss-protection", headerValue)
|
|
1632
|
+
);
|
|
1633
|
+
var permissionsPolicyServerHttpHeadersWriter = (policyDirectives) => {
|
|
1634
|
+
const delegate = policyDirectives === void 0 ? void 0 : staticServerHttpHeadersWriter(
|
|
1635
|
+
new MapHttpHeaders().add("permissions-policy", policyDirectives)
|
|
1636
|
+
);
|
|
1637
|
+
return async (exchange) => {
|
|
1638
|
+
if (delegate !== void 0) {
|
|
1639
|
+
await delegate(exchange);
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
};
|
|
1643
|
+
var contentSecurityPolicyServerHttpHeadersWriter = (policyDirectives, reportOnly) => {
|
|
1644
|
+
const headerName = reportOnly ? "content-security-policy-report-only" : "content-security-policy";
|
|
1645
|
+
const delegate = policyDirectives === void 0 ? void 0 : staticServerHttpHeadersWriter(
|
|
1646
|
+
new MapHttpHeaders().add(headerName, policyDirectives)
|
|
1647
|
+
);
|
|
1648
|
+
return async (exchange) => {
|
|
1649
|
+
if (delegate !== void 0) {
|
|
1650
|
+
await delegate(exchange);
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
};
|
|
1654
|
+
var refererPolicyServerHttpHeadersWriter = (policy = "no-referrer") => {
|
|
1655
|
+
return staticServerHttpHeadersWriter(
|
|
1656
|
+
new MapHttpHeaders().add("referer-policy", policy)
|
|
1657
|
+
);
|
|
1658
|
+
};
|
|
1659
|
+
var crossOriginOpenerPolicyServerHttpHeadersWriter = (policy) => {
|
|
1660
|
+
const delegate = policy === void 0 ? void 0 : staticServerHttpHeadersWriter(
|
|
1661
|
+
new MapHttpHeaders().add("cross-origin-opener-policy", policy)
|
|
1662
|
+
);
|
|
1663
|
+
return async (exchange) => {
|
|
1664
|
+
if (delegate !== void 0) {
|
|
1665
|
+
await delegate(exchange);
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
};
|
|
1669
|
+
var crossOriginEmbedderPolicyServerHttpHeadersWriter = (policy) => {
|
|
1670
|
+
const delegate = policy === void 0 ? void 0 : staticServerHttpHeadersWriter(
|
|
1671
|
+
new MapHttpHeaders().add("cross-origin-embedder-policy", policy)
|
|
1672
|
+
);
|
|
1673
|
+
return async (exchange) => {
|
|
1674
|
+
if (delegate !== void 0) {
|
|
1675
|
+
await delegate(exchange);
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
};
|
|
1679
|
+
var crossOriginResourcePolicyServerHttpHeadersWriter = (policy) => {
|
|
1680
|
+
const delegate = policy === void 0 ? void 0 : staticServerHttpHeadersWriter(
|
|
1681
|
+
new MapHttpHeaders().add("cross-origin-resource-policy", policy)
|
|
1682
|
+
);
|
|
1683
|
+
return async (exchange) => {
|
|
1684
|
+
if (delegate !== void 0) {
|
|
1685
|
+
await delegate(exchange);
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
};
|
|
1689
|
+
var compositeServerHttpHeadersWriter = (...writers) => {
|
|
1690
|
+
return async (exchange) => {
|
|
1691
|
+
for (const writer of writers) {
|
|
1692
|
+
await writer(exchange);
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
};
|
|
1696
|
+
function headers(opts) {
|
|
1697
|
+
const writers = [];
|
|
1698
|
+
if (!opts?.cache?.disabled) {
|
|
1699
|
+
writers.push(cacheControlServerHttpHeadersWriter());
|
|
1700
|
+
}
|
|
1701
|
+
if (!opts?.contentType?.disabled) {
|
|
1702
|
+
writers.push(contentTypeServerHttpHeadersWriter());
|
|
1703
|
+
}
|
|
1704
|
+
if (!opts?.hsts?.disabled) {
|
|
1705
|
+
writers.push(strictTransportSecurityServerHttpHeadersWriter(opts?.hsts?.maxAge ?? 365 * 24 * 60 * 60, opts?.hsts?.includeSubDomains ?? true, opts?.hsts?.preload ?? false));
|
|
1706
|
+
}
|
|
1707
|
+
if (!opts?.frameOptions?.disabled) {
|
|
1708
|
+
writers.push(frameOptionsServerHttpHeadersWriter(opts?.frameOptions?.mode ?? "DENY"));
|
|
1709
|
+
}
|
|
1710
|
+
if (!opts?.xss?.disabled) {
|
|
1711
|
+
writers.push(xssProtectionServerHttpHeadersWriter(opts?.xss?.headerValue ?? "0"));
|
|
1712
|
+
}
|
|
1713
|
+
if (!opts?.permissionsPolicy?.disabled) {
|
|
1714
|
+
writers.push(permissionsPolicyServerHttpHeadersWriter(opts?.permissionsPolicy?.policyDirectives));
|
|
1715
|
+
}
|
|
1716
|
+
if (!opts?.contentSecurityPolicy?.disabled) {
|
|
1717
|
+
writers.push(contentSecurityPolicyServerHttpHeadersWriter(opts?.contentSecurityPolicy?.policyDirectives ?? "default-src 'self'", opts?.contentSecurityPolicy?.reportOnly));
|
|
1718
|
+
}
|
|
1719
|
+
if (!opts?.refererPolicy?.disabled) {
|
|
1720
|
+
writers.push(refererPolicyServerHttpHeadersWriter(opts?.refererPolicy?.policy ?? "no-referrer"));
|
|
1721
|
+
}
|
|
1722
|
+
if (!opts?.crossOriginOpenerPolicy?.disabled) {
|
|
1723
|
+
writers.push(crossOriginOpenerPolicyServerHttpHeadersWriter(opts?.crossOriginOpenerPolicy?.policy));
|
|
1724
|
+
}
|
|
1725
|
+
if (!opts?.crossOriginEmbedderPolicy?.disabled) {
|
|
1726
|
+
writers.push(crossOriginEmbedderPolicyServerHttpHeadersWriter(opts?.crossOriginEmbedderPolicy?.policy));
|
|
1727
|
+
}
|
|
1728
|
+
if (!opts?.crossOriginResourcePolicy?.disabled) {
|
|
1729
|
+
writers.push(crossOriginResourcePolicyServerHttpHeadersWriter(opts?.crossOriginResourcePolicy?.policy));
|
|
1730
|
+
}
|
|
1731
|
+
if (opts?.writers) {
|
|
1732
|
+
writers.push(...opts.writers);
|
|
1733
|
+
}
|
|
1734
|
+
const writer = compositeServerHttpHeadersWriter(...writers);
|
|
1735
|
+
return async (exchange, next) => {
|
|
1736
|
+
await writer(exchange);
|
|
1737
|
+
await next();
|
|
1738
|
+
};
|
|
1449
1739
|
}
|
|
1450
1740
|
|
|
1741
|
+
// src/server/security/types.ts
|
|
1742
|
+
var AuthenticationError = class extends Error {
|
|
1743
|
+
_authentication;
|
|
1744
|
+
get authentication() {
|
|
1745
|
+
return this._authentication;
|
|
1746
|
+
}
|
|
1747
|
+
set authentication(value) {
|
|
1748
|
+
if (value === void 0) {
|
|
1749
|
+
throw new TypeError("Authentication cannot be undefined");
|
|
1750
|
+
}
|
|
1751
|
+
this._authentication = value;
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
var InsufficientAuthenticationError = class extends AuthenticationError {
|
|
1755
|
+
};
|
|
1756
|
+
var BadCredentialsError = class extends AuthenticationError {
|
|
1757
|
+
};
|
|
1758
|
+
var AccessDeniedError = class extends Error {
|
|
1759
|
+
};
|
|
1760
|
+
var AuthorizationDecision = class {
|
|
1761
|
+
constructor(granted) {
|
|
1762
|
+
this.granted = granted;
|
|
1763
|
+
}
|
|
1764
|
+
granted;
|
|
1765
|
+
};
|
|
1766
|
+
var DefaultAuthorizationManager = class {
|
|
1767
|
+
check;
|
|
1768
|
+
constructor(check) {
|
|
1769
|
+
this.check = check;
|
|
1770
|
+
}
|
|
1771
|
+
async verify(authentication, object) {
|
|
1772
|
+
const decision = await this.check(authentication, object);
|
|
1773
|
+
if (!decision?.granted) {
|
|
1774
|
+
throw new AccessDeniedError("Access denied");
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
async authorize(authentication, object) {
|
|
1778
|
+
return await this.check(authentication, object);
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
var AuthenticationServiceError = class extends AuthenticationError {
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
// src/server/security/entry-point-failure-handler.ts
|
|
1785
|
+
var serverAuthenticationEntryPointFailureHandler = (opts) => {
|
|
1786
|
+
const entryPoint = opts.entryPoint;
|
|
1787
|
+
const rethrowAuthenticationServiceError = opts?.rethrowAuthenticationServiceError ?? true;
|
|
1788
|
+
return async ({ exchange }, error) => {
|
|
1789
|
+
if (!rethrowAuthenticationServiceError) {
|
|
1790
|
+
return entryPoint(exchange, error);
|
|
1791
|
+
}
|
|
1792
|
+
if (!(error instanceof AuthenticationServiceError)) {
|
|
1793
|
+
return entryPoint(exchange, error);
|
|
1794
|
+
}
|
|
1795
|
+
throw error;
|
|
1796
|
+
};
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
// src/server/security/http-basic-entry-point.ts
|
|
1800
|
+
var DEFAULT_REALM = "Realm";
|
|
1801
|
+
var createHeaderValue = (realm) => {
|
|
1802
|
+
return `Basic realm="${realm}"`;
|
|
1803
|
+
};
|
|
1804
|
+
var httpBasicEntryPoint = (opts) => {
|
|
1805
|
+
const headerValue = createHeaderValue(opts?.realm ?? DEFAULT_REALM);
|
|
1806
|
+
return async (exchange, _error) => {
|
|
1807
|
+
const { response } = exchange;
|
|
1808
|
+
response.statusCode = 401;
|
|
1809
|
+
response.headers.set("WWW-Authenticate", headerValue);
|
|
1810
|
+
};
|
|
1811
|
+
};
|
|
1812
|
+
|
|
1813
|
+
// src/server/security/http-basic-converter.ts
|
|
1814
|
+
var BASIC = "Basic ";
|
|
1815
|
+
var httpBasicAuthenticationConverter = (opts) => {
|
|
1816
|
+
return async (exchange) => {
|
|
1817
|
+
const { request } = exchange;
|
|
1818
|
+
const authorization = request.headers.one("authorization");
|
|
1819
|
+
if (!authorization || !/basic/i.test(authorization.substring(0))) {
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
const credentials = authorization.length <= BASIC.length ? "" : authorization.substring(BASIC.length);
|
|
1823
|
+
const decoded = Buffer.from(credentials, "base64").toString(opts?.credentialsEncoding ?? "utf-8");
|
|
1824
|
+
const parts = decoded.split(":", 2);
|
|
1825
|
+
if (parts.length !== 2) {
|
|
1826
|
+
return void 0;
|
|
1827
|
+
}
|
|
1828
|
+
return { type: "UsernamePassword", authenticated: false, principal: parts[0], credentials: parts[1] };
|
|
1829
|
+
};
|
|
1830
|
+
};
|
|
1831
|
+
|
|
1832
|
+
// src/server/util/matchers.ts
|
|
1833
|
+
var or = (matchers) => {
|
|
1834
|
+
return async (exchange) => {
|
|
1835
|
+
for (const matcher of matchers) {
|
|
1836
|
+
const result = await matcher(exchange);
|
|
1837
|
+
if (result.match) {
|
|
1838
|
+
return { match: true };
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
return { match: false };
|
|
1842
|
+
};
|
|
1843
|
+
};
|
|
1844
|
+
var and = (matchers) => {
|
|
1845
|
+
return async (exchange) => {
|
|
1846
|
+
for (const matcher of matchers) {
|
|
1847
|
+
const result = await matcher(exchange);
|
|
1848
|
+
if (!result.match) {
|
|
1849
|
+
return { match: false };
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
return { match: true };
|
|
1853
|
+
};
|
|
1854
|
+
};
|
|
1855
|
+
var not = (matcher) => {
|
|
1856
|
+
return async (exchange) => {
|
|
1857
|
+
const result = await matcher(exchange);
|
|
1858
|
+
return { match: !result.match };
|
|
1859
|
+
};
|
|
1860
|
+
};
|
|
1861
|
+
var anyExchange = async (_exchange) => {
|
|
1862
|
+
return { match: true };
|
|
1863
|
+
};
|
|
1864
|
+
var mediaType = (opts) => {
|
|
1865
|
+
const shouldIgnore = (requestedMediaType) => {
|
|
1866
|
+
if (opts.ignoredMediaTypes !== void 0) {
|
|
1867
|
+
for (const ignoredMediaType of opts.ignoredMediaTypes) {
|
|
1868
|
+
if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
|
|
1869
|
+
return true;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
return false;
|
|
1874
|
+
};
|
|
1875
|
+
return async (exchange) => {
|
|
1876
|
+
const request = exchange.request;
|
|
1877
|
+
let requestMediaTypes;
|
|
1878
|
+
try {
|
|
1879
|
+
requestMediaTypes = request.headers.list("accept");
|
|
1880
|
+
} catch (e) {
|
|
1881
|
+
return { match: false };
|
|
1882
|
+
}
|
|
1883
|
+
for (const requestedMediaType of requestMediaTypes) {
|
|
1884
|
+
if (shouldIgnore(requestedMediaType)) {
|
|
1885
|
+
continue;
|
|
1886
|
+
}
|
|
1887
|
+
for (const mediaType2 of opts.mediaTypes) {
|
|
1888
|
+
if (requestedMediaType.startsWith(mediaType2)) {
|
|
1889
|
+
return { match: true };
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return { match: false };
|
|
1894
|
+
};
|
|
1895
|
+
};
|
|
1896
|
+
|
|
1897
|
+
// src/server/security/security-context.ts
|
|
1898
|
+
var import_node_async_hooks = require("node:async_hooks");
|
|
1899
|
+
var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
|
|
1900
|
+
static hasSecurityContext(storage) {
|
|
1901
|
+
return storage.getStore()?.securityContext !== void 0;
|
|
1902
|
+
}
|
|
1903
|
+
static async getSecurityContext(storage) {
|
|
1904
|
+
return await storage.getStore()?.securityContext;
|
|
1905
|
+
}
|
|
1906
|
+
static clearSecurityContext(storage) {
|
|
1907
|
+
delete storage.getStore()?.securityContext;
|
|
1908
|
+
}
|
|
1909
|
+
static withSecurityContext(securityContext) {
|
|
1910
|
+
return (storage = new import_node_async_hooks.AsyncLocalStorage()) => {
|
|
1911
|
+
storage.getStore().securityContext = securityContext;
|
|
1912
|
+
return storage;
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
static withAuthentication(authentication) {
|
|
1916
|
+
return _AsyncStorageSecurityContextHolder.withSecurityContext(Promise.resolve({ authentication }));
|
|
1917
|
+
}
|
|
1918
|
+
static async getContext(storage) {
|
|
1919
|
+
if (_AsyncStorageSecurityContextHolder.hasSecurityContext(storage)) {
|
|
1920
|
+
return _AsyncStorageSecurityContextHolder.getSecurityContext(storage);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
|
|
1925
|
+
// src/server/security/authentication-filter.ts
|
|
1926
|
+
async function authenticate(exchange, next, token, managerResolver, successHandler, storage) {
|
|
1927
|
+
const authManager = await managerResolver(exchange);
|
|
1928
|
+
const authentication = await authManager?.(token);
|
|
1929
|
+
if (authentication === void 0) {
|
|
1930
|
+
throw new Error("No authentication manager found for the exchange");
|
|
1931
|
+
}
|
|
1932
|
+
try {
|
|
1933
|
+
await onAuthenticationSuccess(authentication, { exchange, next }, successHandler, storage);
|
|
1934
|
+
} catch (e) {
|
|
1935
|
+
if (e instanceof AuthenticationError) {
|
|
1936
|
+
}
|
|
1937
|
+
throw e;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
async function onAuthenticationSuccess(authentication, filterExchange, successHandler, storage) {
|
|
1941
|
+
AsyncStorageSecurityContextHolder.withAuthentication(authentication)(storage);
|
|
1942
|
+
await successHandler(filterExchange, authentication);
|
|
1943
|
+
}
|
|
1944
|
+
function authenticationFilter(opts) {
|
|
1945
|
+
const auth = {
|
|
1946
|
+
matcher: anyExchange,
|
|
1947
|
+
successHandler: async ({ next }) => {
|
|
1948
|
+
await next();
|
|
1949
|
+
},
|
|
1950
|
+
converter: httpBasicAuthenticationConverter({}),
|
|
1951
|
+
failureHandler: serverAuthenticationEntryPointFailureHandler({ entryPoint: httpBasicEntryPoint({}) }),
|
|
1952
|
+
...opts
|
|
1953
|
+
};
|
|
1954
|
+
let managerResolver = auth.managerResolver;
|
|
1955
|
+
if (managerResolver === void 0 && auth.manager !== void 0) {
|
|
1956
|
+
const manager = auth.manager;
|
|
1957
|
+
managerResolver = async (_exchange) => {
|
|
1958
|
+
return manager;
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
if (managerResolver === void 0) {
|
|
1962
|
+
throw new Error("Authentication filter requires a managerResolver or a manager");
|
|
1963
|
+
}
|
|
1964
|
+
return async (exchange, next) => {
|
|
1965
|
+
const matchResult = await auth.matcher(exchange);
|
|
1966
|
+
const token = matchResult.match ? await auth.converter(exchange) : void 0;
|
|
1967
|
+
if (token === void 0) {
|
|
1968
|
+
await next();
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
try {
|
|
1972
|
+
await authenticate(exchange, next, token, managerResolver, auth.successHandler, auth.storage);
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
if (error instanceof AuthenticationError) {
|
|
1975
|
+
await auth.failureHandler({ exchange, next }, error);
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
throw error;
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
// src/server/security/oauth2/token-error.ts
|
|
1984
|
+
var BearerTokenErrorCodes = {
|
|
1985
|
+
invalid_request: "invalid_request",
|
|
1986
|
+
invalid_token: "invalid_token"
|
|
1987
|
+
};
|
|
1988
|
+
var DEFAULT_URI = "https://tools.ietf.org/html/rfc6750#section-3.1";
|
|
1989
|
+
function invalidToken(message) {
|
|
1990
|
+
return { errorCode: BearerTokenErrorCodes.invalid_token, httpStatus: 401, description: message, uri: DEFAULT_URI };
|
|
1991
|
+
}
|
|
1992
|
+
function invalidRequest(message) {
|
|
1993
|
+
return { errorCode: BearerTokenErrorCodes.invalid_request, httpStatus: 400, description: message, uri: DEFAULT_URI };
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// src/server/security/oauth2/token-converter.ts
|
|
1997
|
+
var ACCESS_TOKEN_PARAMETER_NAME = "access_token";
|
|
1998
|
+
var authorizationPattern = /^Bearer\s+(?<token>[a-zA-Z0-9-._~+/]+=*)$/i;
|
|
1999
|
+
var Oauth2AuthenticationError = class extends AuthenticationError {
|
|
2000
|
+
error;
|
|
2001
|
+
constructor(error, message, options) {
|
|
2002
|
+
super(message ?? (typeof error === "string" ? void 0 : error.description), options);
|
|
2003
|
+
this.error = typeof error === "string" ? { errorCode: error } : error;
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
var isBearerTokenAuthenticationToken = (authentication) => {
|
|
2007
|
+
return authentication.type === "BearerToken";
|
|
2008
|
+
};
|
|
2009
|
+
var serverBearerTokenAuthenticationConverter = (opts) => {
|
|
2010
|
+
return async (exchange) => {
|
|
2011
|
+
const { request } = exchange;
|
|
2012
|
+
return Promise.all([
|
|
2013
|
+
resolveFromAuthorizationHeader(request.headers, opts?.headerName).then((token) => token !== void 0 ? [token] : void 0),
|
|
2014
|
+
resolveFromQueryString(request, opts?.uriQueryParameter),
|
|
2015
|
+
resolveFromBody(exchange, opts?.formEncodedBodyParameter)
|
|
2016
|
+
]).then((rs) => rs.filter((r) => r !== void 0).flat(1)).then(resolveToken).then((token) => {
|
|
2017
|
+
if (token) return { authenticated: false, type: "BearerToken", token };
|
|
2018
|
+
});
|
|
2019
|
+
};
|
|
2020
|
+
};
|
|
2021
|
+
async function resolveToken(accessTokens) {
|
|
2022
|
+
if (accessTokens.length === 0) {
|
|
2023
|
+
return;
|
|
2024
|
+
}
|
|
2025
|
+
if (accessTokens.length > 1) {
|
|
2026
|
+
const error = invalidRequest("Found multiple access tokens in the request");
|
|
2027
|
+
throw new Oauth2AuthenticationError(error);
|
|
2028
|
+
}
|
|
2029
|
+
const accessToken = accessTokens[0];
|
|
2030
|
+
if (!accessToken || accessToken.length === 0) {
|
|
2031
|
+
const error = invalidRequest("The requested access token parameter is an empty string");
|
|
2032
|
+
throw new Oauth2AuthenticationError(error);
|
|
2033
|
+
}
|
|
2034
|
+
return accessToken;
|
|
2035
|
+
}
|
|
2036
|
+
async function resolveFromAuthorizationHeader(headers2, headerName = "authorization") {
|
|
2037
|
+
const authorization = headers2.one(headerName);
|
|
2038
|
+
if (!authorization || !/bearer/i.test(authorization.substring(0))) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
const match = authorizationPattern.exec(authorization);
|
|
2042
|
+
if (match === null) {
|
|
2043
|
+
const error = invalidToken("Bearer token is malformed");
|
|
2044
|
+
throw new Oauth2AuthenticationError(error);
|
|
2045
|
+
}
|
|
2046
|
+
return match.groups?.token;
|
|
2047
|
+
}
|
|
2048
|
+
async function resolveTokens(parameters) {
|
|
2049
|
+
const accessTokens = parameters.getAll(ACCESS_TOKEN_PARAMETER_NAME);
|
|
2050
|
+
if (accessTokens.length === 0) {
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
return accessTokens;
|
|
2054
|
+
}
|
|
2055
|
+
async function resolveFromQueryString(request, allow = false) {
|
|
2056
|
+
if (!allow || request.method !== "GET") {
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
return resolveTokens(request.URL.searchParams);
|
|
2060
|
+
}
|
|
2061
|
+
async function resolveFromBody(exchange, allow = false) {
|
|
2062
|
+
const { request } = exchange;
|
|
2063
|
+
if (!allow || "application/x-www-form-urlencoded" !== request.headers.one("content-type") || request.method !== "POST") {
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
return resolveTokens(await exchange.request.formData);
|
|
2067
|
+
}
|
|
2068
|
+
var token_converter_default = serverBearerTokenAuthenticationConverter;
|
|
2069
|
+
|
|
2070
|
+
// src/server/security/oauth2/token-entry-point.ts
|
|
2071
|
+
function computeWWWAuthenticate(parameters) {
|
|
2072
|
+
let wwwAuthenticate = "Bearer";
|
|
2073
|
+
if (parameters.size !== 0) {
|
|
2074
|
+
wwwAuthenticate += " ";
|
|
2075
|
+
let i = 0;
|
|
2076
|
+
for (const [key, value] of parameters) {
|
|
2077
|
+
wwwAuthenticate += `${key}="${value}"`;
|
|
2078
|
+
if (i !== parameters.size - 1) {
|
|
2079
|
+
wwwAuthenticate += ", ";
|
|
2080
|
+
}
|
|
2081
|
+
i++;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
return wwwAuthenticate;
|
|
2085
|
+
}
|
|
2086
|
+
var isBearerTokenError = (error) => {
|
|
2087
|
+
return error.httpStatus !== void 0;
|
|
2088
|
+
};
|
|
2089
|
+
function getStatus(authError) {
|
|
2090
|
+
if (authError instanceof Oauth2AuthenticationError) {
|
|
2091
|
+
const { error } = authError;
|
|
2092
|
+
if (isBearerTokenError(error)) {
|
|
2093
|
+
return error.httpStatus;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
return 401;
|
|
2097
|
+
}
|
|
2098
|
+
function createParameters(authError, realm) {
|
|
2099
|
+
const parameters = /* @__PURE__ */ new Map();
|
|
2100
|
+
if (realm) {
|
|
2101
|
+
parameters.set("realm", realm);
|
|
2102
|
+
}
|
|
2103
|
+
if (authError instanceof Oauth2AuthenticationError) {
|
|
2104
|
+
const { error } = authError;
|
|
2105
|
+
parameters.set("error", error.errorCode);
|
|
2106
|
+
if (error.description) {
|
|
2107
|
+
parameters.set("error_description", error.description);
|
|
2108
|
+
}
|
|
2109
|
+
if (error.uri) {
|
|
2110
|
+
parameters.set("error_uri", error.uri);
|
|
2111
|
+
}
|
|
2112
|
+
if (isBearerTokenError(error) && error.scope) {
|
|
2113
|
+
parameters.set("scope", error.scope);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
return parameters;
|
|
2117
|
+
}
|
|
2118
|
+
var bearerTokenServerAuthenticationEntryPoint = (opts) => {
|
|
2119
|
+
return async (exchange, error) => {
|
|
2120
|
+
const status = getStatus(error);
|
|
2121
|
+
const parameters = createParameters(error, opts?.realmName);
|
|
2122
|
+
const wwwAuthenticate = computeWWWAuthenticate(parameters);
|
|
2123
|
+
const { response } = exchange;
|
|
2124
|
+
response.headers.set("WWW-Authenticate", wwwAuthenticate);
|
|
2125
|
+
response.statusCode = status;
|
|
2126
|
+
await response.end();
|
|
2127
|
+
};
|
|
2128
|
+
};
|
|
2129
|
+
var token_entry_point_default = bearerTokenServerAuthenticationEntryPoint;
|
|
2130
|
+
|
|
2131
|
+
// src/server/security/oauth2/jwt-auth-manager.ts
|
|
2132
|
+
var jwtAuthConverter = (opts) => {
|
|
2133
|
+
const principalClaimName = opts?.principalClaimName ?? "sub";
|
|
2134
|
+
return (jwt) => {
|
|
2135
|
+
const name = jwt.getClaimAsString(principalClaimName);
|
|
2136
|
+
return { type: "JwtToken", authenticated: true, name };
|
|
2137
|
+
};
|
|
2138
|
+
};
|
|
2139
|
+
var asyncJwtConverter = (converter) => {
|
|
2140
|
+
return async (jwt) => {
|
|
2141
|
+
return converter(jwt);
|
|
2142
|
+
};
|
|
2143
|
+
};
|
|
2144
|
+
var JwtError = class extends Error {
|
|
2145
|
+
};
|
|
2146
|
+
var BadJwtError = class extends JwtError {
|
|
2147
|
+
};
|
|
2148
|
+
function onError(error) {
|
|
2149
|
+
if (error instanceof BadJwtError) {
|
|
2150
|
+
return new Oauth2AuthenticationError(invalidToken(error.message), error.message, { cause: error });
|
|
2151
|
+
}
|
|
2152
|
+
throw new AuthenticationServiceError(error.message, { cause: error });
|
|
2153
|
+
}
|
|
2154
|
+
function jwtAuthManager(opts) {
|
|
2155
|
+
const decoder = opts.decoder;
|
|
2156
|
+
const authConverter = opts.authConverter ?? asyncJwtConverter(jwtAuthConverter({}));
|
|
2157
|
+
return async (authentication) => {
|
|
2158
|
+
if (isBearerTokenAuthenticationToken(authentication)) {
|
|
2159
|
+
const token = authentication.token;
|
|
2160
|
+
try {
|
|
2161
|
+
const jwt = await decoder(token);
|
|
2162
|
+
return await authConverter(jwt);
|
|
2163
|
+
} catch (e) {
|
|
2164
|
+
if (e instanceof JwtError) {
|
|
2165
|
+
throw onError(e);
|
|
2166
|
+
}
|
|
2167
|
+
throw e;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
// src/server/security/oauth2-resource-server.ts
|
|
2174
|
+
function resourceServer(opts) {
|
|
2175
|
+
const entryPoint = opts.entryPoint ?? token_entry_point_default({});
|
|
2176
|
+
const converter = opts?.converter ?? token_converter_default({});
|
|
2177
|
+
const failureHandler = opts.failureHandler ?? serverAuthenticationEntryPointFailureHandler({ entryPoint });
|
|
2178
|
+
if (opts.managerResolver !== void 0) {
|
|
2179
|
+
return authenticationFilter({
|
|
2180
|
+
storage: opts.storage,
|
|
2181
|
+
converter,
|
|
2182
|
+
failureHandler,
|
|
2183
|
+
managerResolver: opts.managerResolver
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
if (opts.jwt !== void 0) {
|
|
2187
|
+
const manager = opts.jwt.manager ?? jwtAuthManager(opts.jwt);
|
|
2188
|
+
return authenticationFilter({
|
|
2189
|
+
storage: opts.storage,
|
|
2190
|
+
converter,
|
|
2191
|
+
failureHandler,
|
|
2192
|
+
managerResolver: async (_exchange) => {
|
|
2193
|
+
return manager;
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
throw new Error("Invalid resource server configuration: either managerResolver or jwt must be provided");
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
// src/server/security/http-status-entry-point.ts
|
|
2201
|
+
var httpStatusEntryPoint = (opts) => {
|
|
2202
|
+
return async (exchange, _error) => {
|
|
2203
|
+
const response = exchange.response;
|
|
2204
|
+
response.statusCode = opts.httpStatus.code;
|
|
2205
|
+
response.statusMessage = opts.httpStatus.message;
|
|
2206
|
+
};
|
|
2207
|
+
};
|
|
2208
|
+
|
|
2209
|
+
// src/server/security/delegating-entry-point.ts
|
|
2210
|
+
var delegatingEntryPoint = (opts) => {
|
|
2211
|
+
const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
|
|
2212
|
+
response.statusCode = 401;
|
|
2213
|
+
await response.end();
|
|
2214
|
+
});
|
|
2215
|
+
return async (exchange, error) => {
|
|
2216
|
+
for (const [matcher, entryPoint] of opts.entryPoints) {
|
|
2217
|
+
const match = await matcher(exchange);
|
|
2218
|
+
if (match.match) {
|
|
2219
|
+
return entryPoint(exchange, error);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
return defaultEntryPoint(exchange, error);
|
|
2223
|
+
};
|
|
2224
|
+
};
|
|
2225
|
+
|
|
2226
|
+
// src/server/security/delegating-success-handler.ts
|
|
2227
|
+
var delegatingSuccessHandler = (handlers) => {
|
|
2228
|
+
return async ({ exchange, next }, authentication) => {
|
|
2229
|
+
for (const handler2 of handlers) {
|
|
2230
|
+
await handler2({ exchange, next }, authentication);
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
};
|
|
2234
|
+
|
|
2235
|
+
// src/server/security/http-basic.ts
|
|
2236
|
+
function httpBasic(opts) {
|
|
2237
|
+
const xhrMatcher = async (exchange) => {
|
|
2238
|
+
const headers2 = exchange.request.headers;
|
|
2239
|
+
const h = headers2.list("X-Requested-With");
|
|
2240
|
+
if (h.includes("XMLHttpRequest")) {
|
|
2241
|
+
return { match: true };
|
|
2242
|
+
}
|
|
2243
|
+
return { match: false };
|
|
2244
|
+
};
|
|
2245
|
+
const defaultEntryPoint = delegatingEntryPoint({
|
|
2246
|
+
entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus: { code: 401 } })]],
|
|
2247
|
+
defaultEntryPoint: httpBasicEntryPoint({})
|
|
2248
|
+
});
|
|
2249
|
+
const entryPoint = opts.entryPoint ?? defaultEntryPoint;
|
|
2250
|
+
const manager = opts.manager;
|
|
2251
|
+
const restMatcher = mediaType({
|
|
2252
|
+
mediaTypes: [
|
|
2253
|
+
"application/atom+xml",
|
|
2254
|
+
"application/x-www-form-urlencoded",
|
|
2255
|
+
"application/json",
|
|
2256
|
+
"application/octet-stream",
|
|
2257
|
+
"application/xml",
|
|
2258
|
+
"multipart/form-data",
|
|
2259
|
+
"text/xml"
|
|
2260
|
+
],
|
|
2261
|
+
ignoredMediaTypes: ["*/*"]
|
|
2262
|
+
});
|
|
2263
|
+
const notHtmlMatcher = not(mediaType({ mediaTypes: ["text/html"] }));
|
|
2264
|
+
const restNoHtmlMatcher = and([notHtmlMatcher, restMatcher]);
|
|
2265
|
+
const preferredMatcher = or([xhrMatcher, restNoHtmlMatcher]);
|
|
2266
|
+
opts.defaultEntryPoints.push([preferredMatcher, entryPoint]);
|
|
2267
|
+
const failureHandler = opts.failureHandler ?? serverAuthenticationEntryPointFailureHandler({ entryPoint });
|
|
2268
|
+
const successHandler = delegatingSuccessHandler(opts.successHandlers === void 0 ? opts.defaultSuccessHandlers : opts.successHandlers);
|
|
2269
|
+
const converter = httpBasicAuthenticationConverter({});
|
|
2270
|
+
return authenticationFilter({
|
|
2271
|
+
storage: opts.storage,
|
|
2272
|
+
manager,
|
|
2273
|
+
failureHandler,
|
|
2274
|
+
successHandler,
|
|
2275
|
+
converter
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// src/server/security/config.ts
|
|
2280
|
+
var import_jwt = require("@interopio/gateway/jose/jwt");
|
|
2281
|
+
|
|
2282
|
+
// src/server/security/error-filter.ts
|
|
2283
|
+
async function commenceAuthentication(exchange, authentication, entryPoint) {
|
|
2284
|
+
const cause = new InsufficientAuthenticationError(`Full authentication is required to access this resource.`);
|
|
2285
|
+
const e = new AuthenticationError("Access Denied", { cause });
|
|
2286
|
+
if (authentication) {
|
|
2287
|
+
e.authentication = authentication;
|
|
2288
|
+
}
|
|
2289
|
+
await entryPoint(exchange, e);
|
|
2290
|
+
}
|
|
2291
|
+
function httpStatusAccessDeniedHandler(status, message) {
|
|
2292
|
+
return async (exchange, _error) => {
|
|
2293
|
+
exchange.response.statusCode = status;
|
|
2294
|
+
exchange.response.statusMessage = message;
|
|
2295
|
+
exchange.response.headers.set("Content-Type", "text/plain");
|
|
2296
|
+
const bytes = Buffer.from("Access Denied", "utf-8");
|
|
2297
|
+
exchange.response.headers.set("Content-Length", bytes.length);
|
|
2298
|
+
await exchange.response.end(bytes);
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
var errorFilter = (opts) => {
|
|
2302
|
+
const accessDeniedHandler = httpStatusAccessDeniedHandler(403, "Forbidden");
|
|
2303
|
+
const authenticationEntryPoint = opts.authenticationEntryPoint ?? httpBasicEntryPoint();
|
|
2304
|
+
return async (exchange, next) => {
|
|
2305
|
+
try {
|
|
2306
|
+
await next();
|
|
2307
|
+
} catch (error) {
|
|
2308
|
+
if (error instanceof AccessDeniedError) {
|
|
2309
|
+
const principal = await exchange.principal;
|
|
2310
|
+
if (principal === void 0) {
|
|
2311
|
+
await commenceAuthentication(exchange, void 0, authenticationEntryPoint);
|
|
2312
|
+
} else {
|
|
2313
|
+
if (!principal.authenticated) {
|
|
2314
|
+
return accessDeniedHandler(exchange, error);
|
|
2315
|
+
}
|
|
2316
|
+
await commenceAuthentication(exchange, principal, authenticationEntryPoint);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
};
|
|
2322
|
+
|
|
2323
|
+
// src/server/security/authorization-filter.ts
|
|
2324
|
+
function authorizationFilter(opts) {
|
|
2325
|
+
const { manager, storage } = opts;
|
|
2326
|
+
return async (exchange, next) => {
|
|
2327
|
+
const promise = AsyncStorageSecurityContextHolder.getContext(storage).then((c) => c?.authentication);
|
|
2328
|
+
try {
|
|
2329
|
+
await manager.verify(promise, exchange);
|
|
2330
|
+
} catch (error) {
|
|
2331
|
+
if (error instanceof AccessDeniedError) {
|
|
2332
|
+
}
|
|
2333
|
+
throw error;
|
|
2334
|
+
}
|
|
2335
|
+
await next();
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// src/server/security/delegating-authorization-manager.ts
|
|
2340
|
+
function delegatingAuthorizationManager(opts) {
|
|
2341
|
+
const check = async (authentication, exchange) => {
|
|
2342
|
+
let decision;
|
|
2343
|
+
for (const [matcher, manager] of opts.mappings) {
|
|
2344
|
+
if ((await matcher(exchange))?.match) {
|
|
2345
|
+
const checkResult = await manager.check(authentication, { exchange });
|
|
2346
|
+
if (checkResult !== void 0) {
|
|
2347
|
+
decision = checkResult;
|
|
2348
|
+
break;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
decision ??= new AuthorizationDecision(false);
|
|
2353
|
+
return decision;
|
|
2354
|
+
};
|
|
2355
|
+
return new DefaultAuthorizationManager(check);
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// src/server/security/config.ts
|
|
2359
|
+
var filterOrder = {
|
|
2360
|
+
first: Number.MAX_SAFE_INTEGER,
|
|
2361
|
+
http_headers: 1 * 100,
|
|
2362
|
+
https_redirect: 2 * 100,
|
|
2363
|
+
cors: 3 * 100,
|
|
2364
|
+
http_basic: 6 * 100,
|
|
2365
|
+
authentication: 8 * 100,
|
|
2366
|
+
error_translation: 18 * 100,
|
|
2367
|
+
authorization: 19 * 100,
|
|
2368
|
+
last: Number.MAX_SAFE_INTEGER
|
|
2369
|
+
};
|
|
2370
|
+
var filterOrderSymbol = Symbol.for("filterOrder");
|
|
2371
|
+
var config_default = (config, storage) => {
|
|
2372
|
+
const middleware = [];
|
|
2373
|
+
class ServerHttpSecurity {
|
|
2374
|
+
#authenticationEntryPoint;
|
|
2375
|
+
#defaultEntryPoints = [];
|
|
2376
|
+
manager;
|
|
2377
|
+
get authenticationEntryPoint() {
|
|
2378
|
+
if (this.#authenticationEntryPoint !== void 0 || this.#defaultEntryPoints.length === 0) {
|
|
2379
|
+
return this.#authenticationEntryPoint;
|
|
2380
|
+
}
|
|
2381
|
+
if (this.#defaultEntryPoints.length === 1) {
|
|
2382
|
+
return this.#defaultEntryPoints[0][1];
|
|
2383
|
+
}
|
|
2384
|
+
return delegatingEntryPoint({
|
|
2385
|
+
entryPoints: this.#defaultEntryPoints,
|
|
2386
|
+
defaultEntryPoint: this.#defaultEntryPoints[this.#defaultEntryPoints.length - 1][1]
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
build() {
|
|
2390
|
+
if (config.headers !== void 0 && config.headers.disabled !== true) {
|
|
2391
|
+
const writer = headers(config.headers);
|
|
2392
|
+
writer[filterOrderSymbol] = filterOrder.http_headers;
|
|
2393
|
+
middleware.push(writer);
|
|
2394
|
+
}
|
|
2395
|
+
if (config.basic !== void 0 && config.basic?.disabled !== true) {
|
|
2396
|
+
const username = config.basic.user?.name.toLowerCase();
|
|
2397
|
+
const password = config.basic.user?.password ?? "";
|
|
2398
|
+
const authorities = config.basic.user?.authorities ?? [];
|
|
2399
|
+
const manager = async (auth) => {
|
|
2400
|
+
const principal = auth["principal"];
|
|
2401
|
+
const credentials = auth["credentials"];
|
|
2402
|
+
if (principal.toLowerCase() !== username || credentials !== password) {
|
|
2403
|
+
throw new BadCredentialsError("Invalid username or password");
|
|
2404
|
+
}
|
|
2405
|
+
return { type: "UsernamePassword", authenticated: true, principal, credentials, authorities: [...authorities] };
|
|
2406
|
+
};
|
|
2407
|
+
const defaultSuccessHandlers = [
|
|
2408
|
+
async ({ exchange: _, next }, _authentication) => {
|
|
2409
|
+
return next();
|
|
2410
|
+
}
|
|
2411
|
+
];
|
|
2412
|
+
const filter = httpBasic({
|
|
2413
|
+
storage,
|
|
2414
|
+
manager,
|
|
2415
|
+
defaultEntryPoints: this.#defaultEntryPoints,
|
|
2416
|
+
defaultSuccessHandlers
|
|
2417
|
+
});
|
|
2418
|
+
filter[filterOrderSymbol] = filterOrder.http_basic;
|
|
2419
|
+
middleware.push(filter);
|
|
2420
|
+
}
|
|
2421
|
+
if (config.jwt !== void 0 && config.jwt.disabled !== true) {
|
|
2422
|
+
const verifier = (0, import_jwt.jwtVerifier)({
|
|
2423
|
+
issuerBaseUri: config.jwt.issuerBaseUri,
|
|
2424
|
+
issuer: config.jwt.issuer,
|
|
2425
|
+
audience: config.jwt.audience
|
|
2426
|
+
});
|
|
2427
|
+
const decoder = async (token) => {
|
|
2428
|
+
const { payload } = await verifier(token);
|
|
2429
|
+
return {
|
|
2430
|
+
subject: payload.sub,
|
|
2431
|
+
getClaimAsString(claim) {
|
|
2432
|
+
return payload[claim];
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
};
|
|
2436
|
+
const filter = resourceServer({ storage, jwt: { decoder } });
|
|
2437
|
+
filter[filterOrderSymbol] = filterOrder.authentication;
|
|
2438
|
+
middleware.push(filter);
|
|
2439
|
+
}
|
|
2440
|
+
if (config.authorize !== void 0) {
|
|
2441
|
+
const errorFf = errorFilter({ authenticationEntryPoint: this.authenticationEntryPoint });
|
|
2442
|
+
errorFf[filterOrderSymbol] = filterOrder.error_translation;
|
|
2443
|
+
middleware.push(errorFf);
|
|
2444
|
+
const buildAuthorizationManager = (authorize) => {
|
|
2445
|
+
const mappings = [];
|
|
2446
|
+
let anyExchangeRegistered = false;
|
|
2447
|
+
for (const [matcher, access2] of authorize ?? []) {
|
|
2448
|
+
let serverMatcher;
|
|
2449
|
+
if (matcher === "any-exchange") {
|
|
2450
|
+
anyExchangeRegistered = true;
|
|
2451
|
+
serverMatcher = anyExchange;
|
|
2452
|
+
} else if (anyExchangeRegistered) {
|
|
2453
|
+
throw new Error("Cannot register other matchers after 'any-exchange' matcher");
|
|
2454
|
+
} else {
|
|
2455
|
+
serverMatcher = matcher;
|
|
2456
|
+
}
|
|
2457
|
+
let manager2;
|
|
2458
|
+
if (access2.access === "permit-all") {
|
|
2459
|
+
manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(true));
|
|
2460
|
+
} else if (access2.access === "deny-all") {
|
|
2461
|
+
manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(false));
|
|
2462
|
+
} else if (access2.access === "authenticated") {
|
|
2463
|
+
manager2 = new DefaultAuthorizationManager(async (p) => {
|
|
2464
|
+
const authentication = await p;
|
|
2465
|
+
if (authentication !== void 0) {
|
|
2466
|
+
return new AuthorizationDecision(authentication.authenticated);
|
|
2467
|
+
}
|
|
2468
|
+
return new AuthorizationDecision(false);
|
|
2469
|
+
});
|
|
2470
|
+
} else {
|
|
2471
|
+
throw new Error(`Unknown access type: ${JSON.stringify(access2)}`);
|
|
2472
|
+
}
|
|
2473
|
+
mappings.push([serverMatcher, manager2]);
|
|
2474
|
+
}
|
|
2475
|
+
return delegatingAuthorizationManager({ mappings });
|
|
2476
|
+
};
|
|
2477
|
+
const manager = buildAuthorizationManager(config.authorize);
|
|
2478
|
+
const filter = authorizationFilter({ manager, storage });
|
|
2479
|
+
filter[filterOrderSymbol] = filterOrder.authorization;
|
|
2480
|
+
middleware.push(filter);
|
|
2481
|
+
}
|
|
2482
|
+
middleware.sort((a, b) => {
|
|
2483
|
+
const aOrder = a[filterOrderSymbol] ?? filterOrder.last;
|
|
2484
|
+
const bOrder = b[filterOrderSymbol] ?? filterOrder.last;
|
|
2485
|
+
return aOrder - bOrder;
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
const security = new ServerHttpSecurity();
|
|
2490
|
+
security.build();
|
|
2491
|
+
return middleware;
|
|
2492
|
+
};
|
|
2493
|
+
|
|
1451
2494
|
// src/server.ts
|
|
1452
2495
|
var logger7 = getLogger("app");
|
|
1453
2496
|
function secureContextOptions(ssl) {
|
|
@@ -1457,20 +2500,81 @@ function secureContextOptions(ssl) {
|
|
|
1457
2500
|
if (ssl.ca) options.ca = (0, import_node_fs.readFileSync)(ssl.ca);
|
|
1458
2501
|
return options;
|
|
1459
2502
|
}
|
|
1460
|
-
function createListener(middleware, routes3) {
|
|
1461
|
-
const storage = new import_node_async_hooks.AsyncLocalStorage();
|
|
2503
|
+
function createListener(storage, middleware, routes3, onSocketError) {
|
|
1462
2504
|
const listener = compose(
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
2505
|
+
server_header_default(),
|
|
2506
|
+
...config_default({
|
|
2507
|
+
authorize: [
|
|
2508
|
+
[async (exchange) => {
|
|
2509
|
+
return { match: exchange.path === "/health" && exchange.method === "GET" };
|
|
2510
|
+
}, { access: "permit-all" }],
|
|
2511
|
+
// ['any-exchange', {access: 'authenticated'}],
|
|
2512
|
+
["any-exchange", { access: "permit-all" }]
|
|
2513
|
+
],
|
|
2514
|
+
basic: {
|
|
2515
|
+
disabled: true,
|
|
2516
|
+
realm: "Gateway Server"
|
|
2517
|
+
},
|
|
2518
|
+
jwt: {
|
|
2519
|
+
disabled: true
|
|
2520
|
+
}
|
|
2521
|
+
}, storage),
|
|
1467
2522
|
...cors_default({
|
|
1468
|
-
origins: { allow: [/http:\/\/localhost(:\d+)
|
|
2523
|
+
origins: { allow: [/http:\/\/localhost(:\d+)?/, /file:\//] },
|
|
1469
2524
|
methods: { allow: ["GET", "HEAD", "POST", "DELETE"] },
|
|
1470
2525
|
headers: { allow: "*" },
|
|
1471
2526
|
credentials: { allow: true }
|
|
1472
2527
|
}),
|
|
1473
2528
|
...middleware,
|
|
2529
|
+
async ({ request, response }, next) => {
|
|
2530
|
+
const path = request.path ?? "/";
|
|
2531
|
+
const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
|
|
2532
|
+
if (path === "/" && route2.default === true) {
|
|
2533
|
+
return true;
|
|
2534
|
+
}
|
|
2535
|
+
});
|
|
2536
|
+
if (route) {
|
|
2537
|
+
if (request.method === "GET" && request.headers.one("connection") === "Upgrade" && request.headers.one("upgrade")?.toLowerCase() === "websocket") {
|
|
2538
|
+
const socket = request.socket;
|
|
2539
|
+
const host = request.host;
|
|
2540
|
+
const info = socketKey(request._req.socket);
|
|
2541
|
+
if (route?.wss) {
|
|
2542
|
+
socket.removeListener("error", onSocketError);
|
|
2543
|
+
const wss = route.wss;
|
|
2544
|
+
if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
|
|
2545
|
+
logger7.warn(`${info} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
|
|
2546
|
+
socket.destroy();
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
const origin = request.headers["origin"];
|
|
2550
|
+
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
2551
|
+
logger7.info(`${info} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
|
|
2552
|
+
socket.destroy();
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
if (logger7.enabledFor("debug")) {
|
|
2556
|
+
logger7.debug(`${info} accepted new ws connection request from ${host} on ${path}`);
|
|
2557
|
+
}
|
|
2558
|
+
wss.handleUpgrade(request._req, socket, request._req["_upgradeHead"], (ws) => {
|
|
2559
|
+
response._res["_header"] = true;
|
|
2560
|
+
ws.on("pong", () => ws["connected"] = true);
|
|
2561
|
+
ws.on("ping", () => {
|
|
2562
|
+
});
|
|
2563
|
+
wss.emit("connection", ws, request._req);
|
|
2564
|
+
});
|
|
2565
|
+
} else {
|
|
2566
|
+
logger7.warn(`${info} rejected upgrade request from ${host} on ${path}`);
|
|
2567
|
+
socket.destroy();
|
|
2568
|
+
}
|
|
2569
|
+
} else {
|
|
2570
|
+
response.statusCode = 426;
|
|
2571
|
+
response._res.appendHeader("Upgrade", "websocket").appendHeader("Connection", "Upgrade").appendHeader("Content-Type", "text/plain");
|
|
2572
|
+
await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
|
|
2573
|
+
}
|
|
2574
|
+
} else {
|
|
2575
|
+
await next();
|
|
2576
|
+
}
|
|
2577
|
+
},
|
|
1474
2578
|
async ({ request, response }, next) => {
|
|
1475
2579
|
if (request.method === "GET" && request.path === "/health") {
|
|
1476
2580
|
response.statusCode = 200;
|
|
@@ -1481,33 +2585,35 @@ function createListener(middleware, routes3) {
|
|
|
1481
2585
|
},
|
|
1482
2586
|
async ({ request, response }, next) => {
|
|
1483
2587
|
if (request.method === "GET" && request.path === "/") {
|
|
1484
|
-
response.
|
|
2588
|
+
await response.end(`io.Gateway Server`);
|
|
1485
2589
|
} else {
|
|
1486
2590
|
await next();
|
|
1487
2591
|
}
|
|
1488
2592
|
},
|
|
1489
2593
|
async ({ request, response }, _next) => {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
response.statusCode = 426;
|
|
1493
|
-
response._res.appendHeader("Upgrade", "websocket").appendHeader("Connection", "Upgrade").appendHeader("Content-Type", "text/plain");
|
|
1494
|
-
response._res.end(`This service [${request.path}] requires use of the websocket protocol.`);
|
|
1495
|
-
} else {
|
|
1496
|
-
response.statusCode = 404;
|
|
1497
|
-
response._res.end(import_node_http.default.STATUS_CODES[404]);
|
|
1498
|
-
}
|
|
2594
|
+
response.statusCode = 404;
|
|
2595
|
+
await response.end(import_node_http.default.STATUS_CODES[404]);
|
|
1499
2596
|
}
|
|
1500
2597
|
);
|
|
1501
2598
|
return (request, response) => {
|
|
2599
|
+
request.socket.addListener("error", onSocketError);
|
|
1502
2600
|
const exchange = new DefaultWebExchange(new HttpServerRequest(request), new HttpServerResponse(response));
|
|
1503
|
-
return storage.run(exchange, async () => {
|
|
2601
|
+
return storage.run({ exchange }, async () => {
|
|
1504
2602
|
if (logger7.enabledFor("debug")) {
|
|
1505
2603
|
const socket = exchange.request._req.socket;
|
|
1506
2604
|
if (logger7.enabledFor("debug")) {
|
|
1507
2605
|
logger7.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
|
|
1508
2606
|
}
|
|
1509
2607
|
}
|
|
1510
|
-
|
|
2608
|
+
try {
|
|
2609
|
+
return await listener(exchange);
|
|
2610
|
+
} catch (e) {
|
|
2611
|
+
if (logger7.enabledFor("warn")) {
|
|
2612
|
+
logger7.warn(`error processing request for ${exchange.path}`, e);
|
|
2613
|
+
}
|
|
2614
|
+
} finally {
|
|
2615
|
+
await exchange.response.end();
|
|
2616
|
+
}
|
|
1511
2617
|
});
|
|
1512
2618
|
};
|
|
1513
2619
|
}
|
|
@@ -1566,12 +2672,11 @@ var Factory = async (options) => {
|
|
|
1566
2672
|
}
|
|
1567
2673
|
const ports = portRange(options.port ?? 0);
|
|
1568
2674
|
const host = options.host;
|
|
2675
|
+
const storage = new import_node_async_hooks2.AsyncLocalStorage();
|
|
1569
2676
|
const serverP = new Promise((resolve, reject) => {
|
|
1570
2677
|
const onSocketError = (err) => logger7.error(`socket error: ${err}`, err);
|
|
1571
|
-
const
|
|
1572
|
-
|
|
1573
|
-
createListener(middleware, routes3)
|
|
1574
|
-
);
|
|
2678
|
+
const listener = createListener(storage, middleware, routes3, onSocketError);
|
|
2679
|
+
const server2 = createServer({}, listener);
|
|
1575
2680
|
server2.on("error", (e) => {
|
|
1576
2681
|
if (e["code"] === "EADDRINUSE") {
|
|
1577
2682
|
logger7.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
|
|
@@ -1597,7 +2702,7 @@ var Factory = async (options) => {
|
|
|
1597
2702
|
logger7.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
|
|
1598
2703
|
const wss = new import_ws.WebSocketServer({ noServer: true });
|
|
1599
2704
|
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info.port}${path}`;
|
|
1600
|
-
const handler2 = await route.factory({ endpoint, wss });
|
|
2705
|
+
const handler2 = await route.factory({ endpoint, wss, storage });
|
|
1601
2706
|
const pingInterval = route.ping;
|
|
1602
2707
|
if (pingInterval) {
|
|
1603
2708
|
const pingIntervalId = setInterval(() => {
|
|
@@ -1625,42 +2730,10 @@ var Factory = async (options) => {
|
|
|
1625
2730
|
server2.on("upgrade", (req, socket, head) => {
|
|
1626
2731
|
socket.addListener("error", onSocketError);
|
|
1627
2732
|
try {
|
|
1628
|
-
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
return true;
|
|
1633
|
-
}
|
|
1634
|
-
});
|
|
1635
|
-
const host2 = request.host;
|
|
1636
|
-
const info = socketKey(request.socket);
|
|
1637
|
-
if (route?.wss) {
|
|
1638
|
-
socket.removeListener("error", onSocketError);
|
|
1639
|
-
const wss = route.wss;
|
|
1640
|
-
if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
|
|
1641
|
-
logger7.warn(`${info} dropping ws connection request from ${host2} on ${path}. max connections exceeded.`);
|
|
1642
|
-
socket.destroy();
|
|
1643
|
-
return;
|
|
1644
|
-
}
|
|
1645
|
-
const origin = request.headers["origin"];
|
|
1646
|
-
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
1647
|
-
logger7.info(`${info} dropping ws connection request from ${host2} on ${path}. origin ${origin ?? "<missing>"}`);
|
|
1648
|
-
socket.destroy();
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
if (logger7.enabledFor("debug")) {
|
|
1652
|
-
logger7.debug(`${info} accepted new ws connection request from ${host2} on ${path}`);
|
|
1653
|
-
}
|
|
1654
|
-
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
1655
|
-
ws.on("pong", () => ws["connected"] = true);
|
|
1656
|
-
ws.on("ping", () => {
|
|
1657
|
-
});
|
|
1658
|
-
wss.emit("connection", ws, req);
|
|
1659
|
-
});
|
|
1660
|
-
} else {
|
|
1661
|
-
logger7.warn(`${info} rejected upgrade request from ${host2} on ${path}`);
|
|
1662
|
-
socket.destroy();
|
|
1663
|
-
}
|
|
2733
|
+
req._upgradeHead = head;
|
|
2734
|
+
const res = new import_node_http.default.ServerResponse(req);
|
|
2735
|
+
res.assignSocket(socket);
|
|
2736
|
+
listener(req, res);
|
|
1664
2737
|
} catch (err) {
|
|
1665
2738
|
logger7.error(`upgrade error: ${err}`, err);
|
|
1666
2739
|
}
|