emilsoftware-utilities 2.0.0-dev.67 → 2.0.0-dev.69
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/accessi/resource-server/errors.d.ts +1 -1
- package/dist/accessi/resource-server/errors.js.map +1 -1
- package/dist/accessi/resource-server/express.js +21 -0
- package/dist/accessi/resource-server/express.js.map +1 -1
- package/dist/accessi/resource-server/nest.js +13 -0
- package/dist/accessi/resource-server/nest.js.map +1 -1
- package/dist/accessi/resource-server/runtime.js +11 -1
- package/dist/accessi/resource-server/runtime.js.map +1 -1
- package/dist/accessi/resource-server/types.d.ts +7 -0
- package/dist/accessi/resource-server/types.js.map +1 -1
- package/dist/accessi/server/index.d.ts +1 -0
- package/dist/accessi/server/index.js +1 -0
- package/dist/accessi/server/index.js.map +1 -1
- package/dist/accessi/server/standalone.d.ts +41 -0
- package/dist/accessi/server/standalone.js +264 -0
- package/dist/accessi/server/standalone.js.map +1 -0
- package/dist/accessi-module/AccessiModule.d.ts +53 -2
- package/dist/accessi-module/AccessiModule.js +5 -2
- package/dist/accessi-module/AccessiModule.js.map +1 -1
- package/dist/accessi-module/Console/accessiFetch.d.ts +5 -0
- package/dist/accessi-module/Console/accessiFetch.js +7 -4
- package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
- package/dist/accessi-module/Console/console.js +21 -7
- package/dist/accessi-module/Console/generated/emilsoftware-utilities.d.ts +31 -1
- package/dist/accessi-module/Console/generated/emilsoftware-utilities.js +28 -3
- package/dist/accessi-module/Console/generated/emilsoftware-utilities.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/exchangeFederatedIdentityRequest.d.ts +12 -0
- package/dist/accessi-module/Console/generated/model/exchangeFederatedIdentityRequest.js +9 -0
- package/dist/accessi-module/Console/generated/model/exchangeFederatedIdentityRequest.js.map +1 -0
- package/dist/accessi-module/Console/generated/model/federatedAuthenticationResultDto.d.ts +15 -0
- package/dist/accessi-module/Console/generated/model/federatedAuthenticationResultDto.js +3 -0
- package/dist/accessi-module/Console/generated/model/federatedAuthenticationResultDto.js.map +1 -0
- package/dist/accessi-module/Console/generated/model/federatedExchangeResponse.d.ts +14 -0
- package/dist/accessi-module/Console/generated/model/federatedExchangeResponse.js +3 -0
- package/dist/accessi-module/Console/generated/model/federatedExchangeResponse.js.map +1 -0
- package/dist/accessi-module/Console/generated/model/index.d.ts +6 -0
- package/dist/accessi-module/Console/generated/model/index.js +6 -0
- package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/loginResult.d.ts +2 -0
- package/dist/accessi-module/Console/generated/model/loginResult.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.d.ts +10 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.js +9 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.js.map +1 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenResponse.d.ts +14 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenResponse.js +3 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenResponse.js.map +1 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenResult.d.ts +13 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenResult.js +3 -0
- package/dist/accessi-module/Console/generated/model/refreshTokenResult.js.map +1 -0
- package/dist/accessi-module/Console/wiki.js +16 -3
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/Controllers/AccessiConsoleController.d.ts +6 -1
- package/dist/accessi-module/Controllers/AccessiConsoleController.js +24 -1
- package/dist/accessi-module/Controllers/AccessiConsoleController.js.map +1 -1
- package/dist/accessi-module/Controllers/AuthController.d.ts +3 -1
- package/dist/accessi-module/Controllers/AuthController.js +90 -8
- package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
- package/dist/accessi-module/Controllers/ConsoleEntryController.d.ts +2 -0
- package/dist/accessi-module/Controllers/ConsoleEntryController.js +11 -3
- package/dist/accessi-module/Controllers/ConsoleEntryController.js.map +1 -1
- package/dist/accessi-module/Controllers/EmailController.js +1 -1
- package/dist/accessi-module/Controllers/EmailController.js.map +1 -1
- package/dist/accessi-module/Controllers/UserController.js +1 -1
- package/dist/accessi-module/Controllers/UserController.js.map +1 -1
- package/dist/accessi-module/Dtos/FederatedExchangeDtos.d.ts +21 -0
- package/dist/accessi-module/Dtos/FederatedExchangeDtos.js +74 -0
- package/dist/accessi-module/Dtos/FederatedExchangeDtos.js.map +1 -0
- package/dist/accessi-module/Dtos/LoginResponse.d.ts +1 -0
- package/dist/accessi-module/Dtos/LoginResponse.js +6 -0
- package/dist/accessi-module/Dtos/LoginResponse.js.map +1 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.d.ts +12 -0
- package/dist/accessi-module/Dtos/RefreshTokenDtos.js +50 -0
- package/dist/accessi-module/Dtos/RefreshTokenDtos.js.map +1 -0
- package/dist/accessi-module/Dtos/index.d.ts +2 -0
- package/dist/accessi-module/Dtos/index.js +2 -0
- package/dist/accessi-module/Dtos/index.js.map +1 -1
- package/dist/accessi-module/Services/AuthService/AuthService.d.ts +14 -0
- package/dist/accessi-module/Services/AuthService/AuthService.js +99 -6
- package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
- package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js +8 -16
- package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js.map +1 -1
- package/dist/accessi-module/federated-auth/FederatedAuthExchangeController.d.ts +15 -0
- package/dist/accessi-module/federated-auth/FederatedAuthExchangeController.js +85 -0
- package/dist/accessi-module/federated-auth/FederatedAuthExchangeController.js.map +1 -0
- package/dist/accessi-module/federated-auth/FederatedAuthService.js +7 -1
- package/dist/accessi-module/federated-auth/FederatedAuthService.js.map +1 -1
- package/dist/accessi-module/index.d.ts +17 -1
- package/dist/accessi-module/index.js +71 -5
- package/dist/accessi-module/index.js.map +1 -1
- package/dist/accessi-module/jwt/jwt.strategy.js +4 -0
- package/dist/accessi-module/jwt/jwt.strategy.js.map +1 -1
- package/dist/accessi-module/middleware/authenticateGen.js +4 -0
- package/dist/accessi-module/middleware/authenticateGen.js.map +1 -1
- package/dist/accessi-module/security/audit.d.ts +7 -0
- package/dist/accessi-module/security/audit.js +14 -0
- package/dist/accessi-module/security/audit.js.map +1 -0
- package/dist/accessi-module/security/authenticatedToken.d.ts +1 -1
- package/dist/accessi-module/security/authenticatedToken.js +3 -3
- package/dist/accessi-module/security/authenticatedToken.js.map +1 -1
- package/dist/accessi-module/security/consoleEntry.d.ts +58 -11
- package/dist/accessi-module/security/consoleEntry.js +181 -31
- package/dist/accessi-module/security/consoleEntry.js.map +1 -1
- package/dist/accessi-module/security/edgeRateLimit.d.ts +33 -0
- package/dist/accessi-module/security/edgeRateLimit.js +81 -0
- package/dist/accessi-module/security/edgeRateLimit.js.map +1 -0
- package/dist/accessi-module/security/jwks.d.ts +33 -0
- package/dist/accessi-module/security/jwks.js +83 -0
- package/dist/accessi-module/security/jwks.js.map +1 -0
- package/dist/accessi-module/security/jwksBootstrap.d.ts +15 -0
- package/dist/accessi-module/security/jwksBootstrap.js +44 -0
- package/dist/accessi-module/security/jwksBootstrap.js.map +1 -0
- package/dist/accessi-module/security/jwtTokenOptions.d.ts +9 -2
- package/dist/accessi-module/security/jwtTokenOptions.js +18 -4
- package/dist/accessi-module/security/jwtTokenOptions.js.map +1 -1
- package/dist/accessi-module/security/passwordResetToken.d.ts +1 -1
- package/dist/accessi-module/security/passwordResetToken.js.map +1 -1
- package/dist/accessi-module/security/publicAuthRateLimit.d.ts +9 -7
- package/dist/accessi-module/security/publicAuthRateLimit.js +55 -78
- package/dist/accessi-module/security/publicAuthRateLimit.js.map +1 -1
- package/dist/accessi-module/security/rateLimitStore.d.ts +34 -0
- package/dist/accessi-module/security/rateLimitStore.js +145 -0
- package/dist/accessi-module/security/rateLimitStore.js.map +1 -0
- package/dist/accessi-module/security/refreshTokenRotation.d.ts +40 -0
- package/dist/accessi-module/security/refreshTokenRotation.js +91 -0
- package/dist/accessi-module/security/refreshTokenRotation.js.map +1 -0
- package/dist/accessi-module/security/serviceTokenGuard.d.ts +3 -1
- package/dist/accessi-module/security/serviceTokenGuard.js +11 -6
- package/dist/accessi-module/security/serviceTokenGuard.js.map +1 -1
- package/dist/accessi-module/security/serviceTokenRateLimit.d.ts +5 -4
- package/dist/accessi-module/security/serviceTokenRateLimit.js +33 -29
- package/dist/accessi-module/security/serviceTokenRateLimit.js.map +1 -1
- package/dist/accessi-module/security/sessionRevocation.d.ts +52 -0
- package/dist/accessi-module/security/sessionRevocation.js +124 -0
- package/dist/accessi-module/security/sessionRevocation.js.map +1 -0
- package/dist/accessi-module/security/validateOptions.d.ts +19 -0
- package/dist/accessi-module/security/validateOptions.js +115 -0
- package/dist/accessi-module/security/validateOptions.js.map +1 -0
- package/dist/emilsoftware-utilities-2.0.0-dev.69.tgz +0 -0
- package/dist/initEmilsoftwareModule.js +39 -3
- package/dist/initEmilsoftwareModule.js.map +1 -1
- package/dist/scripts/startAccessiService.d.ts +1 -0
- package/dist/scripts/startAccessiService.js +11 -0
- package/dist/scripts/startAccessiService.js.map +1 -0
- package/docs/accessi-openapi-fingerprint.json +190 -0
- package/docs/gateway-rate-limit.md +98 -0
- package/docs/standalone-service.md +176 -0
- package/integration-prompt.md +299 -8
- package/package.json +11 -5
- package/dist/emilsoftware-utilities-2.0.0-dev.67.tgz +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Codici stabili del resource server, allineati al contratto HTTP Accessi. */
|
|
2
|
-
export type AccessiResourceServerErrorCode = "AUTH_HEADER_MISSING" | "AUTH_TOKEN_MISSING" | "AUTH_TOKEN_INVALID" | "AUTH_USER_CODE_MISSING" | "AUTH_USER_DISABLED" | "AUTH_INSUFFICIENT_PERMISSIONS" | "AUTH_CONFIG_INVALID" | "AUTH_GRANTS_SOURCE_MISSING" | "AUTH_INTROSPECTION_UNAVAILABLE" | "AUTH_INTROSPECTION_UNAUTHORIZED" | "AUTH_JWKS_UNAVAILABLE" | "AUTH_INTERNAL_ERROR";
|
|
2
|
+
export type AccessiResourceServerErrorCode = "AUTH_HEADER_MISSING" | "AUTH_TOKEN_MISSING" | "AUTH_TOKEN_INVALID" | "AUTH_USER_CODE_MISSING" | "AUTH_USER_DISABLED" | "AUTH_TOKEN_REVOKED" | "AUTH_INSUFFICIENT_PERMISSIONS" | "AUTH_CONFIG_INVALID" | "AUTH_GRANTS_SOURCE_MISSING" | "AUTH_INTROSPECTION_UNAVAILABLE" | "AUTH_INTROSPECTION_UNAUTHORIZED" | "AUTH_JWKS_UNAVAILABLE" | "AUTH_INTERNAL_ERROR";
|
|
3
3
|
/** Errore tipizzato del resource server, convertibile in risposta HTTP o eccezione Nest. */
|
|
4
4
|
export declare class AccessiResourceServerError extends Error {
|
|
5
5
|
readonly status: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/errors.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/errors.ts"],"names":[],"mappings":";;;AA6BA,4CAOC;AApBD,4FAA4F;AAC5F,MAAa,0BAA2B,SAAQ,KAAK;IACnD,YACkB,MAAc,EACd,IAAoC,EACpD,OAAe,EACC,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAgC;QAEpC,YAAO,GAAP,OAAO,CAA0B;QAGjD,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAVD,gEAUC;AAED,SAAgB,gBAAgB,CAC9B,MAAc,EACd,IAAoC,EACpC,OAAe,EACf,OAAiC;IAEjC,OAAO,IAAI,0BAA0B,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC","sourcesContent":["/** Codici stabili del resource server, allineati al contratto HTTP Accessi. */\nexport type AccessiResourceServerErrorCode =\n | \"AUTH_HEADER_MISSING\"\n | \"AUTH_TOKEN_MISSING\"\n | \"AUTH_TOKEN_INVALID\"\n | \"AUTH_USER_CODE_MISSING\"\n | \"AUTH_USER_DISABLED\"\n | \"AUTH_TOKEN_REVOKED\"\n | \"AUTH_INSUFFICIENT_PERMISSIONS\"\n | \"AUTH_CONFIG_INVALID\"\n | \"AUTH_GRANTS_SOURCE_MISSING\"\n | \"AUTH_INTROSPECTION_UNAVAILABLE\"\n | \"AUTH_INTROSPECTION_UNAUTHORIZED\"\n | \"AUTH_JWKS_UNAVAILABLE\"\n | \"AUTH_INTERNAL_ERROR\";\n\n/** Errore tipizzato del resource server, convertibile in risposta HTTP o eccezione Nest. */\nexport class AccessiResourceServerError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: AccessiResourceServerErrorCode,\n message: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"AccessiResourceServerError\";\n }\n}\n\nexport function accessiAuthError(\n status: number,\n code: AccessiResourceServerErrorCode,\n message: string,\n details?: Record<string, unknown>,\n): AccessiResourceServerError {\n return new AccessiResourceServerError(status, code, message, details);\n}\n"]}
|
|
@@ -13,6 +13,7 @@ exports.toAccessiResourceServerError = toAccessiResourceServerError;
|
|
|
13
13
|
exports.sendAccessiResourceServerError = sendAccessiResourceServerError;
|
|
14
14
|
exports.createAccessiAuthMiddleware = createAccessiAuthMiddleware;
|
|
15
15
|
exports.createAccessiAuthMiddlewareFromRuntime = createAccessiAuthMiddlewareFromRuntime;
|
|
16
|
+
const audit_1 = require("../../accessi-module/security/audit");
|
|
16
17
|
const errors_1 = require("./errors");
|
|
17
18
|
const runtime_1 = require("./runtime");
|
|
18
19
|
/** Normalizza un errore sconosciuto nel contratto di errore Accessi. */
|
|
@@ -47,6 +48,16 @@ function createAccessiAuthMiddleware(options) {
|
|
|
47
48
|
next();
|
|
48
49
|
}
|
|
49
50
|
catch (error) {
|
|
51
|
+
const normalized = toAccessiResourceServerError(error);
|
|
52
|
+
if (runtime.options.audit !== false) {
|
|
53
|
+
(0, audit_1.auditAccessi)("auth_failure", {
|
|
54
|
+
code: normalized.code,
|
|
55
|
+
status: normalized.status,
|
|
56
|
+
method: req.method,
|
|
57
|
+
path: req.path,
|
|
58
|
+
ip: req.ip,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
50
61
|
sendAccessiResourceServerError(res, error);
|
|
51
62
|
}
|
|
52
63
|
});
|
|
@@ -59,6 +70,16 @@ function createAccessiAuthMiddlewareFromRuntime(runtime) {
|
|
|
59
70
|
next();
|
|
60
71
|
}
|
|
61
72
|
catch (error) {
|
|
73
|
+
const normalized = toAccessiResourceServerError(error);
|
|
74
|
+
if (runtime.options.audit !== false) {
|
|
75
|
+
(0, audit_1.auditAccessi)("auth_failure", {
|
|
76
|
+
code: normalized.code,
|
|
77
|
+
status: normalized.status,
|
|
78
|
+
method: req.method,
|
|
79
|
+
path: req.path,
|
|
80
|
+
ip: req.ip,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
62
83
|
sendAccessiResourceServerError(res, error);
|
|
63
84
|
}
|
|
64
85
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/express.ts"],"names":[],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/express.ts"],"names":[],"mappings":";;;;;;;;;;;AAQA,oEAGC;AAGD,wEAUC;AAQD,kEAuBC;AAGD,wFAqBC;AA9ED,+DAAmE;AACnE,qCAAwE;AACxE,uCAAoF;AAIpF,wEAAwE;AACxE,SAAgB,4BAA4B,CAAC,KAAc;IACzD,IAAI,KAAK,YAAY,mCAA0B;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,IAAA,yBAAgB,EAAC,GAAG,EAAE,qBAAqB,EAAE,0CAA0C,CAAC,CAAC;AAClG,CAAC;AAED,wFAAwF;AACxF,SAAgB,8BAA8B,CAAC,GAAa,EAAE,KAAc;IAC1E,MAAM,UAAU,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAC;IACvD,OAAO,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;QACxC,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,UAAU,EAAE,CAAC;QACb,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,KAAK,EAAE,UAAU,CAAC,IAAI;QACtB,OAAO,EAAE,UAAU,CAAC,OAAO;KAC5B,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,2BAA2B,CACzC,OAAqC;IAErC,MAAM,OAAO,GAAG,IAAA,qCAA2B,EAAC,OAAO,CAAC,CAAC;IAErD,OAAO,CAAO,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAiB,EAAE;QAC9E,IAAI,CAAC;YACH,MAAM,IAAA,oCAA0B,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/C,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAC;YACvD,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBACpC,IAAA,oBAAY,EAAC,cAAc,EAAE;oBAC3B,IAAI,EAAE,UAAU,CAAC,IAAI;oBACrB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,EAAE,EAAE,GAAG,CAAC,EAAE;iBACX,CAAC,CAAC;YACL,CAAC;YACD,8BAA8B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAA,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,SAAgB,sCAAsC,CACpD,OAAqC;IAErC,OAAO,CAAO,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAiB,EAAE;QAC9E,IAAI,CAAC;YACH,MAAM,IAAA,oCAA0B,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/C,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAC;YACvD,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBACpC,IAAA,oBAAY,EAAC,cAAc,EAAE;oBAC3B,IAAI,EAAE,UAAU,CAAC,IAAI;oBACrB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,EAAE,EAAE,GAAG,CAAC,EAAE;iBACX,CAAC,CAAC;YACL,CAAC;YACD,8BAA8B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAA,CAAC;AACJ,CAAC","sourcesContent":["import type { NextFunction, Request, Response } from \"express\";\nimport { auditAccessi } from \"../../accessi-module/security/audit\";\nimport { AccessiResourceServerError, accessiAuthError } from \"./errors\";\nimport { authenticateAccessiRequest, createAccessiResourceServer } from \"./runtime\";\nimport type { AccessiResourceServerRuntime } from \"./runtime\";\nimport type { AccessiResourceServerOptions } from \"./types\";\n\n/** Normalizza un errore sconosciuto nel contratto di errore Accessi. */\nexport function toAccessiResourceServerError(error: unknown): AccessiResourceServerError {\n if (error instanceof AccessiResourceServerError) return error;\n return accessiAuthError(500, \"AUTH_INTERNAL_ERROR\", \"Errore interno durante l'autenticazione.\");\n}\n\n/** Invia la risposta di errore Accessi senza esporre stack trace o dettagli interni. */\nexport function sendAccessiResourceServerError(res: Response, error: unknown): Response {\n const normalized = toAccessiResourceServerError(error);\n return res.status(normalized.status).json({\n severity: \"error\",\n status: normalized.status,\n statusCode: 2,\n code: normalized.code,\n error: normalized.code,\n message: normalized.message,\n });\n}\n\n/**\n * Middleware Express per backend che non possiedono il database Accessi.\n *\n * Popola `req.user`, `req.data` e `req.accessi`; con una policy di autorizzazione\n * popola anche `req.userGrants`.\n */\nexport function createAccessiAuthMiddleware(\n options: AccessiResourceServerOptions,\n): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n const runtime = createAccessiResourceServer(options);\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n await authenticateAccessiRequest(req, runtime);\n next();\n } catch (error) {\n const normalized = toAccessiResourceServerError(error);\n if (runtime.options.audit !== false) {\n auditAccessi(\"auth_failure\", {\n code: normalized.code,\n status: normalized.status,\n method: req.method,\n path: req.path,\n ip: req.ip,\n });\n }\n sendAccessiResourceServerError(res, error);\n }\n };\n}\n\n/** Variante con runtime gia creato, utile per test o bootstrap manuale. */\nexport function createAccessiAuthMiddlewareFromRuntime(\n runtime: AccessiResourceServerRuntime,\n): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n await authenticateAccessiRequest(req, runtime);\n next();\n } catch (error) {\n const normalized = toAccessiResourceServerError(error);\n if (runtime.options.audit !== false) {\n auditAccessi(\"auth_failure\", {\n code: normalized.code,\n status: normalized.status,\n method: req.method,\n path: req.path,\n ip: req.ip,\n });\n }\n sendAccessiResourceServerError(res, error);\n }\n };\n}\n"]}
|
|
@@ -17,6 +17,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.createAccessiAuthGuard = createAccessiAuthGuard;
|
|
19
19
|
const common_1 = require("@nestjs/common");
|
|
20
|
+
const audit_1 = require("../../accessi-module/security/audit");
|
|
20
21
|
const errors_1 = require("./errors");
|
|
21
22
|
const runtime_1 = require("./runtime");
|
|
22
23
|
function toNestException(error) {
|
|
@@ -49,6 +50,18 @@ function createAccessiAuthGuard(options) {
|
|
|
49
50
|
return true;
|
|
50
51
|
}
|
|
51
52
|
catch (error) {
|
|
53
|
+
const normalized = error instanceof errors_1.AccessiResourceServerError
|
|
54
|
+
? error
|
|
55
|
+
: new errors_1.AccessiResourceServerError(500, "AUTH_INTERNAL_ERROR", "Errore interno durante l'autenticazione.");
|
|
56
|
+
if (runtime.options.audit !== false) {
|
|
57
|
+
(0, audit_1.auditAccessi)("auth_failure", {
|
|
58
|
+
code: normalized.code,
|
|
59
|
+
status: normalized.status,
|
|
60
|
+
method: request.method,
|
|
61
|
+
path: request.path,
|
|
62
|
+
ip: request.ip,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
52
65
|
throw toNestException(error);
|
|
53
66
|
}
|
|
54
67
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nest.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/nest.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"nest.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/nest.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAkCA,wDA+BC;AAjED,2CASwB;AAExB,+DAAmE;AACnE,qCAAsD;AACtD,uCAAoF;AAGpF,SAAS,eAAe,CAAC,KAAc;IACrC,MAAM,UAAU,GAAG,KAAK,YAAY,mCAA0B;QAC5D,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,IAAI,mCAA0B,CAAC,GAAG,EAAE,qBAAqB,EAAE,0CAA0C,CAAC,CAAC;IAC3G,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC;IAEvE,IAAI,UAAU,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,8BAAqB,CAAC,OAAO,CAAC,CAAC;IACzE,IAAI,UAAU,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,2BAAkB,CAAC,OAAO,CAAC,CAAC;IACtE,IAAI,UAAU,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,oCAA2B,CAAC,OAAO,CAAC,CAAC;IAC/E,OAAO,IAAI,qCAA4B,CAAC,OAAO,CAAC,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,SAAgB,sBAAsB,CACpC,OAAqC;IAErC,MAAM,OAAO,GAAG,IAAA,qCAA2B,EAAC,OAAO,CAAC,CAAC;IAGrD,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;QACxB,WAAW,CAAC,OAAyB;;gBACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAW,CAAC;gBAC7D,IAAI,CAAC;oBACH,MAAM,IAAA,oCAA0B,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBACnD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,UAAU,GAAG,KAAK,YAAY,mCAA0B;wBAC5D,CAAC,CAAC,KAAK;wBACP,CAAC,CAAC,IAAI,mCAA0B,CAAC,GAAG,EAAE,qBAAqB,EAAE,0CAA0C,CAAC,CAAC;oBAC3G,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;wBACpC,IAAA,oBAAY,EAAC,cAAc,EAAE;4BAC3B,IAAI,EAAE,UAAU,CAAC,IAAI;4BACrB,MAAM,EAAE,UAAU,CAAC,MAAM;4BACzB,MAAM,EAAE,OAAO,CAAC,MAAM;4BACtB,IAAI,EAAE,OAAO,CAAC,IAAI;4BAClB,EAAE,EAAE,OAAO,CAAC,EAAE;yBACf,CAAC,CAAC;oBACL,CAAC;oBACD,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;SAAA;KACF,CAAA;IAtBK,0BAA0B;QAD/B,IAAA,mBAAU,GAAE;OACP,0BAA0B,CAsB/B;IAED,OAAO,0BAA0B,CAAC;AACpC,CAAC","sourcesContent":["import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Injectable,\n InternalServerErrorException,\n ServiceUnavailableException,\n Type,\n UnauthorizedException,\n} from \"@nestjs/common\";\nimport type { Request } from \"express\";\nimport { auditAccessi } from \"../../accessi-module/security/audit\";\nimport { AccessiResourceServerError } from \"./errors\";\nimport { authenticateAccessiRequest, createAccessiResourceServer } from \"./runtime\";\nimport type { AccessiResourceServerOptions } from \"./types\";\n\nfunction toNestException(error: unknown): Error {\n const normalized = error instanceof AccessiResourceServerError\n ? error\n : new AccessiResourceServerError(500, \"AUTH_INTERNAL_ERROR\", \"Errore interno durante l'autenticazione.\");\n const payload = { code: normalized.code, message: normalized.message };\n\n if (normalized.status === 401) return new UnauthorizedException(payload);\n if (normalized.status === 403) return new ForbiddenException(payload);\n if (normalized.status === 503) return new ServiceUnavailableException(payload);\n return new InternalServerErrorException(payload);\n}\n\n/**\n * Crea un guard Nest configurato con le opzioni del resource server.\n *\n * Uso:\n * `@UseGuards(createAccessiAuthGuard(options))`\n */\nexport function createAccessiAuthGuard(\n options: AccessiResourceServerOptions,\n): Type<CanActivate> {\n const runtime = createAccessiResourceServer(options);\n\n @Injectable()\n class ConfiguredAccessiAuthGuard implements CanActivate {\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<Request>();\n try {\n await authenticateAccessiRequest(request, runtime);\n return true;\n } catch (error) {\n const normalized = error instanceof AccessiResourceServerError\n ? error\n : new AccessiResourceServerError(500, \"AUTH_INTERNAL_ERROR\", \"Errore interno durante l'autenticazione.\");\n if (runtime.options.audit !== false) {\n auditAccessi(\"auth_failure\", {\n code: normalized.code,\n status: normalized.status,\n method: request.method,\n path: request.path,\n ip: request.ip,\n });\n }\n throw toNestException(error);\n }\n }\n }\n\n return ConfiguredAccessiAuthGuard;\n}\n"]}
|
|
@@ -48,7 +48,7 @@ function mergeIntrospectionUser(payload, user) {
|
|
|
48
48
|
*/
|
|
49
49
|
function authenticateAccessiRequest(request, runtime) {
|
|
50
50
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
-
var _a, _b;
|
|
51
|
+
var _a, _b, _c;
|
|
52
52
|
const token = (0, contracts_1.extractAccessiBearerToken)(request.headers.authorization);
|
|
53
53
|
if (!token) {
|
|
54
54
|
throw (0, errors_1.accessiAuthError)(401, "AUTH_TOKEN_MISSING", "Token Bearer mancante o malformato.");
|
|
@@ -83,6 +83,16 @@ function authenticateAccessiRequest(request, runtime) {
|
|
|
83
83
|
if (!codiceUtente) {
|
|
84
84
|
throw (0, errors_1.accessiAuthError)(401, "AUTH_USER_CODE_MISSING", "Token privo di un utente valido.");
|
|
85
85
|
}
|
|
86
|
+
if (mode === "local" && ((_c = runtime.options.revocation) === null || _c === void 0 ? void 0 : _c.store)) {
|
|
87
|
+
const issuedAt = Number(payload === null || payload === void 0 ? void 0 : payload.iat);
|
|
88
|
+
if (!Number.isFinite(issuedAt)) {
|
|
89
|
+
throw (0, errors_1.accessiAuthError)(401, "AUTH_TOKEN_INVALID", "Token privo di iat.");
|
|
90
|
+
}
|
|
91
|
+
const revokedAt = yield runtime.options.revocation.store.getUserRevocation(codiceUtente);
|
|
92
|
+
if (revokedAt !== null && issuedAt < Math.floor(revokedAt / 1000)) {
|
|
93
|
+
throw (0, errors_1.accessiAuthError)(401, "AUTH_TOKEN_REVOKED", "Sessione revocata.");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
86
96
|
const requirementTree = (0, contracts_1.buildRequirementTree)(runtime.options.authorization);
|
|
87
97
|
if (requirementTree) {
|
|
88
98
|
if (!grants) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Request } from \"express\";\nimport {\n buildRequirementTree,\n evaluateRequirement,\n extractAccessiBearerToken,\n} from \"../contracts\";\nimport type {\n AccessiAccessTokenPayload,\n GrantsResult,\n} from \"../contracts\";\nimport { accessiAuthError } from \"./errors\";\nimport { createAccessiIntrospectionClient } from \"./introspection\";\nimport type { AccessiIntrospectionClient } from \"./introspection\";\nimport { createAccessiTokenVerifier } from \"./token\";\nimport type { AccessiTokenVerifier } from \"./token\";\nimport type {\n AccessiRequestContext,\n AccessiResourceServerOptions,\n} from \"./types\";\n\n/** Request Express arricchita dal resource server. */\nexport interface AccessiAuthenticatedRequest extends Request {\n user?: Record<string, unknown>;\n data?: Record<string, unknown>;\n userGrants?: GrantsResult;\n accessi?: AccessiRequestContext;\n}\n\n/** Runtime creato una volta al bootstrap e riutilizzato a ogni richiesta. */\nexport interface AccessiResourceServerRuntime {\n options: AccessiResourceServerOptions;\n verifier?: AccessiTokenVerifier;\n introspectionClient?: AccessiIntrospectionClient;\n}\n\nfunction resolveMode(options: AccessiResourceServerOptions): \"local\" | \"introspection\" {\n if (options.mode) return options.mode;\n if (options.jwt) return \"local\";\n if (options.introspection) return \"introspection\";\n throw accessiAuthError(\n 500,\n \"AUTH_CONFIG_INVALID\",\n \"Configurare jwt o introspection nel resource server Accessi.\",\n );\n}\n\n/** Valida la configurazione e prepara verificatore e client di introspection. */\nexport function createAccessiResourceServer(\n options: AccessiResourceServerOptions,\n): AccessiResourceServerRuntime {\n const mode = resolveMode(options);\n if (mode === \"local\" && !options.jwt) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"mode local richiede jwt.\");\n }\n if (mode === \"introspection\" && !options.introspection) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"mode introspection richiede introspection.\");\n }\n\n return {\n options: { ...options, mode },\n ...(options.jwt ? { verifier: createAccessiTokenVerifier(options.jwt) } : {}),\n ...(options.introspection\n ? { introspectionClient: createAccessiIntrospectionClient(options.introspection) }\n : {}),\n };\n}\n\nfunction mergeIntrospectionUser(\n payload: AccessiAccessTokenPayload | undefined,\n user: Record<string, unknown>,\n): AccessiAccessTokenPayload {\n return {\n ...(payload ?? { typ: \"access\" }),\n ...user,\n typ: \"access\",\n utente: user,\n };\n}\n\n/**\n * Autentica la request e applica l'eventuale policy di autorizzazione.\n *\n * Non accede mai al database Accessi: la verifica e locale oppure delegata\n * all'introspection di Accessi Service.\n */\nexport async function authenticateAccessiRequest(\n request: Request,\n runtime: AccessiResourceServerRuntime,\n): Promise<AccessiRequestContext> {\n const token = extractAccessiBearerToken(request.headers.authorization);\n if (!token) {\n throw accessiAuthError(401, \"AUTH_TOKEN_MISSING\", \"Token Bearer mancante o malformato.\");\n }\n\n const mode = runtime.options.mode ?? \"local\";\n let payload: AccessiAccessTokenPayload | undefined;\n let user: Record<string, unknown> = {};\n let codiceUtente: number | undefined;\n let grants: GrantsResult | undefined;\n\n if (mode === \"local\") {\n if (!runtime.verifier) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"Verificatore JWT non configurato.\");\n }\n const verification = await runtime.verifier.verify(token);\n payload = verification.payload;\n user = verification.user;\n codiceUtente = verification.codiceUtente;\n } else {\n if (!runtime.introspectionClient) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"Client introspection non configurato.\");\n }\n const introspection = await runtime.introspectionClient.introspect(token);\n if (!introspection.active) {\n throw accessiAuthError(401, \"AUTH_TOKEN_INVALID\", \"Token non valido, scaduto o revocato.\");\n }\n user = introspection.user ?? {};\n codiceUtente = introspection.codiceUtente;\n grants = introspection.grants;\n payload = mergeIntrospectionUser(undefined, user);\n }\n\n if (!codiceUtente) {\n throw accessiAuthError(401, \"AUTH_USER_CODE_MISSING\", \"Token privo di un utente valido.\");\n }\n\n const requirementTree = buildRequirementTree(runtime.options.authorization);\n if (requirementTree) {\n if (!grants) {\n if (!runtime.introspectionClient) {\n throw accessiAuthError(\n 500,\n \"AUTH_GRANTS_SOURCE_MISSING\",\n \"La policy richiede grant ma introspection non e configurata.\",\n );\n }\n const introspection = await runtime.introspectionClient.introspect(token);\n if (!introspection.active) {\n throw accessiAuthError(401, \"AUTH_TOKEN_INVALID\", \"Token non valido, scaduto o revocato.\");\n }\n grants = introspection.grants;\n if (introspection.user) {\n user = introspection.user;\n payload = mergeIntrospectionUser(payload, user);\n }\n }\n\n const hasPermissions = await evaluateRequirement(\n requirementTree,\n {\n req: request,\n decodedToken: payload,\n userCode: codiceUtente,\n getGrantsResult: async () => grants ?? {},\n },\n runtime.options.authorization,\n );\n if (!hasPermissions) {\n throw accessiAuthError(\n 403,\n \"AUTH_INSUFFICIENT_PERMISSIONS\",\n \"Permessi insufficienti per questa operazione.\",\n );\n }\n }\n\n const authenticatedRequest = request as AccessiAuthenticatedRequest;\n const authPayload: Record<string, unknown> = payload ?? {\n typ: \"access\",\n codiceUtente,\n utente: user,\n };\n authenticatedRequest.user = authPayload;\n authenticatedRequest.data = authPayload;\n if (grants) authenticatedRequest.userGrants = grants;\n authenticatedRequest.accessi = {\n token,\n user,\n codiceUtente,\n ...(grants ? { grants } : {}),\n payload: authPayload,\n };\n return authenticatedRequest.accessi;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Request } from \"express\";\nimport {\n buildRequirementTree,\n evaluateRequirement,\n extractAccessiBearerToken,\n} from \"../contracts\";\nimport type {\n AccessiAccessTokenPayload,\n GrantsResult,\n} from \"../contracts\";\nimport { accessiAuthError } from \"./errors\";\nimport { createAccessiIntrospectionClient } from \"./introspection\";\nimport type { AccessiIntrospectionClient } from \"./introspection\";\nimport { createAccessiTokenVerifier } from \"./token\";\nimport type { AccessiTokenVerifier } from \"./token\";\nimport type {\n AccessiRequestContext,\n AccessiResourceServerOptions,\n} from \"./types\";\n\n/** Request Express arricchita dal resource server. */\nexport interface AccessiAuthenticatedRequest extends Request {\n user?: Record<string, unknown>;\n data?: Record<string, unknown>;\n userGrants?: GrantsResult;\n accessi?: AccessiRequestContext;\n}\n\n/** Runtime creato una volta al bootstrap e riutilizzato a ogni richiesta. */\nexport interface AccessiResourceServerRuntime {\n options: AccessiResourceServerOptions;\n verifier?: AccessiTokenVerifier;\n introspectionClient?: AccessiIntrospectionClient;\n}\n\nfunction resolveMode(options: AccessiResourceServerOptions): \"local\" | \"introspection\" {\n if (options.mode) return options.mode;\n if (options.jwt) return \"local\";\n if (options.introspection) return \"introspection\";\n throw accessiAuthError(\n 500,\n \"AUTH_CONFIG_INVALID\",\n \"Configurare jwt o introspection nel resource server Accessi.\",\n );\n}\n\n/** Valida la configurazione e prepara verificatore e client di introspection. */\nexport function createAccessiResourceServer(\n options: AccessiResourceServerOptions,\n): AccessiResourceServerRuntime {\n const mode = resolveMode(options);\n if (mode === \"local\" && !options.jwt) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"mode local richiede jwt.\");\n }\n if (mode === \"introspection\" && !options.introspection) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"mode introspection richiede introspection.\");\n }\n\n return {\n options: { ...options, mode },\n ...(options.jwt ? { verifier: createAccessiTokenVerifier(options.jwt) } : {}),\n ...(options.introspection\n ? { introspectionClient: createAccessiIntrospectionClient(options.introspection) }\n : {}),\n };\n}\n\nfunction mergeIntrospectionUser(\n payload: AccessiAccessTokenPayload | undefined,\n user: Record<string, unknown>,\n): AccessiAccessTokenPayload {\n return {\n ...(payload ?? { typ: \"access\" }),\n ...user,\n typ: \"access\",\n utente: user,\n };\n}\n\n/**\n * Autentica la request e applica l'eventuale policy di autorizzazione.\n *\n * Non accede mai al database Accessi: la verifica e locale oppure delegata\n * all'introspection di Accessi Service.\n */\nexport async function authenticateAccessiRequest(\n request: Request,\n runtime: AccessiResourceServerRuntime,\n): Promise<AccessiRequestContext> {\n const token = extractAccessiBearerToken(request.headers.authorization);\n if (!token) {\n throw accessiAuthError(401, \"AUTH_TOKEN_MISSING\", \"Token Bearer mancante o malformato.\");\n }\n\n const mode = runtime.options.mode ?? \"local\";\n let payload: AccessiAccessTokenPayload | undefined;\n let user: Record<string, unknown> = {};\n let codiceUtente: number | undefined;\n let grants: GrantsResult | undefined;\n\n if (mode === \"local\") {\n if (!runtime.verifier) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"Verificatore JWT non configurato.\");\n }\n const verification = await runtime.verifier.verify(token);\n payload = verification.payload;\n user = verification.user;\n codiceUtente = verification.codiceUtente;\n } else {\n if (!runtime.introspectionClient) {\n throw accessiAuthError(500, \"AUTH_CONFIG_INVALID\", \"Client introspection non configurato.\");\n }\n const introspection = await runtime.introspectionClient.introspect(token);\n if (!introspection.active) {\n throw accessiAuthError(401, \"AUTH_TOKEN_INVALID\", \"Token non valido, scaduto o revocato.\");\n }\n user = introspection.user ?? {};\n codiceUtente = introspection.codiceUtente;\n grants = introspection.grants;\n payload = mergeIntrospectionUser(undefined, user);\n }\n\n if (!codiceUtente) {\n throw accessiAuthError(401, \"AUTH_USER_CODE_MISSING\", \"Token privo di un utente valido.\");\n }\n\n if (mode === \"local\" && runtime.options.revocation?.store) {\n const issuedAt = Number(payload?.iat);\n if (!Number.isFinite(issuedAt)) {\n throw accessiAuthError(401, \"AUTH_TOKEN_INVALID\", \"Token privo di iat.\");\n }\n const revokedAt = await runtime.options.revocation.store.getUserRevocation(codiceUtente);\n if (revokedAt !== null && issuedAt < Math.floor(revokedAt / 1000)) {\n throw accessiAuthError(401, \"AUTH_TOKEN_REVOKED\", \"Sessione revocata.\");\n }\n }\n\n const requirementTree = buildRequirementTree(runtime.options.authorization);\n if (requirementTree) {\n if (!grants) {\n if (!runtime.introspectionClient) {\n throw accessiAuthError(\n 500,\n \"AUTH_GRANTS_SOURCE_MISSING\",\n \"La policy richiede grant ma introspection non e configurata.\",\n );\n }\n const introspection = await runtime.introspectionClient.introspect(token);\n if (!introspection.active) {\n throw accessiAuthError(401, \"AUTH_TOKEN_INVALID\", \"Token non valido, scaduto o revocato.\");\n }\n grants = introspection.grants;\n if (introspection.user) {\n user = introspection.user;\n payload = mergeIntrospectionUser(payload, user);\n }\n }\n\n const hasPermissions = await evaluateRequirement(\n requirementTree,\n {\n req: request,\n decodedToken: payload,\n userCode: codiceUtente,\n getGrantsResult: async () => grants ?? {},\n },\n runtime.options.authorization,\n );\n if (!hasPermissions) {\n throw accessiAuthError(\n 403,\n \"AUTH_INSUFFICIENT_PERMISSIONS\",\n \"Permessi insufficienti per questa operazione.\",\n );\n }\n }\n\n const authenticatedRequest = request as AccessiAuthenticatedRequest;\n const authPayload: Record<string, unknown> = payload ?? {\n typ: \"access\",\n codiceUtente,\n utente: user,\n };\n authenticatedRequest.user = authPayload;\n authenticatedRequest.data = authPayload;\n if (grants) authenticatedRequest.userGrants = grants;\n authenticatedRequest.accessi = {\n token,\n user,\n codiceUtente,\n ...(grants ? { grants } : {}),\n payload: authPayload,\n };\n return authenticatedRequest.accessi;\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AccessiAuthorizationOptions, AccessiGrant, GrantsResult } from "../contracts";
|
|
2
|
+
import type { SessionRevocationStore } from "../../accessi-module/security/sessionRevocation";
|
|
2
3
|
/** Algoritmi JWT supportati dal resource server. */
|
|
3
4
|
export type AccessiJwtAlgorithm = "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "ES256" | "ES384" | "ES512";
|
|
4
5
|
/** Opzioni di verifica locale del JWT. Almeno una tra `secret`, `publicKey` e `jwks` e obbligatoria. */
|
|
@@ -65,6 +66,12 @@ export interface AccessiResourceServerOptions {
|
|
|
65
66
|
introspection?: AccessiIntrospectionOptions;
|
|
66
67
|
/** Policy di autorizzazione opzionale applicata dopo l'autenticazione. */
|
|
67
68
|
authorization?: AccessiAuthorizationOptions;
|
|
69
|
+
/** Store condiviso di revoca sessioni. Opzionale. */
|
|
70
|
+
revocation?: {
|
|
71
|
+
store: SessionRevocationStore;
|
|
72
|
+
};
|
|
73
|
+
/** Audit strutturato degli errori di autenticazione. Default: true. */
|
|
74
|
+
audit?: boolean;
|
|
68
75
|
}
|
|
69
76
|
/** Dati allegati alla request dopo l'autenticazione. */
|
|
70
77
|
export interface AccessiRequestContext {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AccessiAuthorizationOptions,\n AccessiGrant,\n GrantsResult,\n} from \"../contracts\";\n\n/** Algoritmi JWT supportati dal resource server. */\nexport type AccessiJwtAlgorithm =\n | \"HS256\"\n | \"HS384\"\n | \"HS512\"\n | \"RS256\"\n | \"RS384\"\n | \"RS512\"\n | \"ES256\"\n | \"ES384\"\n | \"ES512\";\n\n/** Opzioni di verifica locale del JWT. Almeno una tra `secret`, `publicKey` e `jwks` e obbligatoria. */\nexport interface AccessiResourceServerJwtOptions {\n /** Chiave HMAC gia derivata (access key). Alternativa a `serverSecret`. */\n secret?: string;\n /**\n * Segreto Accessi grezzo (`ACCESSI_JWT_SECRET`).\n *\n * Il resource server deriva automaticamente la chiave access con la stessa\n * KDF del server: e il modo consigliato quando si condivide il segreto HS256.\n */\n serverSecret?: string;\n /** Chiave pubblica PEM per algoritmi RS ed ES. */\n publicKey?: string | Buffer;\n /** JWKS remoto per verificare token firmati con chiavi asimmetriche. */\n jwks?: {\n url: string;\n cacheTtlMs?: number;\n timeoutMs?: number;\n fetch?: typeof fetch;\n };\n /** Default: HS256 con `secret`, altrimenti RS/ES con `publicKey`/`jwks`. */\n algorithms?: AccessiJwtAlgorithm[];\n /** Issuer atteso (`iss`). */\n issuer?: string;\n /** Audience attesa (`aud`). */\n audience?: string | string[];\n /** Tolleranza orologio in secondi. Default: 5. */\n clockToleranceSeconds?: number;\n}\n\n/** Configurazione del client di introspection verso Accessi Service. */\nexport interface AccessiIntrospectionOptions {\n /** URL completo, es. `https://accessi.example.com/api/accessi/auth/introspect`. */\n url: string;\n /** Service token Accessi con scope `accessi:introspect`. */\n serviceToken: string;\n /** TTL della cache locale dei risultati attivi. Default: 30000 ms. */\n cacheTtlMs?: number;\n /** Timeout HTTP. Default: 3000 ms. */\n timeoutMs?: number;\n /** Fetch iniettabile per test o runtime non-Node. */\n fetch?: typeof fetch;\n}\n\n/** Risultato dell'introspection. */\nexport interface AccessiIntrospectionResult {\n active: boolean;\n user?: Record<string, unknown>;\n codiceUtente?: number;\n grants?: GrantsResult;\n}\n\n/** Modalita di autenticazione del resource server. */\nexport type AccessiResourceServerMode = \"local\" | \"introspection\";\n\n/**\n * Configurazione di un backend fratello che non possiede il database Accessi.\n *\n * - `mode: \"local\"` verifica il JWT con chiave locale e, se servono grant, usa\n * l'introspection per leggerli.\n * - `mode: \"introspection\"` delega la verifica del token ad Accessi Service.\n */\nexport interface AccessiResourceServerOptions {\n mode?: AccessiResourceServerMode;\n jwt?: AccessiResourceServerJwtOptions;\n introspection?: AccessiIntrospectionOptions;\n /** Policy di autorizzazione opzionale applicata dopo l'autenticazione. */\n authorization?: AccessiAuthorizationOptions;\n}\n\n/** Dati allegati alla request dopo l'autenticazione. */\nexport interface AccessiRequestContext {\n token: string;\n user: Record<string, unknown>;\n codiceUtente?: number;\n grants?: GrantsResult;\n payload?: Record<string, unknown>;\n}\n\n/** Grant esposto per compatibilita con il middleware storico. */\nexport type AccessiRequestGrants = AccessiGrant[];\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/accessi/resource-server/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AccessiAuthorizationOptions,\n AccessiGrant,\n GrantsResult,\n} from \"../contracts\";\nimport type { SessionRevocationStore } from \"../../accessi-module/security/sessionRevocation\";\n\n/** Algoritmi JWT supportati dal resource server. */\nexport type AccessiJwtAlgorithm =\n | \"HS256\"\n | \"HS384\"\n | \"HS512\"\n | \"RS256\"\n | \"RS384\"\n | \"RS512\"\n | \"ES256\"\n | \"ES384\"\n | \"ES512\";\n\n/** Opzioni di verifica locale del JWT. Almeno una tra `secret`, `publicKey` e `jwks` e obbligatoria. */\nexport interface AccessiResourceServerJwtOptions {\n /** Chiave HMAC gia derivata (access key). Alternativa a `serverSecret`. */\n secret?: string;\n /**\n * Segreto Accessi grezzo (`ACCESSI_JWT_SECRET`).\n *\n * Il resource server deriva automaticamente la chiave access con la stessa\n * KDF del server: e il modo consigliato quando si condivide il segreto HS256.\n */\n serverSecret?: string;\n /** Chiave pubblica PEM per algoritmi RS ed ES. */\n publicKey?: string | Buffer;\n /** JWKS remoto per verificare token firmati con chiavi asimmetriche. */\n jwks?: {\n url: string;\n cacheTtlMs?: number;\n timeoutMs?: number;\n fetch?: typeof fetch;\n };\n /** Default: HS256 con `secret`, altrimenti RS/ES con `publicKey`/`jwks`. */\n algorithms?: AccessiJwtAlgorithm[];\n /** Issuer atteso (`iss`). */\n issuer?: string;\n /** Audience attesa (`aud`). */\n audience?: string | string[];\n /** Tolleranza orologio in secondi. Default: 5. */\n clockToleranceSeconds?: number;\n}\n\n/** Configurazione del client di introspection verso Accessi Service. */\nexport interface AccessiIntrospectionOptions {\n /** URL completo, es. `https://accessi.example.com/api/accessi/auth/introspect`. */\n url: string;\n /** Service token Accessi con scope `accessi:introspect`. */\n serviceToken: string;\n /** TTL della cache locale dei risultati attivi. Default: 30000 ms. */\n cacheTtlMs?: number;\n /** Timeout HTTP. Default: 3000 ms. */\n timeoutMs?: number;\n /** Fetch iniettabile per test o runtime non-Node. */\n fetch?: typeof fetch;\n}\n\n/** Risultato dell'introspection. */\nexport interface AccessiIntrospectionResult {\n active: boolean;\n user?: Record<string, unknown>;\n codiceUtente?: number;\n grants?: GrantsResult;\n}\n\n/** Modalita di autenticazione del resource server. */\nexport type AccessiResourceServerMode = \"local\" | \"introspection\";\n\n/**\n * Configurazione di un backend fratello che non possiede il database Accessi.\n *\n * - `mode: \"local\"` verifica il JWT con chiave locale e, se servono grant, usa\n * l'introspection per leggerli.\n * - `mode: \"introspection\"` delega la verifica del token ad Accessi Service.\n */\nexport interface AccessiResourceServerOptions {\n mode?: AccessiResourceServerMode;\n jwt?: AccessiResourceServerJwtOptions;\n introspection?: AccessiIntrospectionOptions;\n /** Policy di autorizzazione opzionale applicata dopo l'autenticazione. */\n authorization?: AccessiAuthorizationOptions;\n /** Store condiviso di revoca sessioni. Opzionale. */\n revocation?: {\n store: SessionRevocationStore;\n };\n /** Audit strutturato degli errori di autenticazione. Default: true. */\n audit?: boolean;\n}\n\n/** Dati allegati alla request dopo l'autenticazione. */\nexport interface AccessiRequestContext {\n token: string;\n user: Record<string, unknown>;\n codiceUtente?: number;\n grants?: GrantsResult;\n payload?: Record<string, unknown>;\n}\n\n/** Grant esposto per compatibilita con il middleware storico. */\nexport type AccessiRequestGrants = AccessiGrant[];\n"]}
|
|
@@ -6,5 +6,6 @@
|
|
|
6
6
|
* `accessi/resource-server` e `accessi/client`.
|
|
7
7
|
*/
|
|
8
8
|
export * from "../../accessi-module";
|
|
9
|
+
export * from "./standalone";
|
|
9
10
|
export type { AccessiOptions as AccessiServerOptions } from "../../accessi-module";
|
|
10
11
|
export { createAccessiOptionsFromEnv, createCatalogScriptsOptionsFromEnv, } from "../../accessi-module/database-updates/accessiOptionsFromEnv";
|
|
@@ -23,6 +23,7 @@ exports.createCatalogScriptsOptionsFromEnv = exports.createAccessiOptionsFromEnv
|
|
|
23
23
|
* `accessi/resource-server` e `accessi/client`.
|
|
24
24
|
*/
|
|
25
25
|
__exportStar(require("../../accessi-module"), exports);
|
|
26
|
+
__exportStar(require("./standalone"), exports);
|
|
26
27
|
var accessiOptionsFromEnv_1 = require("../../accessi-module/database-updates/accessiOptionsFromEnv");
|
|
27
28
|
Object.defineProperty(exports, "createAccessiOptionsFromEnv", { enumerable: true, get: function () { return accessiOptionsFromEnv_1.createAccessiOptionsFromEnv; } });
|
|
28
29
|
Object.defineProperty(exports, "createCatalogScriptsOptionsFromEnv", { enumerable: true, get: function () { return accessiOptionsFromEnv_1.createCatalogScriptsOptionsFromEnv; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accessi/server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,uDAAqC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accessi/server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,uDAAqC;AACrC,+CAA6B;AAE7B,qGAGqE;AAFnE,oIAAA,2BAA2B,OAAA;AAC3B,2IAAA,kCAAkC,OAAA","sourcesContent":["/**\n * Entry point server Accessi.\n *\n * Questo e l'unico entry point che puo importare database, SMTP, migrazioni,\n * console e servizi Nest completi. I backend fratelli devono usare\n * `accessi/resource-server` e `accessi/client`.\n */\nexport * from \"../../accessi-module\";\nexport * from \"./standalone\";\nexport type { AccessiOptions as AccessiServerOptions } from \"../../accessi-module\";\nexport {\n createAccessiOptionsFromEnv,\n createCatalogScriptsOptionsFromEnv,\n} from \"../../accessi-module/database-updates/accessiOptionsFromEnv\";\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Application } from 'express';
|
|
2
|
+
import type { Server } from 'http';
|
|
3
|
+
import type { AccessiModuleInitializationResult } from '../../accessi-module';
|
|
4
|
+
import type { AccessiOptions } from '../../accessi-module/AccessiModule';
|
|
5
|
+
export interface AccessiReadinessCheck {
|
|
6
|
+
name: string;
|
|
7
|
+
check: () => boolean | Promise<boolean>;
|
|
8
|
+
}
|
|
9
|
+
export interface AccessiServiceOptions {
|
|
10
|
+
/** Applicazione Express gia esistente. Default: nuova app. */
|
|
11
|
+
app?: Application;
|
|
12
|
+
/** Configurazione completa. Default: `createAccessiOptionsFromEnv()`. */
|
|
13
|
+
accessiOptions?: AccessiOptions;
|
|
14
|
+
port?: number;
|
|
15
|
+
host?: string;
|
|
16
|
+
/** Path base degli endpoint health. Default: `/health`. */
|
|
17
|
+
healthPath?: string;
|
|
18
|
+
/** Espone `/health/metrics` in formato Prometheus. Default: true. */
|
|
19
|
+
metrics?: boolean;
|
|
20
|
+
/** Check aggiuntivi per la readiness. */
|
|
21
|
+
readinessChecks?: AccessiReadinessCheck[];
|
|
22
|
+
/** Timeout chiusura server. Default: 10000 ms. */
|
|
23
|
+
shutdownTimeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface AccessiService {
|
|
26
|
+
app: Application;
|
|
27
|
+
nestApp: AccessiModuleInitializationResult['nestApp'];
|
|
28
|
+
document: AccessiModuleInitializationResult['document'];
|
|
29
|
+
start(): Promise<Server>;
|
|
30
|
+
stop(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Crea un servizio Accessi standalone con health, readiness, metriche e shutdown.
|
|
34
|
+
*
|
|
35
|
+
* Non apre la porta finche non viene chiamato `start()`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createAccessiService(options?: AccessiServiceOptions): Promise<AccessiService>;
|
|
38
|
+
/**
|
|
39
|
+
* Avvia Accessi Service e registra SIGTERM/SIGINT per lo shutdown graceful.
|
|
40
|
+
*/
|
|
41
|
+
export declare function startAccessiService(options?: AccessiServiceOptions): Promise<AccessiService>;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createAccessiService = createAccessiService;
|
|
16
|
+
exports.startAccessiService = startAccessiService;
|
|
17
|
+
const express_1 = __importDefault(require("express"));
|
|
18
|
+
const Logger_1 = require("../../Logger");
|
|
19
|
+
const Orm_1 = require("../../Orm");
|
|
20
|
+
const accessi_module_1 = require("../../accessi-module");
|
|
21
|
+
const accessiOptionsFromEnv_1 = require("../../accessi-module/database-updates/accessiOptionsFromEnv");
|
|
22
|
+
const accessiSchema_1 = require("../../accessi-module/database-updates/accessiSchema");
|
|
23
|
+
const AccessiDatabaseUpdater_1 = require("../../accessi-module/database-updates/AccessiDatabaseUpdater");
|
|
24
|
+
const validateOptions_1 = require("../../accessi-module/security/validateOptions");
|
|
25
|
+
function normalizePath(value) {
|
|
26
|
+
return `/${value.replace(/^\/+|\/+$/g, '')}`;
|
|
27
|
+
}
|
|
28
|
+
function createMetrics() {
|
|
29
|
+
const startedAt = Date.now();
|
|
30
|
+
let requests = 0;
|
|
31
|
+
let inFlight = 0;
|
|
32
|
+
const statuses = new Map();
|
|
33
|
+
const middleware = (_req, res, next) => {
|
|
34
|
+
requests += 1;
|
|
35
|
+
inFlight += 1;
|
|
36
|
+
res.on('finish', () => {
|
|
37
|
+
var _a;
|
|
38
|
+
inFlight = Math.max(0, inFlight - 1);
|
|
39
|
+
const status = String(res.statusCode);
|
|
40
|
+
statuses.set(status, ((_a = statuses.get(status)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
41
|
+
});
|
|
42
|
+
next();
|
|
43
|
+
};
|
|
44
|
+
const render = () => {
|
|
45
|
+
const memory = process.memoryUsage();
|
|
46
|
+
const lines = [
|
|
47
|
+
'# HELP accessi_http_requests_total Richieste HTTP totali.',
|
|
48
|
+
'# TYPE accessi_http_requests_total counter',
|
|
49
|
+
...[...statuses.entries()].map(([status, count]) => `accessi_http_requests_total{status="${status}"} ${count}`),
|
|
50
|
+
'# HELP accessi_http_in_flight Richieste HTTP in corso.',
|
|
51
|
+
'# TYPE accessi_http_in_flight gauge',
|
|
52
|
+
`accessi_http_in_flight ${inFlight}`,
|
|
53
|
+
'# HELP accessi_process_uptime_seconds Uptime del processo.',
|
|
54
|
+
'# TYPE accessi_process_uptime_seconds gauge',
|
|
55
|
+
`accessi_process_uptime_seconds ${Math.floor((Date.now() - startedAt) / 1000)}`,
|
|
56
|
+
'# HELP accessi_process_memory_bytes Memoria del processo.',
|
|
57
|
+
'# TYPE accessi_process_memory_bytes gauge',
|
|
58
|
+
`accessi_process_memory_bytes{type="rss"} ${memory.rss}`,
|
|
59
|
+
`accessi_process_memory_bytes{type="heapUsed"} ${memory.heapUsed}`,
|
|
60
|
+
`accessi_process_memory_bytes{type="heapTotal"} ${memory.heapTotal}`,
|
|
61
|
+
'# HELP accessi_http_requests_started_total Richieste HTTP avviate.',
|
|
62
|
+
'# TYPE accessi_http_requests_started_total counter',
|
|
63
|
+
`accessi_http_requests_started_total ${requests}`,
|
|
64
|
+
];
|
|
65
|
+
return `${lines.join('\n')}\n`;
|
|
66
|
+
};
|
|
67
|
+
return { middleware, render };
|
|
68
|
+
}
|
|
69
|
+
function collectHealthStores(options) {
|
|
70
|
+
var _a, _b, _c, _d, _e, _f;
|
|
71
|
+
const stores = [];
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
const add = (name, store) => {
|
|
74
|
+
if (!store || seen.has(store) || typeof store.healthCheck !== 'function')
|
|
75
|
+
return;
|
|
76
|
+
seen.add(store);
|
|
77
|
+
stores.push({ name, store });
|
|
78
|
+
};
|
|
79
|
+
add('publicAuthRateLimit', (_a = options.publicAuthRateLimit) === null || _a === void 0 ? void 0 : _a.store);
|
|
80
|
+
add('serviceTokenRateLimit', (_b = options.serviceTokens) === null || _b === void 0 ? void 0 : _b.rateLimitStore);
|
|
81
|
+
add('consoleEntry', (_c = options.consoleEntry) === null || _c === void 0 ? void 0 : _c.store);
|
|
82
|
+
add('edgeRateLimit', (_d = options.edgeRateLimit) === null || _d === void 0 ? void 0 : _d.store);
|
|
83
|
+
add('sessionRevocation', (_e = options.sessionRevocation) === null || _e === void 0 ? void 0 : _e.store);
|
|
84
|
+
add('refreshTokenRotation', (_f = options.refreshTokenRotation) === null || _f === void 0 ? void 0 : _f.store);
|
|
85
|
+
return stores;
|
|
86
|
+
}
|
|
87
|
+
function checkDatabase(options) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
try {
|
|
90
|
+
const version = yield AccessiDatabaseUpdater_1.AccessiDatabaseUpdater.getCurrentVersion(options);
|
|
91
|
+
if (!version) {
|
|
92
|
+
return { ok: false, detail: 'schema Accessi non inizializzato' };
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
detail: version === accessiSchema_1.ACCESSI_SCHEMA_VERSION
|
|
97
|
+
? version
|
|
98
|
+
: `${version} (atteso ${accessiSchema_1.ACCESSI_SCHEMA_VERSION})`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { ok: false, detail: error instanceof Error ? error.message : String(error) };
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Crea un servizio Accessi standalone con health, readiness, metriche e shutdown.
|
|
108
|
+
*
|
|
109
|
+
* Non apre la porta finche non viene chiamato `start()`.
|
|
110
|
+
*/
|
|
111
|
+
function createAccessiService() {
|
|
112
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
113
|
+
var _a, _b, _c, _d;
|
|
114
|
+
const logger = new Logger_1.Logger('AccessiService');
|
|
115
|
+
const accessiOptions = (_a = options.accessiOptions) !== null && _a !== void 0 ? _a : (0, accessiOptionsFromEnv_1.createAccessiOptionsFromEnv)();
|
|
116
|
+
(0, validateOptions_1.assertValidAccessiOptions)(accessiOptions);
|
|
117
|
+
for (const warning of (0, validateOptions_1.validateAccessiOptions)(accessiOptions).warnings) {
|
|
118
|
+
logger.warning(`Configurazione Accessi: ${warning.code} - ${warning.message}`);
|
|
119
|
+
}
|
|
120
|
+
const app = (_b = options.app) !== null && _b !== void 0 ? _b : (0, express_1.default)();
|
|
121
|
+
const healthPath = normalizePath((_c = options.healthPath) !== null && _c !== void 0 ? _c : '/health');
|
|
122
|
+
const metrics = createMetrics();
|
|
123
|
+
const readinessChecks = (_d = options.readinessChecks) !== null && _d !== void 0 ? _d : [];
|
|
124
|
+
let initialized = false;
|
|
125
|
+
let nestApp;
|
|
126
|
+
let document;
|
|
127
|
+
let server;
|
|
128
|
+
let stopping = false;
|
|
129
|
+
app.use(metrics.middleware);
|
|
130
|
+
app.get(`${healthPath}/live`, (_req, res) => {
|
|
131
|
+
res.status(200).json({
|
|
132
|
+
status: 'ok',
|
|
133
|
+
uptimeSeconds: Math.floor(process.uptime()),
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
app.get(`${healthPath}/ready`, (_req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
137
|
+
if (!initialized || !nestApp) {
|
|
138
|
+
res.status(503).json({
|
|
139
|
+
status: 'error',
|
|
140
|
+
checks: { bootstrap: { ok: false, detail: 'bootstrap non completato' } },
|
|
141
|
+
});
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const checks = {
|
|
145
|
+
bootstrap: { ok: true },
|
|
146
|
+
database: yield checkDatabase(accessiOptions),
|
|
147
|
+
};
|
|
148
|
+
const stores = collectHealthStores(accessiOptions);
|
|
149
|
+
yield Promise.all(stores.map((_a) => __awaiter(this, [_a], void 0, function* ({ name, store }) {
|
|
150
|
+
var _b;
|
|
151
|
+
try {
|
|
152
|
+
checks[name] = { ok: Boolean(yield ((_b = store.healthCheck) === null || _b === void 0 ? void 0 : _b.call(store))) };
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
checks[name] = {
|
|
156
|
+
ok: false,
|
|
157
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
})));
|
|
161
|
+
for (const check of readinessChecks) {
|
|
162
|
+
try {
|
|
163
|
+
const ok = yield check.check();
|
|
164
|
+
checks[check.name] = { ok: Boolean(ok) };
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
checks[check.name] = {
|
|
168
|
+
ok: false,
|
|
169
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const ok = Object.values(checks).every((check) => check.ok);
|
|
174
|
+
res.status(ok ? 200 : 503).json({
|
|
175
|
+
status: ok ? 'ok' : 'error',
|
|
176
|
+
checks,
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
});
|
|
179
|
+
}));
|
|
180
|
+
if (options.metrics !== false) {
|
|
181
|
+
app.get(`${healthPath}/metrics`, (_req, res) => {
|
|
182
|
+
res.setHeader('Content-Type', 'text/plain; version=0.0.4');
|
|
183
|
+
res.status(200).send(metrics.render());
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const result = yield (0, accessi_module_1.initializeAccessiModule)(app, accessiOptions);
|
|
187
|
+
nestApp = result.nestApp;
|
|
188
|
+
document = result.document;
|
|
189
|
+
initialized = true;
|
|
190
|
+
const stop = () => __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
var _a;
|
|
192
|
+
if (stopping)
|
|
193
|
+
return;
|
|
194
|
+
stopping = true;
|
|
195
|
+
const timeout = Math.max(1000, (_a = options.shutdownTimeoutMs) !== null && _a !== void 0 ? _a : 10000);
|
|
196
|
+
yield Promise.race([
|
|
197
|
+
new Promise((resolve) => {
|
|
198
|
+
if (!server)
|
|
199
|
+
return resolve();
|
|
200
|
+
server.close(() => resolve());
|
|
201
|
+
}),
|
|
202
|
+
new Promise((resolve) => setTimeout(resolve, timeout)),
|
|
203
|
+
]);
|
|
204
|
+
try {
|
|
205
|
+
yield (nestApp === null || nestApp === void 0 ? void 0 : nestApp.close());
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
yield Orm_1.Orm.closePools();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
const start = () => __awaiter(this, void 0, void 0, function* () {
|
|
212
|
+
var _a, _b, _c;
|
|
213
|
+
if (server)
|
|
214
|
+
return server;
|
|
215
|
+
const port = (_a = options.port) !== null && _a !== void 0 ? _a : Number((_b = process.env.PORT) !== null && _b !== void 0 ? _b : 3000);
|
|
216
|
+
server = yield new Promise((resolve, reject) => {
|
|
217
|
+
const listener = options.host
|
|
218
|
+
? app.listen(port, options.host, () => resolve(listener))
|
|
219
|
+
: app.listen(port, () => resolve(listener));
|
|
220
|
+
listener.once('error', reject);
|
|
221
|
+
});
|
|
222
|
+
const address = server.address();
|
|
223
|
+
const actualPort = typeof address === 'object' && address ? address.port : port;
|
|
224
|
+
logger.info(`Accessi Service in ascolto su http://${(_c = options.host) !== null && _c !== void 0 ? _c : '0.0.0.0'}:${actualPort}`);
|
|
225
|
+
return server;
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
app,
|
|
229
|
+
get nestApp() {
|
|
230
|
+
if (!nestApp)
|
|
231
|
+
throw new Error('Accessi Service non inizializzato.');
|
|
232
|
+
return nestApp;
|
|
233
|
+
},
|
|
234
|
+
get document() {
|
|
235
|
+
if (!document)
|
|
236
|
+
throw new Error('Documento OpenAPI non disponibile.');
|
|
237
|
+
return document;
|
|
238
|
+
},
|
|
239
|
+
start,
|
|
240
|
+
stop,
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Avvia Accessi Service e registra SIGTERM/SIGINT per lo shutdown graceful.
|
|
246
|
+
*/
|
|
247
|
+
function startAccessiService() {
|
|
248
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
249
|
+
const service = yield createAccessiService(options);
|
|
250
|
+
yield service.start();
|
|
251
|
+
let shuttingDown = false;
|
|
252
|
+
const shutdown = (signal) => __awaiter(this, void 0, void 0, function* () {
|
|
253
|
+
if (shuttingDown)
|
|
254
|
+
return;
|
|
255
|
+
shuttingDown = true;
|
|
256
|
+
new Logger_1.Logger('AccessiService').info(`Ricevuto ${signal}: arresto in corso.`);
|
|
257
|
+
yield service.stop();
|
|
258
|
+
});
|
|
259
|
+
process.once('SIGTERM', () => { void shutdown('SIGTERM'); });
|
|
260
|
+
process.once('SIGINT', () => { void shutdown('SIGINT'); });
|
|
261
|
+
return service;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=standalone.js.map
|