emilsoftware-utilities 2.0.0-dev.43 → 2.0.0-dev.44

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 (44) hide show
  1. package/dist/accessi-module/AccessiModule.d.ts +1 -1
  2. package/dist/accessi-module/AccessiModule.js.map +1 -1
  3. package/dist/accessi-module/Console/console.js +42 -19
  4. package/dist/accessi-module/Console/console.js.map +1 -1
  5. package/dist/accessi-module/Console/generated/accessiApi.d.ts +13 -3
  6. package/dist/accessi-module/Console/generated/accessiApi.js +11 -3
  7. package/dist/accessi-module/Console/generated/accessiApi.js.map +1 -1
  8. package/dist/accessi-module/Console/generated/model/adminBootstrapStatusResponse.d.ts +14 -0
  9. package/dist/accessi-module/Console/generated/model/adminBootstrapStatusResponse.js +3 -0
  10. package/dist/accessi-module/Console/generated/model/adminBootstrapStatusResponse.js.map +1 -0
  11. package/dist/accessi-module/Console/generated/model/adminBootstrapStatusResult.d.ts +10 -0
  12. package/dist/accessi-module/Console/generated/model/adminBootstrapStatusResult.js +9 -0
  13. package/dist/accessi-module/Console/generated/model/adminBootstrapStatusResult.js.map +1 -0
  14. package/dist/accessi-module/Console/generated/model/groupWithMenusEntity.d.ts +1 -1
  15. package/dist/accessi-module/Console/generated/model/groupWithMenusEntity.js.map +1 -1
  16. package/dist/accessi-module/Console/generated/model/index.d.ts +2 -0
  17. package/dist/accessi-module/Console/generated/model/index.js +2 -0
  18. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  19. package/dist/accessi-module/Console/generated/model/registerRequest.d.ts +1 -1
  20. package/dist/accessi-module/Console/generated/model/registerRequest.js.map +1 -1
  21. package/dist/accessi-module/Console/generated/model/userDto.d.ts +2 -2
  22. package/dist/accessi-module/Console/generated/model/userDto.js.map +1 -1
  23. package/dist/accessi-module/Console/index.html +1 -1
  24. package/dist/accessi-module/Console/wiki.js +1 -1
  25. package/dist/accessi-module/Console/wiki.js.map +1 -1
  26. package/dist/accessi-module/Controllers/AuthController.d.ts +1 -0
  27. package/dist/accessi-module/Controllers/AuthController.js +18 -0
  28. package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
  29. package/dist/accessi-module/Dtos/AdminBootstrapDtos.d.ts +7 -0
  30. package/dist/accessi-module/Dtos/AdminBootstrapDtos.js +18 -1
  31. package/dist/accessi-module/Dtos/AdminBootstrapDtos.js.map +1 -1
  32. package/dist/accessi-module/Dtos/GetGroupsWithMenusResponse.js +1 -1
  33. package/dist/accessi-module/Dtos/GetGroupsWithMenusResponse.js.map +1 -1
  34. package/dist/accessi-module/Dtos/RegisterRequest.js +1 -1
  35. package/dist/accessi-module/Dtos/RegisterRequest.js.map +1 -1
  36. package/dist/accessi-module/Dtos/UserDto.js +2 -2
  37. package/dist/accessi-module/Dtos/UserDto.js.map +1 -1
  38. package/dist/accessi-module/Services/UserService/UserService.js +3 -3
  39. package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
  40. package/dist/accessi-module/federated-auth/FederatedAuthController.js +4 -4
  41. package/dist/accessi-module/federated-auth/FederatedAuthController.js.map +1 -1
  42. package/dist/emilsoftware-utilities-2.0.0-dev.44.tgz +0 -0
  43. package/package.json +1 -1
  44. package/dist/emilsoftware-utilities-2.0.0-dev.43.tgz +0 -0
@@ -45,9 +45,9 @@ export interface UserDto {
45
45
  dataScadenzaPassword?: string;
46
46
  /** Email dell'utente. */
47
47
  email: string;
48
- /** Flag che indica se l'utente è configuratore */
48
+ /** Flag che indica se l'utente è configuratore */
49
49
  flagAdmin?: boolean;
50
- /** Flag che indica se l'autenticazione a due fattori è attivata. */
50
+ /** Flag che indica se l'autenticazione a due fattori è attivata. */
51
51
  flagDueFattori?: boolean;
52
52
  /** Flag per l'accettazione del GDPR. */
53
53
  flagGdpr?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"userDto.js","sourceRoot":"","sources":["../../../../../src/accessi-module/Console/generated/model/userDto.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Generated by orval v6.31.0 🍺\n * Do not edit manually.\n * Accessi API\n * OpenAPI spec version: 1.0\n */\nimport type { Permission } from './permission';\nimport type { UserDtoStatoRegistrazione } from './userDtoStatoRegistrazione';\n\nexport interface UserDto {\n /**\n * Avatar (URL o base64).\n * @nullable\n */\n avatar?: string | null;\n /**\n * Numero di cellulare.\n * @nullable\n */\n cellulare?: string | null;\n /** Codice dell'agente associato */\n codAge?: number;\n /** Codice del cliente collegato */\n codCliCol?: number;\n /** Lista di codici clienti separati da virgola */\n codClienti?: string;\n /** Codice Cliente padre (codcli di esgest) */\n codCliSuper?: number;\n /** Codice dipendente di esgest */\n codDip?: number;\n /** Codice lingua preferito. */\n codiceLingua?: string;\n /** Codice identificativo univoco dell'utente. */\n codiceUtente: number;\n /** Codice del vettore */\n codVet?: number;\n /** Cognome dell'utente. */\n cognome?: string;\n /** Data di accettazione del GDPR. */\n dataGdpr?: string;\n /** Data di inserimento dell'utente nel sistema. */\n dataInserimento?: string;\n /** Ultima data di accesso dell'utente. */\n dataLastLogin?: string;\n /** Data scadenza password. */\n dataScadenzaPassword?: string;\n /** Email dell'utente. */\n email: string;\n /** Flag che indica se l'utente è configuratore */\n flagAdmin?: boolean;\n /** Flag che indica se l'autenticazione a due fattori è attivata. */\n flagDueFattori?: boolean;\n /** Flag per l'accettazione del GDPR. */\n flagGdpr?: boolean;\n /** Flag superutente. */\n flagSuper?: boolean;\n /** Idx personale */\n idxPers?: number;\n /** idx della postazione nella tabella ANTENNE_POS di PROLAV */\n idxPos?: number;\n /** Metadata JSON personalizzato. */\n jsonMetadata?: string;\n /** Chiave di registrazione dell'utente. */\n keyRegistrazione?: string;\n /** Nome dell'utente. */\n nome?: string;\n /** Numero del reparto associato */\n numRep?: number;\n /**\n * Pagina di default mostrata all'utente dopo il login (percorso o codice pagina).\n * @maxLength 50\n * @nullable\n */\n paginaDefault?: string | null;\n /** Consente accesso con il solo codice email. Richiede flagDueFattori e viene gestito dagli amministratori. */\n passwordlessLoginEnabled?: boolean;\n /** Abilita il login locale con password. Se false, l utente puo autenticarsi solo tramite identita SSO attive. */\n passwordLoginEnabled?: boolean;\n /** Permessi assegnati all'utente. */\n permissions?: Permission[];\n /** Progressivo identificativo del filtro */\n progressivo?: number;\n /** Ruoli assegnati all'utente. */\n roles?: string[];\n /** Stato della registrazione dell'utente. */\n statoRegistrazione?: UserDtoStatoRegistrazione;\n /** Tipo di filtro applicato */\n tipFil?: number;\n}\n"]}
1
+ {"version":3,"file":"userDto.js","sourceRoot":"","sources":["../../../../../src/accessi-module/Console/generated/model/userDto.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Generated by orval v6.31.0 🍺\n * Do not edit manually.\n * Accessi API\n * OpenAPI spec version: 1.0\n */\nimport type { Permission } from './permission';\nimport type { UserDtoStatoRegistrazione } from './userDtoStatoRegistrazione';\n\nexport interface UserDto {\n /**\n * Avatar (URL o base64).\n * @nullable\n */\n avatar?: string | null;\n /**\n * Numero di cellulare.\n * @nullable\n */\n cellulare?: string | null;\n /** Codice dell'agente associato */\n codAge?: number;\n /** Codice del cliente collegato */\n codCliCol?: number;\n /** Lista di codici clienti separati da virgola */\n codClienti?: string;\n /** Codice Cliente padre (codcli di esgest) */\n codCliSuper?: number;\n /** Codice dipendente di esgest */\n codDip?: number;\n /** Codice lingua preferito. */\n codiceLingua?: string;\n /** Codice identificativo univoco dell'utente. */\n codiceUtente: number;\n /** Codice del vettore */\n codVet?: number;\n /** Cognome dell'utente. */\n cognome?: string;\n /** Data di accettazione del GDPR. */\n dataGdpr?: string;\n /** Data di inserimento dell'utente nel sistema. */\n dataInserimento?: string;\n /** Ultima data di accesso dell'utente. */\n dataLastLogin?: string;\n /** Data scadenza password. */\n dataScadenzaPassword?: string;\n /** Email dell'utente. */\n email: string;\n /** Flag che indica se l'utente è configuratore */\n flagAdmin?: boolean;\n /** Flag che indica se l'autenticazione a due fattori è attivata. */\n flagDueFattori?: boolean;\n /** Flag per l'accettazione del GDPR. */\n flagGdpr?: boolean;\n /** Flag superutente. */\n flagSuper?: boolean;\n /** Idx personale */\n idxPers?: number;\n /** idx della postazione nella tabella ANTENNE_POS di PROLAV */\n idxPos?: number;\n /** Metadata JSON personalizzato. */\n jsonMetadata?: string;\n /** Chiave di registrazione dell'utente. */\n keyRegistrazione?: string;\n /** Nome dell'utente. */\n nome?: string;\n /** Numero del reparto associato */\n numRep?: number;\n /**\n * Pagina di default mostrata all'utente dopo il login (percorso o codice pagina).\n * @maxLength 50\n * @nullable\n */\n paginaDefault?: string | null;\n /** Consente accesso con il solo codice email. Richiede flagDueFattori e viene gestito dagli amministratori. */\n passwordlessLoginEnabled?: boolean;\n /** Abilita il login locale con password. Se false, l utente puo autenticarsi solo tramite identita SSO attive. */\n passwordLoginEnabled?: boolean;\n /** Permessi assegnati all'utente. */\n permissions?: Permission[];\n /** Progressivo identificativo del filtro */\n progressivo?: number;\n /** Ruoli assegnati all'utente. */\n roles?: string[];\n /** Stato della registrazione dell'utente. */\n statoRegistrazione?: UserDtoStatoRegistrazione;\n /** Tipo di filtro applicato */\n tipFil?: number;\n}\n"]}
@@ -37,7 +37,7 @@
37
37
  </form>
38
38
  <button id="restart-login" type="button" class="secondary" hidden>Ricomincia</button>
39
39
  <p id="login-error" class="error" role="alert"></p>
40
- <details id="bootstrap-admin-panel" class="bootstrap-admin">
40
+ <details id="bootstrap-admin-panel" class="bootstrap-admin" hidden>
41
41
  <summary>Crea utente admin (bootstrap)</summary>
42
42
  <p class="form-help">Usa il token di avvio stampato nel log del backend. Disponibile solo se <code>adminBootstrap.enabled</code> è attivo. Non richiede conferma email.</p>
43
43
  <form id="bootstrap-admin-form">
@@ -608,7 +608,7 @@ exports.WIKI_SECTIONS = [
608
608
  note('info', `Gestione dei flag`, `Dalla scheda utente i flag sono mostrati in base al ruolo di chi è connesso: un admin può assegnare o togliere sia il flag Admin sia il flag Superutente; un superutente senza flag Admin può gestire solo il flag Superutente. I flag si impostano anche in creazione (utente locale o SSO).`),
609
609
  note('warn', `Ultimo admin`, `Per non restare chiusi fuori dalla console, l'ultimo admin attivo non può essere disattivato, bloccato, eliminato né privato del flag Admin: l'operazione restituisce 400 finché non viene promosso un altro admin.`),
610
610
  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).`),
611
- note('info', `Bootstrap admin`, `Con adminBootstrap.enabled il backend stampa a ogni avvio un token casuale (monouso, valido fino al riavvio). Nella schermata di login della console trovi "Crea utente admin": incolla il token e crea il primo admin senza email di conferma. La password è opzionale: se assente viene generata e mostrata una sola volta.`),
611
+ 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 è 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 è opzionale: se assente viene generata e mostrata una sola volta.`),
612
612
  note('ok', `Auto-ingresso dal frontend`, `Se un frontend ha già una sessione Accessi valida e l'utente è abilitato alla console, può 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 è utilizzabile una sola volta (endpoint di scambio protetto da rate limit).`),
613
613
  ],
614
614
  },
@@ -1 +1 @@
1
- {"version":3,"file":"wiki.js","sourceRoot":"","sources":["../../../src/accessi-module/Console/wiki.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Contenuto della Wiki della console Accessi.\n *\n * I contenuti sono dati strutturati (non HTML) così da poter essere resi in modo\n * coerente dalla console e convertiti in un digest compatto per l'IA. Nessun\n * segreto o dato specifico di un ambiente deve finire qui.\n */\n\nexport type WikiTone = 'info' | 'ok' | 'warn' | 'danger';\n\nexport type WikiBlock =\n | { kind: 'p'; text: string }\n | { kind: 'h'; text: string }\n | { kind: 'list'; items: string[]; ordered?: boolean }\n | { kind: 'code'; language: string; title?: string; code: string; variant?: 'good' | 'bad' }\n | { kind: 'callout'; tone: WikiTone; title?: string; text: string }\n | { kind: 'table'; head: string[]; rows: string[][] };\n\nexport interface WikiSection {\n id: string;\n group: string;\n title: string;\n summary: string;\n blocks: WikiBlock[];\n}\n\nconst p = (text: string): WikiBlock => ({ kind: 'p', text });\nconst h = (text: string): WikiBlock => ({ kind: 'h', text });\nconst list = (items: string[], ordered = false): WikiBlock => ({ kind: 'list', items, ordered });\nconst code = (language: string, codeText: string, title?: string, variant?: 'good' | 'bad'): WikiBlock => ({ kind: 'code', language, title, code: codeText, variant });\nconst good = (language: string, codeText: string, title?: string): WikiBlock => code(language, codeText, title, 'good');\nconst bad = (language: string, codeText: string, title?: string): WikiBlock => code(language, codeText, title, 'bad');\nconst note = (tone: WikiTone, title: string, text: string): WikiBlock => ({ kind: 'callout', tone, title, text });\nconst table = (head: string[], rows: string[][]): WikiBlock => ({ kind: 'table', head, rows });\n\nexport const WIKI_SECTIONS: WikiSection[] = [\n // -------------------------------------------------------------------------\n {\n id: 'panoramica',\n group: 'Per iniziare',\n title: 'Panoramica e architettura',\n summary: 'Che cosa fa Accessi, come si incastra nel backend e come scorre una richiesta.',\n blocks: [\n p(`Accessi è un modulo Node/NestJS che aggiunge a un backend già esistente: autenticazione JWT, autorizzazione per ruoli e menu, gestione utenti, reset password, codici email (2FA e passwordless), SSO generico, token macchina-a-macchina e una console amministrativa. Il database è Firebird/InterBase.`),\n h(`Che cosa possiede il modulo`),\n list([\n `Le rotte sotto /api/accessi/* (login, utenti, ruoli, menu, filtri, SSO, token, console).`,\n `Lo schema database del dominio Accessi, riconciliato automaticamente al bootstrap.`,\n `Il middleware di autorizzazione authorizeAccessi per le rotte della tua applicazione.`,\n `La console web su /api/accessi/console.`,\n ]),\n h(`Che cosa resta alla tua applicazione`),\n list([\n `Il proprio frontend e le proprie rotte di business.`,\n `La validazione dei token dei provider SSO esterni (Accessi non li vede).`,\n `I segreti: jwtOptions.secret, SMTP, database ed encryptionKey non raggiungono mai il browser.`,\n ]),\n h(`Flusso di una richiesta protetta`),\n list([\n `Il client chiama una rotta della tua app con header Authorization: Bearer <jwt>.`,\n `La tua rotta esegue authorizeAccessi, che verifica firma HS256, scadenza e stato utente corrente.`,\n `Se passi un requisito, il modulo carica ruoli e grant dell'utente e li valuta.`,\n `Su successo req.user, req.data e (se richiesto) req.userGrants sono popolati; altrimenti risponde con il contratto errore Accessi.`,\n ], true),\n note('info', `Due modalità di bootstrap`, `Express esistente: initializeAccessiModule(app, options). NestJS esistente: AccessiModule.forRoot(options) nei tuoi imports. Non usarle entrambe.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'installazione',\n group: 'Per iniziare',\n title: 'Installazione',\n summary: 'Dipendenze, peer e build.',\n blocks: [\n code('bash', `npm install emilsoftware-utilities`, 'Installazione'),\n p(`La libreria dipende da node-firebird per il database e da Express 5 come peer per il montaggio. È consigliato Node 18 o superiore.`),\n note('warn', `Un solo modulo per processo`, `AccessiModule è @Global(): inizializzalo una sola volta per processo. In caso di più istanze, usa un processo per istanza.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'bootstrap-express',\n group: 'Per iniziare',\n title: 'Bootstrap su Express esistente',\n summary: 'initializeAccessiModule: ordine corretto e attesa prima di listen.',\n blocks: [\n p(`Chiama initializeAccessiModule prima di aprire la porta HTTP. Durante il bootstrap, authorizeAccessi risponde 503 ACCESSI_AUTH_INITIALIZING invece di autorizzare richieste parziali.`),\n good('ts', [\n \"import express from 'express';\",\n \"import { initializeAccessiModule, authorizeAccessi } from 'emilsoftware-utilities';\",\n '',\n 'const app = express();',\n 'app.use(express.json());',\n '',\n 'await initializeAccessiModule(app, options); // prima di app.listen',\n '',\n \"app.get('/api/ordini', authorizeAccessi, (req, res) => {\",\n ' res.json({ utente: req.user });',\n '});',\n '',\n \"app.get('/api/ordini/scrivi', (req, res, next) =>\",\n ' authorizeAccessi(req, res, next, {',\n \" requirements: [{ menuCode: 'ORDINI', minPermissionLevel: 20 }],\",\n ' }),',\n '(req, res) => res.json({ ok: true }));',\n '',\n 'app.listen(3000);',\n ].join('\\n'), 'Express + Accessi'),\n bad('ts', [\n 'const app = express();',\n 'app.listen(3000); // porta aperta prima del bootstrap',\n 'await initializeAccessiModule(app, options); // le richieste nel frattempo ricevono 503',\n ].join('\\n'), 'Non aprire la porta prima del bootstrap'),\n h(`Rotte montate`),\n p(`Accessi intercetta solo /api/accessi/*, /accessi/swagger* e /accessi/swagger.json. Tutte le altre rotte passano intatte al tuo stack Express.`),\n note('ok', `req.user`, `authorizeAccessi popola req.user con il payload utente corrente (utente, codiceUtente, flag) e req.data come alias storico. req.userGrants è presente solo se hai richiesto dei requisiti.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'bootstrap-nest',\n group: 'Per iniziare',\n title: 'Bootstrap su NestJS esistente',\n summary: 'AccessiModule.forRoot e uso dei provider esportati.',\n blocks: [\n p(`Se la tua applicazione è già NestJS, importa AccessiModule.forRoot(options). Il modulo è @Global e riesporta i servizi (AuthService, UserService, PermissionService, FederatedAuthService, ServiceTokenService, ServiceTokenGuard, AuthenticateGenService).`),\n good('ts', [\n \"import { Module } from '@nestjs/common';\",\n \"import { AccessiModule } from 'emilsoftware-utilities';\",\n '',\n '@Module({',\n ' imports: [AccessiModule.forRoot(options)],',\n '})',\n 'export class AppModule {}',\n ].join('\\n'), 'app.module.ts'),\n p(`Il global prefix api e le rotte /api/accessi/* sono gestiti dal modulo. Le tue rotte Nest si proteggono con JwtSimpleGuard usando il JWT Accessi.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'opzioni',\n group: 'Configurazione',\n title: 'AccessiOptions (riferimento completo)',\n summary: 'Ogni campo di configurazione con tipo, obbligo e note.',\n blocks: [\n table(\n ['Campo', 'Tipo', 'Obbligo', 'Note'],\n [\n ['databaseOptions', 'Options (node-firebird)', 'sì', 'Connessione Firebird al database Accessi.'],\n ['confirmationEmailUrl', 'string', 'sì', 'Base URL usata per il reset password.'],\n ['confirmationEmailReturnUrl', 'string', 'sì', 'URL di ritorno dopo il reset.'],\n ['confirmationEmailPrefix', 'string', 'no', 'Prefisso passato alla pagina di reset.'],\n ['customResetPage', 'string', 'no', 'Sostituisce la pagina HTML di reset inclusa.'],\n ['encryptionKey', 'string', 'sì', 'Compatibilità e migrazione delle password legacy.'],\n ['mockDemoUser', 'boolean', 'sì', 'Utenti fittizi admin e demo solo fuori produzione.'],\n ['passwordExpiration', 'boolean', 'no', 'Attiva il controllo della scadenza password al login.'],\n ['passwordExpirationDays', 'number', 'no', 'Valore predefinito: 90 giorni.'],\n ['legacyPasswordMigrationOnStartup', 'boolean', 'no', 'Predefinito true: converte le password legacy.'],\n ['autoUpdateDatabase', 'boolean', 'no', 'Predefinito true: riconcilia lo schema al boot.'],\n ['jwtOptions', '{ secret, expiresIn }', 'sì', 'Firma HS256 e durata dei JWT Accessi.'],\n ['emailOptions', 'EmailOptions', 'sì', 'SMTP per reset e codici email.'],\n ['publicAuthRateLimit', 'PublicAuthRateLimitOptions', 'no', 'Limiti in-memory sugli endpoint pubblici.'],\n ['publicRegistration', '{ enabled }', 'no', 'Registrazione pubblica: disattivata per impostazione predefinita.'],\n ['federatedAuthentication', 'FederatedAuthenticationOptions', 'no', `Abilita l'SSO generico.`],\n ['extensionFieldsOptions', 'ExtensionFieldsOptions[]', 'no', 'Tabelle esterne allegate al profilo.'],\n ['serviceTokens', '{ enabled, defaultTtlDays }', 'no', 'Token macchina-a-macchina, abilitati per impostazione predefinita.'],\n ['catalogScripts', '{ enabled, folder, ... }', 'no', 'Catalog migrations SQL eseguite dopo la riconciliazione dello schema.'],\n ['adminBootstrap', '{ enabled, superUser }', 'no', 'Token casuale a ogni avvio per creare il primo admin dalla console.'],\n ['publicBaseUrl', 'string', 'no', 'Base URL usata solo per loggare console/swagger/API.'],\n ],\n ),\n good('ts', [\n \"import type { AccessiOptions } from 'emilsoftware-utilities';\",\n '',\n 'export const options: AccessiOptions = {',\n ' databaseOptions: {',\n ' host: process.env.ACCESSI_DB_HOST!,',\n ' port: Number(process.env.ACCESSI_DB_PORT ?? 3050),',\n ' database: process.env.ACCESSI_DB_NAME!,',\n ' user: process.env.ACCESSI_DB_USER!,',\n ' password: process.env.ACCESSI_DB_PASSWORD!,',\n ' },',\n \" confirmationEmailUrl: 'https://app.example.com',\",\n \" confirmationEmailReturnUrl: 'https://app.example.com/login',\",\n ' encryptionKey: process.env.ACCESSI_ENCRYPTION_KEY!,',\n ' mockDemoUser: false,',\n ' passwordExpiration: true,',\n ' passwordExpirationDays: 90,',\n ' jwtOptions: {',\n \" secret: process.env.ACCESSI_JWT_SECRET!, // almeno 32 caratteri casuali, unico per ambiente\",\n \" expiresIn: '24h',\",\n ' },',\n ' emailOptions: {',\n ' host: process.env.SMTP_HOST!,',\n ' port: Number(process.env.SMTP_PORT ?? 587),',\n ' secure: false,',\n ' requireTLS: true,',\n ' tls: { rejectUnauthorized: true },',\n \" from: 'no-reply@example.com',\",\n ' auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS! },',\n ' },',\n ' publicRegistration: { enabled: false },',\n ' serviceTokens: { enabled: true, defaultTtlDays: 365 },',\n '};',\n ].join('\\n'), 'Configurazione tipica'),\n bad('ts', [\n \"jwtOptions: { secret: 'accessi', expiresIn: '24h' }, // segreto debole e condiviso\",\n 'mockDemoUser: true, // MAI in produzione',\n ].join('\\n'), 'Errori comuni'),\n note('danger', `Segreti`, `Non versionare i segreti e non passarli al frontend. jwtOptions.secret deve essere identico tra le istanze del backend e diverso da quello dei provider SSO.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'database',\n group: 'Configurazione',\n title: 'Schema e aggiornamento del database',\n summary: 'Tabelle, riconciliazione automatica e CLI.',\n blocks: [\n p(`Accessi possiede lo schema del dominio. Con autoUpdateDatabase true (predefinito) lo riconcilia a ogni avvio; con false verifica comunque la compatibilità in sola lettura e blocca gli avvii incompatibili.`),\n h(`Tabelle principali`),\n list([\n `PARAMETRI, UTENTI, UTENTI_CONFIG, UTENTI_PWD, UTENTI_OLDPWD, UTENTI_GDPR`,\n `RUOLI, MENU_GRP, MENU_TIPI, MENU, ABILITAZIONI, RUOLI_MNU, UTENTI_RUOLI`,\n `FILTRI, FILTRI_TIPO`,\n `SSO_PROVIDER, UTENTI_IDENTITA_EXT`,\n `ACCESSI_2FA, ACCESSI_SERVICE_TOKEN, ACCESSI_CATALOG_SCRIPT`,\n ]),\n code('bash', `npm run db:update:accessi # riconcilia lo schema\\nnpm run db:check:accessi # verifica senza modificare`, 'CLI schema'),\n note('warn', `Versioni`, `La libreria richiede uno schema compatibile con la versione corrente (ACCESSI_SCHEMA_VERSION). Se la verifica fallisce, leggi il codice errore ACCESSI_DATABASE_SCHEMA_OUTDATED ed esegui db:update:accessi.`),\n note('info', `Schema esatto`, `La riconciliazione allinea le tabelle Accessi a \"niente in piu, niente in meno\": le colonne non previste vengono segnalate come incompatibili e rimosse automaticamente (dopo rename e copie dei dati legacy). Gli indici, i trigger e i CHECK extra non previsti non vengono tolti, ma un indice non previsto su una tabella Accessi viene riportato come avviso senza bloccare l'avvio.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'catalog-migrations',\n group: 'Configurazione',\n title: 'Catalog migrations SQL',\n summary: 'Allinea menu, ruoli e cataloghi su piu database in modo idempotente.',\n blocks: [\n p(`Gli script SQL applicativi (menu, ruoli, tipi, cataloghi) restano nel repository del backend ospitante e Accessi li esegue dopo la riconciliazione dello schema. Ogni script viene tracciato nella tabella ACCESSI_CATALOG_SCRIPT (nome + checksum), quindi viene applicato una sola volta per database.`),\n h(`Configurazione`),\n good('ts', [\n 'catalogScripts: {',\n \" enabled: true,\",\n \" folder: './db/accessi-catalog', // versionata nel repo del backend\",\n \" onChecksumMismatch: 'error', // 'error' (default) | 'warn' | 'reapply'\",\n \" stopOnError: true,\",\n '}',\n ].join('\\n'), 'AccessiOptions'),\n list([\n 'Gli script sono ordinati per percorso (usa prefissi numerici: 0001_, 0002_, ...).',\n 'Devono essere idempotenti e ripetibili: UPDATE OR INSERT ... MATCHING, MERGE, DELETE+INSERT mirati.',\n 'Non includere utenti: le catalog migrations allineano solo catalogo e configurazione.',\n 'Supportati stringhe, commenti e statement multipli; SET TERM non serve perché gli script sono solo DML.',\n ]),\n note('danger', `Solo righe (DML)`, `Vincolo tassativo: uno script puo eseguire solo INSERT, UPDATE, DELETE, MERGE, SELECT, EXECUTE. Qualunque DDL (CREATE/ALTER/DROP, trigger, procedure, GRANT, transazioni) viene rifiutato. Lo schema si aggiorna solo con db:update:accessi.`),\n note('info', `File non conformi`, `Un file con statement non consentiti non viene applicato ne registrato nel ledger; il run prosegue con gli altri e il report riporta una voce per ogni file con i relativi messaggi di errore (visibili anche con db:catalog:accessi:check).`),\n note('info', `allowDdl`, `Disattivato di default. Impostarlo a true disabilita la validazione e permette DDL: usarlo solo consapevolmente e mai come sostituto dell'updater dello schema.`),\n code('bash', `# Applica (schema + catalog migrations) su un database\\nACCESSI_CATALOG_FOLDER=./db/accessi-catalog npm run db:catalog:accessi\\n\\n# Elenca solo quelle da applicare / modificate\\nACCESSI_CATALOG_FOLDER=./db/accessi-catalog npm run db:catalog:accessi:check`, 'CLI catalog migrations'),\n note('info', `Idempotenza`, `Uno script gia applicato con lo stesso checksum viene saltato. Se il file cambia dopo l'applicazione, con policy error il run si interrompe: non modificare uno script applicato, aggiungine uno nuovo.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'login',\n group: 'Autenticazione',\n title: 'Login locale e JWT',\n summary: 'Endpoint di login, risposta e gestione della password scaduta.',\n blocks: [\n code('http', `POST /api/accessi/auth/login\\nContent-Type: application/json\\n\\n{ \"email\": \"utente@example.com\", \"password\": \"Segreta123!\" }`, 'Login con password'),\n p(`La risposta include Result.token (il JWT) e il profilo utente con ruoli, permessi e filtri. Se l'account è passwordless, ometti la password: il server risponde con un challenge via email (vedi la sezione su 2FA e passwordless).`),\n good('ts', [\n \"const res = await fetch('/api/accessi/auth/login', {\",\n \" method: 'POST',\",\n \" headers: { 'Content-Type': 'application/json' },\",\n ' body: JSON.stringify({ email, password }),',\n '});',\n 'const body = await res.json();',\n 'if (body.Result?.challenge) { /* richiedi il codice */ }',\n 'const token = body.Result?.token;',\n ].join('\\n'), 'Chiamata dal frontend'),\n note('warn', `Password scaduta`, `Con passwordExpiration attivo, una password scaduta produce una PasswordExpiredResponse con code PASSWORD_EXPIRED. Il client deve avviare il reset della password.`),\n h(`Verifica del token`),\n code('http', `POST /api/accessi/auth/get-user-by-token\\nContent-Type: application/json\\n\\n{ \"token\": \"<jwt>\" }`, 'get-user-by-token'),\n p(`Usalo per verificare un JWT e ottenere il profilo corrente (utile al bootstrap di una SPA). Restituisce Result.userData.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'passwordless-2fa',\n group: 'Autenticazione',\n title: 'Codici email, 2FA e passwordless',\n summary: 'Verifica in due passi, challenge e policy per utente.',\n blocks: [\n p(`La verifica con codice email è per-utente (flagDueFattori / UTENTI_CONFIG.FLG2FATT) ed è disattivata per impostazione predefinita. Con passwordlessLoginEnabled (FLGPWDLESS, richiede la 2FA attiva) l'accesso avviene con il solo codice email.`),\n table(\n ['Configurazione', 'Accesso locale', 'Accesso SSO'],\n [\n ['2FA disattivata', 'Password', 'Provider'],\n ['2FA attiva', 'Password + codice email', 'Provider + codice email'],\n ['2FA + passwordless', 'Solo codice email', 'Provider + codice email'],\n ],\n ),\n code('json', `{\\n \"Result\": {\\n \"challenge\": {\\n \"twoFactorRequired\": true,\\n \"challengeId\": \"64-caratteri-esadecimali\",\\n \"method\": \"email\",\\n \"expiresAt\": \"2026-09-11T14:00:00.000Z\",\\n \"resendAfterSeconds\": 60\\n }\\n }\\n}`, 'Risposta con challenge'),\n code('http', `POST /api/accessi/auth/two-factor/verify\\n{ \"challengeId\": \"...\", \"code\": \"012345\" }\\n\\nPOST /api/accessi/auth/two-factor/resend\\n{ \"challengeId\": \"...\" }`, 'Verifica e reinvio'),\n list([\n `Il codice ha 6 cifre, dura 10 minuti, consente 5 tentativi totali e al massimo 3 invii per challenge.`,\n `Fino alla verifica non sono presenti token, profilo o grant.`,\n `Il challenge non è un JWT e non va usato nell'header Authorization.`,\n ]),\n bad('ts', [\n '// Sbagliato: usare il challenge come token',\n \"fetch('/api/ordini', { headers: { Authorization: 'Bearer ' + challengeId } }); // 401\",\n ].join('\\n'), 'Il challenge non è un token'),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'reset-password',\n group: 'Autenticazione',\n title: 'Reset password',\n summary: 'Invio email e conferma con token monouso.',\n blocks: [\n code('http', `POST /api/accessi/email/send-reset-password-email\\n{ \"email\": \"utente@example.com\" }\\n\\nPOST /api/accessi/auth/confirm-reset-password/{token}\\n{ \"newPassword\": \"NuovaSegreta123!\" }`, 'Reset in due passi'),\n p(`L'invio non rivela se l'email esiste (anti-enumerazione). Il token di reset è monouso ed è firmato con una chiave dedicata.`),\n note('warn', `Policy password (solo quando si imposta)`, `Reset, set-password admin e bootstrap richiedono almeno 8 caratteri con maiuscola, minuscola, cifra e carattere speciale, senza spazi e non comuni. Se non conforme l'API risponde 400 con code ACCESSI_WEAK_PASSWORD e l'elenco dei requisiti in details. La policy NON si applica al login: le password preesistenti eventualmente deboli continuano ad autenticare e vengono migrate senza blocco.`),\n note('info', `Pagina personalizzata`, `Imposta customResetPage per usare la tua pagina di reset invece di quella inclusa. confirmationEmailReturnUrl e confirmationEmailPrefix controllano il ritorno.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'middleware',\n group: 'Autorizzazione',\n title: 'authorizeAccessi (middleware)',\n summary: 'Proteggere le rotte della tua app con il JWT Accessi.',\n blocks: [\n p(`authorizeAccessi è un middleware Express che verifica il JWT, ricarica lo stato utente corrente e applica i requisiti. Su successo prosegue; su errore risponde direttamente con il contratto Accessi (non chiamare un tuo next(error)).`),\n good('ts', [\n \"app.get('/api/report',\",\n ' (req, res, next) => authorizeAccessi(req, res, next, {',\n \" requirements: [{ menuCode: 'REPORT', minPermissionLevel: 10 }],\",\n ' }),',\n ' (req, res) => res.json({ user: req.user, grants: req.userGrants }),',\n ');',\n ].join('\\n'), 'Rotta protetta'),\n h(`Che cosa trovi nella request`),\n list([\n `req.user e req.data: payload utente corrente (alias storici).`,\n `req.userGrants: grant effettivi, presente solo quando hai richiesto dei requisiti.`,\n ]),\n bad('ts', [\n \"app.get('/api/report', authorizeAccessi, (req, res) => {\",\n ' // Sbagliato: assumere che req.userGrants esista senza requisiti',\n ' const grants = req.userGrants.grants; // possibile TypeError',\n '});',\n ].join('\\n'), 'Non dare per scontato req.userGrants'),\n note('danger', `Lo stato utente è sempre ricontrollato`, `Un JWT valido non basta: se l'utente è stato bloccato, eliminato o ha cambiato policy, il middleware risponde 401 AUTH_USER_DISABLED.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'requisiti',\n group: 'Autorizzazione',\n title: 'Requisiti e livelli di abilitazione',\n summary: 'DSL accessiRequirement: permission, and, or, not, custom.',\n blocks: [\n p(`I requisiti si compongono con accessiRequirement. La forma storica requirements è un AND di permessi. Il livello è TipoAbilitazione: NESSUNA=0, LETTURA=10, SCRITTURA=20, SPECIAL=30; il confronto è tipoAbilitazione >= soglia.`),\n good('ts', [\n \"import { authorizeAccessi, accessiRequirement as r } from 'emilsoftware-utilities';\",\n '',\n '// AND (entrambi i menu)',\n 'const both = r.and(r.permission(\"ORDINI\", 20), r.permission(\"CLIENTI\", 10));',\n '',\n '// OR',\n 'const either = r.or(r.permission(\"REPORT\", 10), r.permission(\"EXPORT\", 20));',\n '',\n '// NOT + custom',\n 'const policy = r.and(',\n ' r.permission(\"AREA_RISERVATA\", 10),',\n ' r.not(r.permission(\"BLOCCO_TOTALE\", 0)),',\n ');',\n '',\n 'app.get(\"/api/riservato\", (req, res, next) => authorizeAccessi(req, res, next, {',\n ' requirementTree: policy,',\n ' customRequirementHandlers: {',\n ' stessoReparto: async (ctx) => ctx.userCode === Number(ctx.req.query.reparto),',\n ' },',\n '}), handler);',\n ].join('\\n'), 'Policy composte'),\n bad('ts', [\n '// Sbagliato: AND/OR senza figli -> errore di configurazione (500 AUTH_REQUIREMENTS_MISCONFIGURED)',\n 'const empty = r.and();',\n ].join('\\n'), 'Configurazione non valida'),\n note('warn', `custom senza handler`, `Un requisito custom la cui chiave non ha un handler produce 500 AUTH_REQUIREMENTS_MISCONFIGURED: è un errore di programmazione, non un 403.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'utenti',\n group: 'Dominio',\n title: 'Utenti',\n summary: 'Creazione, aggiornamento, stati, GDPR e filtri.',\n blocks: [\n h(`Stati di registrazione (STAREG)`),\n table(\n ['Valore', 'Nome', 'Effetto sul login'],\n [\n ['0', 'NULL', 'Non confermato'],\n ['5', 'INSERT', 'Non confermato'],\n ['10', 'INVIO', 'Richiede il rinnovo della password'],\n ['20', 'CONF', 'Valido'],\n ['50', 'DELETE', 'Bloccato (soft delete)'],\n ['99', 'BLOCC', 'Bloccato'],\n ],\n ),\n h(`Endpoint principali (superutente salvo diversamente indicato)`),\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/user/get-users', 'Elenco con paginazione (limit e offset, header X-Total-Count).'],\n ['POST', '/api/accessi/user/create-managed-user', 'Crea un utente locale e invia il reset password.'],\n ['PUT', '/api/accessi/user/update-user/:codiceUtente', 'Aggiorna profilo e policy (campi privilegiati solo superutente).'],\n ['DELETE', '/api/accessi/user/delete-user/:codiceUtente', 'Soft delete: STAREG=DELETE. Admin o superutente.'],\n ['DELETE', '/api/accessi/user/delete-user-permanent/:codiceUtente', 'Eliminazione definitiva: rimuove utente e record collegati. Solo admin.'],\n ['POST', '/api/accessi/user/set-password/:codiceUtente', 'Imposta direttamente la password: hash lato backend e scadenza secondo policy, nessuna email.'],\n ['POST', '/api/accessi/user/set-stato', 'Imposta lo stato di registrazione.'],\n ['PATCH', '/api/accessi/user/set-gdpr/:codiceUtente', 'Registra il consenso GDPR.'],\n ['POST', '/api/accessi/user/register', 'Registrazione pubblica, solo se publicRegistration.enabled.'],\n ],\n ),\n note('warn', `Eliminazione: soft o definitiva`, `Il soft delete (STAREG=DELETE) è disponibile per admin e superutenti ed è reversibile. L'eliminazione definitiva rimuove utente e record collegati in transazione ed è riservata agli admin; il superutente senza flag Admin può solo flaggare l'utente come eliminato. Se l'utente ha dati collegati in altre tabelle (per esempio campi estesi del backend) l'eliminazione definitiva fallisce con code ACCESSI_USER_DELETE_HAS_REFERENCES e messaggio esplicito: usa allora il soft delete.`),\n note('ok', `Creazione con invito atomica`, `La creazione utente con invio email è atomica: se SMTP non è configurato si fallisce prima di scrivere, e se l'invio fallisce l'utente creato viene annullato (codice ACCESSI_USER_INVITE_EMAIL_FAILED). Non restano utenti senza password. In alternativa un admin crea l'utente e imposta la password dal form dedicato.`),\n note('ok', `Admin: controllo completo del profilo`, `Dalla scheda utente un admin modifica tutti i campi Accessi: anagrafica, cellulare, lingua, avatar, pagina di default, metadata JSON, 2FA/passwordless/login con password, flag, GDPR, stato registrazione, scadenza password e filtri (tabella FILTRI). La password si imposta direttamente con set-password. Le scritture sono idempotenti (UPDATE OR INSERT su UTENTI_CONFIG) e verificate dal backend; non serve accedere al database.`),\n note('info', `Consenso GDPR`, `Il pulsante \"Registra consenso GDPR\" completa il processo: imposta FLGGDPR e DATGDPR e scrive la riga storica in UTENTI_GDPR (endpoint set-gdpr). Anche impostare flagGdpr via update-user completa il processo con la stessa riga storica.`),\n good('ts', [\n \"await fetch('/api/accessi/user/create-managed-user', {\",\n \" method: 'POST',\",\n \" headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + adminToken },\",\n ' body: JSON.stringify({',\n \" email: 'nuovo@example.com',\",\n \" nome: 'Mario',\",\n \" cognome: 'Rossi',\",\n ' roles: [1, 2],',\n \" permissions: [{ codiceMenu: 'REPORT', tipoAbilitazione: 10 }],\",\n ' }),',\n '});',\n ].join('\\n'), 'Creazione di un utente gestito'),\n bad('ts', [\n '// Sbagliato: inviare flag privilegiati dal frontend pubblico',\n 'fetch(\"/api/accessi/user/register\", {',\n ' method: \"POST\",',\n ' body: JSON.stringify({ email, flagSuper: true }), // ignorato o negato',\n '});',\n ].join('\\n'), 'Privilegi dal client'),\n note('info', `Campi di dominio`, `UTENTI_CONFIG contiene i campi di dominio Accessi: FLGSUPER, FLGADMIN, FLG2FATT (2FA), FLGPWDLESS (passwordless), FLGPASSWORD (login locale), CODLINGUA, CELLULARE, PAGDEF (pagina di default) e JSON_METADATA. I flag/colonne applicativi (FLGMOP, FLGPIANA, FLGADDETTI, FLGOSPITI, FLGPIANARFID, FLGCONTA, FLGCUBI, FLGCICLPASS, FLGDIPENDENTI, FLGINVENTARI, CAUMOV, NUMMAC, RAGSOCCLI, ENABLEIA, CELLUTE e NOMECAMPO) sono stati rimossi: i backend devono gestirli in tabelle proprie. Il telefono canonico e UTENTI_CONFIG.CELLULARE.`),\n note('info', `Autorizzazioni`, `I gruppi/menu si abilitano tramite ABILITAZIONI, RUOLI_MNU e UTENTI_RUOLI. Il vecchio meccanismo NOMECAMPO (flag applicativo su UTENTI_CONFIG) e stato rimosso.`),\n note('info', `Campi estesi`, `extensionFieldsOptions allega colonne di tabelle applicative al profilo (per esempio la ragione sociale). Le colonne sono in whitelist: non costruirle mai da input HTTP.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'ruoli',\n group: 'Dominio',\n title: 'Ruoli e permessi',\n summary: 'Assegnazioni sostitutive e composizione dei grant.',\n blocks: [\n p(`I ruoli contengono una lista di menu con livello. Le assegnazioni (ruoli e permessi diretti) sono SOSTITUTIVE: invia sempre la lista completa desiderata. I grant diretti prevalgono su quelli dei ruoli; i superutenti vedono tutti i menu attivi.`),\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/permission/roles', 'Ruoli con i menu associati.'],\n ['POST', '/api/accessi/permission/create-role', 'Crea un ruolo (i menu sono obbligatori).'],\n ['PUT', '/api/accessi/permission/update-role/:codiceRuolo', 'Aggiorna un ruolo (menu sostitutivi).'],\n ['DELETE', '/api/accessi/permission/delete-role/:codiceRuolo', 'Elimina ruolo, mapping e assegnazioni.'],\n ['POST', '/api/accessi/permission/assign-roles/:codiceUtente', `Sostituisce i ruoli dell'utente.`],\n ['POST', '/api/accessi/permission/assign-permissions/:codiceUtente', 'Sostituisce i permessi diretti.'],\n ['GET', '/api/accessi/permission/grants/:codiceUtente', 'Grant effettivi, permessi diretti e ruoli.'],\n ],\n ),\n good('ts', [\n 'const role = {',\n \" descrizioneRuolo: 'Gestore ordini',\",\n ' menu: [',\n \" { codiceMenu: 'ORDINI', tipoAbilitazione: 20 },\",\n \" { codiceMenu: 'CLIENTI', tipoAbilitazione: 10 },\",\n ' ],',\n '};',\n 'await createRole(role); // POST /api/accessi/permission/create-role',\n '',\n \"// Sostitutivo: questa lista diventa l'intero set di ruoli dell'utente\",\n 'await assignRolesToUser(codiceUtente, { roles: [3, 7] });',\n ].join('\\n'), 'Creazione di un ruolo e assegnazione'),\n bad('ts', [\n \"// Sbagliato: pensare che l'assegnazione sia incrementale\",\n 'await assignRolesToUser(codiceUtente, { roles: [3] });',\n \"// ora l'utente ha SOLO il ruolo 3, non 3 più quelli di prima\",\n ].join('\\n'), 'Assegnazione non additiva'),\n note('warn', `Menu obbligatori`, `create-role e update-role rifiutano un ruolo senza menu. Se ti serve un ruolo senza accessi, non crearlo.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'menu',\n group: 'Dominio',\n title: 'Menu, gruppi e tipi menu',\n summary: 'Catalogo di navigazione e CRUD completo (superutente).',\n blocks: [\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/permission/menus', 'Menu attivi.'],\n ['GET', '/api/accessi/permission/menu-types', 'Catalogo dei tipi menu.'],\n ['GET', '/api/accessi/permission/groups-with-menus?includeDisabled=true', 'Gruppi con menu (anche disabilitati).'],\n ['POST', '/api/accessi/permission/menus', 'Crea un menu.'],\n ['PUT', '/api/accessi/permission/menus/:codiceMenu', 'Aggiorna un menu.'],\n ['DELETE', '/api/accessi/permission/menus/:codiceMenu', 'Elimina un menu (con grant e associazioni ai ruoli).'],\n ['POST', '/api/accessi/permission/menu-groups', 'Crea un gruppo.'],\n ['PUT', '/api/accessi/permission/menu-groups/:codiceGruppo', 'Aggiorna un gruppo.'],\n ['DELETE', '/api/accessi/permission/menu-groups/:codiceGruppo', 'Elimina un gruppo (solo se vuoto).'],\n ['POST/PUT/DELETE', '/api/accessi/permission/menu-types[/:codiceTipo]', 'CRUD dei tipi menu.'],\n ],\n ),\n p(`I codici (CODMNU, CODGRP, CODTIP) sono chiavi immutabili: si indicano in creazione e poi si passano nel percorso. Un gruppo o un tipo si elimina solo se non è referenziato.`),\n good('ts', [\n \"await createMenuGroup({ codiceGruppo: 'A', descrizioneGruppo: 'Amministrazione', ordineGruppo: 1, enabled: true });\",\n \"await createMenuType({ codiceTipo: 'X', descrizioneTipo: 'Operativo' });\",\n \"await createMenu({ codiceMenu: 'ORDINI', descrizioneMenu: 'Ordini', codiceGruppo: 'A', tipo: 'X', ordineMenu: 10, enabled: true });\",\n ].join('\\n'), 'Catalogo in ordine'),\n bad('ts', [\n '// Sbagliato: eliminare un gruppo che contiene menu -> 400 con spiegazione',\n \"await deleteMenuGroup('A'); // contiene N menu\",\n ].join('\\n'), 'Vincoli di integrità'),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'filtri',\n group: 'Dominio',\n title: 'Filtri applicativi',\n summary: 'Filtri per utente e catalogo dei tipi filtro.',\n blocks: [\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/filtri/tipi', 'Catalogo dei tipi filtro.'],\n ['POST', '/api/accessi/filtri/tipi', 'Crea un tipo filtro (superutente).'],\n ['PUT', '/api/accessi/filtri/tipi/:tipFil', 'Aggiorna un tipo filtro.'],\n ['DELETE', '/api/accessi/filtri/tipi/:tipFil', 'Elimina un tipo filtro (se non è usato).'],\n ['GET', '/api/accessi/filtri/utente?codUte=123', 'Filtri di un utente (self o superutente).'],\n ['POST', '/api/accessi/filtri/utente', 'Upsert dei filtri (self o superutente).'],\n ],\n ),\n note('info', `Mapping delle colonne`, `I campi filtro sono mappati su colonne FILTRI in whitelist (FILTRI_UTENTE_DB_MAPPING). I campi non configurati nello schema vengono rifiutati.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'sso',\n group: 'SSO',\n title: 'SSO generico (federated authentication)',\n summary: 'Il backend valida il provider, Accessi gestisce identità e sessioni.',\n blocks: [\n p(`Accessi non valida i token Azure, Google o SAML: è il TUO backend a validare il flusso SSO e a passare solo { provider, subject } verificati a FederatedAuthService. Così issuer, secret e token restano fuori dal modulo.`),\n good('ts', [\n \"import { FederatedAuthService } from 'emilsoftware-utilities';\",\n '',\n '// Nel tuo callback SSO, dopo aver validato il token del provider:',\n 'const verified = { provider: \"azure-ad-acme\", subject: claims.oid };',\n 'const result = await federatedAuthService.authenticate(verified);',\n '',\n 'if (result.login.challenge) {',\n ' // 2FA attiva: restituisci il challenge al frontend',\n ' return res.json({ challenge: result.login.challenge });',\n '}',\n 'return res.json({ login: result.login, token: result.token });',\n ].join('\\n'), 'Login SSO'),\n h(`API amministrative (superutente)`),\n list([\n `GET/POST /api/accessi/federated-auth/providers, PATCH/DELETE providers/:provider`,\n `GET/POST /api/accessi/federated-auth/users/:codiceUtente/identities`,\n `POST /api/accessi/federated-auth/users (crea un utente SSO-only)`,\n `PATCH /api/accessi/federated-auth/users/:codiceUtente/password-login`,\n ]),\n bad('ts', [\n \"// Sbagliato: far validare il token del provider ad Accessi o fidarsi del frontend\",\n 'await federatedAuthService.authenticate({ provider, subject: req.body.email }); // subject non verificato',\n ].join('\\n'), `Non fidarti dell'input`),\n note('warn', `Provider in uso`, `Un provider si elimina solo se nessuna identità è collegata; altrimenti disabilitalo. La chiave del provider deve coincidere con la configurazione del backend.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'service-token',\n group: 'Token di servizio',\n title: 'Token macchina-a-macchina',\n summary: 'Autenticazione tecnica con scope: non è un superutente.',\n blocks: [\n p(`Formato: st_<id>.<segreto> (segreto da 256 bit). Nel database si salva solo lo SHA-256 del segreto; il segreto è mostrato una sola volta. La verifica restituisce { tokenId, label, scopes }: nessun ruolo o grant.`),\n note('danger', `Non è un superutente`, `Un service token non apre le API Accessi: abilita solo le rotte della tua app che proteggi con ServiceTokenGuard.`),\n good('ts', [\n \"import { ServiceTokenGuard, RequireServiceTokenScopes } from 'emilsoftware-utilities';\",\n '',\n '@UseGuards(ServiceTokenGuard)',\n \"@RequireServiceTokenScopes('ia')\",\n '@Post(\"internal/ask\")',\n 'ask() { /* req.accessiServiceToken = { tokenId, label, scopes } */ }',\n ].join('\\n'), 'Rotta tecnica protetta'),\n h(`Gestione (superutente)`),\n list([\n `POST /api/accessi/service-token (label, scopes, expiresAt o ttlDays) -> segreto mostrato una volta`,\n `GET /api/accessi/service-token?includeRevoked=true`,\n `DELETE /api/accessi/service-token/:tokenId (revoca immediata)`,\n `POST /api/accessi/service-token/:tokenId/rotate`,\n ]),\n bad('ts', [\n '// Sbagliato: usare un service token per autenticare un utente umano',\n \"fetch('/api/profilo', { headers: { Authorization: 'Bearer ' + serviceToken } }); // req.user assente\",\n ].join('\\n'), 'Non è un utente'),\n note('warn', `Scadenza`, `Senza expiresAt né defaultTtlDays il token non scade: imposta serviceTokens.defaultTtlDays o una scadenza esplicita.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'console',\n group: 'Console',\n title: 'Console amministrativa',\n summary: 'Accesso, sezioni e che cosa si può fare.',\n blocks: [\n p(`La console è servita dal modulo su /api/accessi/console. Non ha stato proprio: ogni azione chiama le API protette con il JWT dell'utente connesso.`),\n list([\n `Utenti: elenco, creazione locale o SSO, profilo, ruoli, grant diretti, stato, 2FA.`,\n `Ruoli e grant: CRUD dei ruoli con l'albero delle abilitazioni.`,\n `Menu e gruppi: CRUD di gruppi, menu e tipi menu.`,\n `Filtri: filtri utente e tipi filtro.`,\n `Token di servizio: creazione, rotazione, revoca.`,\n `SSO: provider e identità.`,\n `Wiki e Changelog: documentazione e novità della libreria.`,\n `SQL: esporta il DDL delle entità Accessi (tabelle, generatori, FK, indici, check, trigger) per replicare il database.`,\n ]),\n note('info', `Superutente (FLGSUPER)`, `Riceve TUTTE le abilitazioni al livello massimo (30) dopo il login. In console vede solo la sezione Utenti e gestisce utenti, ruoli e grant.`),\n note('info', `Admin (FLGADMIN)`, `Accede a tutta la console (catalogo, token, SSO, utenti) ma NON conferisce abilitazioni: i menu restano quelli assegnati via ABILITAZIONI/RUOLI_MNU. Gerarchia: ADMIN > SUPER > utente.`),\n note('info', `Gerarchia dei ruoli`, `Vale ADMIN (2) > SUPER (1) > utente comune (0). Un attore può gestire solo utenti di rango pari o inferiore: un superutente può amministrare totalmente utenti comuni e superutenti (profilo, password, flag, ruoli, grant, stato, eliminazione, filtri e identità/utenti SSO) ma NON può toccare un admin. Un admin può gestire chiunque. La regola è imposta dal backend (ensureCanManageTargetUser): un tentativo non consentito restituisce 403.`),\n note('info', `Gestione dei flag`, `Dalla scheda utente i flag sono mostrati in base al ruolo di chi è connesso: un admin può assegnare o togliere sia il flag Admin sia il flag Superutente; un superutente senza flag Admin può gestire solo il flag Superutente. I flag si impostano anche in creazione (utente locale o SSO).`),\n note('warn', `Ultimo admin`, `Per non restare chiusi fuori dalla console, l'ultimo admin attivo non può essere disattivato, bloccato, eliminato né privato del flag Admin: l'operazione restituisce 400 finché non viene promosso un altro admin.`),\n 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).`),\n note('info', `Bootstrap admin`, `Con adminBootstrap.enabled il backend stampa a ogni avvio un token casuale (monouso, valido fino al riavvio). Nella schermata di login della console trovi \"Crea utente admin\": incolla il token e crea il primo admin senza email di conferma. La password è opzionale: se assente viene generata e mostrata una sola volta.`),\n note('ok', `Auto-ingresso dal frontend`, `Se un frontend ha già una sessione Accessi valida e l'utente è abilitato alla console, può 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 è utilizzabile una sola volta (endpoint di scambio protetto da rate limit).`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'errori',\n group: 'Riferimenti',\n title: 'Contratto errori e codici',\n summary: 'Formato della risposta e codici stabili ACCESSI_* e AUTH_*.',\n blocks: [\n p(`Gli errori HTTP delle API Accessi usano un contratto stabile: severity, status, statusCode, code, error (campo legacy uguale a code), message e details per gli errori di validazione.`),\n code('json', `{\\n \"severity\": \"error\",\\n \"status\": 400,\\n \"statusCode\": 2,\\n \"code\": \"ACCESSI_VALIDATION_ERROR\",\\n \"error\": \"ACCESSI_VALIDATION_ERROR\",\\n \"message\": \"La richiesta contiene dati non validi.\",\\n \"details\": [\"email: deve essere una stringa.\"]\\n}`, 'Errore di validazione'),\n table(\n ['Codice', 'Quando'],\n [\n ['ACCESSI_VALIDATION_ERROR', 'Body o parametri non validi (400).'],\n ['ACCESSI_AUTH_INITIALIZING', 'Bootstrap in corso (503).'],\n ['ACCESSI_AUTH_NOT_INITIALIZED', 'Modulo non inizializzato (500).'],\n ['AUTH_HEADER_MISSING / AUTH_TOKEN_MISSING', 'Header Authorization assente (401).'],\n ['AUTH_TOKEN_INVALID', 'JWT non valido o scaduto (401).'],\n ['AUTH_USER_DISABLED', 'Utente non più autorizzato (401).'],\n ['AUTH_INSUFFICIENT_PERMISSIONS', 'Requisiti non soddisfatti (403).'],\n ['AUTH_REQUIREMENTS_MISCONFIGURED', 'Policy malformata o handler custom mancante (500).'],\n ['ACCESSI_DATABASE_SCHEMA_OUTDATED', 'Schema del database incompatibile.'],\n ['ACCESSI_SERVICE_TOKEN_RATE_LIMITED', 'Troppi tentativi (429).'],\n ],\n ),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'endpoint',\n group: 'Riferimenti',\n title: 'Indice degli endpoint',\n summary: 'Tutte le rotte /api/accessi/* in una tabella.',\n blocks: [\n table(\n ['Area', 'Endpoint principali'],\n [\n ['Auth', 'POST auth/login, auth/get-user-by-token, auth/confirm-reset-password/:token, auth/two-factor/verify, auth/two-factor/resend'],\n ['Email', 'POST email/send-reset-password-email, GET email/reset-password-page/:token'],\n ['Utenti', 'GET user/get-users, POST user/create-managed-user, PUT user/update-user/:codiceUtente, DELETE user/delete-user/:codiceUtente, DELETE user/delete-user-permanent/:codiceUtente, POST user/set-password/:codiceUtente, POST user/set-stato, PATCH user/set-gdpr/:codiceUtente, POST user/register'],\n ['Permessi', 'GET permission/roles, POST permission/create-role, PUT permission/update-role/:codiceRuolo, DELETE permission/delete-role/:codiceRuolo, POST permission/assign-roles/:codiceUtente, POST permission/assign-permissions/:codiceUtente, GET permission/grants/:codiceUtente, GET permission/menus, GET permission/menu-types, GET permission/groups-with-menus'],\n ['Catalogo', 'POST/PUT/DELETE permission/menus[/:codiceMenu], permission/menu-groups[/:codiceGruppo], permission/menu-types[/:codiceTipo]'],\n ['Configurator', 'PATCH configurator/menus/:codiceMenu/enabled, PATCH configurator/groups/:codiceGruppo/enabled'],\n ['Filtri', 'GET filtri/tipi, POST/PUT/DELETE filtri/tipi[/:tipFil], GET/POST filtri/utente'],\n ['SSO', 'GET/POST federated-auth/providers, PATCH/DELETE providers/:provider, GET/POST users/:codiceUtente/identities, POST users, PATCH users/:codiceUtente/password-login'],\n ['Token', 'POST/GET service-token, DELETE service-token/:tokenId, POST service-token/:tokenId/rotate'],\n ['Schema', 'GET schema (DDL completo + entità presenti, solo admin)'],\n ['Console', 'GET console, console/:view, console/:view/:sub, console/assets/:file'],\n ],\n ),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'antipattern',\n group: 'Riferimenti',\n title: 'Antipattern e checklist',\n summary: 'Che cosa non fare, in breve.',\n blocks: [\n list([\n `Non aprire la porta HTTP prima di initializeAccessiModule.`,\n `Non usare authorizeAccessi come unico strato: valida sempre anche l'input applicativo.`,\n `Non dare per scontato che req.userGrants esista se non hai richiesto dei requisiti.`,\n `Non considerare incrementali assignRolesToUser e assignPermissionsToUser: sono sostitutivi.`,\n `Non far validare i token SSO ad Accessi: passagli solo { provider, subject } verificati.`,\n `Non usare un service token come identità utente.`,\n `Non versionare segreti; jwtOptions.secret deve essere unico per ambiente.`,\n `Non eliminare gruppi o tipi in uso: gestisci l'errore 400 e disabilita invece.`,\n `Non usare il challenge 2FA come JWT.`,\n ]),\n note('ok', `Checklist di rilascio`, `Schema verificato (db:check), segreti da un secret manager, mockDemoUser=false, rate limit pubblico configurato, SMTP reale testato, serviceTokens.defaultTtlDays impostato.`),\n ],\n },\n];\n\n/** Elenco ordinato dei gruppi per l'indice. */\nexport function wikiGroups(): Array<{ group: string; sections: WikiSection[] }> {\n const groups: Array<{ group: string; sections: WikiSection[] }> = [];\n for (const section of WIKI_SECTIONS) {\n let bucket = groups.find((item) => item.group === section.group);\n if (!bucket) {\n bucket = { group: section.group, sections: [] };\n groups.push(bucket);\n }\n bucket.sections.push(section);\n }\n return groups;\n}\n\n/**\n * Digest compatto in Markdown per l'IA che costruisce il backend integratore.\n * Deriva dagli stessi dati della wiki, quindi resta sincronizzato.\n */\nexport function buildAiDigest(): string {\n const lines: string[] = [\n `# Accessi (emilsoftware-utilities) - guida compatta per l'integrazione backend`,\n '',\n `Modulo NestJS/Express + Firebird per autenticazione JWT, autorizzazione per menu, utenti, 2FA e passwordless, SSO generico, service token e console.`,\n '',\n ];\n\n for (const { group, sections } of wikiGroups()) {\n lines.push(`## ${group}`);\n for (const section of sections) {\n lines.push(`### ${section.title}`);\n lines.push(section.summary);\n for (const block of section.blocks) {\n switch (block.kind) {\n case 'p':\n lines.push(block.text);\n break;\n case 'h':\n lines.push(`**${block.text}**`);\n break;\n case 'list':\n block.items.forEach((item, index) => lines.push(`${block.ordered ? `${index + 1}.` : '-'} ${item}`));\n break;\n case 'code':\n lines.push('```' + block.language);\n lines.push(block.code);\n lines.push('```');\n break;\n case 'callout':\n lines.push(`[${block.tone.toUpperCase()}] ${block.title ? block.title + ': ' : ''}${block.text}`);\n break;\n case 'table':\n lines.push('| ' + block.head.join(' | ') + ' |');\n lines.push('| ' + block.head.map(() => '---').join(' | ') + ' |');\n block.rows.forEach((row) => lines.push('| ' + row.join(' | ') + ' |'));\n break;\n }\n }\n lines.push('');\n }\n }\n\n return lines.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n"]}
1
+ {"version":3,"file":"wiki.js","sourceRoot":"","sources":["../../../src/accessi-module/Console/wiki.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Contenuto della Wiki della console Accessi.\n *\n * I contenuti sono dati strutturati (non HTML) così da poter essere resi in modo\n * coerente dalla console e convertiti in un digest compatto per l'IA. Nessun\n * segreto o dato specifico di un ambiente deve finire qui.\n */\n\nexport type WikiTone = 'info' | 'ok' | 'warn' | 'danger';\n\nexport type WikiBlock =\n | { kind: 'p'; text: string }\n | { kind: 'h'; text: string }\n | { kind: 'list'; items: string[]; ordered?: boolean }\n | { kind: 'code'; language: string; title?: string; code: string; variant?: 'good' | 'bad' }\n | { kind: 'callout'; tone: WikiTone; title?: string; text: string }\n | { kind: 'table'; head: string[]; rows: string[][] };\n\nexport interface WikiSection {\n id: string;\n group: string;\n title: string;\n summary: string;\n blocks: WikiBlock[];\n}\n\nconst p = (text: string): WikiBlock => ({ kind: 'p', text });\nconst h = (text: string): WikiBlock => ({ kind: 'h', text });\nconst list = (items: string[], ordered = false): WikiBlock => ({ kind: 'list', items, ordered });\nconst code = (language: string, codeText: string, title?: string, variant?: 'good' | 'bad'): WikiBlock => ({ kind: 'code', language, title, code: codeText, variant });\nconst good = (language: string, codeText: string, title?: string): WikiBlock => code(language, codeText, title, 'good');\nconst bad = (language: string, codeText: string, title?: string): WikiBlock => code(language, codeText, title, 'bad');\nconst note = (tone: WikiTone, title: string, text: string): WikiBlock => ({ kind: 'callout', tone, title, text });\nconst table = (head: string[], rows: string[][]): WikiBlock => ({ kind: 'table', head, rows });\n\nexport const WIKI_SECTIONS: WikiSection[] = [\n // -------------------------------------------------------------------------\n {\n id: 'panoramica',\n group: 'Per iniziare',\n title: 'Panoramica e architettura',\n summary: 'Che cosa fa Accessi, come si incastra nel backend e come scorre una richiesta.',\n blocks: [\n p(`Accessi è un modulo Node/NestJS che aggiunge a un backend già esistente: autenticazione JWT, autorizzazione per ruoli e menu, gestione utenti, reset password, codici email (2FA e passwordless), SSO generico, token macchina-a-macchina e una console amministrativa. Il database è Firebird/InterBase.`),\n h(`Che cosa possiede il modulo`),\n list([\n `Le rotte sotto /api/accessi/* (login, utenti, ruoli, menu, filtri, SSO, token, console).`,\n `Lo schema database del dominio Accessi, riconciliato automaticamente al bootstrap.`,\n `Il middleware di autorizzazione authorizeAccessi per le rotte della tua applicazione.`,\n `La console web su /api/accessi/console.`,\n ]),\n h(`Che cosa resta alla tua applicazione`),\n list([\n `Il proprio frontend e le proprie rotte di business.`,\n `La validazione dei token dei provider SSO esterni (Accessi non li vede).`,\n `I segreti: jwtOptions.secret, SMTP, database ed encryptionKey non raggiungono mai il browser.`,\n ]),\n h(`Flusso di una richiesta protetta`),\n list([\n `Il client chiama una rotta della tua app con header Authorization: Bearer <jwt>.`,\n `La tua rotta esegue authorizeAccessi, che verifica firma HS256, scadenza e stato utente corrente.`,\n `Se passi un requisito, il modulo carica ruoli e grant dell'utente e li valuta.`,\n `Su successo req.user, req.data e (se richiesto) req.userGrants sono popolati; altrimenti risponde con il contratto errore Accessi.`,\n ], true),\n note('info', `Due modalità di bootstrap`, `Express esistente: initializeAccessiModule(app, options). NestJS esistente: AccessiModule.forRoot(options) nei tuoi imports. Non usarle entrambe.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'installazione',\n group: 'Per iniziare',\n title: 'Installazione',\n summary: 'Dipendenze, peer e build.',\n blocks: [\n code('bash', `npm install emilsoftware-utilities`, 'Installazione'),\n p(`La libreria dipende da node-firebird per il database e da Express 5 come peer per il montaggio. È consigliato Node 18 o superiore.`),\n note('warn', `Un solo modulo per processo`, `AccessiModule è @Global(): inizializzalo una sola volta per processo. In caso di più istanze, usa un processo per istanza.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'bootstrap-express',\n group: 'Per iniziare',\n title: 'Bootstrap su Express esistente',\n summary: 'initializeAccessiModule: ordine corretto e attesa prima di listen.',\n blocks: [\n p(`Chiama initializeAccessiModule prima di aprire la porta HTTP. Durante il bootstrap, authorizeAccessi risponde 503 ACCESSI_AUTH_INITIALIZING invece di autorizzare richieste parziali.`),\n good('ts', [\n \"import express from 'express';\",\n \"import { initializeAccessiModule, authorizeAccessi } from 'emilsoftware-utilities';\",\n '',\n 'const app = express();',\n 'app.use(express.json());',\n '',\n 'await initializeAccessiModule(app, options); // prima di app.listen',\n '',\n \"app.get('/api/ordini', authorizeAccessi, (req, res) => {\",\n ' res.json({ utente: req.user });',\n '});',\n '',\n \"app.get('/api/ordini/scrivi', (req, res, next) =>\",\n ' authorizeAccessi(req, res, next, {',\n \" requirements: [{ menuCode: 'ORDINI', minPermissionLevel: 20 }],\",\n ' }),',\n '(req, res) => res.json({ ok: true }));',\n '',\n 'app.listen(3000);',\n ].join('\\n'), 'Express + Accessi'),\n bad('ts', [\n 'const app = express();',\n 'app.listen(3000); // porta aperta prima del bootstrap',\n 'await initializeAccessiModule(app, options); // le richieste nel frattempo ricevono 503',\n ].join('\\n'), 'Non aprire la porta prima del bootstrap'),\n h(`Rotte montate`),\n p(`Accessi intercetta solo /api/accessi/*, /accessi/swagger* e /accessi/swagger.json. Tutte le altre rotte passano intatte al tuo stack Express.`),\n note('ok', `req.user`, `authorizeAccessi popola req.user con il payload utente corrente (utente, codiceUtente, flag) e req.data come alias storico. req.userGrants è presente solo se hai richiesto dei requisiti.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'bootstrap-nest',\n group: 'Per iniziare',\n title: 'Bootstrap su NestJS esistente',\n summary: 'AccessiModule.forRoot e uso dei provider esportati.',\n blocks: [\n p(`Se la tua applicazione è già NestJS, importa AccessiModule.forRoot(options). Il modulo è @Global e riesporta i servizi (AuthService, UserService, PermissionService, FederatedAuthService, ServiceTokenService, ServiceTokenGuard, AuthenticateGenService).`),\n good('ts', [\n \"import { Module } from '@nestjs/common';\",\n \"import { AccessiModule } from 'emilsoftware-utilities';\",\n '',\n '@Module({',\n ' imports: [AccessiModule.forRoot(options)],',\n '})',\n 'export class AppModule {}',\n ].join('\\n'), 'app.module.ts'),\n p(`Il global prefix api e le rotte /api/accessi/* sono gestiti dal modulo. Le tue rotte Nest si proteggono con JwtSimpleGuard usando il JWT Accessi.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'opzioni',\n group: 'Configurazione',\n title: 'AccessiOptions (riferimento completo)',\n summary: 'Ogni campo di configurazione con tipo, obbligo e note.',\n blocks: [\n table(\n ['Campo', 'Tipo', 'Obbligo', 'Note'],\n [\n ['databaseOptions', 'Options (node-firebird)', 'sì', 'Connessione Firebird al database Accessi.'],\n ['confirmationEmailUrl', 'string', 'sì', 'Base URL usata per il reset password.'],\n ['confirmationEmailReturnUrl', 'string', 'sì', 'URL di ritorno dopo il reset.'],\n ['confirmationEmailPrefix', 'string', 'no', 'Prefisso passato alla pagina di reset.'],\n ['customResetPage', 'string', 'no', 'Sostituisce la pagina HTML di reset inclusa.'],\n ['encryptionKey', 'string', 'sì', 'Compatibilità e migrazione delle password legacy.'],\n ['mockDemoUser', 'boolean', 'sì', 'Utenti fittizi admin e demo solo fuori produzione.'],\n ['passwordExpiration', 'boolean', 'no', 'Attiva il controllo della scadenza password al login.'],\n ['passwordExpirationDays', 'number', 'no', 'Valore predefinito: 90 giorni.'],\n ['legacyPasswordMigrationOnStartup', 'boolean', 'no', 'Predefinito true: converte le password legacy.'],\n ['autoUpdateDatabase', 'boolean', 'no', 'Predefinito true: riconcilia lo schema al boot.'],\n ['jwtOptions', '{ secret, expiresIn }', 'sì', 'Firma HS256 e durata dei JWT Accessi.'],\n ['emailOptions', 'EmailOptions', 'sì', 'SMTP per reset e codici email.'],\n ['publicAuthRateLimit', 'PublicAuthRateLimitOptions', 'no', 'Limiti in-memory sugli endpoint pubblici.'],\n ['publicRegistration', '{ enabled }', 'no', 'Registrazione pubblica: disattivata per impostazione predefinita.'],\n ['federatedAuthentication', 'FederatedAuthenticationOptions', 'no', `Abilita l'SSO generico.`],\n ['extensionFieldsOptions', 'ExtensionFieldsOptions[]', 'no', 'Tabelle esterne allegate al profilo.'],\n ['serviceTokens', '{ enabled, defaultTtlDays }', 'no', 'Token macchina-a-macchina, abilitati per impostazione predefinita.'],\n ['catalogScripts', '{ enabled, folder, ... }', 'no', 'Catalog migrations SQL eseguite dopo la riconciliazione dello schema.'],\n ['adminBootstrap', '{ enabled, superUser }', 'no', 'Token casuale a ogni avvio per creare il primo admin dalla console.'],\n ['publicBaseUrl', 'string', 'no', 'Base URL usata solo per loggare console/swagger/API.'],\n ],\n ),\n good('ts', [\n \"import type { AccessiOptions } from 'emilsoftware-utilities';\",\n '',\n 'export const options: AccessiOptions = {',\n ' databaseOptions: {',\n ' host: process.env.ACCESSI_DB_HOST!,',\n ' port: Number(process.env.ACCESSI_DB_PORT ?? 3050),',\n ' database: process.env.ACCESSI_DB_NAME!,',\n ' user: process.env.ACCESSI_DB_USER!,',\n ' password: process.env.ACCESSI_DB_PASSWORD!,',\n ' },',\n \" confirmationEmailUrl: 'https://app.example.com',\",\n \" confirmationEmailReturnUrl: 'https://app.example.com/login',\",\n ' encryptionKey: process.env.ACCESSI_ENCRYPTION_KEY!,',\n ' mockDemoUser: false,',\n ' passwordExpiration: true,',\n ' passwordExpirationDays: 90,',\n ' jwtOptions: {',\n \" secret: process.env.ACCESSI_JWT_SECRET!, // almeno 32 caratteri casuali, unico per ambiente\",\n \" expiresIn: '24h',\",\n ' },',\n ' emailOptions: {',\n ' host: process.env.SMTP_HOST!,',\n ' port: Number(process.env.SMTP_PORT ?? 587),',\n ' secure: false,',\n ' requireTLS: true,',\n ' tls: { rejectUnauthorized: true },',\n \" from: 'no-reply@example.com',\",\n ' auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS! },',\n ' },',\n ' publicRegistration: { enabled: false },',\n ' serviceTokens: { enabled: true, defaultTtlDays: 365 },',\n '};',\n ].join('\\n'), 'Configurazione tipica'),\n bad('ts', [\n \"jwtOptions: { secret: 'accessi', expiresIn: '24h' }, // segreto debole e condiviso\",\n 'mockDemoUser: true, // MAI in produzione',\n ].join('\\n'), 'Errori comuni'),\n note('danger', `Segreti`, `Non versionare i segreti e non passarli al frontend. jwtOptions.secret deve essere identico tra le istanze del backend e diverso da quello dei provider SSO.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'database',\n group: 'Configurazione',\n title: 'Schema e aggiornamento del database',\n summary: 'Tabelle, riconciliazione automatica e CLI.',\n blocks: [\n p(`Accessi possiede lo schema del dominio. Con autoUpdateDatabase true (predefinito) lo riconcilia a ogni avvio; con false verifica comunque la compatibilità in sola lettura e blocca gli avvii incompatibili.`),\n h(`Tabelle principali`),\n list([\n `PARAMETRI, UTENTI, UTENTI_CONFIG, UTENTI_PWD, UTENTI_OLDPWD, UTENTI_GDPR`,\n `RUOLI, MENU_GRP, MENU_TIPI, MENU, ABILITAZIONI, RUOLI_MNU, UTENTI_RUOLI`,\n `FILTRI, FILTRI_TIPO`,\n `SSO_PROVIDER, UTENTI_IDENTITA_EXT`,\n `ACCESSI_2FA, ACCESSI_SERVICE_TOKEN, ACCESSI_CATALOG_SCRIPT`,\n ]),\n code('bash', `npm run db:update:accessi # riconcilia lo schema\\nnpm run db:check:accessi # verifica senza modificare`, 'CLI schema'),\n note('warn', `Versioni`, `La libreria richiede uno schema compatibile con la versione corrente (ACCESSI_SCHEMA_VERSION). Se la verifica fallisce, leggi il codice errore ACCESSI_DATABASE_SCHEMA_OUTDATED ed esegui db:update:accessi.`),\n note('info', `Schema esatto`, `La riconciliazione allinea le tabelle Accessi a \"niente in piu, niente in meno\": le colonne non previste vengono segnalate come incompatibili e rimosse automaticamente (dopo rename e copie dei dati legacy). Gli indici, i trigger e i CHECK extra non previsti non vengono tolti, ma un indice non previsto su una tabella Accessi viene riportato come avviso senza bloccare l'avvio.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'catalog-migrations',\n group: 'Configurazione',\n title: 'Catalog migrations SQL',\n summary: 'Allinea menu, ruoli e cataloghi su piu database in modo idempotente.',\n blocks: [\n p(`Gli script SQL applicativi (menu, ruoli, tipi, cataloghi) restano nel repository del backend ospitante e Accessi li esegue dopo la riconciliazione dello schema. Ogni script viene tracciato nella tabella ACCESSI_CATALOG_SCRIPT (nome + checksum), quindi viene applicato una sola volta per database.`),\n h(`Configurazione`),\n good('ts', [\n 'catalogScripts: {',\n \" enabled: true,\",\n \" folder: './db/accessi-catalog', // versionata nel repo del backend\",\n \" onChecksumMismatch: 'error', // 'error' (default) | 'warn' | 'reapply'\",\n \" stopOnError: true,\",\n '}',\n ].join('\\n'), 'AccessiOptions'),\n list([\n 'Gli script sono ordinati per percorso (usa prefissi numerici: 0001_, 0002_, ...).',\n 'Devono essere idempotenti e ripetibili: UPDATE OR INSERT ... MATCHING, MERGE, DELETE+INSERT mirati.',\n 'Non includere utenti: le catalog migrations allineano solo catalogo e configurazione.',\n 'Supportati stringhe, commenti e statement multipli; SET TERM non serve perché gli script sono solo DML.',\n ]),\n note('danger', `Solo righe (DML)`, `Vincolo tassativo: uno script puo eseguire solo INSERT, UPDATE, DELETE, MERGE, SELECT, EXECUTE. Qualunque DDL (CREATE/ALTER/DROP, trigger, procedure, GRANT, transazioni) viene rifiutato. Lo schema si aggiorna solo con db:update:accessi.`),\n note('info', `File non conformi`, `Un file con statement non consentiti non viene applicato ne registrato nel ledger; il run prosegue con gli altri e il report riporta una voce per ogni file con i relativi messaggi di errore (visibili anche con db:catalog:accessi:check).`),\n note('info', `allowDdl`, `Disattivato di default. Impostarlo a true disabilita la validazione e permette DDL: usarlo solo consapevolmente e mai come sostituto dell'updater dello schema.`),\n code('bash', `# Applica (schema + catalog migrations) su un database\\nACCESSI_CATALOG_FOLDER=./db/accessi-catalog npm run db:catalog:accessi\\n\\n# Elenca solo quelle da applicare / modificate\\nACCESSI_CATALOG_FOLDER=./db/accessi-catalog npm run db:catalog:accessi:check`, 'CLI catalog migrations'),\n note('info', `Idempotenza`, `Uno script gia applicato con lo stesso checksum viene saltato. Se il file cambia dopo l'applicazione, con policy error il run si interrompe: non modificare uno script applicato, aggiungine uno nuovo.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'login',\n group: 'Autenticazione',\n title: 'Login locale e JWT',\n summary: 'Endpoint di login, risposta e gestione della password scaduta.',\n blocks: [\n code('http', `POST /api/accessi/auth/login\\nContent-Type: application/json\\n\\n{ \"email\": \"utente@example.com\", \"password\": \"Segreta123!\" }`, 'Login con password'),\n p(`La risposta include Result.token (il JWT) e il profilo utente con ruoli, permessi e filtri. Se l'account è passwordless, ometti la password: il server risponde con un challenge via email (vedi la sezione su 2FA e passwordless).`),\n good('ts', [\n \"const res = await fetch('/api/accessi/auth/login', {\",\n \" method: 'POST',\",\n \" headers: { 'Content-Type': 'application/json' },\",\n ' body: JSON.stringify({ email, password }),',\n '});',\n 'const body = await res.json();',\n 'if (body.Result?.challenge) { /* richiedi il codice */ }',\n 'const token = body.Result?.token;',\n ].join('\\n'), 'Chiamata dal frontend'),\n note('warn', `Password scaduta`, `Con passwordExpiration attivo, una password scaduta produce una PasswordExpiredResponse con code PASSWORD_EXPIRED. Il client deve avviare il reset della password.`),\n h(`Verifica del token`),\n code('http', `POST /api/accessi/auth/get-user-by-token\\nContent-Type: application/json\\n\\n{ \"token\": \"<jwt>\" }`, 'get-user-by-token'),\n p(`Usalo per verificare un JWT e ottenere il profilo corrente (utile al bootstrap di una SPA). Restituisce Result.userData.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'passwordless-2fa',\n group: 'Autenticazione',\n title: 'Codici email, 2FA e passwordless',\n summary: 'Verifica in due passi, challenge e policy per utente.',\n blocks: [\n p(`La verifica con codice email è per-utente (flagDueFattori / UTENTI_CONFIG.FLG2FATT) ed è disattivata per impostazione predefinita. Con passwordlessLoginEnabled (FLGPWDLESS, richiede la 2FA attiva) l'accesso avviene con il solo codice email.`),\n table(\n ['Configurazione', 'Accesso locale', 'Accesso SSO'],\n [\n ['2FA disattivata', 'Password', 'Provider'],\n ['2FA attiva', 'Password + codice email', 'Provider + codice email'],\n ['2FA + passwordless', 'Solo codice email', 'Provider + codice email'],\n ],\n ),\n code('json', `{\\n \"Result\": {\\n \"challenge\": {\\n \"twoFactorRequired\": true,\\n \"challengeId\": \"64-caratteri-esadecimali\",\\n \"method\": \"email\",\\n \"expiresAt\": \"2026-09-11T14:00:00.000Z\",\\n \"resendAfterSeconds\": 60\\n }\\n }\\n}`, 'Risposta con challenge'),\n code('http', `POST /api/accessi/auth/two-factor/verify\\n{ \"challengeId\": \"...\", \"code\": \"012345\" }\\n\\nPOST /api/accessi/auth/two-factor/resend\\n{ \"challengeId\": \"...\" }`, 'Verifica e reinvio'),\n list([\n `Il codice ha 6 cifre, dura 10 minuti, consente 5 tentativi totali e al massimo 3 invii per challenge.`,\n `Fino alla verifica non sono presenti token, profilo o grant.`,\n `Il challenge non è un JWT e non va usato nell'header Authorization.`,\n ]),\n bad('ts', [\n '// Sbagliato: usare il challenge come token',\n \"fetch('/api/ordini', { headers: { Authorization: 'Bearer ' + challengeId } }); // 401\",\n ].join('\\n'), 'Il challenge non è un token'),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'reset-password',\n group: 'Autenticazione',\n title: 'Reset password',\n summary: 'Invio email e conferma con token monouso.',\n blocks: [\n code('http', `POST /api/accessi/email/send-reset-password-email\\n{ \"email\": \"utente@example.com\" }\\n\\nPOST /api/accessi/auth/confirm-reset-password/{token}\\n{ \"newPassword\": \"NuovaSegreta123!\" }`, 'Reset in due passi'),\n p(`L'invio non rivela se l'email esiste (anti-enumerazione). Il token di reset è monouso ed è firmato con una chiave dedicata.`),\n note('warn', `Policy password (solo quando si imposta)`, `Reset, set-password admin e bootstrap richiedono almeno 8 caratteri con maiuscola, minuscola, cifra e carattere speciale, senza spazi e non comuni. Se non conforme l'API risponde 400 con code ACCESSI_WEAK_PASSWORD e l'elenco dei requisiti in details. La policy NON si applica al login: le password preesistenti eventualmente deboli continuano ad autenticare e vengono migrate senza blocco.`),\n note('info', `Pagina personalizzata`, `Imposta customResetPage per usare la tua pagina di reset invece di quella inclusa. confirmationEmailReturnUrl e confirmationEmailPrefix controllano il ritorno.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'middleware',\n group: 'Autorizzazione',\n title: 'authorizeAccessi (middleware)',\n summary: 'Proteggere le rotte della tua app con il JWT Accessi.',\n blocks: [\n p(`authorizeAccessi è un middleware Express che verifica il JWT, ricarica lo stato utente corrente e applica i requisiti. Su successo prosegue; su errore risponde direttamente con il contratto Accessi (non chiamare un tuo next(error)).`),\n good('ts', [\n \"app.get('/api/report',\",\n ' (req, res, next) => authorizeAccessi(req, res, next, {',\n \" requirements: [{ menuCode: 'REPORT', minPermissionLevel: 10 }],\",\n ' }),',\n ' (req, res) => res.json({ user: req.user, grants: req.userGrants }),',\n ');',\n ].join('\\n'), 'Rotta protetta'),\n h(`Che cosa trovi nella request`),\n list([\n `req.user e req.data: payload utente corrente (alias storici).`,\n `req.userGrants: grant effettivi, presente solo quando hai richiesto dei requisiti.`,\n ]),\n bad('ts', [\n \"app.get('/api/report', authorizeAccessi, (req, res) => {\",\n ' // Sbagliato: assumere che req.userGrants esista senza requisiti',\n ' const grants = req.userGrants.grants; // possibile TypeError',\n '});',\n ].join('\\n'), 'Non dare per scontato req.userGrants'),\n note('danger', `Lo stato utente è sempre ricontrollato`, `Un JWT valido non basta: se l'utente è stato bloccato, eliminato o ha cambiato policy, il middleware risponde 401 AUTH_USER_DISABLED.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'requisiti',\n group: 'Autorizzazione',\n title: 'Requisiti e livelli di abilitazione',\n summary: 'DSL accessiRequirement: permission, and, or, not, custom.',\n blocks: [\n p(`I requisiti si compongono con accessiRequirement. La forma storica requirements è un AND di permessi. Il livello è TipoAbilitazione: NESSUNA=0, LETTURA=10, SCRITTURA=20, SPECIAL=30; il confronto è tipoAbilitazione >= soglia.`),\n good('ts', [\n \"import { authorizeAccessi, accessiRequirement as r } from 'emilsoftware-utilities';\",\n '',\n '// AND (entrambi i menu)',\n 'const both = r.and(r.permission(\"ORDINI\", 20), r.permission(\"CLIENTI\", 10));',\n '',\n '// OR',\n 'const either = r.or(r.permission(\"REPORT\", 10), r.permission(\"EXPORT\", 20));',\n '',\n '// NOT + custom',\n 'const policy = r.and(',\n ' r.permission(\"AREA_RISERVATA\", 10),',\n ' r.not(r.permission(\"BLOCCO_TOTALE\", 0)),',\n ');',\n '',\n 'app.get(\"/api/riservato\", (req, res, next) => authorizeAccessi(req, res, next, {',\n ' requirementTree: policy,',\n ' customRequirementHandlers: {',\n ' stessoReparto: async (ctx) => ctx.userCode === Number(ctx.req.query.reparto),',\n ' },',\n '}), handler);',\n ].join('\\n'), 'Policy composte'),\n bad('ts', [\n '// Sbagliato: AND/OR senza figli -> errore di configurazione (500 AUTH_REQUIREMENTS_MISCONFIGURED)',\n 'const empty = r.and();',\n ].join('\\n'), 'Configurazione non valida'),\n note('warn', `custom senza handler`, `Un requisito custom la cui chiave non ha un handler produce 500 AUTH_REQUIREMENTS_MISCONFIGURED: è un errore di programmazione, non un 403.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'utenti',\n group: 'Dominio',\n title: 'Utenti',\n summary: 'Creazione, aggiornamento, stati, GDPR e filtri.',\n blocks: [\n h(`Stati di registrazione (STAREG)`),\n table(\n ['Valore', 'Nome', 'Effetto sul login'],\n [\n ['0', 'NULL', 'Non confermato'],\n ['5', 'INSERT', 'Non confermato'],\n ['10', 'INVIO', 'Richiede il rinnovo della password'],\n ['20', 'CONF', 'Valido'],\n ['50', 'DELETE', 'Bloccato (soft delete)'],\n ['99', 'BLOCC', 'Bloccato'],\n ],\n ),\n h(`Endpoint principali (superutente salvo diversamente indicato)`),\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/user/get-users', 'Elenco con paginazione (limit e offset, header X-Total-Count).'],\n ['POST', '/api/accessi/user/create-managed-user', 'Crea un utente locale e invia il reset password.'],\n ['PUT', '/api/accessi/user/update-user/:codiceUtente', 'Aggiorna profilo e policy (campi privilegiati solo superutente).'],\n ['DELETE', '/api/accessi/user/delete-user/:codiceUtente', 'Soft delete: STAREG=DELETE. Admin o superutente.'],\n ['DELETE', '/api/accessi/user/delete-user-permanent/:codiceUtente', 'Eliminazione definitiva: rimuove utente e record collegati. Solo admin.'],\n ['POST', '/api/accessi/user/set-password/:codiceUtente', 'Imposta direttamente la password: hash lato backend e scadenza secondo policy, nessuna email.'],\n ['POST', '/api/accessi/user/set-stato', 'Imposta lo stato di registrazione.'],\n ['PATCH', '/api/accessi/user/set-gdpr/:codiceUtente', 'Registra il consenso GDPR.'],\n ['POST', '/api/accessi/user/register', 'Registrazione pubblica, solo se publicRegistration.enabled.'],\n ],\n ),\n note('warn', `Eliminazione: soft o definitiva`, `Il soft delete (STAREG=DELETE) è disponibile per admin e superutenti ed è reversibile. L'eliminazione definitiva rimuove utente e record collegati in transazione ed è riservata agli admin; il superutente senza flag Admin può solo flaggare l'utente come eliminato. Se l'utente ha dati collegati in altre tabelle (per esempio campi estesi del backend) l'eliminazione definitiva fallisce con code ACCESSI_USER_DELETE_HAS_REFERENCES e messaggio esplicito: usa allora il soft delete.`),\n note('ok', `Creazione con invito atomica`, `La creazione utente con invio email è atomica: se SMTP non è configurato si fallisce prima di scrivere, e se l'invio fallisce l'utente creato viene annullato (codice ACCESSI_USER_INVITE_EMAIL_FAILED). Non restano utenti senza password. In alternativa un admin crea l'utente e imposta la password dal form dedicato.`),\n note('ok', `Admin: controllo completo del profilo`, `Dalla scheda utente un admin modifica tutti i campi Accessi: anagrafica, cellulare, lingua, avatar, pagina di default, metadata JSON, 2FA/passwordless/login con password, flag, GDPR, stato registrazione, scadenza password e filtri (tabella FILTRI). La password si imposta direttamente con set-password. Le scritture sono idempotenti (UPDATE OR INSERT su UTENTI_CONFIG) e verificate dal backend; non serve accedere al database.`),\n note('info', `Consenso GDPR`, `Il pulsante \"Registra consenso GDPR\" completa il processo: imposta FLGGDPR e DATGDPR e scrive la riga storica in UTENTI_GDPR (endpoint set-gdpr). Anche impostare flagGdpr via update-user completa il processo con la stessa riga storica.`),\n good('ts', [\n \"await fetch('/api/accessi/user/create-managed-user', {\",\n \" method: 'POST',\",\n \" headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + adminToken },\",\n ' body: JSON.stringify({',\n \" email: 'nuovo@example.com',\",\n \" nome: 'Mario',\",\n \" cognome: 'Rossi',\",\n ' roles: [1, 2],',\n \" permissions: [{ codiceMenu: 'REPORT', tipoAbilitazione: 10 }],\",\n ' }),',\n '});',\n ].join('\\n'), 'Creazione di un utente gestito'),\n bad('ts', [\n '// Sbagliato: inviare flag privilegiati dal frontend pubblico',\n 'fetch(\"/api/accessi/user/register\", {',\n ' method: \"POST\",',\n ' body: JSON.stringify({ email, flagSuper: true }), // ignorato o negato',\n '});',\n ].join('\\n'), 'Privilegi dal client'),\n note('info', `Campi di dominio`, `UTENTI_CONFIG contiene i campi di dominio Accessi: FLGSUPER, FLGADMIN, FLG2FATT (2FA), FLGPWDLESS (passwordless), FLGPASSWORD (login locale), CODLINGUA, CELLULARE, PAGDEF (pagina di default) e JSON_METADATA. I flag/colonne applicativi (FLGMOP, FLGPIANA, FLGADDETTI, FLGOSPITI, FLGPIANARFID, FLGCONTA, FLGCUBI, FLGCICLPASS, FLGDIPENDENTI, FLGINVENTARI, CAUMOV, NUMMAC, RAGSOCCLI, ENABLEIA, CELLUTE e NOMECAMPO) sono stati rimossi: i backend devono gestirli in tabelle proprie. Il telefono canonico e UTENTI_CONFIG.CELLULARE.`),\n note('info', `Autorizzazioni`, `I gruppi/menu si abilitano tramite ABILITAZIONI, RUOLI_MNU e UTENTI_RUOLI. Il vecchio meccanismo NOMECAMPO (flag applicativo su UTENTI_CONFIG) e stato rimosso.`),\n note('info', `Campi estesi`, `extensionFieldsOptions allega colonne di tabelle applicative al profilo (per esempio la ragione sociale). Le colonne sono in whitelist: non costruirle mai da input HTTP.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'ruoli',\n group: 'Dominio',\n title: 'Ruoli e permessi',\n summary: 'Assegnazioni sostitutive e composizione dei grant.',\n blocks: [\n p(`I ruoli contengono una lista di menu con livello. Le assegnazioni (ruoli e permessi diretti) sono SOSTITUTIVE: invia sempre la lista completa desiderata. I grant diretti prevalgono su quelli dei ruoli; i superutenti vedono tutti i menu attivi.`),\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/permission/roles', 'Ruoli con i menu associati.'],\n ['POST', '/api/accessi/permission/create-role', 'Crea un ruolo (i menu sono obbligatori).'],\n ['PUT', '/api/accessi/permission/update-role/:codiceRuolo', 'Aggiorna un ruolo (menu sostitutivi).'],\n ['DELETE', '/api/accessi/permission/delete-role/:codiceRuolo', 'Elimina ruolo, mapping e assegnazioni.'],\n ['POST', '/api/accessi/permission/assign-roles/:codiceUtente', `Sostituisce i ruoli dell'utente.`],\n ['POST', '/api/accessi/permission/assign-permissions/:codiceUtente', 'Sostituisce i permessi diretti.'],\n ['GET', '/api/accessi/permission/grants/:codiceUtente', 'Grant effettivi, permessi diretti e ruoli.'],\n ],\n ),\n good('ts', [\n 'const role = {',\n \" descrizioneRuolo: 'Gestore ordini',\",\n ' menu: [',\n \" { codiceMenu: 'ORDINI', tipoAbilitazione: 20 },\",\n \" { codiceMenu: 'CLIENTI', tipoAbilitazione: 10 },\",\n ' ],',\n '};',\n 'await createRole(role); // POST /api/accessi/permission/create-role',\n '',\n \"// Sostitutivo: questa lista diventa l'intero set di ruoli dell'utente\",\n 'await assignRolesToUser(codiceUtente, { roles: [3, 7] });',\n ].join('\\n'), 'Creazione di un ruolo e assegnazione'),\n bad('ts', [\n \"// Sbagliato: pensare che l'assegnazione sia incrementale\",\n 'await assignRolesToUser(codiceUtente, { roles: [3] });',\n \"// ora l'utente ha SOLO il ruolo 3, non 3 più quelli di prima\",\n ].join('\\n'), 'Assegnazione non additiva'),\n note('warn', `Menu obbligatori`, `create-role e update-role rifiutano un ruolo senza menu. Se ti serve un ruolo senza accessi, non crearlo.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'menu',\n group: 'Dominio',\n title: 'Menu, gruppi e tipi menu',\n summary: 'Catalogo di navigazione e CRUD completo (superutente).',\n blocks: [\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/permission/menus', 'Menu attivi.'],\n ['GET', '/api/accessi/permission/menu-types', 'Catalogo dei tipi menu.'],\n ['GET', '/api/accessi/permission/groups-with-menus?includeDisabled=true', 'Gruppi con menu (anche disabilitati).'],\n ['POST', '/api/accessi/permission/menus', 'Crea un menu.'],\n ['PUT', '/api/accessi/permission/menus/:codiceMenu', 'Aggiorna un menu.'],\n ['DELETE', '/api/accessi/permission/menus/:codiceMenu', 'Elimina un menu (con grant e associazioni ai ruoli).'],\n ['POST', '/api/accessi/permission/menu-groups', 'Crea un gruppo.'],\n ['PUT', '/api/accessi/permission/menu-groups/:codiceGruppo', 'Aggiorna un gruppo.'],\n ['DELETE', '/api/accessi/permission/menu-groups/:codiceGruppo', 'Elimina un gruppo (solo se vuoto).'],\n ['POST/PUT/DELETE', '/api/accessi/permission/menu-types[/:codiceTipo]', 'CRUD dei tipi menu.'],\n ],\n ),\n p(`I codici (CODMNU, CODGRP, CODTIP) sono chiavi immutabili: si indicano in creazione e poi si passano nel percorso. Un gruppo o un tipo si elimina solo se non è referenziato.`),\n good('ts', [\n \"await createMenuGroup({ codiceGruppo: 'A', descrizioneGruppo: 'Amministrazione', ordineGruppo: 1, enabled: true });\",\n \"await createMenuType({ codiceTipo: 'X', descrizioneTipo: 'Operativo' });\",\n \"await createMenu({ codiceMenu: 'ORDINI', descrizioneMenu: 'Ordini', codiceGruppo: 'A', tipo: 'X', ordineMenu: 10, enabled: true });\",\n ].join('\\n'), 'Catalogo in ordine'),\n bad('ts', [\n '// Sbagliato: eliminare un gruppo che contiene menu -> 400 con spiegazione',\n \"await deleteMenuGroup('A'); // contiene N menu\",\n ].join('\\n'), 'Vincoli di integrità'),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'filtri',\n group: 'Dominio',\n title: 'Filtri applicativi',\n summary: 'Filtri per utente e catalogo dei tipi filtro.',\n blocks: [\n table(\n ['Metodo', 'Percorso', 'Descrizione'],\n [\n ['GET', '/api/accessi/filtri/tipi', 'Catalogo dei tipi filtro.'],\n ['POST', '/api/accessi/filtri/tipi', 'Crea un tipo filtro (superutente).'],\n ['PUT', '/api/accessi/filtri/tipi/:tipFil', 'Aggiorna un tipo filtro.'],\n ['DELETE', '/api/accessi/filtri/tipi/:tipFil', 'Elimina un tipo filtro (se non è usato).'],\n ['GET', '/api/accessi/filtri/utente?codUte=123', 'Filtri di un utente (self o superutente).'],\n ['POST', '/api/accessi/filtri/utente', 'Upsert dei filtri (self o superutente).'],\n ],\n ),\n note('info', `Mapping delle colonne`, `I campi filtro sono mappati su colonne FILTRI in whitelist (FILTRI_UTENTE_DB_MAPPING). I campi non configurati nello schema vengono rifiutati.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'sso',\n group: 'SSO',\n title: 'SSO generico (federated authentication)',\n summary: 'Il backend valida il provider, Accessi gestisce identità e sessioni.',\n blocks: [\n p(`Accessi non valida i token Azure, Google o SAML: è il TUO backend a validare il flusso SSO e a passare solo { provider, subject } verificati a FederatedAuthService. Così issuer, secret e token restano fuori dal modulo.`),\n good('ts', [\n \"import { FederatedAuthService } from 'emilsoftware-utilities';\",\n '',\n '// Nel tuo callback SSO, dopo aver validato il token del provider:',\n 'const verified = { provider: \"azure-ad-acme\", subject: claims.oid };',\n 'const result = await federatedAuthService.authenticate(verified);',\n '',\n 'if (result.login.challenge) {',\n ' // 2FA attiva: restituisci il challenge al frontend',\n ' return res.json({ challenge: result.login.challenge });',\n '}',\n 'return res.json({ login: result.login, token: result.token });',\n ].join('\\n'), 'Login SSO'),\n h(`API amministrative (superutente)`),\n list([\n `GET/POST /api/accessi/federated-auth/providers, PATCH/DELETE providers/:provider`,\n `GET/POST /api/accessi/federated-auth/users/:codiceUtente/identities`,\n `POST /api/accessi/federated-auth/users (crea un utente SSO-only)`,\n `PATCH /api/accessi/federated-auth/users/:codiceUtente/password-login`,\n ]),\n bad('ts', [\n \"// Sbagliato: far validare il token del provider ad Accessi o fidarsi del frontend\",\n 'await federatedAuthService.authenticate({ provider, subject: req.body.email }); // subject non verificato',\n ].join('\\n'), `Non fidarti dell'input`),\n note('warn', `Provider in uso`, `Un provider si elimina solo se nessuna identità è collegata; altrimenti disabilitalo. La chiave del provider deve coincidere con la configurazione del backend.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'service-token',\n group: 'Token di servizio',\n title: 'Token macchina-a-macchina',\n summary: 'Autenticazione tecnica con scope: non è un superutente.',\n blocks: [\n p(`Formato: st_<id>.<segreto> (segreto da 256 bit). Nel database si salva solo lo SHA-256 del segreto; il segreto è mostrato una sola volta. La verifica restituisce { tokenId, label, scopes }: nessun ruolo o grant.`),\n note('danger', `Non è un superutente`, `Un service token non apre le API Accessi: abilita solo le rotte della tua app che proteggi con ServiceTokenGuard.`),\n good('ts', [\n \"import { ServiceTokenGuard, RequireServiceTokenScopes } from 'emilsoftware-utilities';\",\n '',\n '@UseGuards(ServiceTokenGuard)',\n \"@RequireServiceTokenScopes('ia')\",\n '@Post(\"internal/ask\")',\n 'ask() { /* req.accessiServiceToken = { tokenId, label, scopes } */ }',\n ].join('\\n'), 'Rotta tecnica protetta'),\n h(`Gestione (superutente)`),\n list([\n `POST /api/accessi/service-token (label, scopes, expiresAt o ttlDays) -> segreto mostrato una volta`,\n `GET /api/accessi/service-token?includeRevoked=true`,\n `DELETE /api/accessi/service-token/:tokenId (revoca immediata)`,\n `POST /api/accessi/service-token/:tokenId/rotate`,\n ]),\n bad('ts', [\n '// Sbagliato: usare un service token per autenticare un utente umano',\n \"fetch('/api/profilo', { headers: { Authorization: 'Bearer ' + serviceToken } }); // req.user assente\",\n ].join('\\n'), 'Non è un utente'),\n note('warn', `Scadenza`, `Senza expiresAt né defaultTtlDays il token non scade: imposta serviceTokens.defaultTtlDays o una scadenza esplicita.`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'console',\n group: 'Console',\n title: 'Console amministrativa',\n summary: 'Accesso, sezioni e che cosa si può fare.',\n blocks: [\n p(`La console è servita dal modulo su /api/accessi/console. Non ha stato proprio: ogni azione chiama le API protette con il JWT dell'utente connesso.`),\n list([\n `Utenti: elenco, creazione locale o SSO, profilo, ruoli, grant diretti, stato, 2FA.`,\n `Ruoli e grant: CRUD dei ruoli con l'albero delle abilitazioni.`,\n `Menu e gruppi: CRUD di gruppi, menu e tipi menu.`,\n `Filtri: filtri utente e tipi filtro.`,\n `Token di servizio: creazione, rotazione, revoca.`,\n `SSO: provider e identità.`,\n `Wiki e Changelog: documentazione e novità della libreria.`,\n `SQL: esporta il DDL delle entità Accessi (tabelle, generatori, FK, indici, check, trigger) per replicare il database.`,\n ]),\n note('info', `Superutente (FLGSUPER)`, `Riceve TUTTE le abilitazioni al livello massimo (30) dopo il login. In console vede solo la sezione Utenti e gestisce utenti, ruoli e grant.`),\n note('info', `Admin (FLGADMIN)`, `Accede a tutta la console (catalogo, token, SSO, utenti) ma NON conferisce abilitazioni: i menu restano quelli assegnati via ABILITAZIONI/RUOLI_MNU. Gerarchia: ADMIN > SUPER > utente.`),\n note('info', `Gerarchia dei ruoli`, `Vale ADMIN (2) > SUPER (1) > utente comune (0). Un attore può gestire solo utenti di rango pari o inferiore: un superutente può amministrare totalmente utenti comuni e superutenti (profilo, password, flag, ruoli, grant, stato, eliminazione, filtri e identità/utenti SSO) ma NON può toccare un admin. Un admin può gestire chiunque. La regola è imposta dal backend (ensureCanManageTargetUser): un tentativo non consentito restituisce 403.`),\n note('info', `Gestione dei flag`, `Dalla scheda utente i flag sono mostrati in base al ruolo di chi è connesso: un admin può assegnare o togliere sia il flag Admin sia il flag Superutente; un superutente senza flag Admin può gestire solo il flag Superutente. I flag si impostano anche in creazione (utente locale o SSO).`),\n note('warn', `Ultimo admin`, `Per non restare chiusi fuori dalla console, l'ultimo admin attivo non può essere disattivato, bloccato, eliminato né privato del flag Admin: l'operazione restituisce 400 finché non viene promosso un altro admin.`),\n 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).`),\n 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 è 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 è opzionale: se assente viene generata e mostrata una sola volta.`),\n note('ok', `Auto-ingresso dal frontend`, `Se un frontend ha già una sessione Accessi valida e l'utente è abilitato alla console, può 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 è utilizzabile una sola volta (endpoint di scambio protetto da rate limit).`),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'errori',\n group: 'Riferimenti',\n title: 'Contratto errori e codici',\n summary: 'Formato della risposta e codici stabili ACCESSI_* e AUTH_*.',\n blocks: [\n p(`Gli errori HTTP delle API Accessi usano un contratto stabile: severity, status, statusCode, code, error (campo legacy uguale a code), message e details per gli errori di validazione.`),\n code('json', `{\\n \"severity\": \"error\",\\n \"status\": 400,\\n \"statusCode\": 2,\\n \"code\": \"ACCESSI_VALIDATION_ERROR\",\\n \"error\": \"ACCESSI_VALIDATION_ERROR\",\\n \"message\": \"La richiesta contiene dati non validi.\",\\n \"details\": [\"email: deve essere una stringa.\"]\\n}`, 'Errore di validazione'),\n table(\n ['Codice', 'Quando'],\n [\n ['ACCESSI_VALIDATION_ERROR', 'Body o parametri non validi (400).'],\n ['ACCESSI_AUTH_INITIALIZING', 'Bootstrap in corso (503).'],\n ['ACCESSI_AUTH_NOT_INITIALIZED', 'Modulo non inizializzato (500).'],\n ['AUTH_HEADER_MISSING / AUTH_TOKEN_MISSING', 'Header Authorization assente (401).'],\n ['AUTH_TOKEN_INVALID', 'JWT non valido o scaduto (401).'],\n ['AUTH_USER_DISABLED', 'Utente non più autorizzato (401).'],\n ['AUTH_INSUFFICIENT_PERMISSIONS', 'Requisiti non soddisfatti (403).'],\n ['AUTH_REQUIREMENTS_MISCONFIGURED', 'Policy malformata o handler custom mancante (500).'],\n ['ACCESSI_DATABASE_SCHEMA_OUTDATED', 'Schema del database incompatibile.'],\n ['ACCESSI_SERVICE_TOKEN_RATE_LIMITED', 'Troppi tentativi (429).'],\n ],\n ),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'endpoint',\n group: 'Riferimenti',\n title: 'Indice degli endpoint',\n summary: 'Tutte le rotte /api/accessi/* in una tabella.',\n blocks: [\n table(\n ['Area', 'Endpoint principali'],\n [\n ['Auth', 'POST auth/login, auth/get-user-by-token, auth/confirm-reset-password/:token, auth/two-factor/verify, auth/two-factor/resend'],\n ['Email', 'POST email/send-reset-password-email, GET email/reset-password-page/:token'],\n ['Utenti', 'GET user/get-users, POST user/create-managed-user, PUT user/update-user/:codiceUtente, DELETE user/delete-user/:codiceUtente, DELETE user/delete-user-permanent/:codiceUtente, POST user/set-password/:codiceUtente, POST user/set-stato, PATCH user/set-gdpr/:codiceUtente, POST user/register'],\n ['Permessi', 'GET permission/roles, POST permission/create-role, PUT permission/update-role/:codiceRuolo, DELETE permission/delete-role/:codiceRuolo, POST permission/assign-roles/:codiceUtente, POST permission/assign-permissions/:codiceUtente, GET permission/grants/:codiceUtente, GET permission/menus, GET permission/menu-types, GET permission/groups-with-menus'],\n ['Catalogo', 'POST/PUT/DELETE permission/menus[/:codiceMenu], permission/menu-groups[/:codiceGruppo], permission/menu-types[/:codiceTipo]'],\n ['Configurator', 'PATCH configurator/menus/:codiceMenu/enabled, PATCH configurator/groups/:codiceGruppo/enabled'],\n ['Filtri', 'GET filtri/tipi, POST/PUT/DELETE filtri/tipi[/:tipFil], GET/POST filtri/utente'],\n ['SSO', 'GET/POST federated-auth/providers, PATCH/DELETE providers/:provider, GET/POST users/:codiceUtente/identities, POST users, PATCH users/:codiceUtente/password-login'],\n ['Token', 'POST/GET service-token, DELETE service-token/:tokenId, POST service-token/:tokenId/rotate'],\n ['Schema', 'GET schema (DDL completo + entità presenti, solo admin)'],\n ['Console', 'GET console, console/:view, console/:view/:sub, console/assets/:file'],\n ],\n ),\n ],\n },\n // -------------------------------------------------------------------------\n {\n id: 'antipattern',\n group: 'Riferimenti',\n title: 'Antipattern e checklist',\n summary: 'Che cosa non fare, in breve.',\n blocks: [\n list([\n `Non aprire la porta HTTP prima di initializeAccessiModule.`,\n `Non usare authorizeAccessi come unico strato: valida sempre anche l'input applicativo.`,\n `Non dare per scontato che req.userGrants esista se non hai richiesto dei requisiti.`,\n `Non considerare incrementali assignRolesToUser e assignPermissionsToUser: sono sostitutivi.`,\n `Non far validare i token SSO ad Accessi: passagli solo { provider, subject } verificati.`,\n `Non usare un service token come identità utente.`,\n `Non versionare segreti; jwtOptions.secret deve essere unico per ambiente.`,\n `Non eliminare gruppi o tipi in uso: gestisci l'errore 400 e disabilita invece.`,\n `Non usare il challenge 2FA come JWT.`,\n ]),\n note('ok', `Checklist di rilascio`, `Schema verificato (db:check), segreti da un secret manager, mockDemoUser=false, rate limit pubblico configurato, SMTP reale testato, serviceTokens.defaultTtlDays impostato.`),\n ],\n },\n];\n\n/** Elenco ordinato dei gruppi per l'indice. */\nexport function wikiGroups(): Array<{ group: string; sections: WikiSection[] }> {\n const groups: Array<{ group: string; sections: WikiSection[] }> = [];\n for (const section of WIKI_SECTIONS) {\n let bucket = groups.find((item) => item.group === section.group);\n if (!bucket) {\n bucket = { group: section.group, sections: [] };\n groups.push(bucket);\n }\n bucket.sections.push(section);\n }\n return groups;\n}\n\n/**\n * Digest compatto in Markdown per l'IA che costruisce il backend integratore.\n * Deriva dagli stessi dati della wiki, quindi resta sincronizzato.\n */\nexport function buildAiDigest(): string {\n const lines: string[] = [\n `# Accessi (emilsoftware-utilities) - guida compatta per l'integrazione backend`,\n '',\n `Modulo NestJS/Express + Firebird per autenticazione JWT, autorizzazione per menu, utenti, 2FA e passwordless, SSO generico, service token e console.`,\n '',\n ];\n\n for (const { group, sections } of wikiGroups()) {\n lines.push(`## ${group}`);\n for (const section of sections) {\n lines.push(`### ${section.title}`);\n lines.push(section.summary);\n for (const block of section.blocks) {\n switch (block.kind) {\n case 'p':\n lines.push(block.text);\n break;\n case 'h':\n lines.push(`**${block.text}**`);\n break;\n case 'list':\n block.items.forEach((item, index) => lines.push(`${block.ordered ? `${index + 1}.` : '-'} ${item}`));\n break;\n case 'code':\n lines.push('```' + block.language);\n lines.push(block.code);\n lines.push('```');\n break;\n case 'callout':\n lines.push(`[${block.tone.toUpperCase()}] ${block.title ? block.title + ': ' : ''}${block.text}`);\n break;\n case 'table':\n lines.push('| ' + block.head.join(' | ') + ' |');\n lines.push('| ' + block.head.map(() => '---').join(' | ') + ' |');\n block.rows.forEach((row) => lines.push('| ' + row.join(' | ') + ' |'));\n break;\n }\n }\n lines.push('');\n }\n }\n\n return lines.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n"]}
@@ -17,4 +17,5 @@ export declare class AuthController {
17
17
  verifyTwoFactor(request: Request, body: VerifyTwoFactorRequest, res: Response): Promise<Response<any, Record<string, any>>>;
18
18
  resendTwoFactor(request: Request, body: ResendTwoFactorRequest, res: Response): Promise<Response<any, Record<string, any>>>;
19
19
  bootstrapAdmin(request: Request, body: BootstrapAdminRequest, res: Response): Promise<Response<any, Record<string, any>>>;
20
+ adminBootstrapStatus(res: Response): Promise<Response<any, Record<string, any>>>;
20
21
  }
@@ -156,6 +156,11 @@ let AuthController = AuthController_1 = class AuthController {
156
156
  }
157
157
  });
158
158
  }
159
+ adminBootstrapStatus(res) {
160
+ return __awaiter(this, void 0, void 0, function* () {
161
+ return Utilities_1.RestUtilities.sendBaseResponse(res, { enabled: this.adminBootstrapService.isEnabled() });
162
+ });
163
+ }
159
164
  };
160
165
  exports.AuthController = AuthController;
161
166
  __decorate([
@@ -284,6 +289,19 @@ __decorate([
284
289
  __metadata("design:paramtypes", [Object, Dtos_1.BootstrapAdminRequest, Object]),
285
290
  __metadata("design:returntype", Promise)
286
291
  ], AuthController.prototype, "bootstrapAdmin", null);
292
+ __decorate([
293
+ (0, common_1.Get)('bootstrap-admin/status'),
294
+ (0, swagger_1.ApiOperation)({
295
+ summary: 'Indica se il bootstrap admin e abilitato',
296
+ operationId: 'getAdminBootstrapStatus',
297
+ description: 'Endpoint pubblico: la console mostra la sezione "Crea utente admin" solo quando enabled e true.',
298
+ }),
299
+ (0, swagger_1.ApiResponse)({ status: 200, type: Dtos_1.AdminBootstrapStatusResponse }),
300
+ __param(0, (0, common_1.Res)()),
301
+ __metadata("design:type", Function),
302
+ __metadata("design:paramtypes", [Object]),
303
+ __metadata("design:returntype", Promise)
304
+ ], AuthController.prototype, "adminBootstrapStatus", null);
287
305
  exports.AuthController = AuthController = AuthController_1 = __decorate([
288
306
  (0, swagger_1.ApiTags)('Auth'),
289
307
  (0, common_1.Controller)('accessi/auth')
@@ -1 +1 @@
1
- {"version":3,"file":"AuthController.js","sourceRoot":"","sources":["../../../src/accessi-module/Controllers/AuthController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Body, Controller, HttpException, HttpStatus, Inject, Param, Post, Req, Res } from '@nestjs/common';\nimport { ResendTwoFactorRequest, VerifyTwoFactorRequest } from '../Dtos/TwoFactorDtos';\nimport { ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';\nimport { Request, Response } from 'express';\nimport { Logger } from '../../Logger';\nimport { RestUtilities } from '../../Utilities';\nimport type { AccessiOptions } from '../AccessiModule';\nimport {\n ActionResponse,\n BootstrapAdminRequest,\n BootstrapAdminResponse,\n ConfirmResetPasswordRequest,\n ErrorResponse,\n GetUserByTokenRequest,\n GetUserByTokenResponse,\n LoginRequest,\n LoginResponse,\n PasswordExpiredResponse,\n} from '../Dtos';\nimport { AuthService, PASSWORD_LOGIN_DISABLED } from '../Services/AuthService/AuthService';\nimport { AdminBootstrapService } from '../Services/AdminBootstrapService/AdminBootstrapService';\nimport {\n checkPublicAuthRateLimit,\n sendPublicAuthRateLimitExceeded,\n} from '../security/publicAuthRateLimit';\n\n@ApiTags('Auth')\n@Controller('accessi/auth')\n/**\n * Endpoint pubblici di autenticazione locale e verifica sessione.\n * Il login SSO non riceve token esterni qui: il backend validante usa `FederatedAuthService.authenticate`.\n */\nexport class AuthController {\n logger: Logger = new Logger(AuthController.name);\n\n constructor(\n private readonly authService: AuthService,\n private readonly adminBootstrapService: AdminBootstrapService,\n @Inject('ACCESSI_OPTIONS') private readonly options: AccessiOptions,\n ) {}\n\n @ApiOperation({ summary: 'Conferma il reset della password', operationId: 'resetPassword' })\n @ApiParam({ name: 'token', description: 'Token per il reset della password', required: true })\n @ApiBody({ type: ConfirmResetPasswordRequest })\n @ApiResponse({\n status: 200,\n description: 'Password aggiornata con successo',\n type: ActionResponse,\n })\n @ApiResponse({\n status: 400,\n description: 'Errore nella richiesta o token non valido',\n type: ErrorResponse,\n })\n @Post('confirm-reset-password/:token')\n async resetPassword(\n @Req() request: Request,\n @Res() res: Response,\n @Param('token') token: string,\n @Body() body: ConfirmResetPasswordRequest,\n ) {\n try {\n const rateLimitDecision = checkPublicAuthRateLimit(\n this.options,\n 'passwordResetConfirm',\n request,\n );\n if (!rateLimitDecision.allowed) {\n return sendPublicAuthRateLimitExceeded(res, rateLimitDecision.retryAfterSeconds);\n }\n\n await this.authService.confirmResetPassword(token, body.newPassword);\n return RestUtilities.sendOKMessage(res, 'Password aggiornata con successo!');\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, HttpStatus.BAD_REQUEST);\n }\n }\n\n @ApiOperation({\n summary: 'Recupera le informazioni utente dal token JWT',\n operationId: 'getUserByToken',\n })\n @ApiBody({ type: GetUserByTokenRequest })\n @ApiResponse({\n status: 200,\n description: 'Informazioni utente recuperate con successo',\n type: GetUserByTokenResponse,\n })\n @ApiResponse({\n status: 401,\n description: 'Token non valido o scaduto',\n type: ErrorResponse,\n })\n @Post('get-user-by-token')\n async getUserByToken(\n @Req() request: Request,\n @Body() body: GetUserByTokenRequest,\n @Res() res: Response,\n ) {\n try {\n const rateLimitDecision = checkPublicAuthRateLimit(\n this.options,\n 'getUserByToken',\n request,\n );\n if (!rateLimitDecision.allowed) {\n return sendPublicAuthRateLimitExceeded(res, rateLimitDecision.retryAfterSeconds);\n }\n\n if (!body?.token) {\n return RestUtilities.sendErrorMessage(\n res,\n 'Token non fornito',\n AuthController.name,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n const authenticatedPayload = await this.authService.getAuthenticatedTokenPayload(body.token);\n const userData = (authenticatedPayload as { utente?: unknown })?.utente;\n if (!userData) {\n throw new Error('Il token non contiene un utente Accessi valido.');\n }\n\n // Keep the runtime response aligned with GetUserByTokenResponse and Orval:\n // Result.userData contains the authenticated user directly, not a nested JWT payload.\n return RestUtilities.sendBaseResponse(res, { userData });\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, HttpStatus.UNAUTHORIZED);\n }\n }\n\n @ApiOperation({\n summary: 'Effettua il login utente',\n description:\n \"Con sola email restituisce passwordRequired oppure avvia il codice per utenti passwordless. Con password valida restituisce il login completo o challenge se la 2FA e attiva. Nessun JWT viene emesso prima della verifica del codice.\",\n operationId: 'login',\n })\n @ApiBody({ type: LoginRequest })\n @ApiResponse({\n status: 200,\n description: 'Login effettuato con successo',\n type: LoginResponse,\n })\n @ApiResponse({\n status: 401,\n description: 'Credenziali non valide',\n type: ErrorResponse,\n })\n @ApiResponse({\n status: 403,\n description: 'Password scaduta, e necessario aggiornarla.',\n type: PasswordExpiredResponse,\n })\n @ApiResponse({ status: 403, description: 'Utente abilitato solo a SSO.', type: ErrorResponse })\n @Post('login')\n async login(@Req() request: Request, @Body() loginRequest: LoginRequest, @Res() res: Response) {\n try {\n const rateLimitDecision = checkPublicAuthRateLimit(\n this.options,\n 'login',\n request,\n [loginRequest?.email],\n );\n if (!rateLimitDecision.allowed) {\n return sendPublicAuthRateLimitExceeded(res, rateLimitDecision.retryAfterSeconds);\n }\n\n const userData = await this.authService.login(loginRequest);\n if (!userData) {\n return RestUtilities.sendInvalidCredentials(res);\n }\n\n if (!userData.challenge && !userData.passwordRequired) userData.token = this.authService.createAccessiToken(userData.utente, ['password']);\n\n return RestUtilities.sendBaseResponse(res, userData);\n } catch (error) {\n if ((error as Error)?.message === 'PASSWORD_EXPIRED') {\n this.logger.warning('Password scaduta, cambiare password ', error);\n return RestUtilities.sendPasswordExpired(res);\n }\n\n if (error instanceof HttpException) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error.getStatus());\n }\n\n if ((error as Error)?.message === PASSWORD_LOGIN_DISABLED) {\n return res.status(HttpStatus.FORBIDDEN).json({\n severity: 'error', status: HttpStatus.FORBIDDEN, statusCode: 2,\n code: PASSWORD_LOGIN_DISABLED,\n error: PASSWORD_LOGIN_DISABLED,\n message: 'Questo utente e abilitato solo tramite SSO. Accedi con un provider collegato.',\n });\n }\n\n this.logger.error('Errore durante il login', error);\n if (RestUtilities.isDatabaseSchemaError(error)) {\n return RestUtilities.sendErrorMessage(\n res,\n error,\n AuthController.name,\n HttpStatus.SERVICE_UNAVAILABLE,\n );\n }\n return RestUtilities.sendInvalidCredentials(res);\n }\n }\n\n @Post('two-factor/verify')\n @ApiOperation({ summary: 'Verifica il codice e completa il login', operationId: 'verifyTwoFactor' })\n @ApiBody({ type: VerifyTwoFactorRequest })\n @ApiResponse({ status: 200, type: LoginResponse })\n @ApiResponse({ status: 401, type: ErrorResponse })\n @ApiResponse({ status: 429, type: ErrorResponse })\n async verifyTwoFactor(@Req() request: Request, @Body() body: VerifyTwoFactorRequest, @Res() res: Response) {\n try {\n const limit = checkPublicAuthRateLimit(this.options, 'twoFactorVerify', request, [body.challengeId]);\n if (!limit.allowed) return sendPublicAuthRateLimitExceeded(res, limit.retryAfterSeconds);\n return RestUtilities.sendBaseResponse(res, await this.authService.verifyTwoFactor(body.challengeId, body.code));\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error instanceof HttpException ? error.getStatus() : 500);\n }\n }\n\n @Post('two-factor/resend')\n @ApiOperation({ summary: 'Reinvia il codice, invalidando il precedente', operationId: 'resendTwoFactor' })\n @ApiBody({ type: ResendTwoFactorRequest })\n @ApiResponse({ status: 200, type: LoginResponse })\n @ApiResponse({ status: 401, type: ErrorResponse })\n @ApiResponse({ status: 429, type: ErrorResponse })\n async resendTwoFactor(@Req() request: Request, @Body() body: ResendTwoFactorRequest, @Res() res: Response) {\n try {\n const limit = checkPublicAuthRateLimit(this.options, 'twoFactorResend', request, [body.challengeId]);\n if (!limit.allowed) return sendPublicAuthRateLimitExceeded(res, limit.retryAfterSeconds);\n return RestUtilities.sendBaseResponse(res, await this.authService.resendTwoFactor(body.challengeId));\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error instanceof HttpException ? error.getStatus() : 500);\n }\n }\n\n @Post('bootstrap-admin')\n @ApiOperation({\n summary: 'Crea il primo utente admin (bootstrap)',\n operationId: 'bootstrapAdmin',\n description:\n 'Endpoint pubblico ma protetto dal token di avvio: attivo solo con adminBootstrap.enabled. ' +\n 'Crea un utente in stato confermato senza email, con flag admin (e super opzionale) e password fornita o generata.',\n })\n @ApiBody({ type: BootstrapAdminRequest })\n @ApiResponse({ status: 200, type: BootstrapAdminResponse })\n @ApiResponse({ status: 401, description: 'Token di bootstrap non valido', type: ErrorResponse })\n @ApiResponse({ status: 403, description: 'Bootstrap admin non abilitato', type: ErrorResponse })\n @ApiResponse({ status: 429, description: 'Troppi tentativi', type: ErrorResponse })\n async bootstrapAdmin(@Req() request: Request, @Body() body: BootstrapAdminRequest, @Res() res: Response) {\n try {\n const limit = checkPublicAuthRateLimit(this.options, 'adminBootstrap', request);\n if (!limit.allowed) return sendPublicAuthRateLimitExceeded(res, limit.retryAfterSeconds);\n return RestUtilities.sendBaseResponse(res, await this.adminBootstrapService.bootstrap(body));\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error instanceof HttpException ? error.getStatus() : 500);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"AuthController.js","sourceRoot":"","sources":["../../../src/accessi-module/Controllers/AuthController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Body, Controller, Get, HttpException, HttpStatus, Inject, Param, Post, Req, Res } from '@nestjs/common';\nimport { ResendTwoFactorRequest, VerifyTwoFactorRequest } from '../Dtos/TwoFactorDtos';\nimport { ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';\nimport { Request, Response } from 'express';\nimport { Logger } from '../../Logger';\nimport { RestUtilities } from '../../Utilities';\nimport type { AccessiOptions } from '../AccessiModule';\nimport {\n ActionResponse,\n AdminBootstrapStatusResponse,\n BootstrapAdminRequest,\n BootstrapAdminResponse,\n ConfirmResetPasswordRequest,\n ErrorResponse,\n GetUserByTokenRequest,\n GetUserByTokenResponse,\n LoginRequest,\n LoginResponse,\n PasswordExpiredResponse,\n} from '../Dtos';\nimport { AuthService, PASSWORD_LOGIN_DISABLED } from '../Services/AuthService/AuthService';\nimport { AdminBootstrapService } from '../Services/AdminBootstrapService/AdminBootstrapService';\nimport {\n checkPublicAuthRateLimit,\n sendPublicAuthRateLimitExceeded,\n} from '../security/publicAuthRateLimit';\n\n@ApiTags('Auth')\n@Controller('accessi/auth')\n/**\n * Endpoint pubblici di autenticazione locale e verifica sessione.\n * Il login SSO non riceve token esterni qui: il backend validante usa `FederatedAuthService.authenticate`.\n */\nexport class AuthController {\n logger: Logger = new Logger(AuthController.name);\n\n constructor(\n private readonly authService: AuthService,\n private readonly adminBootstrapService: AdminBootstrapService,\n @Inject('ACCESSI_OPTIONS') private readonly options: AccessiOptions,\n ) {}\n\n @ApiOperation({ summary: 'Conferma il reset della password', operationId: 'resetPassword' })\n @ApiParam({ name: 'token', description: 'Token per il reset della password', required: true })\n @ApiBody({ type: ConfirmResetPasswordRequest })\n @ApiResponse({\n status: 200,\n description: 'Password aggiornata con successo',\n type: ActionResponse,\n })\n @ApiResponse({\n status: 400,\n description: 'Errore nella richiesta o token non valido',\n type: ErrorResponse,\n })\n @Post('confirm-reset-password/:token')\n async resetPassword(\n @Req() request: Request,\n @Res() res: Response,\n @Param('token') token: string,\n @Body() body: ConfirmResetPasswordRequest,\n ) {\n try {\n const rateLimitDecision = checkPublicAuthRateLimit(\n this.options,\n 'passwordResetConfirm',\n request,\n );\n if (!rateLimitDecision.allowed) {\n return sendPublicAuthRateLimitExceeded(res, rateLimitDecision.retryAfterSeconds);\n }\n\n await this.authService.confirmResetPassword(token, body.newPassword);\n return RestUtilities.sendOKMessage(res, 'Password aggiornata con successo!');\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, HttpStatus.BAD_REQUEST);\n }\n }\n\n @ApiOperation({\n summary: 'Recupera le informazioni utente dal token JWT',\n operationId: 'getUserByToken',\n })\n @ApiBody({ type: GetUserByTokenRequest })\n @ApiResponse({\n status: 200,\n description: 'Informazioni utente recuperate con successo',\n type: GetUserByTokenResponse,\n })\n @ApiResponse({\n status: 401,\n description: 'Token non valido o scaduto',\n type: ErrorResponse,\n })\n @Post('get-user-by-token')\n async getUserByToken(\n @Req() request: Request,\n @Body() body: GetUserByTokenRequest,\n @Res() res: Response,\n ) {\n try {\n const rateLimitDecision = checkPublicAuthRateLimit(\n this.options,\n 'getUserByToken',\n request,\n );\n if (!rateLimitDecision.allowed) {\n return sendPublicAuthRateLimitExceeded(res, rateLimitDecision.retryAfterSeconds);\n }\n\n if (!body?.token) {\n return RestUtilities.sendErrorMessage(\n res,\n 'Token non fornito',\n AuthController.name,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n const authenticatedPayload = await this.authService.getAuthenticatedTokenPayload(body.token);\n const userData = (authenticatedPayload as { utente?: unknown })?.utente;\n if (!userData) {\n throw new Error('Il token non contiene un utente Accessi valido.');\n }\n\n // Keep the runtime response aligned with GetUserByTokenResponse and Orval:\n // Result.userData contains the authenticated user directly, not a nested JWT payload.\n return RestUtilities.sendBaseResponse(res, { userData });\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, HttpStatus.UNAUTHORIZED);\n }\n }\n\n @ApiOperation({\n summary: 'Effettua il login utente',\n description:\n \"Con sola email restituisce passwordRequired oppure avvia il codice per utenti passwordless. Con password valida restituisce il login completo o challenge se la 2FA e attiva. Nessun JWT viene emesso prima della verifica del codice.\",\n operationId: 'login',\n })\n @ApiBody({ type: LoginRequest })\n @ApiResponse({\n status: 200,\n description: 'Login effettuato con successo',\n type: LoginResponse,\n })\n @ApiResponse({\n status: 401,\n description: 'Credenziali non valide',\n type: ErrorResponse,\n })\n @ApiResponse({\n status: 403,\n description: 'Password scaduta, e necessario aggiornarla.',\n type: PasswordExpiredResponse,\n })\n @ApiResponse({ status: 403, description: 'Utente abilitato solo a SSO.', type: ErrorResponse })\n @Post('login')\n async login(@Req() request: Request, @Body() loginRequest: LoginRequest, @Res() res: Response) {\n try {\n const rateLimitDecision = checkPublicAuthRateLimit(\n this.options,\n 'login',\n request,\n [loginRequest?.email],\n );\n if (!rateLimitDecision.allowed) {\n return sendPublicAuthRateLimitExceeded(res, rateLimitDecision.retryAfterSeconds);\n }\n\n const userData = await this.authService.login(loginRequest);\n if (!userData) {\n return RestUtilities.sendInvalidCredentials(res);\n }\n\n if (!userData.challenge && !userData.passwordRequired) userData.token = this.authService.createAccessiToken(userData.utente, ['password']);\n\n return RestUtilities.sendBaseResponse(res, userData);\n } catch (error) {\n if ((error as Error)?.message === 'PASSWORD_EXPIRED') {\n this.logger.warning('Password scaduta, cambiare password ', error);\n return RestUtilities.sendPasswordExpired(res);\n }\n\n if (error instanceof HttpException) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error.getStatus());\n }\n\n if ((error as Error)?.message === PASSWORD_LOGIN_DISABLED) {\n return res.status(HttpStatus.FORBIDDEN).json({\n severity: 'error', status: HttpStatus.FORBIDDEN, statusCode: 2,\n code: PASSWORD_LOGIN_DISABLED,\n error: PASSWORD_LOGIN_DISABLED,\n message: 'Questo utente e abilitato solo tramite SSO. Accedi con un provider collegato.',\n });\n }\n\n this.logger.error('Errore durante il login', error);\n if (RestUtilities.isDatabaseSchemaError(error)) {\n return RestUtilities.sendErrorMessage(\n res,\n error,\n AuthController.name,\n HttpStatus.SERVICE_UNAVAILABLE,\n );\n }\n return RestUtilities.sendInvalidCredentials(res);\n }\n }\n\n @Post('two-factor/verify')\n @ApiOperation({ summary: 'Verifica il codice e completa il login', operationId: 'verifyTwoFactor' })\n @ApiBody({ type: VerifyTwoFactorRequest })\n @ApiResponse({ status: 200, type: LoginResponse })\n @ApiResponse({ status: 401, type: ErrorResponse })\n @ApiResponse({ status: 429, type: ErrorResponse })\n async verifyTwoFactor(@Req() request: Request, @Body() body: VerifyTwoFactorRequest, @Res() res: Response) {\n try {\n const limit = checkPublicAuthRateLimit(this.options, 'twoFactorVerify', request, [body.challengeId]);\n if (!limit.allowed) return sendPublicAuthRateLimitExceeded(res, limit.retryAfterSeconds);\n return RestUtilities.sendBaseResponse(res, await this.authService.verifyTwoFactor(body.challengeId, body.code));\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error instanceof HttpException ? error.getStatus() : 500);\n }\n }\n\n @Post('two-factor/resend')\n @ApiOperation({ summary: 'Reinvia il codice, invalidando il precedente', operationId: 'resendTwoFactor' })\n @ApiBody({ type: ResendTwoFactorRequest })\n @ApiResponse({ status: 200, type: LoginResponse })\n @ApiResponse({ status: 401, type: ErrorResponse })\n @ApiResponse({ status: 429, type: ErrorResponse })\n async resendTwoFactor(@Req() request: Request, @Body() body: ResendTwoFactorRequest, @Res() res: Response) {\n try {\n const limit = checkPublicAuthRateLimit(this.options, 'twoFactorResend', request, [body.challengeId]);\n if (!limit.allowed) return sendPublicAuthRateLimitExceeded(res, limit.retryAfterSeconds);\n return RestUtilities.sendBaseResponse(res, await this.authService.resendTwoFactor(body.challengeId));\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error instanceof HttpException ? error.getStatus() : 500);\n }\n }\n\n @Post('bootstrap-admin')\n @ApiOperation({\n summary: 'Crea il primo utente admin (bootstrap)',\n operationId: 'bootstrapAdmin',\n description:\n 'Endpoint pubblico ma protetto dal token di avvio: attivo solo con adminBootstrap.enabled. ' +\n 'Crea un utente in stato confermato senza email, con flag admin (e super opzionale) e password fornita o generata.',\n })\n @ApiBody({ type: BootstrapAdminRequest })\n @ApiResponse({ status: 200, type: BootstrapAdminResponse })\n @ApiResponse({ status: 401, description: 'Token di bootstrap non valido', type: ErrorResponse })\n @ApiResponse({ status: 403, description: 'Bootstrap admin non abilitato', type: ErrorResponse })\n @ApiResponse({ status: 429, description: 'Troppi tentativi', type: ErrorResponse })\n async bootstrapAdmin(@Req() request: Request, @Body() body: BootstrapAdminRequest, @Res() res: Response) {\n try {\n const limit = checkPublicAuthRateLimit(this.options, 'adminBootstrap', request);\n if (!limit.allowed) return sendPublicAuthRateLimitExceeded(res, limit.retryAfterSeconds);\n return RestUtilities.sendBaseResponse(res, await this.adminBootstrapService.bootstrap(body));\n } catch (error) {\n return RestUtilities.sendErrorMessage(res, error, AuthController.name, error instanceof HttpException ? error.getStatus() : 500);\n }\n }\n\n @Get('bootstrap-admin/status')\n @ApiOperation({\n summary: 'Indica se il bootstrap admin e abilitato',\n operationId: 'getAdminBootstrapStatus',\n description:\n 'Endpoint pubblico: la console mostra la sezione \"Crea utente admin\" solo quando enabled e true.',\n })\n @ApiResponse({ status: 200, type: AdminBootstrapStatusResponse })\n async adminBootstrapStatus(@Res() res: Response) {\n return RestUtilities.sendBaseResponse(res, { enabled: this.adminBootstrapService.isEnabled() });\n }\n}\n"]}
@@ -17,3 +17,10 @@ export declare class BootstrapAdminResultDto {
17
17
  export declare class BootstrapAdminResponse extends BaseResponse {
18
18
  Result: BootstrapAdminResultDto;
19
19
  }
20
+ /** Stato del bootstrap admin: la console mostra la sezione solo se abilitato. */
21
+ export declare class AdminBootstrapStatusResult {
22
+ enabled: boolean;
23
+ }
24
+ export declare class AdminBootstrapStatusResponse extends BaseResponse {
25
+ Result: AdminBootstrapStatusResult;
26
+ }