emilsoftware-utilities 2.0.0-dev.65 → 2.0.0-dev.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.MD +28 -1
  2. package/dist/EmilsoftwareModule.d.ts +11 -3
  3. package/dist/EmilsoftwareModule.js +1 -1
  4. package/dist/EmilsoftwareModule.js.map +1 -1
  5. package/dist/Orm.d.ts +1 -1
  6. package/dist/Orm.js +4 -23
  7. package/dist/Orm.js.map +1 -1
  8. package/dist/Utilities.js +1 -0
  9. package/dist/Utilities.js.map +1 -1
  10. package/dist/accessi/client/index.d.ts +26 -0
  11. package/dist/accessi/client/index.js +49 -0
  12. package/dist/accessi/client/index.js.map +1 -0
  13. package/dist/accessi/console/index.d.ts +15 -0
  14. package/dist/accessi/console/index.js +25 -0
  15. package/dist/accessi/console/index.js.map +1 -0
  16. package/dist/accessi/contracts/dtos.d.ts +8 -0
  17. package/dist/accessi/contracts/dtos.js +25 -0
  18. package/dist/accessi/contracts/dtos.js.map +1 -0
  19. package/dist/accessi/contracts/index.d.ts +25 -0
  20. package/dist/accessi/contracts/index.js +28 -0
  21. package/dist/accessi/contracts/index.js.map +1 -0
  22. package/dist/accessi/openapi/index.d.ts +2 -0
  23. package/dist/accessi/openapi/index.js +19 -0
  24. package/dist/accessi/openapi/index.js.map +1 -0
  25. package/dist/accessi/resource-server/core.d.ts +6 -0
  26. package/dist/accessi/resource-server/core.js +23 -0
  27. package/dist/accessi/resource-server/core.js.map +1 -0
  28. package/dist/accessi/resource-server/errors.d.ts +10 -0
  29. package/dist/accessi/resource-server/errors.js +19 -0
  30. package/dist/accessi/resource-server/errors.js.map +1 -0
  31. package/dist/accessi/resource-server/express.d.ts +17 -0
  32. package/dist/accessi/resource-server/express.js +66 -0
  33. package/dist/accessi/resource-server/express.js.map +1 -0
  34. package/dist/accessi/resource-server/index.d.ts +3 -0
  35. package/dist/accessi/resource-server/index.js +20 -0
  36. package/dist/accessi/resource-server/index.js.map +1 -0
  37. package/dist/accessi/resource-server/introspection.d.ts +16 -0
  38. package/dist/accessi/resource-server/introspection.js +115 -0
  39. package/dist/accessi/resource-server/introspection.js.map +1 -0
  40. package/dist/accessi/resource-server/keys.d.ts +11 -0
  41. package/dist/accessi/resource-server/keys.js +22 -0
  42. package/dist/accessi/resource-server/keys.js.map +1 -0
  43. package/dist/accessi/resource-server/nest.d.ts +9 -0
  44. package/dist/accessi/resource-server/nest.js +62 -0
  45. package/dist/accessi/resource-server/nest.js.map +1 -0
  46. package/dist/accessi/resource-server/runtime.d.ts +27 -0
  47. package/dist/accessi/resource-server/runtime.js +128 -0
  48. package/dist/accessi/resource-server/runtime.js.map +1 -0
  49. package/dist/accessi/resource-server/token.d.ts +23 -0
  50. package/dist/accessi/resource-server/token.js +230 -0
  51. package/dist/accessi/resource-server/token.js.map +1 -0
  52. package/dist/accessi/resource-server/types.d.ts +78 -0
  53. package/dist/accessi/resource-server/types.js +3 -0
  54. package/dist/accessi/resource-server/types.js.map +1 -0
  55. package/dist/accessi/server/index.d.ts +10 -0
  56. package/dist/accessi/server/index.js +29 -0
  57. package/dist/accessi/server/index.js.map +1 -0
  58. package/dist/accessi/ui/index.d.ts +13 -0
  59. package/dist/accessi/ui/index.js +19 -0
  60. package/dist/accessi/ui/index.js.map +1 -0
  61. package/dist/accessi-module/AccessiModule.d.ts +25 -1
  62. package/dist/accessi-module/AccessiModule.js +2 -0
  63. package/dist/accessi-module/AccessiModule.js.map +1 -1
  64. package/dist/accessi-module/Console/accessiFetch.d.ts +4 -0
  65. package/dist/accessi-module/Console/accessiFetch.js +18 -4
  66. package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
  67. package/dist/accessi-module/Console/console.js +114 -9
  68. package/dist/accessi-module/Console/console.js.map +1 -1
  69. package/dist/accessi-module/Console/generated/{accessiApi.d.ts → emilsoftware-utilities.d.ts} +11 -1
  70. package/dist/accessi-module/Console/generated/{accessiApi.js → emilsoftware-utilities.js} +10 -2
  71. package/dist/accessi-module/Console/generated/emilsoftware-utilities.js.map +1 -0
  72. package/dist/accessi-module/Console/generated/model/accessiIntrospectionResultDto.d.ts +16 -0
  73. package/dist/accessi-module/Console/generated/model/accessiIntrospectionResultDto.js +3 -0
  74. package/dist/accessi-module/Console/generated/model/accessiIntrospectionResultDto.js.map +1 -0
  75. package/dist/accessi-module/Console/generated/model/index.d.ts +3 -0
  76. package/dist/accessi-module/Console/generated/model/index.js +3 -0
  77. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  78. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenRequest.d.ts +10 -0
  79. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenRequest.js +9 -0
  80. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenRequest.js.map +1 -0
  81. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenResponse.d.ts +14 -0
  82. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenResponse.js +3 -0
  83. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenResponse.js.map +1 -0
  84. package/dist/accessi-module/Console/wiki.js +98 -3
  85. package/dist/accessi-module/Console/wiki.js.map +1 -1
  86. package/dist/accessi-module/Controllers/AccessiIntrospectionController.d.ts +17 -0
  87. package/dist/accessi-module/Controllers/AccessiIntrospectionController.js +93 -0
  88. package/dist/accessi-module/Controllers/AccessiIntrospectionController.js.map +1 -0
  89. package/dist/accessi-module/Dtos/IntrospectionDtos.d.ts +16 -0
  90. package/dist/accessi-module/Dtos/IntrospectionDtos.js +59 -0
  91. package/dist/accessi-module/Dtos/IntrospectionDtos.js.map +1 -0
  92. package/dist/accessi-module/Dtos/index.d.ts +1 -0
  93. package/dist/accessi-module/Dtos/index.js +1 -0
  94. package/dist/accessi-module/Dtos/index.js.map +1 -1
  95. package/dist/accessi-module/Services/AuthService/AuthService.d.ts +13 -0
  96. package/dist/accessi-module/Services/AuthService/AuthService.js +42 -10
  97. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  98. package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js +11 -4
  99. package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js.map +1 -1
  100. package/dist/accessi-module/database-updates/menuScopeSeed.d.ts +2 -2
  101. package/dist/accessi-module/database-updates/menuScopeSeed.js +101 -53
  102. package/dist/accessi-module/database-updates/menuScopeSeed.js.map +1 -1
  103. package/dist/accessi-module/database-updates/menuScopeSeeder.js +1 -1
  104. package/dist/accessi-module/database-updates/menuScopeSeeder.js.map +1 -1
  105. package/dist/accessi-module/index.d.ts +16 -3
  106. package/dist/accessi-module/index.js +15 -19
  107. package/dist/accessi-module/index.js.map +1 -1
  108. package/dist/accessi-module/jwt/jwt.strategy.js +5 -5
  109. package/dist/accessi-module/jwt/jwt.strategy.js.map +1 -1
  110. package/dist/accessi-module/middleware/authenticateGen.js +5 -5
  111. package/dist/accessi-module/middleware/authenticateGen.js.map +1 -1
  112. package/dist/accessi-module/security/jwtTokenOptions.d.ts +15 -0
  113. package/dist/accessi-module/security/jwtTokenOptions.js +44 -0
  114. package/dist/accessi-module/security/jwtTokenOptions.js.map +1 -0
  115. package/dist/accessi-module/startupLog.d.ts +3 -2
  116. package/dist/accessi-module/startupLog.js +8 -4
  117. package/dist/accessi-module/startupLog.js.map +1 -1
  118. package/dist/allegati-module/index.js +2 -2
  119. package/dist/allegati-module/index.js.map +1 -1
  120. package/dist/angular-components/aree-disponibili/demo.html +4 -4
  121. package/dist/emilsoftware-utilities-2.0.0-dev.67.tgz +0 -0
  122. package/dist/firebird-compat.d.ts +27 -0
  123. package/dist/firebird-compat.js +52 -2
  124. package/dist/firebird-compat.js.map +1 -1
  125. package/dist/index.d.ts +2 -1
  126. package/dist/index.js +2 -1
  127. package/dist/index.js.map +1 -1
  128. package/dist/initEmilsoftwareModule.d.ts +11 -1
  129. package/dist/initEmilsoftwareModule.js +11 -23
  130. package/dist/initEmilsoftwareModule.js.map +1 -1
  131. package/dist/openapi/OpenApiConfig.d.ts +68 -0
  132. package/dist/openapi/OpenApiConfig.js +157 -0
  133. package/dist/openapi/OpenApiConfig.js.map +1 -0
  134. package/dist/openapi/OpenApiMerge.d.ts +15 -0
  135. package/dist/openapi/OpenApiMerge.js +119 -0
  136. package/dist/openapi/OpenApiMerge.js.map +1 -0
  137. package/integration-prompt.md +481 -0
  138. package/package.json +89 -6
  139. package/dist/accessi-module/Console/generated/accessiApi.js.map +0 -1
  140. package/dist/emilsoftware-utilities-2.0.0-dev.65.tgz +0 -0
  141. package/dist/swagger/SwaggerConfig.d.ts +0 -8
  142. package/dist/swagger/SwaggerConfig.js +0 -41
  143. package/dist/swagger/SwaggerConfig.js.map +0 -1
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Entry point server Accessi.
3
+ *
4
+ * Questo e l'unico entry point che puo importare database, SMTP, migrazioni,
5
+ * console e servizi Nest completi. I backend fratelli devono usare
6
+ * `accessi/resource-server` e `accessi/client`.
7
+ */
8
+ export * from "../../accessi-module";
9
+ export type { AccessiOptions as AccessiServerOptions } from "../../accessi-module";
10
+ export { createAccessiOptionsFromEnv, createCatalogScriptsOptionsFromEnv, } from "../../accessi-module/database-updates/accessiOptionsFromEnv";
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createCatalogScriptsOptionsFromEnv = exports.createAccessiOptionsFromEnv = void 0;
18
+ /**
19
+ * Entry point server Accessi.
20
+ *
21
+ * Questo e l'unico entry point che puo importare database, SMTP, migrazioni,
22
+ * console e servizi Nest completi. I backend fratelli devono usare
23
+ * `accessi/resource-server` e `accessi/client`.
24
+ */
25
+ __exportStar(require("../../accessi-module"), exports);
26
+ var accessiOptionsFromEnv_1 = require("../../accessi-module/database-updates/accessiOptionsFromEnv");
27
+ Object.defineProperty(exports, "createAccessiOptionsFromEnv", { enumerable: true, get: function () { return accessiOptionsFromEnv_1.createAccessiOptionsFromEnv; } });
28
+ Object.defineProperty(exports, "createCatalogScriptsOptionsFromEnv", { enumerable: true, get: function () { return accessiOptionsFromEnv_1.createCatalogScriptsOptionsFromEnv; } });
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accessi/server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,uDAAqC;AAErC,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 type { AccessiOptions as AccessiServerOptions } from \"../../accessi-module\";\nexport {\n createAccessiOptionsFromEnv,\n createCatalogScriptsOptionsFromEnv,\n} from \"../../accessi-module/database-updates/accessiOptionsFromEnv\";\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Asset UI frameworkless pubblicati con il pacchetto.
3
+ *
4
+ * I componenti sono Web Components senza dipendenze runtime: vanno inclusi
5
+ * come `<script>` o copiati nel build dell'app. I percorsi sono relativi alla
6
+ * root del pacchetto distribuito (`dist/`).
7
+ */
8
+ export declare const ACCESSI_UI_ASSETS: {
9
+ readonly areeDisponibili: "angular-components/aree-disponibili/aree-disponibili.js";
10
+ };
11
+ export type AccessiUiComponent = keyof typeof ACCESSI_UI_ASSETS;
12
+ /** Restituisce il path dell'asset UI relativo a `dist/`. */
13
+ export declare function getAccessiUiAssetPath(component: AccessiUiComponent): string;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ACCESSI_UI_ASSETS = void 0;
4
+ exports.getAccessiUiAssetPath = getAccessiUiAssetPath;
5
+ /**
6
+ * Asset UI frameworkless pubblicati con il pacchetto.
7
+ *
8
+ * I componenti sono Web Components senza dipendenze runtime: vanno inclusi
9
+ * come `<script>` o copiati nel build dell'app. I percorsi sono relativi alla
10
+ * root del pacchetto distribuito (`dist/`).
11
+ */
12
+ exports.ACCESSI_UI_ASSETS = {
13
+ areeDisponibili: "angular-components/aree-disponibili/aree-disponibili.js",
14
+ };
15
+ /** Restituisce il path dell'asset UI relativo a `dist/`. */
16
+ function getAccessiUiAssetPath(component) {
17
+ return exports.ACCESSI_UI_ASSETS[component];
18
+ }
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accessi/ui/index.ts"],"names":[],"mappings":";;;AAcA,sDAEC;AAhBD;;;;;;GAMG;AACU,QAAA,iBAAiB,GAAG;IAC/B,eAAe,EAAE,yDAAyD;CAClE,CAAC;AAIX,4DAA4D;AAC5D,SAAgB,qBAAqB,CAAC,SAA6B;IACjE,OAAO,yBAAiB,CAAC,SAAS,CAAC,CAAC;AACtC,CAAC","sourcesContent":["/**\n * Asset UI frameworkless pubblicati con il pacchetto.\n *\n * I componenti sono Web Components senza dipendenze runtime: vanno inclusi\n * come `<script>` o copiati nel build dell'app. I percorsi sono relativi alla\n * root del pacchetto distribuito (`dist/`).\n */\nexport const ACCESSI_UI_ASSETS = {\n areeDisponibili: \"angular-components/aree-disponibili/aree-disponibili.js\",\n} as const;\n\nexport type AccessiUiComponent = keyof typeof ACCESSI_UI_ASSETS;\n\n/** Restituisce il path dell'asset UI relativo a `dist/`. */\nexport function getAccessiUiAssetPath(component: AccessiUiComponent): string {\n return ACCESSI_UI_ASSETS[component];\n}\n"]}
@@ -6,10 +6,25 @@
6
6
  */
7
7
  import { Options } from 'node-firebird';
8
8
  import { DynamicModule } from '@nestjs/common';
9
+ import type { LoggerService } from '@nestjs/common';
10
+ import type { OpenApiSetupOptions } from '../openapi/OpenApiConfig';
11
+ import type { AccessiAccessTokenAlgorithm } from './security/jwtTokenOptions';
9
12
  /** JWT emesso da Accessi dopo un login locale o SSO. Non riutilizzare il segreto del provider SSO. */
10
13
  export interface JwtOptions {
11
14
  /** Segreto simmetrico per firmare e verificare i JWT Accessi; deve essere unico per ambiente. */
12
15
  secret: string;
16
+ /** Issuer (`iss`) opzionale degli access token. Consigliato in una topologia multi-servizio. */
17
+ issuer?: string;
18
+ /** Audience (`aud`) opzionale degli access token. */
19
+ audience?: string | string[];
20
+ /** Algoritmo degli access token. Default `HS256` per retrocompatibilita. */
21
+ algorithm?: AccessiAccessTokenAlgorithm;
22
+ /** Chiave privata PEM per RS/ES. Obbligatoria quando `algorithm` non e HS*. */
23
+ privateKey?: string;
24
+ /** Chiave pubblica PEM per la verifica RS/ES. */
25
+ publicKey?: string;
26
+ /** Key id pubblicato nella JWKS, per rotazione chiavi. */
27
+ keyId?: string;
13
28
  /** Durata compatibile con `jsonwebtoken`, per esempio `24h` o `3600s`. */
14
29
  expiresIn: string;
15
30
  }
@@ -178,8 +193,17 @@ export interface AccessiOptions {
178
193
  systemSeeds?: SystemSeedsOptions;
179
194
  /** Bootstrap del primo admin tramite token casuale stampato a ogni avvio. */
180
195
  adminBootstrap?: AdminBootstrapOptions;
181
- /** Base URL pubblica usata solo per loggare gli indirizzi (console, swagger, API). */
196
+ /** Base URL pubblica usata solo per loggare gli indirizzi (console, OpenAPI, API). */
182
197
  publicBaseUrl?: string;
198
+ /**
199
+ * Documentazione OpenAPI del modulo. Default invariati:
200
+ * UI `accessi/openapi`, JSON `accessi/openapi.json`.
201
+ */
202
+ openapi?: OpenApiSetupOptions;
203
+ /** Logger Nest usato dal sotto-app Accessi. Se assente Nest usa il proprio logger. */
204
+ logger?: LoggerService;
205
+ /** Buffer dei log di bootstrap del sotto-app. Default `true` (comportamento storico). */
206
+ bufferLogs?: boolean;
183
207
  }
184
208
  export declare class AccessiModule {
185
209
  /**
@@ -33,6 +33,7 @@ const ServiceTokenController_1 = require("./Controllers/ServiceTokenController")
33
33
  const ServiceTokenService_1 = require("./Services/ServiceTokenService/ServiceTokenService");
34
34
  const serviceTokenGuard_1 = require("./security/serviceTokenGuard");
35
35
  const SchemaController_1 = require("./Controllers/SchemaController");
36
+ const AccessiIntrospectionController_1 = require("./Controllers/AccessiIntrospectionController");
36
37
  const SchemaExportService_1 = require("./Services/SchemaExportService/SchemaExportService");
37
38
  const AdminBootstrapService_1 = require("./Services/AdminBootstrapService/AdminBootstrapService");
38
39
  const emailConfiguration_1 = require("./security/emailConfiguration");
@@ -101,6 +102,7 @@ exports.AccessiModule = AccessiModule = AccessiModule_1 = __decorate([
101
102
  ConsoleEntryController_1.ConsoleEntryController,
102
103
  ServiceTokenController_1.ServiceTokenController,
103
104
  SchemaController_1.SchemaController,
105
+ AccessiIntrospectionController_1.AccessiIntrospectionController,
104
106
  ],
105
107
  providers: [AuthService_1.AuthService, TwoFactorService_1.TwoFactorService, UserService_1.UserService, EmailService_1.EmailService, PermissionService_1.PermissionService, FiltriService_1.FiltriService, ConfiguratorService_1.ConfiguratorService, jwt_strategy_1.JwtSimpleGuard, authenticateGen_1.AuthenticateGenService, AccessiDatabaseUpdater_1.AccessiDatabaseUpdater, FederatedAuthService_1.FederatedAuthService, ServiceTokenService_1.ServiceTokenService, serviceTokenGuard_1.ServiceTokenGuard, SchemaExportService_1.SchemaExportService, AdminBootstrapService_1.AdminBootstrapService],
106
108
  exports: [AuthService_1.AuthService, UserService_1.UserService, EmailService_1.EmailService, PermissionService_1.PermissionService, FiltriService_1.FiltriService, ConfiguratorService_1.ConfiguratorService, jwt_strategy_1.JwtSimpleGuard, authenticateGen_1.AuthenticateGenService, FederatedAuthService_1.FederatedAuthService, ServiceTokenService_1.ServiceTokenService, serviceTokenGuard_1.ServiceTokenGuard, SchemaExportService_1.SchemaExportService, AdminBootstrapService_1.AdminBootstrapService],
@@ -1 +1 @@
1
- {"version":3,"file":"AccessiModule.js","sourceRoot":"","sources":["../../src/accessi-module/AccessiModule.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,mFAAgF;AAQhF,2CAA+D;AAC/D,oEAAiE;AACjE,uEAAoE;AACpE,sFAAmF;AACnF,oEAAiE;AACjE,mEAAgE;AAChE,iEAA8D;AAC9D,6EAA0E;AAC1E,iEAA8D;AAC9D,0EAAuE;AACvE,qEAAkE;AAClE,iFAA8E;AAC9E,4FAAyF;AACzF,qDAAoD;AACpD,kEAAsE;AACtE,sFAAmF;AACnF,gFAA6E;AAC7E,sFAAmF;AACnF,qFAAkF;AAClF,iFAA8E;AAC9E,iFAA8E;AAC9E,4FAAyF;AACzF,oEAAiE;AACjE,qEAAkE;AAClE,4FAAyF;AACzF,kGAA+F;AAC/F,sEAAsE;AAgN/D,IAAM,aAAa,qBAAnB,MAAM,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,OAAuB;QACpC,yFAAyF;QACzF,IAAA,0CAAqB,EAAC,OAAO,CAAC,CAAC;QAC/B,OAAO;YACL,MAAM,EAAE,eAAa;YACrB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,iBAAiB;oBAC1B,QAAQ,EAAE,OAAO;iBAClB;gBACD,yBAAW;gBACX,mCAAgB;gBAChB,yBAAW;gBACX,2BAAY;gBACZ,qCAAiB;gBACjB,6BAAa;gBACb,yCAAmB;gBACnB,6BAAc;gBACd,wCAAsB;gBACtB,+CAAsB;gBACtB,2CAAoB;gBACpB,yCAAmB;gBACnB,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB;aAAC;YAChE,OAAO,EAAE;gBACP,iBAAiB;gBACjB,yBAAW;gBACX,yBAAW;gBACX,2BAAY;gBACZ,qCAAiB;gBACjB,6BAAa;gBACb,yCAAmB;gBACnB,6BAAc;gBACd,wCAAsB;gBACtB,2CAAoB;gBACpB,yCAAmB;gBACnB,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB;aAAC;SACjE,CAAC;IACJ,CAAC;CACF,CAAA;AA9CY,sCAAa;wBAAb,aAAa;IAlBzB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,WAAW,EAAE;YACX,iCAAe;YACf,+BAAc;YACd,2CAAoB;YACpB,+BAAc;YACd,mCAAgB;YAChB,+CAAsB;YACtB,iDAAuB;YACvB,mDAAwB;YACxB,+CAAsB;YACtB,+CAAsB;YACtB,mCAAgB;SACjB;QACD,SAAS,EAAE,CAAC,yBAAW,EAAE,mCAAgB,EAAE,yBAAW,EAAE,2BAAY,EAAE,qCAAiB,EAAE,6BAAa,EAAE,yCAAmB,EAAE,6BAAc,EAAE,wCAAsB,EAAE,+CAAsB,EAAE,2CAAoB,EAAE,yCAAmB,EAAE,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB,CAAC;QACtS,OAAO,EAAE,CAAC,yBAAW,EAAE,yBAAW,EAAE,2BAAY,EAAE,qCAAiB,EAAE,6BAAa,EAAE,yCAAmB,EAAE,6BAAc,EAAE,wCAAsB,EAAE,2CAAoB,EAAE,yCAAmB,EAAE,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB,CAAC;KAC3P,CAAC;GACW,aAAa,CA8CzB","sourcesContent":["import { TwoFactorService } from './Services/TwoFactorService/TwoFactorService';\n/**\n * Modulo che gestisce le operazioni di accesso degli utenti, incluse le rotte, il controller e il modello.\n *\n * @module AccessiModule\n * @author mttdev382\n */\nimport { Options } from 'node-firebird';\nimport { DynamicModule, Global, Module } from '@nestjs/common';\nimport { AuthService } from './Services/AuthService/AuthService';\nimport { EmailService } from './Services/EmailService/EmailService';\nimport { PermissionService } from './Services/PermissionService/PermissionService';\nimport { UserService } from './Services/UserService/UserService';\nimport { EmailController } from './Controllers/EmailController';\nimport { AuthController } from './Controllers/AuthController';\nimport { PermissionController } from './Controllers/PermissionController';\nimport { UserController } from './Controllers/UserController';\nimport { FiltriService } from './Services/FiltriService/FiltriService';\nimport { FiltriController } from './Controllers/FiltriController';\nimport { ConfiguratorController } from './Controllers/ConfiguratorController';\nimport { ConfiguratorService } from './Services/ConfiguratorService/ConfiguratorService';\nimport { JwtSimpleGuard } from './jwt/jwt.strategy';\nimport { AuthenticateGenService } from './middleware/authenticateGen';\nimport { AccessiDatabaseUpdater } from './database-updates/AccessiDatabaseUpdater';\nimport { FederatedAuthService } from './federated-auth/FederatedAuthService';\nimport { FederatedAuthController } from './federated-auth/FederatedAuthController';\nimport { AccessiConsoleController } from './Controllers/AccessiConsoleController';\nimport { ConsoleEntryController } from './Controllers/ConsoleEntryController';\nimport { ServiceTokenController } from './Controllers/ServiceTokenController';\nimport { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';\nimport { ServiceTokenGuard } from './security/serviceTokenGuard';\nimport { SchemaController } from './Controllers/SchemaController';\nimport { SchemaExportService } from './Services/SchemaExportService/SchemaExportService';\nimport { AdminBootstrapService } from './Services/AdminBootstrapService/AdminBootstrapService';\nimport { assertEmailConfigured } from './security/emailConfiguration';\n\n/** JWT emesso da Accessi dopo un login locale o SSO. Non riutilizzare il segreto del provider SSO. */\nexport interface JwtOptions {\n /** Segreto simmetrico per firmare e verificare i JWT Accessi; deve essere unico per ambiente. */\n secret: string;\n /** Durata compatibile con `jsonwebtoken`, per esempio `24h` o `3600s`. */\n expiresIn: string;\n}\n\n/** Parametri SMTP usati esclusivamente per impostazione e reset della password. */\nexport interface EmailOptions {\n host: string;\n port: number;\n secure: boolean;\n requireTLS : boolean;\n tls: {\n rejectUnauthorized: boolean,\n }\n from: string;\n auth: {\n user: string;\n pass: string;\n };\n}\n\n/** Configura campi applicativi aggiuntivi restituiti insieme al profilo Accessi. */\nexport interface ExtensionFieldsOptions {\n /** Connessione al database contenente la tabella estesa. */\n databaseOptions: Options;\n /** Chiave con cui i dati saranno esposti nella risposta. */\n objectKey: string;\n /** Tabella applicativa contenente i dati estesi. */\n tableName: string;\n /** Colonne consentite; non valorizzarle mai con input HTTP. */\n tableFields: string[];\n /** Colonna correlata a `UTENTI.CODUTE`. */\n tableJoinFieldName: string;\n}\n\n/** Limite per una singola categoria di endpoint pubblici. */\nexport interface PublicAuthRateLimitRuleOptions {\n windowMs: number;\n maxAttempts: number;\n}\n\n/** Protezione in-memory degli endpoint pubblici. In cluster aggiungere un limite anche al gateway. */\nexport interface PublicAuthRateLimitOptions {\n enabled?: boolean;\n login?: PublicAuthRateLimitRuleOptions;\n register?: PublicAuthRateLimitRuleOptions;\n passwordResetEmail?: PublicAuthRateLimitRuleOptions;\n passwordResetConfirm?: PublicAuthRateLimitRuleOptions;\n getUserByToken?: PublicAuthRateLimitRuleOptions;\n twoFactorVerify?: PublicAuthRateLimitRuleOptions;\n twoFactorResend?: PublicAuthRateLimitRuleOptions;\n adminBootstrap?: PublicAuthRateLimitRuleOptions;\n consoleEntry?: PublicAuthRateLimitRuleOptions;\n}\n\n/**\n * Bootstrap del primo admin. Se abilitato, Accessi genera un token casuale a ogni avvio del\n * processo, lo stampa nel log e lo accetta sull'endpoint pubblico `POST /accessi/auth/bootstrap-admin`.\n * Serve a creare il primo utente admin senza email di conferma e senza un admin gia esistente.\n */\nexport interface AdminBootstrapOptions {\n /** Abilita il bootstrap admin. Default `false`. */\n enabled?: boolean;\n /** Crea l'utente come superutente oltre che admin. Default `true`. */\n superUser?: boolean;\n}\n\n/** Controls the legacy public username/password registration endpoint. Disabled by default. */\nexport interface PublicRegistrationOptions {\n /** Enables POST /accessi/user/register. Applications that used it before must opt in explicitly. */\n enabled?: boolean;\n}\n\n/** Configurazione dei token di servizio (macchina-a-macchina, senza utente). */\nexport interface ServiceTokenOptions {\n /** Abilita gli endpoint amministrativi dei service token. Default `true`. */\n enabled?: boolean;\n /** Durata predefinita in giorni quando non e indicata una scadenza esplicita. Default: nessuna scadenza. */\n defaultTtlDays?: number;\n}\n\n/** Cosa fare quando uno script gia applicato presenta un checksum diverso (file modificato). */\nexport type CatalogScriptChecksumPolicy = 'error' | 'warn' | 'reapply';\n\n/**\n * Catalog migrations applicative (menu, ruoli, tipi, cataloghi). Gli script `.sql` vivono nel\n * backend ospitante (versionati in repository) e vengono eseguiti da Accessi dopo la\n * riconciliazione dello schema. Ogni script e tracciato nella tabella `ACCESSI_CATALOG_SCRIPT`,\n * quindi viene applicato una sola volta per database (idempotenza lato DB).\n */\nexport interface SystemSeedsOptions {\n /** `false` disabilita il seed automatico, ad esempio per ambienti gestiti manualmente. */\n enabled?: boolean;\n}\n\nexport interface CatalogScriptsOptions {\n /** Abilita l'esecuzione automatica al bootstrap. Default `false`. */\n enabled?: boolean;\n /** Cartella contenente gli script `.sql` (assoluta o relativa alla working directory). */\n folder: string;\n /** Cosa fare se il checksum di uno script applicato e cambiato. Default `error`. */\n onChecksumMismatch?: CatalogScriptChecksumPolicy;\n /** Se `true` (default) un errore interrompe l'esecuzione e non avanza allo script successivo. */\n stopOnError?: boolean;\n /** Globi/estensioni inclusi. Default: tutti i `.sql`. */\n include?: string[];\n /** Numero massimo di script eseguiti in un singolo run (protezione operativa). Default: illimitato. */\n maxScriptsPerRun?: number;\n /**\n * Permette statement DDL/schema negli script. Default `false`: le catalog migrations possono\n * modificare solo righe. Sconsigliato: lo schema e di competenza di `db:update:accessi`.\n */\n allowDdl?: boolean;\n}\n\n/**\n * Enables Accessi's provider-agnostic federated identity capability.\n *\n * Accessi never validates Azure, Google, Apple, SAML, or other provider tokens.\n * The hosting backend must validate its own SSO flow and pass only a verified\n * `{ provider, subject }` identity to FederatedAuthService.\n */\nexport interface FederatedAuthenticationOptions {\n /** Attiva le API SSO generiche; con `false` il comportamento password storico rimane invariato. */\n enabled?: boolean;\n /** @deprecated Schema management is centralized in autoUpdateDatabase. This legacy flag no longer starts separate SSO migrations. */\n autoUpdateSchema?: boolean;\n /** Allows a backend to create a user from a verified external identity without a master user. Default false. */\n allowSelfRegistration?: boolean;\n}\n\n/**\n * Configurazione completa del modulo Accessi, fornita esclusivamente dal backend ospitante.\n * Segreti JWT, SMTP, database e le configurazioni tecniche dei provider SSO non devono mai raggiungere il browser.\n */\nexport interface AccessiOptions {\n /** Connessione Firebird alla base dati Accessi. */\n databaseOptions: Options;\n /**\n * Basepath del sito es: 'http://www.il-mio-sito.it/nome-progetto(se c'è)'\n */\n confirmationEmailUrl: string;\n /**\n * Percorso della pagina di reset personalizzata es. http://localhost:4200/#/admin/reset-password\n * N.B si sostituisce al confirmationMailURl\n */\n customResetPage?: string;\n /** URL di ritorno della pagina standard di reset dopo il completamento. */\n confirmationEmailReturnUrl: string;\n /** Prefisso applicativo facoltativo trasmesso alla pagina standard di reset. */\n confirmationEmailPrefix?: string;\n /** Chiave riservata alla compatibilita e alla migrazione delle password legacy cifrate. */\n encryptionKey: string;\n /** Abilita gli utenti fittizi `admin` e `demo` solo fuori produzione. */\n mockDemoUser: boolean;\n /** Attiva il controllo della scadenza password durante il login locale. */\n passwordExpiration?: boolean;\n /** Giorni di validita della password; quando assente Accessi usa 90 giorni. */\n passwordExpirationDays?: number;\n /** Esegue al bootstrap la conversione non distruttiva delle password legacy. Default `true`. */\n legacyPasswordMigrationOnStartup?: boolean;\n /** Riconcilia lo schema al bootstrap (default `true`). Con `false` verifica comunque la compatibilita in sola lettura e blocca avvii incompatibili. */\n autoUpdateDatabase?: boolean;\n /** Parametri JWT dei token emessi da Accessi. */\n jwtOptions: JwtOptions;\n /** Parametri SMTP per reset e attivazione password. */\n emailOptions: EmailOptions;\n publicAuthRateLimit?: PublicAuthRateLimitOptions;\n /** Public local registration is deliberately opt-in. Existing applications must set enabled: true to retain it. */\n publicRegistration?: PublicRegistrationOptions;\n /** Optional provider-agnostic SSO support. Provider-specific configuration remains in the hosting backend. */\n federatedAuthentication?: FederatedAuthenticationOptions;\n extensionFieldsOptions?: ExtensionFieldsOptions[];\n /** Token di servizio per chiamate tecniche macchina-a-macchina. Abilitati di default. */\n serviceTokens?: ServiceTokenOptions;\n /** Catalog migrations applicative eseguite dopo la riconciliazione dello schema. */\n catalogScripts?: CatalogScriptsOptions;\n /** Seed di sistema eseguiti dopo la riconciliazione dello schema. Default: abilitati. */\n systemSeeds?: SystemSeedsOptions;\n /** Bootstrap del primo admin tramite token casuale stampato a ogni avvio. */\n adminBootstrap?: AdminBootstrapOptions;\n /** Base URL pubblica usata solo per loggare gli indirizzi (console, swagger, API). */\n publicBaseUrl?: string;\n}\n\n@Global()\n@Module({\n controllers: [\n EmailController,\n AuthController,\n PermissionController,\n UserController,\n FiltriController,\n ConfiguratorController,\n FederatedAuthController,\n AccessiConsoleController,\n ConsoleEntryController,\n ServiceTokenController,\n SchemaController,\n ],\n providers: [AuthService, TwoFactorService, UserService, EmailService, PermissionService, FiltriService, ConfiguratorService, JwtSimpleGuard, AuthenticateGenService, AccessiDatabaseUpdater, FederatedAuthService, ServiceTokenService, ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n exports: [AuthService, UserService, EmailService, PermissionService, FiltriService, ConfiguratorService, JwtSimpleGuard, AuthenticateGenService, FederatedAuthService, ServiceTokenService, ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n})\nexport class AccessiModule {\n /**\n * Registra Accessi nel contenitore Nest del backend ospitante.\n *\n * Usare questo metodo una sola volta nella composizione dell'applicazione. Per un backend\n * Express esistente usare invece `initializeAccessiModule`, che crea e monta l'host Nest interno.\n * La configurazione viene registrata con il token DI `ACCESSI_OPTIONS` ed e condivisa dai servizi.\n */\n static forRoot(options: AccessiOptions): DynamicModule {\n // Il servizio email e obbligatorio: reset password, 2FA e creazione utente ne dipendono.\n assertEmailConfigured(options);\n return {\n module: AccessiModule,\n providers: [\n {\n provide: 'ACCESSI_OPTIONS',\n useValue: options,\n },\n AuthService,\n TwoFactorService,\n UserService,\n EmailService,\n PermissionService,\n FiltriService,\n ConfiguratorService,\n JwtSimpleGuard,\n AuthenticateGenService,\n AccessiDatabaseUpdater,\n FederatedAuthService,\n ServiceTokenService,\n ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n exports: [\n 'ACCESSI_OPTIONS',\n AuthService,\n UserService,\n EmailService,\n PermissionService,\n FiltriService,\n ConfiguratorService,\n JwtSimpleGuard,\n AuthenticateGenService,\n FederatedAuthService,\n ServiceTokenService,\n ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n };\n }\n}\n"]}
1
+ {"version":3,"file":"AccessiModule.js","sourceRoot":"","sources":["../../src/accessi-module/AccessiModule.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,mFAAgF;AAQhF,2CAA+D;AAI/D,oEAAiE;AACjE,uEAAoE;AACpE,sFAAmF;AACnF,oEAAiE;AACjE,mEAAgE;AAChE,iEAA8D;AAC9D,6EAA0E;AAC1E,iEAA8D;AAC9D,0EAAuE;AACvE,qEAAkE;AAClE,iFAA8E;AAC9E,4FAAyF;AACzF,qDAAoD;AACpD,kEAAsE;AACtE,sFAAmF;AACnF,gFAA6E;AAC7E,sFAAmF;AACnF,qFAAkF;AAClF,iFAA8E;AAC9E,iFAA8E;AAC9E,4FAAyF;AACzF,oEAAiE;AACjE,qEAAkE;AAClE,iGAA8F;AAC9F,4FAAyF;AACzF,kGAA+F;AAC/F,sEAAsE;AAsO/D,IAAM,aAAa,qBAAnB,MAAM,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,OAAuB;QACpC,yFAAyF;QACzF,IAAA,0CAAqB,EAAC,OAAO,CAAC,CAAC;QAC/B,OAAO;YACL,MAAM,EAAE,eAAa;YACrB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,iBAAiB;oBAC1B,QAAQ,EAAE,OAAO;iBAClB;gBACD,yBAAW;gBACX,mCAAgB;gBAChB,yBAAW;gBACX,2BAAY;gBACZ,qCAAiB;gBACjB,6BAAa;gBACb,yCAAmB;gBACnB,6BAAc;gBACd,wCAAsB;gBACtB,+CAAsB;gBACtB,2CAAoB;gBACpB,yCAAmB;gBACnB,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB;aAAC;YAChE,OAAO,EAAE;gBACP,iBAAiB;gBACjB,yBAAW;gBACX,yBAAW;gBACX,2BAAY;gBACZ,qCAAiB;gBACjB,6BAAa;gBACb,yCAAmB;gBACnB,6BAAc;gBACd,wCAAsB;gBACtB,2CAAoB;gBACpB,yCAAmB;gBACnB,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB;aAAC;SACjE,CAAC;IACJ,CAAC;CACF,CAAA;AA9CY,sCAAa;wBAAb,aAAa;IAnBzB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,WAAW,EAAE;YACX,iCAAe;YACf,+BAAc;YACd,2CAAoB;YACpB,+BAAc;YACd,mCAAgB;YAChB,+CAAsB;YACtB,iDAAuB;YACvB,mDAAwB;YACxB,+CAAsB;YACtB,+CAAsB;YACtB,mCAAgB;YAChB,+DAA8B;SAC/B;QACD,SAAS,EAAE,CAAC,yBAAW,EAAE,mCAAgB,EAAE,yBAAW,EAAE,2BAAY,EAAE,qCAAiB,EAAE,6BAAa,EAAE,yCAAmB,EAAE,6BAAc,EAAE,wCAAsB,EAAE,+CAAsB,EAAE,2CAAoB,EAAE,yCAAmB,EAAE,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB,CAAC;QACtS,OAAO,EAAE,CAAC,yBAAW,EAAE,yBAAW,EAAE,2BAAY,EAAE,qCAAiB,EAAE,6BAAa,EAAE,yCAAmB,EAAE,6BAAc,EAAE,wCAAsB,EAAE,2CAAoB,EAAE,yCAAmB,EAAE,qCAAiB,EAAE,yCAAmB,EAAE,6CAAqB,CAAC;KAC3P,CAAC;GACW,aAAa,CA8CzB","sourcesContent":["import { TwoFactorService } from './Services/TwoFactorService/TwoFactorService';\n/**\n * Modulo che gestisce le operazioni di accesso degli utenti, incluse le rotte, il controller e il modello.\n *\n * @module AccessiModule\n * @author mttdev382\n */\nimport { Options } from 'node-firebird';\nimport { DynamicModule, Global, Module } from '@nestjs/common';\nimport type { LoggerService } from '@nestjs/common';\nimport type { OpenApiSetupOptions } from '../openapi/OpenApiConfig';\nimport type { AccessiAccessTokenAlgorithm } from './security/jwtTokenOptions';\nimport { AuthService } from './Services/AuthService/AuthService';\nimport { EmailService } from './Services/EmailService/EmailService';\nimport { PermissionService } from './Services/PermissionService/PermissionService';\nimport { UserService } from './Services/UserService/UserService';\nimport { EmailController } from './Controllers/EmailController';\nimport { AuthController } from './Controllers/AuthController';\nimport { PermissionController } from './Controllers/PermissionController';\nimport { UserController } from './Controllers/UserController';\nimport { FiltriService } from './Services/FiltriService/FiltriService';\nimport { FiltriController } from './Controllers/FiltriController';\nimport { ConfiguratorController } from './Controllers/ConfiguratorController';\nimport { ConfiguratorService } from './Services/ConfiguratorService/ConfiguratorService';\nimport { JwtSimpleGuard } from './jwt/jwt.strategy';\nimport { AuthenticateGenService } from './middleware/authenticateGen';\nimport { AccessiDatabaseUpdater } from './database-updates/AccessiDatabaseUpdater';\nimport { FederatedAuthService } from './federated-auth/FederatedAuthService';\nimport { FederatedAuthController } from './federated-auth/FederatedAuthController';\nimport { AccessiConsoleController } from './Controllers/AccessiConsoleController';\nimport { ConsoleEntryController } from './Controllers/ConsoleEntryController';\nimport { ServiceTokenController } from './Controllers/ServiceTokenController';\nimport { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';\nimport { ServiceTokenGuard } from './security/serviceTokenGuard';\nimport { SchemaController } from './Controllers/SchemaController';\nimport { AccessiIntrospectionController } from './Controllers/AccessiIntrospectionController';\nimport { SchemaExportService } from './Services/SchemaExportService/SchemaExportService';\nimport { AdminBootstrapService } from './Services/AdminBootstrapService/AdminBootstrapService';\nimport { assertEmailConfigured } from './security/emailConfiguration';\n\n/** JWT emesso da Accessi dopo un login locale o SSO. Non riutilizzare il segreto del provider SSO. */\nexport interface JwtOptions {\n /** Segreto simmetrico per firmare e verificare i JWT Accessi; deve essere unico per ambiente. */\n secret: string;\n /** Issuer (`iss`) opzionale degli access token. Consigliato in una topologia multi-servizio. */\n issuer?: string;\n /** Audience (`aud`) opzionale degli access token. */\n audience?: string | string[];\n /** Algoritmo degli access token. Default `HS256` per retrocompatibilita. */\n algorithm?: AccessiAccessTokenAlgorithm;\n /** Chiave privata PEM per RS/ES. Obbligatoria quando `algorithm` non e HS*. */\n privateKey?: string;\n /** Chiave pubblica PEM per la verifica RS/ES. */\n publicKey?: string;\n /** Key id pubblicato nella JWKS, per rotazione chiavi. */\n keyId?: string;\n /** Durata compatibile con `jsonwebtoken`, per esempio `24h` o `3600s`. */\n expiresIn: string;\n}\n\n/** Parametri SMTP usati esclusivamente per impostazione e reset della password. */\nexport interface EmailOptions {\n host: string;\n port: number;\n secure: boolean;\n requireTLS : boolean;\n tls: {\n rejectUnauthorized: boolean,\n }\n from: string;\n auth: {\n user: string;\n pass: string;\n };\n}\n\n/** Configura campi applicativi aggiuntivi restituiti insieme al profilo Accessi. */\nexport interface ExtensionFieldsOptions {\n /** Connessione al database contenente la tabella estesa. */\n databaseOptions: Options;\n /** Chiave con cui i dati saranno esposti nella risposta. */\n objectKey: string;\n /** Tabella applicativa contenente i dati estesi. */\n tableName: string;\n /** Colonne consentite; non valorizzarle mai con input HTTP. */\n tableFields: string[];\n /** Colonna correlata a `UTENTI.CODUTE`. */\n tableJoinFieldName: string;\n}\n\n/** Limite per una singola categoria di endpoint pubblici. */\nexport interface PublicAuthRateLimitRuleOptions {\n windowMs: number;\n maxAttempts: number;\n}\n\n/** Protezione in-memory degli endpoint pubblici. In cluster aggiungere un limite anche al gateway. */\nexport interface PublicAuthRateLimitOptions {\n enabled?: boolean;\n login?: PublicAuthRateLimitRuleOptions;\n register?: PublicAuthRateLimitRuleOptions;\n passwordResetEmail?: PublicAuthRateLimitRuleOptions;\n passwordResetConfirm?: PublicAuthRateLimitRuleOptions;\n getUserByToken?: PublicAuthRateLimitRuleOptions;\n twoFactorVerify?: PublicAuthRateLimitRuleOptions;\n twoFactorResend?: PublicAuthRateLimitRuleOptions;\n adminBootstrap?: PublicAuthRateLimitRuleOptions;\n consoleEntry?: PublicAuthRateLimitRuleOptions;\n}\n\n/**\n * Bootstrap del primo admin. Se abilitato, Accessi genera un token casuale a ogni avvio del\n * processo, lo stampa nel log e lo accetta sull'endpoint pubblico `POST /accessi/auth/bootstrap-admin`.\n * Serve a creare il primo utente admin senza email di conferma e senza un admin gia esistente.\n */\nexport interface AdminBootstrapOptions {\n /** Abilita il bootstrap admin. Default `false`. */\n enabled?: boolean;\n /** Crea l'utente come superutente oltre che admin. Default `true`. */\n superUser?: boolean;\n}\n\n/** Controls the legacy public username/password registration endpoint. Disabled by default. */\nexport interface PublicRegistrationOptions {\n /** Enables POST /accessi/user/register. Applications that used it before must opt in explicitly. */\n enabled?: boolean;\n}\n\n/** Configurazione dei token di servizio (macchina-a-macchina, senza utente). */\nexport interface ServiceTokenOptions {\n /** Abilita gli endpoint amministrativi dei service token. Default `true`. */\n enabled?: boolean;\n /** Durata predefinita in giorni quando non e indicata una scadenza esplicita. Default: nessuna scadenza. */\n defaultTtlDays?: number;\n}\n\n/** Cosa fare quando uno script gia applicato presenta un checksum diverso (file modificato). */\nexport type CatalogScriptChecksumPolicy = 'error' | 'warn' | 'reapply';\n\n/**\n * Catalog migrations applicative (menu, ruoli, tipi, cataloghi). Gli script `.sql` vivono nel\n * backend ospitante (versionati in repository) e vengono eseguiti da Accessi dopo la\n * riconciliazione dello schema. Ogni script e tracciato nella tabella `ACCESSI_CATALOG_SCRIPT`,\n * quindi viene applicato una sola volta per database (idempotenza lato DB).\n */\nexport interface SystemSeedsOptions {\n /** `false` disabilita il seed automatico, ad esempio per ambienti gestiti manualmente. */\n enabled?: boolean;\n}\n\nexport interface CatalogScriptsOptions {\n /** Abilita l'esecuzione automatica al bootstrap. Default `false`. */\n enabled?: boolean;\n /** Cartella contenente gli script `.sql` (assoluta o relativa alla working directory). */\n folder: string;\n /** Cosa fare se il checksum di uno script applicato e cambiato. Default `error`. */\n onChecksumMismatch?: CatalogScriptChecksumPolicy;\n /** Se `true` (default) un errore interrompe l'esecuzione e non avanza allo script successivo. */\n stopOnError?: boolean;\n /** Globi/estensioni inclusi. Default: tutti i `.sql`. */\n include?: string[];\n /** Numero massimo di script eseguiti in un singolo run (protezione operativa). Default: illimitato. */\n maxScriptsPerRun?: number;\n /**\n * Permette statement DDL/schema negli script. Default `false`: le catalog migrations possono\n * modificare solo righe. Sconsigliato: lo schema e di competenza di `db:update:accessi`.\n */\n allowDdl?: boolean;\n}\n\n/**\n * Enables Accessi's provider-agnostic federated identity capability.\n *\n * Accessi never validates Azure, Google, Apple, SAML, or other provider tokens.\n * The hosting backend must validate its own SSO flow and pass only a verified\n * `{ provider, subject }` identity to FederatedAuthService.\n */\nexport interface FederatedAuthenticationOptions {\n /** Attiva le API SSO generiche; con `false` il comportamento password storico rimane invariato. */\n enabled?: boolean;\n /** @deprecated Schema management is centralized in autoUpdateDatabase. This legacy flag no longer starts separate SSO migrations. */\n autoUpdateSchema?: boolean;\n /** Allows a backend to create a user from a verified external identity without a master user. Default false. */\n allowSelfRegistration?: boolean;\n}\n\n/**\n * Configurazione completa del modulo Accessi, fornita esclusivamente dal backend ospitante.\n * Segreti JWT, SMTP, database e le configurazioni tecniche dei provider SSO non devono mai raggiungere il browser.\n */\nexport interface AccessiOptions {\n /** Connessione Firebird alla base dati Accessi. */\n databaseOptions: Options;\n /**\n * Basepath del sito es: 'http://www.il-mio-sito.it/nome-progetto(se c'è)'\n */\n confirmationEmailUrl: string;\n /**\n * Percorso della pagina di reset personalizzata es. http://localhost:4200/#/admin/reset-password\n * N.B si sostituisce al confirmationMailURl\n */\n customResetPage?: string;\n /** URL di ritorno della pagina standard di reset dopo il completamento. */\n confirmationEmailReturnUrl: string;\n /** Prefisso applicativo facoltativo trasmesso alla pagina standard di reset. */\n confirmationEmailPrefix?: string;\n /** Chiave riservata alla compatibilita e alla migrazione delle password legacy cifrate. */\n encryptionKey: string;\n /** Abilita gli utenti fittizi `admin` e `demo` solo fuori produzione. */\n mockDemoUser: boolean;\n /** Attiva il controllo della scadenza password durante il login locale. */\n passwordExpiration?: boolean;\n /** Giorni di validita della password; quando assente Accessi usa 90 giorni. */\n passwordExpirationDays?: number;\n /** Esegue al bootstrap la conversione non distruttiva delle password legacy. Default `true`. */\n legacyPasswordMigrationOnStartup?: boolean;\n /** Riconcilia lo schema al bootstrap (default `true`). Con `false` verifica comunque la compatibilita in sola lettura e blocca avvii incompatibili. */\n autoUpdateDatabase?: boolean;\n /** Parametri JWT dei token emessi da Accessi. */\n jwtOptions: JwtOptions;\n /** Parametri SMTP per reset e attivazione password. */\n emailOptions: EmailOptions;\n publicAuthRateLimit?: PublicAuthRateLimitOptions;\n /** Public local registration is deliberately opt-in. Existing applications must set enabled: true to retain it. */\n publicRegistration?: PublicRegistrationOptions;\n /** Optional provider-agnostic SSO support. Provider-specific configuration remains in the hosting backend. */\n federatedAuthentication?: FederatedAuthenticationOptions;\n extensionFieldsOptions?: ExtensionFieldsOptions[];\n /** Token di servizio per chiamate tecniche macchina-a-macchina. Abilitati di default. */\n serviceTokens?: ServiceTokenOptions;\n /** Catalog migrations applicative eseguite dopo la riconciliazione dello schema. */\n catalogScripts?: CatalogScriptsOptions;\n /** Seed di sistema eseguiti dopo la riconciliazione dello schema. Default: abilitati. */\n systemSeeds?: SystemSeedsOptions;\n /** Bootstrap del primo admin tramite token casuale stampato a ogni avvio. */\n adminBootstrap?: AdminBootstrapOptions;\n /** Base URL pubblica usata solo per loggare gli indirizzi (console, OpenAPI, API). */\n publicBaseUrl?: string;\n /**\n * Documentazione OpenAPI del modulo. Default invariati:\n * UI `accessi/openapi`, JSON `accessi/openapi.json`.\n */\n openapi?: OpenApiSetupOptions;\n /** Logger Nest usato dal sotto-app Accessi. Se assente Nest usa il proprio logger. */\n logger?: LoggerService;\n /** Buffer dei log di bootstrap del sotto-app. Default `true` (comportamento storico). */\n bufferLogs?: boolean;\n}\n\n@Global()\n@Module({\n controllers: [\n EmailController,\n AuthController,\n PermissionController,\n UserController,\n FiltriController,\n ConfiguratorController,\n FederatedAuthController,\n AccessiConsoleController,\n ConsoleEntryController,\n ServiceTokenController,\n SchemaController,\n AccessiIntrospectionController,\n ],\n providers: [AuthService, TwoFactorService, UserService, EmailService, PermissionService, FiltriService, ConfiguratorService, JwtSimpleGuard, AuthenticateGenService, AccessiDatabaseUpdater, FederatedAuthService, ServiceTokenService, ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n exports: [AuthService, UserService, EmailService, PermissionService, FiltriService, ConfiguratorService, JwtSimpleGuard, AuthenticateGenService, FederatedAuthService, ServiceTokenService, ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n})\nexport class AccessiModule {\n /**\n * Registra Accessi nel contenitore Nest del backend ospitante.\n *\n * Usare questo metodo una sola volta nella composizione dell'applicazione. Per un backend\n * Express esistente usare invece `initializeAccessiModule`, che crea e monta l'host Nest interno.\n * La configurazione viene registrata con il token DI `ACCESSI_OPTIONS` ed e condivisa dai servizi.\n */\n static forRoot(options: AccessiOptions): DynamicModule {\n // Il servizio email e obbligatorio: reset password, 2FA e creazione utente ne dipendono.\n assertEmailConfigured(options);\n return {\n module: AccessiModule,\n providers: [\n {\n provide: 'ACCESSI_OPTIONS',\n useValue: options,\n },\n AuthService,\n TwoFactorService,\n UserService,\n EmailService,\n PermissionService,\n FiltriService,\n ConfiguratorService,\n JwtSimpleGuard,\n AuthenticateGenService,\n AccessiDatabaseUpdater,\n FederatedAuthService,\n ServiceTokenService,\n ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n exports: [\n 'ACCESSI_OPTIONS',\n AuthService,\n UserService,\n EmailService,\n PermissionService,\n FiltriService,\n ConfiguratorService,\n JwtSimpleGuard,\n AuthenticateGenService,\n FederatedAuthService,\n ServiceTokenService,\n ServiceTokenGuard, SchemaExportService, AdminBootstrapService],\n };\n }\n}\n"]}
@@ -9,6 +9,10 @@ export declare class AccessiRequestError extends Error {
9
9
  }
10
10
  /** Imposta il JWT usato da tutte le funzioni generate da Orval. */
11
11
  export declare function setAccessiConsoleToken(token: string | null): void;
12
+ /** Prefissa le chiamate relative del client generato (es. `https://accessi.example.com`). */
13
+ export declare function setAccessiFetchBaseUrl(url: string | null): void;
14
+ /** Token provider per client esterni alla console (server-to-server o frontend). */
15
+ export declare function setAccessiFetchTokenProvider(provider: (() => string | null | Promise<string | null>) | null): void;
12
16
  /**
13
17
  * Trasporto comune del client Orval: aggiunge il Bearer token e converte le
14
18
  * risposte di errore Accessi in Error JavaScript leggibili dalla console.
@@ -11,8 +11,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.AccessiRequestError = exports.ACCESSI_UNAUTHORIZED_EVENT = void 0;
13
13
  exports.setAccessiConsoleToken = setAccessiConsoleToken;
14
+ exports.setAccessiFetchBaseUrl = setAccessiFetchBaseUrl;
15
+ exports.setAccessiFetchTokenProvider = setAccessiFetchTokenProvider;
14
16
  exports.accessiFetch = accessiFetch;
15
17
  let accessToken = null;
18
+ let baseUrl = null;
19
+ let tokenProvider = null;
16
20
  const loadingEventName = 'accessi-console-network';
17
21
  /** Emesso quando una richiesta autenticata riceve 401: la console deve riportare al login. */
18
22
  exports.ACCESSI_UNAUTHORIZED_EVENT = 'accessi-console-unauthorized';
@@ -37,6 +41,14 @@ function notifyNetworkActivity(active) {
37
41
  function setAccessiConsoleToken(token) {
38
42
  accessToken = token;
39
43
  }
44
+ /** Prefissa le chiamate relative del client generato (es. `https://accessi.example.com`). */
45
+ function setAccessiFetchBaseUrl(url) {
46
+ baseUrl = url ? url.replace(/\/+$/, '') : null;
47
+ }
48
+ /** Token provider per client esterni alla console (server-to-server o frontend). */
49
+ function setAccessiFetchTokenProvider(provider) {
50
+ tokenProvider = provider;
51
+ }
40
52
  /** Legge una stringa non vuota dal payload di errore Accessi, ignorando tipi inattesi. */
41
53
  function payloadString(payload, key) {
42
54
  if (!payload || typeof payload !== 'object')
@@ -56,12 +68,14 @@ function accessiFetch(url, options) {
56
68
  if (options.body !== undefined && !headers.has('Content-Type')) {
57
69
  headers.set('Content-Type', 'application/json');
58
70
  }
59
- const authenticatedRequest = accessToken !== null;
60
- if (accessToken)
61
- headers.set('Authorization', `Bearer ${accessToken}`);
71
+ const resolvedToken = tokenProvider ? yield tokenProvider() : accessToken;
72
+ const authenticatedRequest = resolvedToken !== null && resolvedToken !== undefined;
73
+ if (resolvedToken)
74
+ headers.set('Authorization', `Bearer ${resolvedToken}`);
75
+ const requestUrl = baseUrl && url.startsWith('/') ? `${baseUrl}${url}` : url;
62
76
  notifyNetworkActivity(true);
63
77
  try {
64
- const response = yield fetch(url, Object.assign(Object.assign({}, options), { headers }));
78
+ const response = yield fetch(requestUrl, Object.assign(Object.assign({}, options), { headers }));
65
79
  const payload = yield response.json().catch(() => undefined);
66
80
  if (!response.ok) {
67
81
  const message = (_b = (_a = payloadString(payload, 'message')) !== null && _a !== void 0 ? _a : payloadString(payload, 'error')) !== null && _b !== void 0 ? _b : `Errore HTTP ${response.status}`;
@@ -1 +1 @@
1
- {"version":3,"file":"accessiFetch.js","sourceRoot":"","sources":["../../../src/accessi-module/Console/accessiFetch.ts"],"names":[],"mappings":"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","sourcesContent":["let accessToken: string | null = null;\n\nconst loadingEventName = 'accessi-console-network';\n/** Emesso quando una richiesta autenticata riceve 401: la console deve riportare al login. */\nexport const ACCESSI_UNAUTHORIZED_EVENT = 'accessi-console-unauthorized';\n\n/** Errore del client console: conserva status, codice stabile Accessi e dettagli di validazione. */\nexport class AccessiRequestError extends Error {\n constructor(message: string, readonly status: number, readonly code?: string, readonly details?: string[]) {\n super(message);\n this.name = 'AccessiRequestError';\n }\n}\n\n/** Notifies the browser shell without coupling generated Orval calls to the UI implementation. */\nfunction notifyNetworkActivity(active: boolean): void {\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(loadingEventName, { detail: { active } }));\n }\n}\n\n/** Imposta il JWT usato da tutte le funzioni generate da Orval. */\nexport function setAccessiConsoleToken(token: string | null): void {\n accessToken = token;\n}\n\n/** Legge una stringa non vuota dal payload di errore Accessi, ignorando tipi inattesi. */\nfunction payloadString(payload: unknown, key: string): string | undefined {\n if (!payload || typeof payload !== 'object') return undefined;\n const value = (payload as Record<string, unknown>)[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * Trasporto comune del client Orval: aggiunge il Bearer token e converte le\n * risposte di errore Accessi in Error JavaScript leggibili dalla console.\n */\nexport async function accessiFetch<T>(url: string, options: RequestInit): Promise<T> {\n const headers = new Headers(options.headers);\n headers.set('Accept', 'application/json');\n if (options.body !== undefined && !headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json');\n }\n const authenticatedRequest = accessToken !== null;\n if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`);\n\n notifyNetworkActivity(true);\n try {\n const response = await fetch(url, { ...options, headers });\n const payload = await response.json().catch(() => undefined);\n if (!response.ok) {\n const message = payloadString(payload, 'message') ?? payloadString(payload, 'error') ?? `Errore HTTP ${response.status}`;\n const rawDetails = payload && typeof payload === 'object' ? (payload as { details?: unknown }).details : undefined;\n const details = Array.isArray(rawDetails) ? rawDetails.map((item) => String(item)) : undefined;\n // Un 401 su una richiesta autenticata significa sessione scaduta/revocata: il token non e piu valido.\n if (response.status === 401 && authenticatedRequest && typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(ACCESSI_UNAUTHORIZED_EVENT));\n }\n throw new AccessiRequestError(message, response.status, payloadString(payload, 'code') ?? payloadString(payload, 'error'), details);\n }\n return {\n data: payload,\n status: response.status,\n headers: response.headers,\n } as T;\n } finally {\n notifyNetworkActivity(false);\n }\n}\n"]}
1
+ {"version":3,"file":"accessiFetch.js","sourceRoot":"","sources":["../../../src/accessi-module/Console/accessiFetch.ts"],"names":[],"mappings":"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","sourcesContent":["let accessToken: string | null = null;\nlet baseUrl: string | null = null;\nlet tokenProvider: (() => string | null | Promise<string | null>) | null = null;\n\nconst loadingEventName = 'accessi-console-network';\n/** Emesso quando una richiesta autenticata riceve 401: la console deve riportare al login. */\nexport const ACCESSI_UNAUTHORIZED_EVENT = 'accessi-console-unauthorized';\n\n/** Errore del client console: conserva status, codice stabile Accessi e dettagli di validazione. */\nexport class AccessiRequestError extends Error {\n constructor(message: string, readonly status: number, readonly code?: string, readonly details?: string[]) {\n super(message);\n this.name = 'AccessiRequestError';\n }\n}\n\n/** Notifies the browser shell without coupling generated Orval calls to the UI implementation. */\nfunction notifyNetworkActivity(active: boolean): void {\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(loadingEventName, { detail: { active } }));\n }\n}\n\n/** Imposta il JWT usato da tutte le funzioni generate da Orval. */\nexport function setAccessiConsoleToken(token: string | null): void {\n accessToken = token;\n}\n\n/** Prefissa le chiamate relative del client generato (es. `https://accessi.example.com`). */\nexport function setAccessiFetchBaseUrl(url: string | null): void {\n baseUrl = url ? url.replace(/\\/+$/, '') : null;\n}\n\n/** Token provider per client esterni alla console (server-to-server o frontend). */\nexport function setAccessiFetchTokenProvider(\n provider: (() => string | null | Promise<string | null>) | null,\n): void {\n tokenProvider = provider;\n}\n\n/** Legge una stringa non vuota dal payload di errore Accessi, ignorando tipi inattesi. */\nfunction payloadString(payload: unknown, key: string): string | undefined {\n if (!payload || typeof payload !== 'object') return undefined;\n const value = (payload as Record<string, unknown>)[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * Trasporto comune del client Orval: aggiunge il Bearer token e converte le\n * risposte di errore Accessi in Error JavaScript leggibili dalla console.\n */\nexport async function accessiFetch<T>(url: string, options: RequestInit): Promise<T> {\n const headers = new Headers(options.headers);\n headers.set('Accept', 'application/json');\n if (options.body !== undefined && !headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json');\n }\n const resolvedToken = tokenProvider ? await tokenProvider() : accessToken;\n const authenticatedRequest = resolvedToken !== null && resolvedToken !== undefined;\n if (resolvedToken) headers.set('Authorization', `Bearer ${resolvedToken}`);\n const requestUrl = baseUrl && url.startsWith('/') ? `${baseUrl}${url}` : url;\n\n notifyNetworkActivity(true);\n try {\n const response = await fetch(requestUrl, { ...options, headers });\n const payload = await response.json().catch(() => undefined);\n if (!response.ok) {\n const message = payloadString(payload, 'message') ?? payloadString(payload, 'error') ?? `Errore HTTP ${response.status}`;\n const rawDetails = payload && typeof payload === 'object' ? (payload as { details?: unknown }).details : undefined;\n const details = Array.isArray(rawDetails) ? rawDetails.map((item) => String(item)) : undefined;\n // Un 401 su una richiesta autenticata significa sessione scaduta/revocata: il token non e piu valido.\n if (response.status === 401 && authenticatedRequest && typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(ACCESSI_UNAUTHORIZED_EVENT));\n }\n throw new AccessiRequestError(message, response.status, payloadString(payload, 'code') ?? payloadString(payload, 'error'), details);\n }\n return {\n data: payload,\n status: response.status,\n headers: response.headers,\n } as T;\n } finally {\n notifyNetworkActivity(false);\n }\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  // src/accessi-module/Console/accessiFetch.ts
2
2
  var accessToken = null;
3
+ var baseUrl = null;
4
+ var tokenProvider = null;
3
5
  var loadingEventName = "accessi-console-network";
4
6
  var ACCESSI_UNAUTHORIZED_EVENT = "accessi-console-unauthorized";
5
7
  var AccessiRequestError = class extends Error {
@@ -30,11 +32,13 @@ async function accessiFetch(url, options) {
30
32
  if (options.body !== void 0 && !headers.has("Content-Type")) {
31
33
  headers.set("Content-Type", "application/json");
32
34
  }
33
- const authenticatedRequest = accessToken !== null;
34
- if (accessToken) headers.set("Authorization", `Bearer ${accessToken}`);
35
+ const resolvedToken = tokenProvider ? await tokenProvider() : accessToken;
36
+ const authenticatedRequest = resolvedToken !== null && resolvedToken !== void 0;
37
+ if (resolvedToken) headers.set("Authorization", `Bearer ${resolvedToken}`);
38
+ const requestUrl = baseUrl && url.startsWith("/") ? `${baseUrl}${url}` : url;
35
39
  notifyNetworkActivity(true);
36
40
  try {
37
- const response = await fetch(url, { ...options, headers });
41
+ const response = await fetch(requestUrl, { ...options, headers });
38
42
  const payload = await response.json().catch(() => void 0);
39
43
  if (!response.ok) {
40
44
  const message = payloadString(payload, "message") ?? payloadString(payload, "error") ?? `Errore HTTP ${response.status}`;
@@ -55,7 +59,7 @@ async function accessiFetch(url, options) {
55
59
  }
56
60
  }
57
61
 
58
- // src/accessi-module/Console/generated/accessiApi.ts
62
+ // src/accessi-module/Console/generated/emilsoftware-utilities.ts
59
63
  var getGetUserByTokenUrl = () => {
60
64
  return `/api/accessi/auth/get-user-by-token`;
61
65
  };
@@ -957,7 +961,7 @@ var WIKI_SECTIONS = [
957
961
  "await initializeAccessiModule(app, options); // le richieste nel frattempo ricevono 503"
958
962
  ].join("\n"), "Non aprire la porta prima del bootstrap"),
959
963
  h(`Rotte montate`),
960
- p(`Accessi intercetta solo /api/accessi/*, /accessi/openapi* e /accessi/openapi.json. Tutte le altre rotte passano intatte al tuo stack Express.`),
964
+ p(`Accessi intercetta solo /api/accessi/* e i path OpenAPI configurati (default /accessi/openapi* e /accessi/openapi.json). Tutte le altre rotte passano intatte al tuo stack Express.`),
961
965
  note("ok", `req.user`, `authorizeAccessi popola req.user con il payload utente corrente (utente, codiceUtente, flag) e req.data come alias storico. req.userGrants \xE8 presente solo se hai richiesto dei requisiti.`)
962
966
  ]
963
967
  },
@@ -982,6 +986,40 @@ var WIKI_SECTIONS = [
982
986
  ]
983
987
  },
984
988
  // -------------------------------------------------------------------------
989
+ {
990
+ id: "distribuzione",
991
+ group: "Per iniziare",
992
+ title: "Distribuzione multi-app e package entry point",
993
+ summary: "Come usare Accessi come servizio centrale, come resource server o solo come contratti.",
994
+ blocks: [
995
+ p(`Accessi non va montato per forza in ogni backend. La distribuzione consigliata separa il servizio che possiede il database e la console dai backend fratelli che devono solo autenticare le richieste.`),
996
+ table(
997
+ ["Entry point", "Uso", "Dipendenze server"],
998
+ [
999
+ ["accessi/server", "Bootstrap completo: DB, SMTP, migrazioni, console, API.", "s\xEC"],
1000
+ ["accessi/resource-server", "Guard/middleware per backend fratelli: verifica JWT e autorizzazione remota.", "no"],
1001
+ ["accessi/client", "Client OpenAPI generato per chiamare Accessi Service.", "no"],
1002
+ ["accessi/contracts", "Tipi, enum e helper puri browser-safe.", "no"],
1003
+ ["accessi/contracts/dtos", "DTO Nest con class-validator e decoratori OpenAPI.", "no"],
1004
+ ["accessi/openapi", "setupOpenApi e merge del documento OpenAPI.", "no"],
1005
+ ["accessi/console", "Helper per URL console, ticket e 2FA handoff.", "no"],
1006
+ ["accessi/ui", "Percorsi degli asset Web Component frameworkless.", "no"]
1007
+ ]
1008
+ ),
1009
+ p(`Topologia consigliata: un solo Accessi Service possiede database, encryptionKey, SMTP, chiavi JWT e console. Gli altri backend usano resource-server e client, senza credenziali del database Accessi.`),
1010
+ good("ts", [
1011
+ "// Backend che ospita Accessi",
1012
+ "import { initializeAccessiModule } from 'emilsoftware-utilities/accessi/server';",
1013
+ "",
1014
+ "// Backend fratello: nessun AccessiModule, nessun DB Accessi",
1015
+ "import { createAccessiAuthMiddleware } from 'emilsoftware-utilities/accessi/resource-server';",
1016
+ "import { configureAccessiClient, login } from 'emilsoftware-utilities/accessi/client';"
1017
+ ].join("\n"), "Import corretti per ruolo"),
1018
+ note("warn", `Un solo proprietario`, `Non montare AccessiModule in due backend puntati allo stesso database: migrazioni, bootstrap admin, ticket console e rate limit diventano ambigui.`),
1019
+ note("info", `Prompt di integrazione`, `Il file integration-prompt.md nella root del repository contiene il contesto completo per frontend, backend e agenti AI.`)
1020
+ ]
1021
+ },
1022
+ // -------------------------------------------------------------------------
985
1023
  {
986
1024
  id: "opzioni",
987
1025
  group: "Configurazione",
@@ -1002,7 +1040,7 @@ var WIKI_SECTIONS = [
1002
1040
  ["passwordExpirationDays", "number", "no", "Valore predefinito: 90 giorni."],
1003
1041
  ["legacyPasswordMigrationOnStartup", "boolean", "no", "Predefinito true: converte le password legacy."],
1004
1042
  ["autoUpdateDatabase", "boolean", "no", "Predefinito true: riconcilia lo schema al boot."],
1005
- ["jwtOptions", "{ secret, expiresIn }", "s\xEC", "Firma HS256 e durata dei JWT Accessi."],
1043
+ ["jwtOptions", "{ secret, expiresIn, algorithm, issuer, audience, privateKey, publicKey, keyId }", "s\xEC", "Firma e durata JWT. Default HS256; RS/ES consigliati per resource server multi-servizio."],
1006
1044
  ["emailOptions", "EmailOptions", "s\xEC", "SMTP per reset e codici email."],
1007
1045
  ["publicAuthRateLimit", "PublicAuthRateLimitOptions", "no", "Limiti in-memory sugli endpoint pubblici."],
1008
1046
  ["publicRegistration", "{ enabled }", "no", "Registrazione pubblica: disattivata per impostazione predefinita."],
@@ -1011,7 +1049,10 @@ var WIKI_SECTIONS = [
1011
1049
  ["serviceTokens", "{ enabled, defaultTtlDays }", "no", "Token macchina-a-macchina, abilitati per impostazione predefinita."],
1012
1050
  ["catalogScripts", "{ enabled, folder, ... }", "no", "Catalog migrations SQL eseguite dopo la riconciliazione dello schema."],
1013
1051
  ["adminBootstrap", "{ enabled, superUser }", "no", "Token casuale a ogni avvio per creare il primo admin dalla console."],
1014
- ["publicBaseUrl", "string", "no", "Base URL usata solo per loggare console/swagger/API."]
1052
+ ["publicBaseUrl", "string", "no", "Base URL usata solo per loggare console/OpenAPI/API."],
1053
+ ["openapi", "OpenApiSetupOptions", "no", "Path, titolo, descrizione e enabled della documentazione OpenAPI. Default invariati."],
1054
+ ["logger", "LoggerService", "no", "Logger Nest del sotto-app; se assente Nest usa il proprio logger."],
1055
+ ["bufferLogs", "boolean", "no", "Buffer dei log di bootstrap del sotto-app. Default true."]
1015
1056
  ]
1016
1057
  ),
1017
1058
  good("ts", [
@@ -1034,6 +1075,8 @@ var WIKI_SECTIONS = [
1034
1075
  " jwtOptions: {",
1035
1076
  " secret: process.env.ACCESSI_JWT_SECRET!, // almeno 32 caratteri casuali, unico per ambiente",
1036
1077
  " expiresIn: '24h',",
1078
+ " issuer: 'accessi',",
1079
+ " audience: 'backend-app',",
1037
1080
  " },",
1038
1081
  " emailOptions: {",
1039
1082
  " host: process.env.SMTP_HOST!,",
@@ -1048,6 +1091,14 @@ var WIKI_SECTIONS = [
1048
1091
  " serviceTokens: { enabled: true, defaultTtlDays: 365 },",
1049
1092
  "};"
1050
1093
  ].join("\n"), "Configurazione tipica"),
1094
+ p(`options.openapi personalizza path, titolo e descrizione della documentazione OpenAPI. Con enabled: false UI e JSON non vengono montati, ma le API /api/accessi restano attive. initializeAccessiModule restituisce { nestApp, document } per mergiare il contratto nel documento OpenAPI unico dell'host.`),
1095
+ good("ts", [
1096
+ "const { document } = await initializeAccessiModule(app, {",
1097
+ " ...options,",
1098
+ " openapi: { openApiPath: 'auth/docs', openApiJsonPath: 'auth/docs.json', title: 'Auth API' },",
1099
+ "});",
1100
+ "// document.paths e document.components.schemas sono pronti per il merge"
1101
+ ].join("\n"), "OpenAPI personalizzato"),
1051
1102
  bad("ts", [
1052
1103
  "jwtOptions: { secret: 'accessi', expiresIn: '24h' }, // segreto debole e condiviso",
1053
1104
  "mockDemoUser: true, // MAI in produzione"
@@ -1235,6 +1286,58 @@ POST /api/accessi/auth/confirm-reset-password/{token}
1235
1286
  ]
1236
1287
  },
1237
1288
  // -------------------------------------------------------------------------
1289
+ {
1290
+ id: "resource-server",
1291
+ group: "Autorizzazione",
1292
+ title: "Resource server e introspection",
1293
+ summary: "Proteggere un backend fratello senza accedere al database Accessi.",
1294
+ blocks: [
1295
+ p(`Un backend che non possiede Accessi usa resource-server. La verifica puo essere locale (JWT) oppure delegata all'introspection di Accessi Service. La policy di autorizzazione usa gli stessi accessiRequirement del middleware storico.`),
1296
+ good("ts", [
1297
+ "import { createAccessiAuthMiddleware } from 'emilsoftware-utilities/accessi/resource-server';",
1298
+ "",
1299
+ "app.use(createAccessiAuthMiddleware({",
1300
+ " jwt: {",
1301
+ " // In una topologia multi-servizio preferire RS256/JWKS:",
1302
+ " // publicKey: process.env.ACCESSI_JWT_PUBLIC_KEY,",
1303
+ ' // algorithms: ["RS256"],',
1304
+ " serverSecret: process.env.ACCESSI_JWT_SECRET, // HS256: deriva la chiave access",
1305
+ ' issuer: "accessi",',
1306
+ ' audience: "backend-a",',
1307
+ " },",
1308
+ " introspection: {",
1309
+ ' url: "https://accessi.example.com/api/accessi/auth/introspect",',
1310
+ " serviceToken: process.env.ACCESSI_SERVICE_TOKEN!,",
1311
+ " },",
1312
+ "}));"
1313
+ ].join("\n"), "Express resource server"),
1314
+ good("ts", [
1315
+ "import { createAccessiAuthGuard } from 'emilsoftware-utilities/accessi/resource-server';",
1316
+ "",
1317
+ "@UseGuards(createAccessiAuthGuard({",
1318
+ ' jwt: { publicKey: process.env.ACCESSI_JWT_PUBLIC_KEY, algorithms: ["RS256"] },',
1319
+ " introspection: { url: process.env.ACCESSI_INTROSPECT_URL!, serviceToken: process.env.ACCESSI_SERVICE_TOKEN! },",
1320
+ ' authorization: { requirements: [{ menuCode: "ORDINI", minPermissionLevel: 20 }] },',
1321
+ "}))",
1322
+ '@Get("ordini")',
1323
+ "list(@Req() req: Request) { return req.user; }"
1324
+ ].join("\n"), "Guard Nest"),
1325
+ table(
1326
+ ["Campo", "Obbligo", "Note"],
1327
+ [
1328
+ ["jwt.serverSecret", "HS256", "Segreto grezzo Accessi; il resource server applica la KDF access."],
1329
+ ["jwt.publicKey / jwt.jwks", "RS/ES", "Consigliato in multi-servizio. JWKS ha cache e timeout."],
1330
+ ["jwt.issuer / jwt.audience", "no", "Consigliati per evitare token di altri ambienti."],
1331
+ ["introspection.url", "se servono grant", "Endpoint service-to-service protetto da service token."],
1332
+ ["introspection.serviceToken", "se introspection", "Scope richiesto: accessi:introspect."],
1333
+ ["authorization", "no", "Policy accessiRequirement; senza introspection i grant sono obbligatori."]
1334
+ ]
1335
+ ),
1336
+ note("warn", `Fail closed`, `Se introspection e configurata ma non raggiungibile, il resource server risponde 503. Non autorizza la richiesta.`),
1337
+ note("info", `Introspection`, `POST /api/accessi/auth/introspect restituisce active=false per token invalido, scaduto o utente disabilitato. I risultati attivi sono cachati lato resource server per 30 secondi per default.`)
1338
+ ]
1339
+ },
1340
+ // -------------------------------------------------------------------------
1238
1341
  {
1239
1342
  id: "requisiti",
1240
1343
  group: "Autorizzazione",
@@ -1453,6 +1556,7 @@ POST /api/accessi/auth/confirm-reset-password/{token}
1453
1556
  blocks: [
1454
1557
  p(`Formato: st_<id>.<segreto> (segreto da 256 bit). Nel database si salva solo lo SHA-256 del segreto; il segreto \xE8 mostrato una sola volta. La verifica restituisce { tokenId, label, scopes }: nessun ruolo o grant.`),
1455
1558
  note("danger", `Non \xE8 un superutente`, `Un service token non apre le API Accessi: abilita solo le rotte della tua app che proteggi con ServiceTokenGuard.`),
1559
+ note("info", `Introspection`, `Per un backend fratello creare un service token con scope accessi:introspect e usarlo in POST /api/accessi/auth/introspect. Il resource server lo configura in introspection.serviceToken.`),
1456
1560
  good("ts", [
1457
1561
  "import { ServiceTokenGuard, RequireServiceTokenScopes } from 'emilsoftware-utilities';",
1458
1562
  "",
@@ -1499,7 +1603,8 @@ POST /api/accessi/auth/confirm-reset-password/{token}
1499
1603
  note("warn", `Ultimo admin`, `Per non restare chiusi fuori dalla console, l'ultimo admin attivo non pu\xF2 essere disattivato, bloccato, eliminato n\xE9 privato del flag Admin: l'operazione restituisce 400 finch\xE9 non viene promosso un altro admin.`),
1500
1604
  note("info", `Accesso`, `Apri /api/accessi/console e accedi con un utente con FLGSUPER o FLGADMIN. Le sezioni sono raggiungibili anche tramite deep link (per esempio /api/accessi/console/menu-e-gruppi/menu).`),
1501
1605
  note("info", `Bootstrap admin`, `Con adminBootstrap.enabled il backend stampa a ogni avvio un token casuale (monouso, valido fino al riavvio). La sezione "Crea utente admin" nella schermata di login compare solo quando adminBootstrap \xE8 abilitato (stato esposto da GET /api/accessi/auth/bootstrap-admin/status): incolla il token e crea il primo admin senza email di conferma. La password \xE8 opzionale: se assente viene generata e mostrata una sola volta.`),
1502
- note("ok", `Auto-ingresso dal frontend`, `Se un frontend ha gi\xE0 una sessione Accessi valida e l'utente \xE8 abilitato alla console, pu\xF2 aprire la console senza rifare il login: \`POST /api/accessi/console/entry\` con \`Authorization: Bearer <jwt>\` restituisce un ticket monouso; apri poi \`/api/accessi/console#entry=<ticket>\`. Il JWT non finisce mai nell'URL; il ticket dura 60 secondi ed \xE8 utilizzabile una sola volta (endpoint di scambio protetto da rate limit).`)
1606
+ note("ok", `Auto-ingresso dal frontend`, `Se un frontend ha gi\xE0 una sessione Accessi valida e l'utente \xE8 abilitato alla console, pu\xF2 aprire la console senza rifare il login: \`POST /api/accessi/console/entry\` con \`Authorization: Bearer <jwt>\` restituisce un ticket monouso; apri poi \`/api/accessi/console#entry=<ticket>\`. Il JWT non finisce mai nell'URL; il ticket dura 60 secondi ed \xE8 utilizzabile una sola volta (endpoint di scambio protetto da rate limit).`),
1607
+ note("info", `Console centralizzata`, `In una topologia multi-backend la console va ospitata una sola volta su Accessi Service. Gli altri backend possono linkarla con \`buildAccessiConsoleUrl\` di \`accessi/console\` oppure proxare \`/api/accessi/*\` sullo stesso dominio. Non copiare gli asset della console in ogni applicazione.`)
1503
1608
  ]
1504
1609
  },
1505
1610
  // -------------------------------------------------------------------------
@@ -1666,7 +1771,7 @@ function buildAiDigest() {
1666
1771
  }
1667
1772
 
1668
1773
  // src/accessi-module/Console/changelog.md
1669
- var changelog_default = "# Changelog Accessi\n\nNovit\xE0 e modifiche rilevanti della libreria `emilsoftware-utilities` (modulo Accessi).\n\n## Non rilasciato\n\n### Cambiamenti\n- Token Accessi: `TokenResult.expiresIn` riporta sempre la validita effettiva in secondi (es. `86400`), anche quando `jwtOptions.expiresIn` usa un formato leggibile come `24h`. I client possono quindi esporre `expires_in` anche per il token di login normale.\n- Console: nuova funzione **Login as** per superutenti e admin. Dalla lista utenti si genera un URL verso l'applicazione scelta con `?loginAsToken=<jwt>`; il token dura quanto la sessione Accessi standard (`jwtOptions.expiresIn`), registra l'attore nelle claim `act`/`loginAs` e resta soggetto alla gerarchia ADMIN > SUPER. Endpoint: `POST /api/accessi/user/login-as/:codiceUtente`.\n- Console: rimosse le sezioni **Filtri utente** e **Tipi filtro**. I filtri applicativi sono deprecati e non vengono pi\xF9 letti n\xE9 scritti dalla console; le tabelle restano invariate e la gestione torna manuale sul database.\n- Console: le conferme operative (es. \"Ruoli aggiornati\") ora compaiono come notifiche in alto a destra e scompaiono automaticamente.\n- Console: il salvataggio del profilo non invia pi\xF9 i campi della tabella `FILTRI`, evitando payload rifiutati dal backend. `UserService` tocca `FILTRI` solo se il chiamante invia esplicitamente almeno un campo legacy.\n- Console: scheda utente riorganizzata. \"Indietro\" resta nella barra superiore; stato registrazione, disattivazione ed eliminazione definitiva sono in un form coerente \"Stato e sicurezza\", con icone e tooltip allineati.\n- Console: pagina Changelog a larghezza piena, con intestazioni di versione evidenziate e sezioni orizzontali leggibili.\n- Seed `npm run accessi:seed-admin`: crea o aggiorna un admin completo (`FLGSUPER = 1` e `FLGADMIN = 1`) e corregge il nome colonna usato in precedenza (`FLGADMIN`).\n- Console: la pagina di default nella scheda utente \xE8 ora un autocomplete con ricerca. Propone tutte le rotte del catalogo menu, si filtra scrivendo ed \xE8 ancora possibile inserire un valore libero.\n- Autorizzazioni: i grant effettivi sommano ruoli e permessi diretti prendendo il livello massimo per menu. Un permesso diretto con livello inferiore (o 0) non annulla pi\xF9 il grant del ruolo.\n- Ruoli legacy: un menu senza descrizione non viene pi\xF9 scartato e un `TIPABI` mancante viene letto come livello 10, evitando utenti con ruolo ma senza accesso.\n- Grant dei ruoli arricchiti con i metadati di navigazione del catalogo menu (`pagina`, icona, gruppo, tipo): il frontend riceve gli stessi campi dei grant diretti e pu\xF2 costruire correttamente menu e rotte.\n- Sicurezza: `GET /api/accessi/permission/grants/:codiceUtente` accetta solo un JWT Accessi valido e consente la lettura solo al proprietario o a chi pu\xF2 gestire quel target (gerarchia ADMIN > SUPER). Un superutente non pu\xF2 leggere i grant di un admin.\n\n## 2.0.0-dev.50 (2026-10-15)\n\n### Cambiamenti\n- ORM: `Row` e `DbValue` sono i tipi predefiniti di `Orm.query` e `Orm.execute`; `transactionQuery` usa `Row[]`. Gli usi generici non devono pi\xF9 dichiarare `Record<string, unknown>`.\n- Build: il contratto OpenAPI generato (`src/accessi-module/openapi/`) \xE8 ignorato da Git e viene rigenerato da `npm run generate:accessi-openapi`.\n\n## 2.0.0-dev.49 (2026-10-15)\n\n### Cambiamenti\n- Documentazione HTTP rinominata da Swagger a **OpenAPI**: il modulo unificato espone `/accessi/openapi` e `/accessi/openapi.json`; `setupSwagger` usa `/openapi` come default.\n- Il path \xE8 configurabile tramite `EmilsoftwareOptions.swagger` (utile per far convivere pi\xF9 documentazioni).\n- Allineati log di avvio, Wiki e dispatch delle rotte del modulo.\n- L'alias storico `<path>-json` resta supportato.\n\n### Note\n- I consumer che aprivano `/swagger` o `/accessi/swagger` devono aggiornare l'URL alla nuova documentazione OpenAPI.\n\n## 2.0.0-dev.48 (2026-10-13)\n\n### Correzioni\n- `RestUtilities.sendErrorMessage` restituisce status HTTP specifici (400/401/403/4xx/5xx) e codici generici (`VALIDATION_ERROR`, `UNAUTHORIZED`, `FORBIDDEN`, `INTERNAL_ERROR`, `REQUEST_ERROR`), senza usare codici `ACCESSI_*` nel business helper.\n- Il messaggio restituito \xE8 quello reale dell'eccezione; gli errori testuali di validazione vengono riconosciuti come 400.\n\n## 2.0.0-dev.47 (2026-10-13)\n\n### Correzioni\n- Errore schema database reso generico: codice `DATABASE_SCHEMA_OUTDATED` e messaggio senza riferimenti specifici ad Accessi.\n\n## 2.0.0-dev.46 (2026-10-12)\n\n### Correzioni\n- `sendBaseResponse` resta retro-compatibile quando il terzo argomento \xE8 un tag stringa invece dello status HTTP.\n- Gestita la serializzazione dei `bigint`; gli errori di risposta vengono registrati con stack tramite `sendErrorMessage`.\n- `AccessiHttpExceptionFilter` registra gli errori con stack per facilitare la diagnosi.\n\n## 2.0.0-dev.45 (2026-10-12)\n\n### Correzioni\n- `set-gdpr` e l'aggiornamento di `flagGdpr` sono idempotenti: `UPDATE OR INSERT` su `UTENTI_GDPR` con `MATCHING (CODUTE, DATACC)`.\n- Evitate violazioni di chiave primaria e risposte 500 quando il consenso GDPR viene registrato pi\xF9 volte nello stesso giorno.\n\n## 2.0.0-dev.44\n\n### Aggiunte\n- Endpoint pubblico `GET /api/accessi/auth/bootstrap-admin/status` (`getAdminBootstrapStatus`): la console mostra la sezione \"Crea utente admin\" **solo** se `adminBootstrap.enabled` \xE8 `true`.\n\n### Correzioni\n- Rimossi i caratteri corrotti (mojibake UTF-8 \u2194 Windows-1252) da DTO, servizi, controller, console, OpenAPI e client generato (es. \"L'email \xE8 gi\xE0 associata a un utente Accessi.\").\n\n## 2.0.0-dev.43\n\n### Cambiamenti\n- Gerarchia dei ruoli formalizzata **ADMIN > SUPER > utente**: un superutente non puo' gestire un admin su `update-user`, `delete-user` (soft e hard), `set-password`, `force-password-reset`, `set-stato`, `assign-roles` e `assign-permissions` (`ensureCanManageTargetUser`, 403 se violato).\n- Guard **ultimo admin attivo**: non puo' essere disattivato, bloccato, eliminato o privato del flag Admin (400), per evitare il lockout della console.\n- Creazione utente (`create-managed-user` e registrazione pubblica) resa **atomica** rispetto all'invio email: fail-fast se SMTP non configurato e rollback dell'utente creato se l'invio fallisce (`ACCESSI_USER_INVITE_EMAIL_FAILED`).\n- Console: form password separato (Invio imposta la password invece di salvare il profilo), scheda utente in sola lettura per target di rango superiore.\n- SSO: anche il superutente puo' creare utenti SSO e gestirne identita/policy (`federated-auth/users/*`) sugli utenti non-admin; il catalogo dei provider SSO resta riservato agli admin.\n- Coerenza: i filtri utente (`saveFiltriUtente`) rispettano la gerarchia; il flag GDPR impostato dal profilo allinea anche `DATGDPR`; la scheda in sola lettura disabilita anche il collegamento SSO.\n- Eliminazione definitiva: se l'utente ha dati collegati (tabelle esterne/FK) risponde 400 con `code: ACCESSI_USER_DELETE_HAS_REFERENCES` e messaggio esplicito, suggerendo il soft delete (la hard delete resta invariata quando non ci sono vincoli).\n- Consenso GDPR completo: `update-user` con `flagGdpr` scrive anche la riga storica `UTENTI_GDPR`; in console il pulsante \"Registra consenso GDPR\" usa `set-gdpr`.\n- Console: aggiunto il pulsante \"Invia reset password legacy\" (super e admin).\n- Console: **auto-ingresso dal frontend** tramite ticket monouso (`POST /api/accessi/console/entry` + apertura di `#entry=<ticket>`), senza JWT nell'URL; il ticket e' legato all'utente, dura 60s, e' monouso e lo scambio e' protetto da rate limit (`publicAuthRateLimit.consoleEntry`).\n\n## 2.0.0-dev.42\n\n### Aggiunte\n- Gestione dei flag di privilegio dalla console Accessi:\n - un admin pu\xF2 assegnare o togliere sia `FLGADMIN` sia `FLGSUPER`;\n - un superutente senza `FLGADMIN` pu\xF2 gestire solo `FLGSUPER`;\n - i flag sono impostabili in creazione (utente locale o SSO) e in modifica profilo.\n- Eliminazione utenti con due modalit\xE0:\n - soft delete (`delete-user`, `STAREG=DELETE`) per admin e superutenti;\n - eliminazione definitiva (`delete-user-permanent`, solo admin) che rimuove in transazione utente e record collegati.\n- Gestione completa dell'utente dalla console (admin): anagrafica estesa (cellulare, lingua, avatar, metadata JSON), GDPR, scadenza password, tutti i filtri (`FILTRI`) e **impostazione diretta della password** (`POST /api/accessi/user/set-password/:codiceUtente`), senza accesso al database.\n- Scritture utente idempotenti: `UTENTI_CONFIG` in `UPDATE OR INSERT ... MATCHING (CODUTE)`; i campi non inviati restano invariati.\n- Policy password moderna quando si imposta una password (reset, set-password admin, bootstrap): minimo 8 caratteri con maiuscola, minuscola, cifra e carattere speciale, senza spazi e non comune. In caso di violazione risposta 400 con `code: ACCESSI_WEAK_PASSWORD` e `details`. La policy **non** si applica al login, cos\xEC le password preesistenti deboli continuano a funzionare.\n\n### Correzioni\n- Il backend verifica i flag in base al ruolo dell'utente connesso (`ensurePrivilegeFlagChanges`): inviare un flag non consentito restituisce 403.\n\n## 2.0.0-dev.33\n\n### Cambiamenti\n- Rimosse le logiche applicative residue dallo schema Accessi:\n - `MENU_GRP.NOMECAMPO`\n - `UTENTI.ENABLEIA`\n - `UTENTI_CONFIG.PAGDEF`\n - `UTENTI.CELLUTE`\n- Telefono canonico: `UTENTI_CONFIG.CELLULARE` (allargato a `VARCHAR(30)`).\n- Migrazione automatica: copia `UTENTI.CELLUTE` \u2192 `UTENTI_CONFIG.CELLULARE`, poi `DROP` delle colonne obsolete.\n- Ruoli chiariti:\n - `FLGSUPER`: superutente, riceve **tutte** le abilitazioni al livello massimo (30) dopo il login; in console vede **solo** la sezione **Utenti**.\n - `FLGADMIN`: accede a **tutta** la console (catalogo, token, SSO, utenti) ma **non** influisce sulle abilitazioni.\n- Aggiunta la sezione **Changelog** in console (sotto la Wiki).\n\n### Note\n- `FILTRI` e `FILTRI_TIPO` restano invariati (piano di migrazione futuro).\n- I backend che usavano i campi rimossi devono gestirli in tabelle proprie.\n\n## 2.0.0-dev.32\n\n### Cambiamenti\n- Rimosso il supporto ai flag applicativi in `UTENTI_CONFIG`: `FLGMOP`, `FLGPIANA`, `FLGADDETTI`, `FLGOSPITI`, `FLGPIANARFID`, `FLGCONTA`, `FLGCUBI`, `FLGCICLPASS`, `FLGDIPENDENTI`, `FLGINVENTARI`, `CAUMOV`, `NUMMAC`, `RAGSOCCLI`.\n- Rinominata la colonna `FLGADMINCONFIG` in `FLGADMIN` (con copia dei valori e `DROP`).\n\n## 2.0.0-dev.31\n\n### Aggiunte\n- Esposizione dei servizi Accessi all'host Express (`getAccessiModuleServices`) per delegare login, SSO e token senza reimplementarli.\n\n### Correzioni\n- `UserService.getUsers`: filtri estesi (`codiceUtente[]`, `numRep[]`, `codDipendente`, `cellulare`, `tipFil`, `flagSuper`).\n\n## 2.0.0-dev.24\n\n### Cambiamenti (breaking)\n- Chiavi JWT derivate per scopo (`access`, `reset`, `2fa`) e claim `typ` obbligatorio: gli access token precedenti non sono pi\xF9 validi.\n- Endpoint Allegati protetti di default.\n- Pool di connessioni attivo di default (`poolSize` 5).\n\n### Aggiunte\n- Service token macchina-a-macchina (gestione, guard, console).\n- CRUD completo dei cataloghi dalla console Accessi.\n- Supporto Firebird 2.5 nel migrator e nei test di integrazione.\n";
1774
+ var changelog_default = "# Changelog Accessi\n\nNovit\xE0 e modifiche rilevanti della libreria `emilsoftware-utilities` (modulo Accessi).\n\n## Non rilasciato\n\n### Cambiamenti\n- Distribuzione: nuovi entry point `accessi/server`, `accessi/resource-server`, `accessi/client`, `accessi/contracts`, `accessi/contracts/dtos`, `accessi/openapi`, `accessi/console` e `accessi/ui`. Gli entry point sono dichiarati in `package.json#exports`; il root export resta per retrocompatibilita.\n- Resource server: aggiunti `createAccessiAuthMiddleware`, `createAccessiAuthGuard`, `createAccessiTokenVerifier` e `createAccessiIntrospectionClient` per backend fratelli che non possiedono il database Accessi. Supporta HS256, RS/ES con chiave pubblica PEM e JWKS con cache.\n- Introspection: nuovo `POST /api/accessi/auth/introspect` protetto da service token con scope `accessi:introspect`. Restituisce `active`, profilo utente e grant correnti.\n- JWT: `jwtOptions` accetta `algorithm`, `issuer`, `audience`, `privateKey`, `publicKey` e `keyId`. Default HS256 invariato; RS/ES sono consigliati per la topologia multi-servizio.\n- OpenAPI: aggiunto `mergeOpenApiDocuments` per unire il documento Accessi nel documento unico dell'host con conflitti espliciti.\n- Console: aggiunto `buildAccessiConsoleUrl` per linkare la console centrale senza mettere JWT nell'URL.\n- Rinomina: la configurazione e gli helper della documentazione usano `openapi`/`OpenApi` (`AccessiOptions.openapi`, `OpenApiSetupOptions`, `setupOpenApi`, `DEFAULT_ACCESSI_OPENAPI`); i vecchi nomi `swagger` non sono piu esportati.\n- Token Accessi: `TokenResult.expiresIn` riporta sempre la validita effettiva in secondi (es. `86400`), anche quando `jwtOptions.expiresIn` usa un formato leggibile come `24h`. I client possono quindi esporre `expires_in` anche per il token di login normale.\n- Console: nuova funzione **Login as** per superutenti e admin. Dalla lista utenti si genera un URL verso l'applicazione scelta con `?loginAsToken=<jwt>`; il token dura quanto la sessione Accessi standard (`jwtOptions.expiresIn`), registra l'attore nelle claim `act`/`loginAs` e resta soggetto alla gerarchia ADMIN > SUPER. Endpoint: `POST /api/accessi/user/login-as/:codiceUtente`.\n- Console: rimosse le sezioni **Filtri utente** e **Tipi filtro**. I filtri applicativi sono deprecati e non vengono pi\xF9 letti n\xE9 scritti dalla console; le tabelle restano invariate e la gestione torna manuale sul database.\n- Console: le conferme operative (es. \"Ruoli aggiornati\") ora compaiono come notifiche in alto a destra e scompaiono automaticamente.\n- Console: il salvataggio del profilo non invia pi\xF9 i campi della tabella `FILTRI`, evitando payload rifiutati dal backend. `UserService` tocca `FILTRI` solo se il chiamante invia esplicitamente almeno un campo legacy.\n- Console: scheda utente riorganizzata. \"Indietro\" resta nella barra superiore; stato registrazione, disattivazione ed eliminazione definitiva sono in un form coerente \"Stato e sicurezza\", con icone e tooltip allineati.\n- Console: pagina Changelog a larghezza piena, con intestazioni di versione evidenziate e sezioni orizzontali leggibili.\n- Seed `npm run accessi:seed-admin`: crea o aggiorna un admin completo (`FLGSUPER = 1` e `FLGADMIN = 1`) e corregge il nome colonna usato in precedenza (`FLGADMIN`).\n- Console: la pagina di default nella scheda utente \xE8 ora un autocomplete con ricerca. Propone tutte le rotte del catalogo menu, si filtra scrivendo ed \xE8 ancora possibile inserire un valore libero.\n- Autorizzazioni: i grant effettivi sommano ruoli e permessi diretti prendendo il livello massimo per menu. Un permesso diretto con livello inferiore (o 0) non annulla pi\xF9 il grant del ruolo.\n- Ruoli legacy: un menu senza descrizione non viene pi\xF9 scartato e un `TIPABI` mancante viene letto come livello 10, evitando utenti con ruolo ma senza accesso.\n- Grant dei ruoli arricchiti con i metadati di navigazione del catalogo menu (`pagina`, icona, gruppo, tipo): il frontend riceve gli stessi campi dei grant diretti e pu\xF2 costruire correttamente menu e rotte.\n- Sicurezza: `GET /api/accessi/permission/grants/:codiceUtente` accetta solo un JWT Accessi valido e consente la lettura solo al proprietario o a chi pu\xF2 gestire quel target (gerarchia ADMIN > SUPER). Un superutente non pu\xF2 leggere i grant di un admin.\n\n## 2.0.0-dev.50 (2026-10-15)\n\n### Cambiamenti\n- ORM: `Row` e `DbValue` sono i tipi predefiniti di `Orm.query` e `Orm.execute`; `transactionQuery` usa `Row[]`. Gli usi generici non devono pi\xF9 dichiarare `Record<string, unknown>`.\n- Build: il contratto OpenAPI generato (`src/accessi-module/openapi/`) \xE8 ignorato da Git e viene rigenerato da `npm run generate:accessi-openapi`.\n\n## 2.0.0-dev.49 (2026-10-15)\n\n### Cambiamenti\n- Documentazione HTTP rinominata da Swagger a **OpenAPI**: il modulo unificato espone `/accessi/openapi` e `/accessi/openapi.json`; `setupSwagger` usa `/openapi` come default.\n- Il path \xE8 configurabile tramite `EmilsoftwareOptions.swagger` (utile per far convivere pi\xF9 documentazioni).\n- Allineati log di avvio, Wiki e dispatch delle rotte del modulo.\n- L'alias storico `<path>-json` resta supportato.\n\n### Note\n- I consumer che aprivano `/swagger` o `/accessi/swagger` devono aggiornare l'URL alla nuova documentazione OpenAPI.\n\n## 2.0.0-dev.48 (2026-10-13)\n\n### Correzioni\n- `RestUtilities.sendErrorMessage` restituisce status HTTP specifici (400/401/403/4xx/5xx) e codici generici (`VALIDATION_ERROR`, `UNAUTHORIZED`, `FORBIDDEN`, `INTERNAL_ERROR`, `REQUEST_ERROR`), senza usare codici `ACCESSI_*` nel business helper.\n- Il messaggio restituito \xE8 quello reale dell'eccezione; gli errori testuali di validazione vengono riconosciuti come 400.\n\n## 2.0.0-dev.47 (2026-10-13)\n\n### Correzioni\n- Errore schema database reso generico: codice `DATABASE_SCHEMA_OUTDATED` e messaggio senza riferimenti specifici ad Accessi.\n\n## 2.0.0-dev.46 (2026-10-12)\n\n### Correzioni\n- `sendBaseResponse` resta retro-compatibile quando il terzo argomento \xE8 un tag stringa invece dello status HTTP.\n- Gestita la serializzazione dei `bigint`; gli errori di risposta vengono registrati con stack tramite `sendErrorMessage`.\n- `AccessiHttpExceptionFilter` registra gli errori con stack per facilitare la diagnosi.\n\n## 2.0.0-dev.45 (2026-10-12)\n\n### Correzioni\n- `set-gdpr` e l'aggiornamento di `flagGdpr` sono idempotenti: `UPDATE OR INSERT` su `UTENTI_GDPR` con `MATCHING (CODUTE, DATACC)`.\n- Evitate violazioni di chiave primaria e risposte 500 quando il consenso GDPR viene registrato pi\xF9 volte nello stesso giorno.\n\n## 2.0.0-dev.44\n\n### Aggiunte\n- Endpoint pubblico `GET /api/accessi/auth/bootstrap-admin/status` (`getAdminBootstrapStatus`): la console mostra la sezione \"Crea utente admin\" **solo** se `adminBootstrap.enabled` \xE8 `true`.\n\n### Correzioni\n- Rimossi i caratteri corrotti (mojibake UTF-8 \u2194 Windows-1252) da DTO, servizi, controller, console, OpenAPI e client generato (es. \"L'email \xE8 gi\xE0 associata a un utente Accessi.\").\n\n## 2.0.0-dev.43\n\n### Cambiamenti\n- Gerarchia dei ruoli formalizzata **ADMIN > SUPER > utente**: un superutente non puo' gestire un admin su `update-user`, `delete-user` (soft e hard), `set-password`, `force-password-reset`, `set-stato`, `assign-roles` e `assign-permissions` (`ensureCanManageTargetUser`, 403 se violato).\n- Guard **ultimo admin attivo**: non puo' essere disattivato, bloccato, eliminato o privato del flag Admin (400), per evitare il lockout della console.\n- Creazione utente (`create-managed-user` e registrazione pubblica) resa **atomica** rispetto all'invio email: fail-fast se SMTP non configurato e rollback dell'utente creato se l'invio fallisce (`ACCESSI_USER_INVITE_EMAIL_FAILED`).\n- Console: form password separato (Invio imposta la password invece di salvare il profilo), scheda utente in sola lettura per target di rango superiore.\n- SSO: anche il superutente puo' creare utenti SSO e gestirne identita/policy (`federated-auth/users/*`) sugli utenti non-admin; il catalogo dei provider SSO resta riservato agli admin.\n- Coerenza: i filtri utente (`saveFiltriUtente`) rispettano la gerarchia; il flag GDPR impostato dal profilo allinea anche `DATGDPR`; la scheda in sola lettura disabilita anche il collegamento SSO.\n- Eliminazione definitiva: se l'utente ha dati collegati (tabelle esterne/FK) risponde 400 con `code: ACCESSI_USER_DELETE_HAS_REFERENCES` e messaggio esplicito, suggerendo il soft delete (la hard delete resta invariata quando non ci sono vincoli).\n- Consenso GDPR completo: `update-user` con `flagGdpr` scrive anche la riga storica `UTENTI_GDPR`; in console il pulsante \"Registra consenso GDPR\" usa `set-gdpr`.\n- Console: aggiunto il pulsante \"Invia reset password legacy\" (super e admin).\n- Console: **auto-ingresso dal frontend** tramite ticket monouso (`POST /api/accessi/console/entry` + apertura di `#entry=<ticket>`), senza JWT nell'URL; il ticket e' legato all'utente, dura 60s, e' monouso e lo scambio e' protetto da rate limit (`publicAuthRateLimit.consoleEntry`).\n\n## 2.0.0-dev.42\n\n### Aggiunte\n- Gestione dei flag di privilegio dalla console Accessi:\n - un admin pu\xF2 assegnare o togliere sia `FLGADMIN` sia `FLGSUPER`;\n - un superutente senza `FLGADMIN` pu\xF2 gestire solo `FLGSUPER`;\n - i flag sono impostabili in creazione (utente locale o SSO) e in modifica profilo.\n- Eliminazione utenti con due modalit\xE0:\n - soft delete (`delete-user`, `STAREG=DELETE`) per admin e superutenti;\n - eliminazione definitiva (`delete-user-permanent`, solo admin) che rimuove in transazione utente e record collegati.\n- Gestione completa dell'utente dalla console (admin): anagrafica estesa (cellulare, lingua, avatar, metadata JSON), GDPR, scadenza password, tutti i filtri (`FILTRI`) e **impostazione diretta della password** (`POST /api/accessi/user/set-password/:codiceUtente`), senza accesso al database.\n- Scritture utente idempotenti: `UTENTI_CONFIG` in `UPDATE OR INSERT ... MATCHING (CODUTE)`; i campi non inviati restano invariati.\n- Policy password moderna quando si imposta una password (reset, set-password admin, bootstrap): minimo 8 caratteri con maiuscola, minuscola, cifra e carattere speciale, senza spazi e non comune. In caso di violazione risposta 400 con `code: ACCESSI_WEAK_PASSWORD` e `details`. La policy **non** si applica al login, cos\xEC le password preesistenti deboli continuano a funzionare.\n\n### Correzioni\n- Il backend verifica i flag in base al ruolo dell'utente connesso (`ensurePrivilegeFlagChanges`): inviare un flag non consentito restituisce 403.\n\n## 2.0.0-dev.33\n\n### Cambiamenti\n- Rimosse le logiche applicative residue dallo schema Accessi:\n - `MENU_GRP.NOMECAMPO`\n - `UTENTI.ENABLEIA`\n - `UTENTI_CONFIG.PAGDEF`\n - `UTENTI.CELLUTE`\n- Telefono canonico: `UTENTI_CONFIG.CELLULARE` (allargato a `VARCHAR(30)`).\n- Migrazione automatica: copia `UTENTI.CELLUTE` \u2192 `UTENTI_CONFIG.CELLULARE`, poi `DROP` delle colonne obsolete.\n- Ruoli chiariti:\n - `FLGSUPER`: superutente, riceve **tutte** le abilitazioni al livello massimo (30) dopo il login; in console vede **solo** la sezione **Utenti**.\n - `FLGADMIN`: accede a **tutta** la console (catalogo, token, SSO, utenti) ma **non** influisce sulle abilitazioni.\n- Aggiunta la sezione **Changelog** in console (sotto la Wiki).\n\n### Note\n- `FILTRI` e `FILTRI_TIPO` restano invariati (piano di migrazione futuro).\n- I backend che usavano i campi rimossi devono gestirli in tabelle proprie.\n\n## 2.0.0-dev.32\n\n### Cambiamenti\n- Rimosso il supporto ai flag applicativi in `UTENTI_CONFIG`: `FLGMOP`, `FLGPIANA`, `FLGADDETTI`, `FLGOSPITI`, `FLGPIANARFID`, `FLGCONTA`, `FLGCUBI`, `FLGCICLPASS`, `FLGDIPENDENTI`, `FLGINVENTARI`, `CAUMOV`, `NUMMAC`, `RAGSOCCLI`.\n- Rinominata la colonna `FLGADMINCONFIG` in `FLGADMIN` (con copia dei valori e `DROP`).\n\n## 2.0.0-dev.31\n\n### Aggiunte\n- Esposizione dei servizi Accessi all'host Express (`getAccessiModuleServices`) per delegare login, SSO e token senza reimplementarli.\n\n### Correzioni\n- `UserService.getUsers`: filtri estesi (`codiceUtente[]`, `numRep[]`, `codDipendente`, `cellulare`, `tipFil`, `flagSuper`).\n\n## 2.0.0-dev.24\n\n### Cambiamenti (breaking)\n- Chiavi JWT derivate per scopo (`access`, `reset`, `2fa`) e claim `typ` obbligatorio: gli access token precedenti non sono pi\xF9 validi.\n- Endpoint Allegati protetti di default.\n- Pool di connessioni attivo di default (`poolSize` 5).\n\n### Aggiunte\n- Service token macchina-a-macchina (gestione, guard, console).\n- CRUD completo dei cataloghi dalla console Accessi.\n- Supporto Firebird 2.5 nel migrator e nei test di integrazione.\n";
1670
1775
 
1671
1776
  // src/accessi-module/Console/help.ts
1672
1777
  var p2 = (text) => ({ kind: "p", text });