emilsoftware-utilities 2.0.0-dev.60 → 2.0.0-dev.61

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 (79) hide show
  1. package/dist/accessi-module/AccessiModule.d.ts +6 -0
  2. package/dist/accessi-module/AccessiModule.js.map +1 -1
  3. package/dist/accessi-module/Console/console.js +163 -4
  4. package/dist/accessi-module/Console/console.js.map +1 -1
  5. package/dist/accessi-module/Console/generated/accessiApi.d.ts +68 -5
  6. package/dist/accessi-module/Console/generated/accessiApi.js +84 -10
  7. package/dist/accessi-module/Console/generated/accessiApi.js.map +1 -1
  8. package/dist/accessi-module/Console/generated/model/createScopeRequest.d.ts +18 -0
  9. package/dist/accessi-module/Console/generated/model/createScopeRequest.js +9 -0
  10. package/dist/accessi-module/Console/generated/model/createScopeRequest.js.map +1 -0
  11. package/dist/accessi-module/Console/generated/model/getGroupsWithMenusParams.d.ts +4 -0
  12. package/dist/accessi-module/Console/generated/model/getGroupsWithMenusParams.js.map +1 -1
  13. package/dist/accessi-module/Console/generated/model/getMenuScopesResponse.d.ts +14 -0
  14. package/dist/accessi-module/Console/generated/model/getMenuScopesResponse.js +3 -0
  15. package/dist/accessi-module/Console/generated/model/getMenuScopesResponse.js.map +1 -0
  16. package/dist/accessi-module/Console/generated/model/getMenusParams.d.ts +12 -0
  17. package/dist/accessi-module/Console/generated/model/getMenusParams.js +9 -0
  18. package/dist/accessi-module/Console/generated/model/getMenusParams.js.map +1 -0
  19. package/dist/accessi-module/Console/generated/model/getScopesResponse.d.ts +14 -0
  20. package/dist/accessi-module/Console/generated/model/getScopesResponse.js +3 -0
  21. package/dist/accessi-module/Console/generated/model/getScopesResponse.js.map +1 -0
  22. package/dist/accessi-module/Console/generated/model/getUserRolesAndGrantsParams.d.ts +12 -0
  23. package/dist/accessi-module/Console/generated/model/getUserRolesAndGrantsParams.js +9 -0
  24. package/dist/accessi-module/Console/generated/model/getUserRolesAndGrantsParams.js.map +1 -0
  25. package/dist/accessi-module/Console/generated/model/index.d.ts +9 -0
  26. package/dist/accessi-module/Console/generated/model/index.js +9 -0
  27. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  28. package/dist/accessi-module/Console/generated/model/loginRequest.d.ts +2 -0
  29. package/dist/accessi-module/Console/generated/model/loginRequest.js.map +1 -1
  30. package/dist/accessi-module/Console/generated/model/scopeEntity.d.ts +12 -0
  31. package/dist/accessi-module/Console/generated/model/scopeEntity.js +9 -0
  32. package/dist/accessi-module/Console/generated/model/scopeEntity.js.map +1 -0
  33. package/dist/accessi-module/Console/generated/model/setMenuScopesRequest.d.ts +10 -0
  34. package/dist/accessi-module/Console/generated/model/setMenuScopesRequest.js +9 -0
  35. package/dist/accessi-module/Console/generated/model/setMenuScopesRequest.js.map +1 -0
  36. package/dist/accessi-module/Console/generated/model/setScopeMenusRequest.d.ts +10 -0
  37. package/dist/accessi-module/Console/generated/model/setScopeMenusRequest.js +9 -0
  38. package/dist/accessi-module/Console/generated/model/setScopeMenusRequest.js.map +1 -0
  39. package/dist/accessi-module/Console/generated/model/updateScopeRequest.d.ts +13 -0
  40. package/dist/accessi-module/Console/generated/model/updateScopeRequest.js +9 -0
  41. package/dist/accessi-module/Console/generated/model/updateScopeRequest.js.map +1 -0
  42. package/dist/accessi-module/Console/index.html +1 -0
  43. package/dist/accessi-module/Controllers/PermissionController.d.ts +11 -3
  44. package/dist/accessi-module/Controllers/PermissionController.js +208 -12
  45. package/dist/accessi-module/Controllers/PermissionController.js.map +1 -1
  46. package/dist/accessi-module/Dtos/LoginRequest.d.ts +2 -0
  47. package/dist/accessi-module/Dtos/LoginRequest.js +10 -0
  48. package/dist/accessi-module/Dtos/LoginRequest.js.map +1 -1
  49. package/dist/accessi-module/Dtos/ScopeDtos.d.ts +28 -0
  50. package/dist/accessi-module/Dtos/ScopeDtos.js +103 -0
  51. package/dist/accessi-module/Dtos/ScopeDtos.js.map +1 -0
  52. package/dist/accessi-module/Dtos/index.d.ts +1 -0
  53. package/dist/accessi-module/Dtos/index.js +1 -0
  54. package/dist/accessi-module/Dtos/index.js.map +1 -1
  55. package/dist/accessi-module/Services/AuthService/AuthService.d.ts +1 -1
  56. package/dist/accessi-module/Services/AuthService/AuthService.js +3 -3
  57. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  58. package/dist/accessi-module/Services/PermissionService/PermissionService.d.ts +27 -4
  59. package/dist/accessi-module/Services/PermissionService/PermissionService.js +177 -24
  60. package/dist/accessi-module/Services/PermissionService/PermissionService.js.map +1 -1
  61. package/dist/accessi-module/Services/UserService/UserService.d.ts +1 -0
  62. package/dist/accessi-module/Services/UserService/UserService.js +1 -1
  63. package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
  64. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js +2 -0
  65. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js.map +1 -1
  66. package/dist/accessi-module/database-updates/accessiSchema.d.ts +1 -1
  67. package/dist/accessi-module/database-updates/accessiSchema.js +12 -1
  68. package/dist/accessi-module/database-updates/accessiSchema.js.map +1 -1
  69. package/dist/accessi-module/database-updates/menuScopeSeed.d.ts +15 -0
  70. package/dist/accessi-module/database-updates/menuScopeSeed.js +338 -0
  71. package/dist/accessi-module/database-updates/menuScopeSeed.js.map +1 -0
  72. package/dist/accessi-module/database-updates/menuScopeSeeder.d.ts +8 -0
  73. package/dist/accessi-module/database-updates/menuScopeSeeder.js +131 -0
  74. package/dist/accessi-module/database-updates/menuScopeSeeder.js.map +1 -0
  75. package/dist/accessi-module/database-updates/runAccessiDbUpdate.js +4 -1
  76. package/dist/accessi-module/database-updates/runAccessiDbUpdate.js.map +1 -1
  77. package/dist/emilsoftware-utilities-2.0.0-dev.61.tgz +0 -0
  78. package/package.json +3 -3
  79. package/dist/emilsoftware-utilities-2.0.0-dev.60.tgz +0 -0
@@ -90,6 +90,10 @@ export type CatalogScriptChecksumPolicy = 'error' | 'warn' | 'reapply';
90
90
  * riconciliazione dello schema. Ogni script e tracciato nella tabella `ACCESSI_CATALOG_SCRIPT`,
91
91
  * quindi viene applicato una sola volta per database (idempotenza lato DB).
92
92
  */
93
+ export interface SystemSeedsOptions {
94
+ /** `false` disabilita il seed automatico, ad esempio per ambienti gestiti manualmente. */
95
+ enabled?: boolean;
96
+ }
93
97
  export interface CatalogScriptsOptions {
94
98
  /** Abilita l'esecuzione automatica al bootstrap. Default `false`. */
95
99
  enabled?: boolean;
@@ -170,6 +174,8 @@ export interface AccessiOptions {
170
174
  serviceTokens?: ServiceTokenOptions;
171
175
  /** Catalog migrations applicative eseguite dopo la riconciliazione dello schema. */
172
176
  catalogScripts?: CatalogScriptsOptions;
177
+ /** Seed di sistema eseguiti dopo la riconciliazione dello schema. Default: abilitati. */
178
+ systemSeeds?: SystemSeedsOptions;
173
179
  /** Bootstrap del primo admin tramite token casuale stampato a ogni avvio. */
174
180
  adminBootstrap?: AdminBootstrapOptions;
175
181
  /** Base URL pubblica usata solo per loggare gli indirizzi (console, swagger, API). */
@@ -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;AAyM/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 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 /** 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;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"]}
@@ -385,18 +385,107 @@ var deleteMenuType = async (codiceTipo, options) => {
385
385
  }
386
386
  );
387
387
  };
388
- var getGetUserRolesAndGrantsUrl = (codiceUtente) => {
389
- return `/api/accessi/permission/grants/${codiceUtente}`;
388
+ var getGetUserRolesAndGrantsUrl = (codiceUtente, params) => {
389
+ const normalizedParams = new URLSearchParams();
390
+ Object.entries(params || {}).forEach(([key, value]) => {
391
+ if (value === null) {
392
+ normalizedParams.append(key, "null");
393
+ } else if (value !== void 0) {
394
+ normalizedParams.append(key, value.toString());
395
+ }
396
+ });
397
+ return `/api/accessi/permission/grants/${codiceUtente}?${normalizedParams.toString()}`;
398
+ };
399
+ var getUserRolesAndGrants = async (codiceUtente, params, options) => {
400
+ return accessiFetch(
401
+ getGetUserRolesAndGrantsUrl(codiceUtente, params),
402
+ {
403
+ ...options,
404
+ method: "GET"
405
+ }
406
+ );
407
+ };
408
+ var getGetScopesUrl = () => {
409
+ return `/api/accessi/permission/scopes`;
410
+ };
411
+ var getScopes = async (options) => {
412
+ return accessiFetch(
413
+ getGetScopesUrl(),
414
+ {
415
+ ...options,
416
+ method: "GET"
417
+ }
418
+ );
419
+ };
420
+ var getCreateScopeUrl = () => {
421
+ return `/api/accessi/permission/scopes`;
422
+ };
423
+ var createScope = async (createScopeRequest, options) => {
424
+ return accessiFetch(
425
+ getCreateScopeUrl(),
426
+ {
427
+ ...options,
428
+ method: "POST",
429
+ body: JSON.stringify(
430
+ createScopeRequest
431
+ )
432
+ }
433
+ );
434
+ };
435
+ var getUpdateScopeUrl = (codiceScope) => {
436
+ return `/api/accessi/permission/scopes/${codiceScope}`;
437
+ };
438
+ var updateScope = async (codiceScope, updateScopeRequest, options) => {
439
+ return accessiFetch(
440
+ getUpdateScopeUrl(codiceScope),
441
+ {
442
+ ...options,
443
+ method: "PUT",
444
+ body: JSON.stringify(
445
+ updateScopeRequest
446
+ )
447
+ }
448
+ );
449
+ };
450
+ var getDeleteScopeUrl = (codiceScope) => {
451
+ return `/api/accessi/permission/scopes/${codiceScope}`;
390
452
  };
391
- var getUserRolesAndGrants = async (codiceUtente, options) => {
453
+ var deleteScope = async (codiceScope, options) => {
392
454
  return accessiFetch(
393
- getGetUserRolesAndGrantsUrl(codiceUtente),
455
+ getDeleteScopeUrl(codiceScope),
456
+ {
457
+ ...options,
458
+ method: "DELETE"
459
+ }
460
+ );
461
+ };
462
+ var getGetMenuScopesUrl = (codiceMenu) => {
463
+ return `/api/accessi/permission/menus/${codiceMenu}/scopes`;
464
+ };
465
+ var getMenuScopes = async (codiceMenu, options) => {
466
+ return accessiFetch(
467
+ getGetMenuScopesUrl(codiceMenu),
394
468
  {
395
469
  ...options,
396
470
  method: "GET"
397
471
  }
398
472
  );
399
473
  };
474
+ var getSetScopeMenusUrl = (codiceScope) => {
475
+ return `/api/accessi/permission/scopes/${codiceScope}/menus`;
476
+ };
477
+ var setScopeMenus = async (codiceScope, setScopeMenusRequest, options) => {
478
+ return accessiFetch(
479
+ getSetScopeMenusUrl(codiceScope),
480
+ {
481
+ ...options,
482
+ method: "PUT",
483
+ body: JSON.stringify(
484
+ setScopeMenusRequest
485
+ )
486
+ }
487
+ );
488
+ };
400
489
  var getGetUsersUrl = (params) => {
401
490
  const normalizedParams = new URLSearchParams();
402
491
  Object.entries(params || {}).forEach(([key, value]) => {
@@ -2013,6 +2102,7 @@ var routeSegment = {
2013
2102
  menuGroups: "menu-e-gruppi/gruppi",
2014
2103
  menuItems: "menu-e-gruppi/menu",
2015
2104
  menuTypes: "menu-e-gruppi/tipi-menu",
2105
+ menuScopes: "menu-e-gruppi/scope",
2016
2106
  ssoProviders: "sso/provider",
2017
2107
  ssoUsers: "sso/utenti",
2018
2108
  tokens: "token-di-servizio",
@@ -3273,6 +3363,74 @@ async function showMenuItems() {
3273
3363
  });
3274
3364
  });
3275
3365
  }
3366
+ async function showMenuScopes() {
3367
+ const scopes = result(await getScopes());
3368
+ render(`<div class="toolbar"><button id="new-scope">Nuovo scope</button><button id="reload-scopes" class="secondary">Aggiorna</button></div>
3369
+ <h2>Scope applicativi</h2><p class="form-help">Uno scope decide in quali applicazioni un menu e visibile. Un menu puo appartenere a piu scope.</p>
3370
+ <table class="entity-table"><thead><tr><th>Codice</th><th>Nome</th><th>Azioni</th></tr></thead><tbody>
3371
+ ${scopes.map((scope) => `<tr><td><code>${escapeHtml(scope.codice)}</code></td><td>${escapeHtml(scope.nome)}</td><td class="row-actions"><button type="button" class="secondary" data-scope-menus="${escapeHtml(scope.codice)}">Menu</button><button type="button" class="secondary" data-scope-edit="${escapeHtml(scope.codice)}">Rinomina</button><button type="button" class="danger-button" data-scope-delete="${escapeHtml(scope.codice)}">Elimina</button></td></tr>`).join("") || '<tr><td colspan="3">Nessuno scope configurato.</td></tr>'}
3372
+ </tbody></table>`);
3373
+ byId("new-scope").onclick = () => showScopeForm();
3374
+ byId("reload-scopes").onclick = () => handleAction(showMenuScopes);
3375
+ document.querySelectorAll("[data-scope-menus]").forEach((button) => {
3376
+ button.onclick = () => handleAction(() => showScopeMenus(button.dataset.scopeMenus ?? ""));
3377
+ });
3378
+ document.querySelectorAll("[data-scope-edit]").forEach((button) => {
3379
+ button.onclick = () => showScopeForm(scopes.find((scope) => scope.codice === button.dataset.scopeEdit));
3380
+ });
3381
+ document.querySelectorAll("[data-scope-delete]").forEach((button) => {
3382
+ button.onclick = () => handleAction(async () => {
3383
+ if (!window.confirm(`Eliminare lo scope ${button.dataset.scopeDelete}? Le associazioni ai menu verranno rimosse.`)) return;
3384
+ await deleteScope(button.dataset.scopeDelete ?? "");
3385
+ showNotice("Scope eliminato.");
3386
+ await showMenuScopes();
3387
+ });
3388
+ });
3389
+ }
3390
+ function showScopeForm(scope) {
3391
+ render(`<form id="scope-form"><h2>${scope ? "Rinomina scope" : "Nuovo scope"}</h2>
3392
+ <label>Codice<input name="codice" maxlength="20" required ${scope ? "readonly" : ""} value="${escapeHtml(scope?.codice ?? "")}"><span class="form-help">Es. STEX. Maiuscole, cifre e underscore.</span></label>
3393
+ <label>Nome<input name="nome" maxlength="100" required value="${escapeHtml(scope?.nome ?? "")}"><span class="form-help">Etichetta leggibile, es. SteX.</span></label>
3394
+ <div class="toolbar"><button>Salva</button><button type="button" class="secondary" id="cancel-scope">Annulla</button></div></form>`);
3395
+ const form = byId("scope-form");
3396
+ form.onsubmit = (event) => {
3397
+ event.preventDefault();
3398
+ handleAction(async () => {
3399
+ const values = formValues(form);
3400
+ const nome = values["nome"] ?? "";
3401
+ if (scope) {
3402
+ await updateScope(scope.codice, { nome });
3403
+ } else {
3404
+ await createScope({ codice: (values["codice"] ?? "").trim().toUpperCase(), nome });
3405
+ }
3406
+ showNotice("Scope salvato.");
3407
+ await showMenuScopes();
3408
+ });
3409
+ };
3410
+ byId("cancel-scope").onclick = () => handleAction(showMenuScopes);
3411
+ }
3412
+ async function showScopeMenus(codiceScope) {
3413
+ const [groupsResponse, assignedResponse] = await Promise.all([
3414
+ getGroupsWithMenus({ includeDisabled: true }),
3415
+ getMenuScopes(codiceScope)
3416
+ ]);
3417
+ const groups = result(groupsResponse);
3418
+ const assigned = new Set(result(assignedResponse));
3419
+ render(`<form id="scope-menus"><h2>Menu dello scope ${escapeHtml(codiceScope)}</h2><p class="form-help">Seleziona i menu visibili nelle applicazioni associate a questo scope. Il salvataggio sostituisce l'elenco.</p>
3420
+ ${groups.map((group) => `<fieldset class="grant-menu-group"><legend>${escapeHtml(group.descrizioneGruppo)}</legend>${group.menus.map((menu) => `<label class="check"><input type="checkbox" name="menu" value="${escapeHtml(menu.codiceMenu)}" ${assigned.has(menu.codiceMenu) ? "checked" : ""}><span>${escapeHtml(menu.descrizioneMenu)} <code>${escapeHtml(menu.codiceMenu)}</code></span></label>`).join("")}</fieldset>`).join("")}
3421
+ <div class="toolbar"><button>Salva</button><button type="button" class="secondary" id="cancel-scope-menus">Indietro</button></div></form>`);
3422
+ const form = byId("scope-menus");
3423
+ form.onsubmit = (event) => {
3424
+ event.preventDefault();
3425
+ handleAction(async () => {
3426
+ const selected = new FormData(form).getAll("menu").map((value) => String(value));
3427
+ await setScopeMenus(codiceScope, { menu: selected });
3428
+ showNotice("Menu dello scope aggiornati.");
3429
+ await showMenuScopes();
3430
+ });
3431
+ };
3432
+ byId("cancel-scope-menus").onclick = () => handleAction(showMenuScopes);
3433
+ }
3276
3434
  async function showMenuTypes() {
3277
3435
  const menuTypes = result(await getMenuTypes());
3278
3436
  render(`<div class="toolbar"><button id="new-menu-type">Nuovo tipo menu</button><button id="reload-menu-types" class="secondary">Aggiorna</button></div>
@@ -3677,6 +3835,7 @@ async function show(view) {
3677
3835
  menuGroups: showMenuGroups,
3678
3836
  menuItems: showMenuItems,
3679
3837
  menuTypes: showMenuTypes,
3838
+ menuScopes: showMenuScopes,
3680
3839
  ssoProviders: showSsoProviders,
3681
3840
  ssoUsers: showSsoUsers,
3682
3841
  tokens: showServiceTokens,