@nodefony/framework 10.0.0-beta.5 → 10.0.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/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js +7 -0
- package/dist/index.js +14 -10
- package/dist/nodefony/config/config.js +16 -2
- package/dist/nodefony/controller/AdminApiController.js +18 -5
- package/dist/nodefony/controller/BenchController.js +1 -1
- package/dist/nodefony/controller/CspReportController.js +70 -0
- package/dist/nodefony/controller/IssuerMetadataController.js +1 -1
- package/dist/nodefony/controller/LoginPageController.js +5 -7
- package/dist/nodefony/controller/ProtectedResourceMetadataController.js +1 -1
- package/dist/nodefony/controller/SessionAuthController.js +6 -7
- package/dist/nodefony/controller/TokenAuthController.js +3 -3
- package/dist/nodefony/decorators/routerDecorators.js +2 -2
- package/dist/nodefony/service/AdminBroker.js +1 -1
- package/dist/nodefony/service/IdempotencyStore.js +1 -1
- package/dist/nodefony/service/router.js +5 -4
- package/dist/nodefony/src/Controller.js +311 -114
- package/dist/nodefony/src/PlaygroundAdminApi.js +1 -1
- package/dist/nodefony/src/Resolver.js +3 -9
- package/dist/nodefony/src/SecurityApiController.js +30 -7
- package/dist/nodefony/src/conditionalRequest.js +103 -0
- package/dist/nodefony/src/docsReader.js +1 -1
- package/dist/nodefony/src/idempotency.js +1 -1
- package/dist/nodefony/src/loginPage.js +2 -1
- package/dist/nodefony/src/scopeCatalog.js +1 -1
- package/dist/nodefony/src/viewNonce.js +58 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/nodefony/config/config.d.ts +20 -0
- package/dist/types/nodefony/controller/AdminApiController.d.ts +4 -2
- package/dist/types/nodefony/controller/CspReportController.d.ts +35 -0
- package/dist/types/nodefony/controller/LoginPageController.d.ts +3 -2
- package/dist/types/nodefony/controller/SessionAuthController.d.ts +5 -6
- package/dist/types/nodefony/controller/TokenAuthController.d.ts +3 -3
- package/dist/types/nodefony/interfaces/IController.d.ts +2 -2
- package/dist/types/nodefony/service/router.d.ts +3 -2
- package/dist/types/nodefony/src/Controller.d.ts +110 -12
- package/dist/types/nodefony/src/Resolver.d.ts +1 -1
- package/dist/types/nodefony/src/SecurityApiController.d.ts +17 -4
- package/dist/types/nodefony/src/conditionalRequest.d.ts +82 -0
- package/dist/types/nodefony/src/loginPage.d.ts +1 -1
- package/dist/types/nodefony/src/viewNonce.d.ts +33 -0
- package/docs/controller.md +145 -26
- package/docs/decorateurs.md +1 -1
- package/docs/idempotence.md +2 -2
- package/docs/index.md +4 -2
- package/docs/templates.md +15 -0
- package/package.json +10 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// \0@oxc-project+runtime@0.
|
|
1
|
+
// \0@oxc-project+runtime@0.157.0/helpers/esm/decorate.js
|
|
2
2
|
function __decorate(decorators, target, key, desc) {
|
|
3
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
package/dist/index.js
CHANGED
|
@@ -3,17 +3,14 @@ import { defineFrameworkConfig, frameworkConfigJsonSchema } from "./nodefony/con
|
|
|
3
3
|
import { getIdempotencyStoreFactory, listIdempotencyBackends, listIdempotencyStores, registerIdempotencyStore } from "./nodefony/src/idempotencyStoreRegistry.js";
|
|
4
4
|
import { RedisIdempotencyStore } from "./nodefony/src/RedisIdempotencyStore.js";
|
|
5
5
|
import { scheduleIdempotencyGc } from "./nodefony/src/idempotencyGc.js";
|
|
6
|
-
import Route from "./nodefony/src/Route.js";
|
|
7
|
-
import { validateRouteInput } from "./nodefony/src/routeSchema.js";
|
|
8
|
-
import Controller from "./nodefony/src/Controller.js";
|
|
9
|
-
import { All, Anonymous, Body, BypassFirewall, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, Options, Param, Patch, Post, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Scope, Session, UploadedFile, UploadedFiles, UseSession, buildContextParamArgs, controller, controllers, getParamArgsMeta, route, routeExpectsBodyStream } from "./nodefony/decorators/routerDecorators.js";
|
|
10
|
-
import Resolver from "./nodefony/src/Resolver.js";
|
|
11
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
12
6
|
import router_default, { FRAMEWORK_ROUTER, isFrameworkRouter } from "./nodefony/service/router.js";
|
|
7
|
+
import Route from "./nodefony/src/Route.js";
|
|
8
|
+
import Controller, { configureFileServing } from "./nodefony/src/Controller.js";
|
|
13
9
|
import ResourceController from "./nodefony/src/ResourceController.js";
|
|
14
|
-
import
|
|
10
|
+
import Resolver from "./nodefony/src/Resolver.js";
|
|
15
11
|
import AdminBroker_default from "./nodefony/service/AdminBroker.js";
|
|
16
12
|
import IdempotencyStore_default from "./nodefony/service/IdempotencyStore.js";
|
|
13
|
+
import AdminApiController from "./nodefony/controller/AdminApiController.js";
|
|
17
14
|
import SessionAuthController, { mountSessionAuthRoutes } from "./nodefony/controller/SessionAuthController.js";
|
|
18
15
|
import LoginPageController, { mountLoginPage } from "./nodefony/controller/LoginPageController.js";
|
|
19
16
|
import TokenAuthController, { mountTokenAuthRoutes } from "./nodefony/controller/TokenAuthController.js";
|
|
@@ -21,14 +18,18 @@ import IssuerMetadataController, { mountIssuerMetadataRoutes } from "./nodefony/
|
|
|
21
18
|
import ProtectedResourceMetadataController, { collectProtectedResources, mountProtectedResourceRoutes, protectedResourceRoutePaths } from "./nodefony/controller/ProtectedResourceMetadataController.js";
|
|
22
19
|
import WebAuthnController, { mountWebAuthnRoutes } from "./nodefony/controller/WebAuthnController.js";
|
|
23
20
|
import OAuth2Controller, { mountOAuth2Routes } from "./nodefony/controller/OAuth2Controller.js";
|
|
21
|
+
import CspReportController, { mountCspReportRoute } from "./nodefony/controller/CspReportController.js";
|
|
24
22
|
import BenchController, { mountBenchRoutes } from "./nodefony/controller/BenchController.js";
|
|
25
23
|
import ApiKeyController, { mountApiKeyRoutes } from "./nodefony/controller/ApiKeyController.js";
|
|
26
24
|
import TotpController, { mountTotpRoutes } from "./nodefony/controller/TotpController.js";
|
|
27
25
|
import { createKernelAdminApi } from "./nodefony/src/KernelAdminApi.js";
|
|
28
|
-
import { buildPlaygroundSnapshot } from "./nodefony/src/PlaygroundAdminApi.js";
|
|
29
26
|
import { createFrameworkAdminApi } from "./nodefony/src/FrameworkAdminApi.js";
|
|
27
|
+
import { buildPlaygroundSnapshot } from "./nodefony/src/PlaygroundAdminApi.js";
|
|
30
28
|
import { createSyslogAdminApi } from "./nodefony/src/SyslogAdminApi.js";
|
|
31
29
|
import Eta from "./nodefony/service/Eta.js";
|
|
30
|
+
import { All, Anonymous, Body, BypassFirewall, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, Options, Param, Patch, Post, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Scope, Session, UploadedFile, UploadedFiles, UseSession, buildContextParamArgs, controller, controllers, getParamArgsMeta, getRouteSchema, route, routeExpectsBodyStream } from "./nodefony/decorators/routerDecorators.js";
|
|
31
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
32
|
+
import { validateRouteInput } from "./nodefony/src/routeSchema.js";
|
|
32
33
|
import path from "node:path";
|
|
33
34
|
import { AUTO_STORE, EMPTY_INFRA, Module, durableStoreRemedy, readStoreConnector, readStoreLocation, resolveAutoStore, services, storeConnectivity } from "nodefony";
|
|
34
35
|
// index.ts
|
|
@@ -63,7 +64,9 @@ let Framework = class Framework extends Module {
|
|
|
63
64
|
* `this.options` (matérialise les défauts, préserve `router`/`adminBroker`).
|
|
64
65
|
*/
|
|
65
66
|
async onKernelRegister() {
|
|
66
|
-
|
|
67
|
+
const validated = defineFrameworkConfig(this.options ?? {});
|
|
68
|
+
this.options = validated;
|
|
69
|
+
configureFileServing(validated.fileServing);
|
|
67
70
|
return this;
|
|
68
71
|
}
|
|
69
72
|
/**
|
|
@@ -191,6 +194,7 @@ let Framework = class Framework extends Module {
|
|
|
191
194
|
}
|
|
192
195
|
if (this.kernel?.container?.get("webauthn")) mountWebAuthnRoutes(this);
|
|
193
196
|
if (this.kernel?.container?.get("oauth2")) mountOAuth2Routes(this);
|
|
197
|
+
if (this.kernel?.container?.get("cspReports")) mountCspReportRoute(this);
|
|
194
198
|
if (process.env.NF_BENCH_ROUTE === "1") mountBenchRoutes(this);
|
|
195
199
|
if (this.kernel?.container?.get("apiKeys")) mountApiKeyRoutes(this);
|
|
196
200
|
if (this.kernel?.container?.get("totp")) mountTotpRoutes(this);
|
|
@@ -204,4 +208,4 @@ Framework = __decorate([services([
|
|
|
204
208
|
IdempotencyStore_default
|
|
205
209
|
])], Framework);
|
|
206
210
|
var framework_default = Framework;
|
|
207
|
-
export { AdminApiController, AdminBroker_default as AdminBroker, All, Anonymous, ApiKeyController, BenchController, Body, BypassFirewall, Controller, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Eta, FRAMEWORK_ROUTER, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, IssuerMetadataController, LoginPageController, IdempotencyStore_default as MemoryIdempotencyStore, OAuth2Controller, Options, Param, Patch, Post, ProtectedResourceMetadataController, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Resolver, ResourceController, Route, router_default as Router, Scope, Session, SessionAuthController, TokenAuthController, TotpController, UploadedFile, UploadedFiles, UseSession, WebAuthnController, buildContextParamArgs, buildPlaygroundSnapshot, collectProtectedResources, controller, controllers, createFrameworkAdminApi, createKernelAdminApi, createSyslogAdminApi, framework_default as default, defineFrameworkConfig, frameworkConfigJsonSchema, frameworkConfigSchema, getIdempotencyStoreFactory, getParamArgsMeta, isFrameworkRouter, listIdempotencyStores, mountApiKeyRoutes, mountBenchRoutes, mountIssuerMetadataRoutes, mountLoginPage, mountOAuth2Routes, mountProtectedResourceRoutes, mountSessionAuthRoutes, mountTokenAuthRoutes, mountTotpRoutes, mountWebAuthnRoutes, protectedResourceRoutePaths, registerIdempotencyStore, route, routeExpectsBodyStream, validateRouteInput };
|
|
211
|
+
export { AdminApiController, AdminBroker_default as AdminBroker, All, Anonymous, ApiKeyController, BenchController, Body, BypassFirewall, Controller, Cookie, Csp, CspReportController, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Eta, FRAMEWORK_ROUTER, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, IssuerMetadataController, LoginPageController, IdempotencyStore_default as MemoryIdempotencyStore, OAuth2Controller, Options, Param, Patch, Post, ProtectedResourceMetadataController, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Resolver, ResourceController, Route, router_default as Router, Scope, Session, SessionAuthController, TokenAuthController, TotpController, UploadedFile, UploadedFiles, UseSession, WebAuthnController, buildContextParamArgs, buildPlaygroundSnapshot, collectProtectedResources, controller, controllers, createFrameworkAdminApi, createKernelAdminApi, createSyslogAdminApi, framework_default as default, defineFrameworkConfig, frameworkConfigJsonSchema, frameworkConfigSchema, getIdempotencyStoreFactory, getParamArgsMeta, getRouteSchema, isFrameworkRouter, listIdempotencyStores, mountApiKeyRoutes, mountBenchRoutes, mountCspReportRoute, mountIssuerMetadataRoutes, mountLoginPage, mountOAuth2Routes, mountProtectedResourceRoutes, mountSessionAuthRoutes, mountTokenAuthRoutes, mountTotpRoutes, mountWebAuthnRoutes, protectedResourceRoutePaths, registerIdempotencyStore, route, routeExpectsBodyStream, validateRouteInput };
|
|
@@ -25,6 +25,9 @@ import { z } from "zod";
|
|
|
25
25
|
* tels quels aux Services `Router` / `AdminBroker` (4ᵉ arg du `super(...)`).
|
|
26
26
|
* Aucune forme métier figée → `looseObject` + `optional` (ne RIEN stripper,
|
|
27
27
|
* absent par défaut = `undefined`, comme avant validation).
|
|
28
|
+
* - `idempotency` — store du cache d'idempotence.
|
|
29
|
+
* - `fileServing` — défaut des fichiers servis par une action (validateurs,
|
|
30
|
+
* `Cache-Control`), posé au boot dans `Controller.ts#configureFileServing`.
|
|
28
31
|
*
|
|
29
32
|
* ## Pureté
|
|
30
33
|
*
|
|
@@ -38,10 +41,21 @@ const idempotencySchema = z.strictObject({
|
|
|
38
41
|
gcIntervalS: z.number().int().min(0).default(600).describe("Intervalle de purge des clés d'idempotence expirées (s), HORS hot-path. N'a d'effet QUE pour un store SANS expiration native (`drizzle` → `DELETE WHERE expiresAt<=now`) ; `redis` (TTL `PX`) et `memory` (purge passive) l'ignorent. 0 = timer désarmé (cron/k8s)."),
|
|
39
42
|
gcJitter: z.boolean().default(true).describe("Étale le départ du gc d'idempotence par process — anti thundering-herd sur le store SQL partagé en cluster.")
|
|
40
43
|
});
|
|
44
|
+
/**
|
|
45
|
+
* Requêtes conditionnelles des fichiers servis par une action (`streamFile`,
|
|
46
|
+
* `renderFileDownload`, `renderMediaStream`). Le défaut de TOUTE
|
|
47
|
+
* l'application ; un appel le remplace par ses options (`validators`) ou ses
|
|
48
|
+
* en-têtes (`Cache-Control`, `ETag`).
|
|
49
|
+
*/
|
|
50
|
+
const fileServingSchema = z.strictObject({
|
|
51
|
+
validators: z.boolean().default(true).describe("ETag forte + Last-Modified tirés de la taille et de la date du fichier, et réponses 304/412 (RFC 9110 §13). `false` les coupe pour toute l'application — dates non fiables (archive décompressée, build reproductible : même taille et même date pour deux contenus). Un appel peut rétablir ou couper par `{ validators }`."),
|
|
52
|
+
cacheControl: z.string().trim().regex(/^[\t\x20-\x7e\x80-\xff]+$/, "valeur d'en-tête invalide (CR, LF ou caractère de contrôle)").nullable().default("private, no-cache").describe("`Cache-Control` posé quand l'action n'en fixe aucun. Défaut `private, no-cache` : `Last-Modified` rendrait sinon la réponse réutilisable par un cache PARTAGÉ sans revalidation (RFC 9111 §4.2.2), or un fichier servi par une action est souvent protégé. `null` = n'en poser aucun. Sans effet quand `validators` est coupé.")
|
|
53
|
+
});
|
|
41
54
|
const frameworkConfigSchema = z.strictObject({
|
|
42
55
|
router: serviceOptionsSchema.optional().describe("Options transmises au Service `Router` (bag d'options de Service de base : logger, timers…). Loose : non strippées. Absent (défaut) = aucune."),
|
|
43
56
|
adminBroker: serviceOptionsSchema.optional().describe("Options transmises au Service `AdminBroker` (data plane admin `/nodefony/<ns>/api/*`). Loose : non strippées. Absent (défaut) = aucune."),
|
|
44
|
-
idempotency: idempotencySchema.default(() => idempotencySchema.parse({})).describe("Idempotence des mutations (anti double-effet). Cf draft-ietf-httpapi-idempotency-key-header.")
|
|
57
|
+
idempotency: idempotencySchema.default(() => idempotencySchema.parse({})).describe("Idempotence des mutations (anti double-effet). Cf draft-ietf-httpapi-idempotency-key-header."),
|
|
58
|
+
fileServing: fileServingSchema.default(() => fileServingSchema.parse({})).describe("Fichiers servis par une action : validateurs (ETag, Last-Modified, 304/412) et Cache-Control par défaut.")
|
|
45
59
|
}).describe("Configuration de @nodefony/framework.");
|
|
46
60
|
var config_default = {
|
|
47
61
|
...frameworkConfigSchema.parse({}),
|
|
@@ -58,4 +72,4 @@ var config_default = {
|
|
|
58
72
|
}
|
|
59
73
|
} }
|
|
60
74
|
};
|
|
61
|
-
export { config_default as default, frameworkConfigSchema };
|
|
75
|
+
export { config_default as default, fileServingSchema, frameworkConfigSchema };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Controller from "../src/Controller.js";
|
|
2
|
+
import { withAuthChallenge } from "../src/SecurityApiController.js";
|
|
2
3
|
import { computeFingerprint, evaluateIdempotency, resolveIdempotencyKey, resolveIdentity } from "../src/idempotency.js";
|
|
3
|
-
import { JsonRpcServerError, RequestContext, RpcError, executeAdminEndpoint } from "nodefony";
|
|
4
|
+
import { BYTES_MARKER_HEADER, JsonRpcServerError, RequestContext, RpcError, executeAdminEndpoint } from "nodefony";
|
|
4
5
|
// nodefony/controller/AdminApiController.ts
|
|
5
6
|
/** Vrai pour une chaîne ou un tableau de chaînes — ce que porte {@link IAdminRequest.query}. */
|
|
6
7
|
function isAdminQueryValue(value) {
|
|
@@ -62,8 +63,10 @@ var AdminApiController = class AdminApiController extends Controller {
|
|
|
62
63
|
* (une action, deux transports) : même exécution sur les deux transports, seul
|
|
63
64
|
* l'emballage diffère.
|
|
64
65
|
*
|
|
65
|
-
* - **HTTP** :
|
|
66
|
-
*
|
|
66
|
+
* - **HTTP** : `renderJson` + status + header `x-nodefony-instance`. Quand
|
|
67
|
+
* l'application active un codec binaire, le corps passe par le codec texte
|
|
68
|
+
* de la socket (convention `$bytes`, annoncée par `x-nodefony-bytes`) : des
|
|
69
|
+
* octets se relisent alors à l'identique sur les deux transports.
|
|
67
70
|
* - **Pont WS-RPC `api.request`** : valeur **nue** (le pont l'enveloppe
|
|
68
71
|
* `{id, result}` — snapshot ≡ GET par construction) ; statut ≥ 400 →
|
|
69
72
|
* {@link RpcError} (`data.status` + `data.body`), symétrie d'un `fetch`
|
|
@@ -85,9 +88,19 @@ var AdminApiController = class AdminApiController extends Controller {
|
|
|
85
88
|
}
|
|
86
89
|
return body;
|
|
87
90
|
}
|
|
88
|
-
|
|
89
|
-
...headers,
|
|
91
|
+
const stamped = {
|
|
92
|
+
...status === 401 ? withAuthChallenge(headers) : headers,
|
|
90
93
|
"x-nodefony-instance": AdminApiController.instanceId
|
|
94
|
+
};
|
|
95
|
+
const codec = this.get("realtimeService")?.textFrameCodec;
|
|
96
|
+
const marker = codec?.bytesMarker;
|
|
97
|
+
if (codec === void 0 || marker === void 0) return this.renderJson(body, status, stamped);
|
|
98
|
+
const text = codec.encode(body);
|
|
99
|
+
if (typeof text !== "string") return this.renderJson(body, status, stamped);
|
|
100
|
+
this.setContextJson();
|
|
101
|
+
return this.renderResponse(text, "utf8", status, {
|
|
102
|
+
...stamped,
|
|
103
|
+
[BYTES_MARKER_HEADER]: marker
|
|
91
104
|
});
|
|
92
105
|
}
|
|
93
106
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Controller from "../src/Controller.js";
|
|
2
1
|
import router_default from "../service/router.js";
|
|
2
|
+
import Controller from "../src/Controller.js";
|
|
3
3
|
// nodefony/controller/BenchController.ts
|
|
4
4
|
/**
|
|
5
5
|
* Corps servi par la cible de banc — **gelé et partagé** : ce qu'on chronomètre
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import router_default from "../service/router.js";
|
|
2
|
+
import Controller from "../src/Controller.js";
|
|
3
|
+
import { Body, CsrfExempt } from "../decorators/routerDecorators.js";
|
|
4
|
+
import __decorateParam from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js";
|
|
5
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
6
|
+
import { CSP_REPORT_MAX_BYTES, CSP_REPORT_PATH } from "nodefony";
|
|
7
|
+
import { HttpError } from "@nodefony/http";
|
|
8
|
+
// nodefony/controller/CspReportController.ts
|
|
9
|
+
const NO_STORE = { "cache-control": "no-store" };
|
|
10
|
+
let mounted = false;
|
|
11
|
+
/**
|
|
12
|
+
* Réception PUBLIQUE des rapports de violation CSP (ADR-0024 §C7) —
|
|
13
|
+
* `POST /nodefony/security/api/csp-report`, adaptateur MINCE au-dessus du
|
|
14
|
+
* service `cspReports`.
|
|
15
|
+
*
|
|
16
|
+
* Le navigateur poste sans identifiant : la route contourne le pare-feu, et la
|
|
17
|
+
* défense CSRF (`@CsrfExempt`) — elle ne change l'état de personne, et un
|
|
18
|
+
* rapport n'a aucune session à détourner. Le corps arrive en FLUX
|
|
19
|
+
* (`@Body({ stream: true })`) pour être borné PENDANT la lecture : au-delà de
|
|
20
|
+
* {@link CSP_REPORT_MAX_BYTES}, 413 sans rien garder. Tout le reste — quota,
|
|
21
|
+
* type de contenu, forme, expurgation — appartient au collecteur, et la
|
|
22
|
+
* réponse est 204 quoi qu'il en pense : l'émetteur n'apprend rien.
|
|
23
|
+
*/
|
|
24
|
+
var CspReportController = class extends Controller {
|
|
25
|
+
constructor(context) {
|
|
26
|
+
super("CspReportController", context);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reçoit un rapport, ou un lot de rapports.
|
|
30
|
+
*
|
|
31
|
+
* @param stream - le corps brut, jamais lu par le pipeline.
|
|
32
|
+
* @throws HttpError 413 quand le corps dépasse le plafond.
|
|
33
|
+
*/
|
|
34
|
+
async report(stream) {
|
|
35
|
+
const headers = this.context?.request?.headers;
|
|
36
|
+
const declared = Number(headers?.["content-length"]);
|
|
37
|
+
if (Number.isFinite(declared) && declared > CSP_REPORT_MAX_BYTES) throw new HttpError("CSP report too large", 413, this.context);
|
|
38
|
+
const chunks = [];
|
|
39
|
+
let seen = 0;
|
|
40
|
+
for await (const chunk of stream) {
|
|
41
|
+
seen += chunk.byteLength;
|
|
42
|
+
if (seen > CSP_REPORT_MAX_BYTES) throw new HttpError("CSP report too large", 413, this.context);
|
|
43
|
+
chunks.push(chunk);
|
|
44
|
+
}
|
|
45
|
+
const sink = this.get("cspReports");
|
|
46
|
+
if (sink) sink.receive(headers?.["content-type"], Buffer.concat(chunks, seen), this.context?.remoteAddress ?? null, headers?.["user-agent"] ?? null);
|
|
47
|
+
return this.renderResponse("", "utf8", 204, NO_STORE);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
__decorate([CsrfExempt(), __decorateParam(0, Body({ stream: true }))], CspReportController.prototype, "report", null);
|
|
51
|
+
/**
|
|
52
|
+
* Monte la route de réception — appelé par le module framework à
|
|
53
|
+
* `onKernelReady`, seulement si le service `cspReports` est présent (rapports
|
|
54
|
+
* demandés vers la route du framework).
|
|
55
|
+
*
|
|
56
|
+
* @param frameworkModule - module qui porte le contrôleur.
|
|
57
|
+
*/
|
|
58
|
+
function mountCspReportRoute(frameworkModule) {
|
|
59
|
+
if (mounted) return;
|
|
60
|
+
router_default.createRoute("security.cspReport", {
|
|
61
|
+
path: CSP_REPORT_PATH,
|
|
62
|
+
constructor: CspReportController,
|
|
63
|
+
classMethod: "report",
|
|
64
|
+
requirements: { methods: ["POST"] },
|
|
65
|
+
bypassFirewall: true
|
|
66
|
+
});
|
|
67
|
+
if (!Object.prototype.hasOwnProperty.call(CspReportController.prototype, "module")) router_default.setController(CspReportController, frameworkModule);
|
|
68
|
+
mounted = true;
|
|
69
|
+
}
|
|
70
|
+
export { CspReportController as default, mountCspReportRoute };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Controller from "../src/Controller.js";
|
|
2
1
|
import router_default from "../service/router.js";
|
|
2
|
+
import Controller from "../src/Controller.js";
|
|
3
3
|
import { askedAuthority, onDeclaredAuthority } from "./oauthAuthority.js";
|
|
4
4
|
import { JWKS_PATH, authorizationServerMetadataPath, buildAuthorizationServerMetadata } from "nodefony";
|
|
5
5
|
// nodefony/controller/IssuerMetadataController.ts
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { isDevelopment } from "../src/singletonGuard.js";
|
|
2
|
-
import Controller from "../src/Controller.js";
|
|
3
|
-
import { Csp } from "../decorators/routerDecorators.js";
|
|
4
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
5
1
|
import router_default from "../service/router.js";
|
|
2
|
+
import Controller from "../src/Controller.js";
|
|
3
|
+
import { isDevelopment } from "../src/singletonGuard.js";
|
|
6
4
|
import { LOGIN_PAGE_ASSET_FILES, LOGIN_PAGE_TEMPLATE, buildLoginPageView, keepSuccess } from "../src/loginPage.js";
|
|
7
5
|
import path from "node:path";
|
|
8
6
|
import { LOGIN_PAGE_ASSETS_BASE, LOGIN_PAGE_SKINS, loginPageSkinFile } from "nodefony";
|
|
@@ -53,8 +51,9 @@ const version = keepSuccess(async () => {
|
|
|
53
51
|
* feuille `nodefony/login.css`, script `nodefony/login.js`. Aucun front, aucun
|
|
54
52
|
* build côté application.
|
|
55
53
|
*
|
|
56
|
-
* Joignable sans session ; jamais mise en cache (`no-store`)
|
|
57
|
-
*
|
|
54
|
+
* Joignable sans session ; jamais mise en cache (`no-store`). L’encadrement
|
|
55
|
+
* suit `http.securityHeaders.frameOptions`, qui pose `X-Frame-Options` ET
|
|
56
|
+
* `frame-ancestors` sur toute la politique (ADR-0024 §C5).
|
|
58
57
|
*/
|
|
59
58
|
var LoginPageController = class extends Controller {
|
|
60
59
|
constructor(context) {
|
|
@@ -87,7 +86,6 @@ var LoginPageController = class extends Controller {
|
|
|
87
86
|
});
|
|
88
87
|
}
|
|
89
88
|
};
|
|
90
|
-
__decorate([Csp({ "frame-ancestors": ["'none'"] })], LoginPageController.prototype, "page", null);
|
|
91
89
|
/**
|
|
92
90
|
* Monte la page de connexion par défaut quand security la décrit : la route
|
|
93
91
|
* sur `loginPage.path`, et ses fichiers sous `LOGIN_PAGE_ASSETS_BASE`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Controller from "../src/Controller.js";
|
|
2
1
|
import router_default from "../service/router.js";
|
|
2
|
+
import Controller from "../src/Controller.js";
|
|
3
3
|
import { askedAuthority, onDeclaredAuthority } from "./oauthAuthority.js";
|
|
4
4
|
import { buildProtectedResourceMetadata, canonicalResourceUri, protectedResourceMetadataPath } from "nodefony";
|
|
5
5
|
// nodefony/controller/ProtectedResourceMetadataController.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CsrfProtect } from "../decorators/routerDecorators.js";
|
|
2
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
3
2
|
import router_default from "../service/router.js";
|
|
4
3
|
import SecurityApiController from "../src/SecurityApiController.js";
|
|
4
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
5
5
|
import { AUTH_API_BASE } from "nodefony";
|
|
6
6
|
// nodefony/controller/SessionAuthController.ts
|
|
7
7
|
let mounted = false;
|
|
@@ -29,12 +29,11 @@ let mounted = false;
|
|
|
29
29
|
* (Auth.js, better-auth). Le `401` y écrivait une erreur réseau dans la console
|
|
30
30
|
* de chaque visiteur anonyme, sans rien protéger : la réponse ne révèle rien.
|
|
31
31
|
*
|
|
32
|
-
* @remarks
|
|
33
|
-
*
|
|
34
|
-
* session, et un
|
|
35
|
-
* navigateur
|
|
36
|
-
*
|
|
37
|
-
* déclare un).
|
|
32
|
+
* @remarks Les 401 de `login` portent le défi `Session realm="nodefony"`
|
|
33
|
+
* (RFC 9110 §15.5.2, posé par `SecurityApiController`), celui du pare-feu :
|
|
34
|
+
* aucun schéma HTTP ne décrit un cookie de session, et un `Basic` ouvrirait la
|
|
35
|
+
* fenêtre de connexion native du navigateur — l'anti-pattern que le BFF
|
|
36
|
+
* élimine. Un schéma inconnu des navigateurs n'en ouvre aucune.
|
|
38
37
|
*/
|
|
39
38
|
var SessionAuthController = class extends SecurityApiController {
|
|
40
39
|
constructor(context) {
|
|
@@ -24,9 +24,9 @@ function parseScope(scope) {
|
|
|
24
24
|
* framework ne peut pas importer les classes d'erreur de security ; un 429
|
|
25
25
|
* reporte le `Retry-After` (RFC 6585) du throttler NIST.
|
|
26
26
|
*
|
|
27
|
-
* @remarks
|
|
28
|
-
*
|
|
29
|
-
* ressource protégée par Bearer, il DÉLIVRE le Bearer.
|
|
27
|
+
* @remarks Ces 401 portent le défi `Session realm="nodefony"` (RFC 9110
|
|
28
|
+
* §15.5.2, posé par `SecurityApiController`) et non un `Bearer` : l'endpoint
|
|
29
|
+
* d'émission n'est pas une ressource protégée par Bearer, il DÉLIVRE le Bearer.
|
|
30
30
|
*/
|
|
31
31
|
var TokenAuthController = class extends SecurityApiController {
|
|
32
32
|
constructor(context) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { assertRouteSchemaShape } from "../src/routeSchema.js";
|
|
2
|
-
import Controller from "../src/Controller.js";
|
|
3
1
|
import router_default from "../service/router.js";
|
|
2
|
+
import Controller from "../src/Controller.js";
|
|
3
|
+
import { assertRouteSchemaShape } from "../src/routeSchema.js";
|
|
4
4
|
import { BootConfigurationError, RequestContext } from "nodefony";
|
|
5
5
|
import "reflect-metadata";
|
|
6
6
|
// nodefony/decorators/routerDecorators.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
2
1
|
import router_default from "./router.js";
|
|
3
2
|
import AdminApiController from "../controller/AdminApiController.js";
|
|
3
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
4
4
|
import { ADMIN_DEFAULT_ROLE, Service, injectable, resolveAdminRole } from "nodefony";
|
|
5
5
|
// nodefony/service/AdminBroker.ts
|
|
6
6
|
const serviceName = "adminBroker";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
2
2
|
import { Service, assertPageQuery, injectable } from "nodefony";
|
|
3
3
|
// nodefony/service/IdempotencyStore.ts
|
|
4
4
|
const serviceName = "idempotencyStore";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Route from "../src/Route.js";
|
|
2
|
-
import { assertRouteSchemaDeclaration, routeExpectsBodyStream } from "../decorators/routerDecorators.js";
|
|
3
2
|
import Resolver from "../src/Resolver.js";
|
|
4
|
-
import
|
|
3
|
+
import { assertRouteSchemaDeclaration, routeExpectsBodyStream } from "../decorators/routerDecorators.js";
|
|
4
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
5
5
|
import { BootConfigurationError, Injector, Module, Service, injectable, isPromise, stripTrailingSlashes } from "nodefony";
|
|
6
6
|
import { HttpError, isDomainAllowed } from "@nodefony/http";
|
|
7
7
|
// nodefony/service/router.ts
|
|
@@ -349,11 +349,12 @@ Router = _Router = __decorate([injectable()], Router);
|
|
|
349
349
|
* installées du framework feraient échouer `instanceof` en silence. Une
|
|
350
350
|
* lecture de propriété.
|
|
351
351
|
*
|
|
352
|
-
* @param router - le routeur lu sur le contexte (`context.router`)
|
|
352
|
+
* @param router - le routeur lu sur le contexte (`context.router`) ou le
|
|
353
|
+
* service `router` du conteneur.
|
|
353
354
|
* @returns `true` si c'est le Router du framework.
|
|
354
355
|
*/
|
|
355
356
|
function isFrameworkRouter(router) {
|
|
356
|
-
return router
|
|
357
|
+
return typeof router === "object" && router !== null && FRAMEWORK_ROUTER in router;
|
|
357
358
|
}
|
|
358
359
|
var router_default = Router;
|
|
359
360
|
export { FRAMEWORK_ROUTER, router_default as default, isFrameworkRouter };
|