emilsoftware-utilities 2.0.0-dev.39 → 2.0.0-dev.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accessi-module/Console/console.js +10 -2
- package/dist/accessi-module/Console/console.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/publicRegisterRequest.d.ts +6 -0
- package/dist/accessi-module/Console/generated/model/publicRegisterRequest.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/registerRequest.d.ts +6 -0
- package/dist/accessi-module/Console/generated/model/registerRequest.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/userDto.d.ts +6 -0
- package/dist/accessi-module/Console/generated/model/userDto.js.map +1 -1
- package/dist/accessi-module/Console/help.js +7 -0
- package/dist/accessi-module/Console/help.js.map +1 -1
- package/dist/accessi-module/Console/wiki.js +1 -1
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/Dtos/RegisterRequest.d.ts +1 -0
- package/dist/accessi-module/Dtos/RegisterRequest.js +13 -0
- package/dist/accessi-module/Dtos/RegisterRequest.js.map +1 -1
- package/dist/accessi-module/Dtos/UserDto.d.ts +1 -0
- package/dist/accessi-module/Dtos/UserDto.js +13 -0
- package/dist/accessi-module/Dtos/UserDto.js.map +1 -1
- package/dist/accessi-module/Services/UserService/UserService.js +11 -0
- package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
- package/dist/accessi-module/database-updates/accessiSchema.d.ts +1 -1
- package/dist/accessi-module/database-updates/accessiSchema.js +3 -3
- package/dist/accessi-module/database-updates/accessiSchema.js.map +1 -1
- package/dist/emilsoftware-utilities-2.0.0-dev.40.tgz +0 -0
- package/package.json +1 -1
- package/dist/emilsoftware-utilities-2.0.0-dev.39.tgz +0 -0
|
@@ -38,6 +38,12 @@ export interface PublicRegisterRequest {
|
|
|
38
38
|
nome?: string;
|
|
39
39
|
/** Numero del reparto associato */
|
|
40
40
|
numRep?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Pagina di default mostrata all'utente dopo il login (percorso o codice pagina).
|
|
43
|
+
* @maxLength 50
|
|
44
|
+
* @nullable
|
|
45
|
+
*/
|
|
46
|
+
paginaDefault?: string | null;
|
|
41
47
|
/** Progressivo identificativo del filtro */
|
|
42
48
|
progressivo?: number;
|
|
43
49
|
/** Tipo di filtro applicato */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicRegisterRequest.js","sourceRoot":"","sources":["../../../../../src/accessi-module/Console/generated/model/publicRegisterRequest.ts"],"names":[],"mappings":";AAAA;;;;;GAKG","sourcesContent":["/**\n * Generated by orval v6.31.0 🍺\n * Do not edit manually.\n * Accessi API\n * OpenAPI spec version: 1.0\n */\n\nexport interface PublicRegisterRequest {\n /** Avatar dell'utente. */\n avatar?: string;\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 del vettore */\n codVet?: number;\n /** Cognome dell'utente. */\n cognome?: string;\n /** Email dell'utente. */\n email: string;\n /** HTML mail personalizzato */\n htmlMail?: string;\n /** Idx personale */\n idxPers?: number;\n /** idx della postazione nella tabella ANTENNE_POS di PROLAV */\n idxPos?: number;\n /** Nome dell'utente. */\n nome?: string;\n /** Numero del reparto associato */\n numRep?: number;\n /** Progressivo identificativo del filtro */\n progressivo?: number;\n /** Tipo di filtro applicato */\n tipFil?: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"publicRegisterRequest.js","sourceRoot":"","sources":["../../../../../src/accessi-module/Console/generated/model/publicRegisterRequest.ts"],"names":[],"mappings":";AAAA;;;;;GAKG","sourcesContent":["/**\n * Generated by orval v6.31.0 🍺\n * Do not edit manually.\n * Accessi API\n * OpenAPI spec version: 1.0\n */\n\nexport interface PublicRegisterRequest {\n /** Avatar dell'utente. */\n avatar?: string;\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 del vettore */\n codVet?: number;\n /** Cognome dell'utente. */\n cognome?: string;\n /** Email dell'utente. */\n email: string;\n /** HTML mail personalizzato */\n htmlMail?: string;\n /** Idx personale */\n idxPers?: number;\n /** idx della postazione nella tabella ANTENNE_POS di PROLAV */\n idxPos?: number;\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 /** Progressivo identificativo del filtro */\n progressivo?: number;\n /** Tipo di filtro applicato */\n tipFil?: number;\n}\n"]}
|
|
@@ -45,6 +45,12 @@ export interface RegisterRequest {
|
|
|
45
45
|
nome?: string;
|
|
46
46
|
/** Numero del reparto associato */
|
|
47
47
|
numRep?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Pagina di default mostrata all'utente dopo il login (percorso o codice pagina).
|
|
50
|
+
* @maxLength 50
|
|
51
|
+
* @nullable
|
|
52
|
+
*/
|
|
53
|
+
paginaDefault?: string | null;
|
|
48
54
|
/** Permessi assegnati all'utente. */
|
|
49
55
|
permissions?: Permission[];
|
|
50
56
|
/** Progressivo identificativo del filtro */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerRequest.js","sourceRoot":"","sources":["../../../../../src/accessi-module/Console/generated/model/registerRequest.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';\n\nexport interface RegisterRequest {\n /** Avatar dell'utente. */\n avatar?: string;\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 del vettore */\n codVet?: number;\n /** Cognome dell'utente. */\n cognome?: string;\n /** Email dell'utente. */\n email: string;\n /** Flag che indica se l'utente è configuratore */\n flagAdmin?: boolean;\n /** Flag autenticazione a due fattori. */\n flagDueFattori?: boolean;\n /** Flag superutente. */\n flagSuper?: boolean;\n /** HTML mail personalizzato */\n htmlMail?: string;\n /** Idx personale */\n idxPers?: number;\n /** idx della postazione nella tabella ANTENNE_POS di PROLAV */\n idxPos?: number;\n /** Nome dell'utente. */\n nome?: string;\n /** Numero del reparto associato */\n numRep?: number;\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 /** Tipo di filtro applicato */\n tipFil?: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"registerRequest.js","sourceRoot":"","sources":["../../../../../src/accessi-module/Console/generated/model/registerRequest.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';\n\nexport interface RegisterRequest {\n /** Avatar dell'utente. */\n avatar?: string;\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 del vettore */\n codVet?: number;\n /** Cognome dell'utente. */\n cognome?: string;\n /** Email dell'utente. */\n email: string;\n /** Flag che indica se l'utente è configuratore */\n flagAdmin?: boolean;\n /** Flag autenticazione a due fattori. */\n flagDueFattori?: boolean;\n /** Flag superutente. */\n flagSuper?: boolean;\n /** HTML mail personalizzato */\n htmlMail?: string;\n /** Idx personale */\n idxPers?: number;\n /** idx della postazione nella tabella ANTENNE_POS di PROLAV */\n idxPos?: number;\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 /** Permessi assegnati all'utente. */\n permissions?: Permission[];\n /** Progressivo identificativo del filtro */\n progressivo?: number;\n /** Ruoli assegnati all'utente. */\n roles?: string[];\n /** Tipo di filtro applicato */\n tipFil?: number;\n}\n"]}
|
|
@@ -65,6 +65,12 @@ export interface UserDto {
|
|
|
65
65
|
nome?: string;
|
|
66
66
|
/** Numero del reparto associato */
|
|
67
67
|
numRep?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Pagina di default mostrata all'utente dopo il login (percorso o codice pagina).
|
|
70
|
+
* @maxLength 50
|
|
71
|
+
* @nullable
|
|
72
|
+
*/
|
|
73
|
+
paginaDefault?: string | null;
|
|
68
74
|
/** Consente accesso con il solo codice email. Richiede flagDueFattori e viene gestito dagli amministratori. */
|
|
69
75
|
passwordlessLoginEnabled?: boolean;
|
|
70
76
|
/** Abilita il login locale con password. Se false, l utente puo autenticarsi solo tramite identita SSO attive. */
|
|
@@ -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 /** 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"]}
|
|
@@ -146,6 +146,13 @@ exports.HELP = {
|
|
|
146
146
|
note('warn', 'Non sono due fattori', `L'accesso con il solo codice è autenticazione tramite possesso della casella email: un solo fattore.`),
|
|
147
147
|
],
|
|
148
148
|
},
|
|
149
|
+
'user-default-page': {
|
|
150
|
+
title: 'Pagina di default',
|
|
151
|
+
blocks: [
|
|
152
|
+
p(`Percorso o codice della pagina verso cui indirizzare l'utente dopo il login. Esempio: /dashboard oppure la chiave di una pagina gestita dal frontend.`),
|
|
153
|
+
p(`E una stringa libera (massimo 50 caratteri): la libreria la conserva e la restituisce nel profilo; l'instradamento effettivo lo decide il frontend.`),
|
|
154
|
+
],
|
|
155
|
+
},
|
|
149
156
|
'user-state': {
|
|
150
157
|
title: 'Stato di registrazione',
|
|
151
158
|
blocks: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/accessi-module/Console/help.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Contenuti di aiuto contestuale della console Accessi.\n *\n * Ogni voce spiega, in italiano corretto, che cosa inserire in un campo\n * tecnico e da dove prendere il valore. Sono mostrati come miniguida nel\n * tooltip aperto dall'icona \"i\" accanto al campo.\n */\nimport type { WikiBlock } from './wiki';\n\nexport interface HelpEntry {\n title: string;\n blocks: WikiBlock[];\n}\n\nconst p = (text: string): WikiBlock => ({ kind: 'p', text });\nconst list = (items: string[]): WikiBlock => ({ kind: 'list', items });\nconst code = (language: string, codeText: string, title?: string): WikiBlock => ({ kind: 'code', language, title, code: codeText });\nconst note = (tone: 'info' | 'ok' | 'warn' | 'danger', title: string, text: string): WikiBlock => ({ kind: 'callout', tone, title, text });\nconst table = (head: string[], rows: string[][]): WikiBlock => ({ kind: 'table', head, rows });\n\nexport const HELP: Record<string, HelpEntry> = {\n // ------------------------------------------------------------------ SSO\n 'sso-provider': {\n title: 'Provider SSO',\n blocks: [\n p(`La chiave del provider censito in Accessi. Deve coincidere esattamente con quella usata dal backend quando valida il login SSO e chiama FederatedAuthService.authenticate({ provider, subject }).`),\n p(`Se il backend usa la chiave azure-ad-acme-prod, qui va la stessa stringa: una differenza, anche di un carattere, fa fallire il collegamento.`),\n note('info', 'Chi crea il provider', `Prima si censisce il provider in \"SSO → Provider\" (superutente), poi lo si sceglie qui. Se l'elenco è vuoto, non ci sono provider attivi.`),\n ],\n },\n 'sso-subject': {\n title: 'ID utente del provider (subject)',\n blocks: [\n p(`È l'identificatore STABILE della persona nel provider, non l'email. Il backend lo ottiene dopo aver validato il token del provider e lo passa ad Accessi; Accessi non vede mai il token esterno.`),\n p(`Ecco il claim corretto per i provider più comuni:`),\n table(\n ['Provider', 'Claim da usare', 'Note'],\n [\n ['Azure AD / Entra ID', 'oid', 'Object ID. Non usare preferred_username né email.'],\n ['Google', 'sub', 'ID numerico di 21 cifre. Non l indirizzo email.'],\n ['Apple', 'sub', 'Identificatore stabile per la tua app; disponibile solo al primo login, va salvato subito.'],\n ['Okta', 'sub', 'Claim standard OIDC.'],\n ['Auth0', 'sub', 'Formato auth0|... oppure google-oauth2|... .'],\n ['Keycloak', 'sub', 'Claim standard OIDC.'],\n ['Microsoft personale (MSA)', 'sub', 'Account outlook/live.'],\n ['SAML 2.0', 'NameID', 'Identificatore persistente dell utente.'],\n ['OIDC generico', 'sub', 'Claim standard OIDC.'],\n ],\n ),\n note('danger', 'Mai usare l email', `L'email può cambiare e non è un identificatore stabile: usarla come subject crea duplicati e collegamenti errati.`),\n code('ts', [\n '// Nel backend, dopo aver validato il token del provider:',\n '// Azure AD / Entra ID',\n \"const subject = claims.oid;\",\n '// Google / Okta / Keycloak / OIDC generico',\n \"const subject = claims.sub;\",\n '// Apple (disponibile al primo login, salvalo subito)',\n \"const subject = claims.sub;\",\n '// SAML',\n \"const subject = assertion.nameId;\",\n ].join('\\n'), 'Come ricavare il subject'),\n ],\n },\n 'sso-note': {\n title: 'Nota',\n blocks: [\n p(`Nota interna per gli amministratori, utile a ricordare perché il collegamento esiste (per esempio \"migrato da AD il 2026-01-10\"). Non viene inviata al provider.`),\n ],\n },\n 'provider-key': {\n title: 'Chiave provider',\n blocks: [\n p(`Identificativo tecnico del provider, usato dal backend nel payload SSO. Scegli una chiave descrittiva e stabile, per esempio azure-ad-acme-prod o google-workspace-acme.`),\n note('warn', 'Immutabile', `Dopo il censimento la chiave non si modifica: identità e configurazione del backend la referenziano. Se serve cambiarla, disabilita il provider e creane uno nuovo.`),\n ],\n },\n 'provider-description': {\n title: 'Descrizione',\n blocks: [\n p(`Nome leggibile mostrato agli amministratori (per esempio \"Azure AD – Acme produzione\"). Non è la chiave tecnica: può contenere spazi.`),\n ],\n },\n 'provider-note': {\n title: 'Nota',\n blocks: [\n p(`Nota interna del catalogo. Non inserire segreti, issuer, client id o dati personali: quei valori restano nel backend.`),\n ],\n },\n 'provider-active': {\n title: 'Provider attivo',\n blocks: [\n p(`Se disattivato, il provider non accetta nuovi login e nuovi collegamenti; le identità e lo storico restano. Riattivalo quando il backend è pronto.`),\n ],\n },\n\n // --------------------------------------------------------- service token\n 'token-label': {\n title: 'Descrizione del token',\n blocks: [\n p(`Nome del token, mostrato agli amministratori (per esempio \"Integrazione IA – produzione\"). Non è un segreto.`),\n ],\n },\n 'token-scopes': {\n title: 'Scope',\n blocks: [\n p(`Etichette che il backend controlla per autorizzare le chiamate tecniche. Un token non è un superutente: abilita solo le rotte protette con ServiceTokenGuard e @RequireServiceTokenScopes.`),\n list([\n `Formato: lettere, numeri, ':', '_' o '-', massimo 40 caratteri.`,\n `Esempi: ia, chat, tools, report:read.`,\n `Separa gli scope con una virgola.`,\n ]),\n code('ts', [\n '@UseGuards(ServiceTokenGuard)',\n \"@RequireServiceTokenScopes('ia')\",\n '@Post(\"internal/ask\")',\n 'ask() { /* il token deve avere lo scope ia */ }',\n ].join('\\n'), 'Come li usa il backend'),\n note('warn', 'Scope vuoto', `Senza scope il token non soddisfa nessun @RequireServiceTokenScopes, ma passa comunque le rotte protette senza scope richiesto.`),\n ],\n },\n 'token-ttl': {\n title: 'Durata (giorni)',\n blocks: [\n p(`Giorni di validità del token. Alla scadenza il token smette di funzionare.`),\n note('danger', 'Nessuna scadenza', `Se lasci vuoto questo campo e non imposti una data, il token non scade mai: sconsigliato in produzione.`),\n ],\n },\n\n // ----------------------------------------------------------------- utenti\n 'user-email': {\n title: 'Email',\n blocks: [\n p(`Identificativo di accesso dell'utente e recapito per le email di reset o codici. Deve essere univoca.`),\n ],\n },\n 'user-name': {\n title: 'Nome e cognome',\n blocks: [\n p(`Dati anagrafici mostrati in console e restituiti nel profilo. Non incidono sull'autenticazione.`),\n ],\n },\n 'user-password-login': {\n title: 'Login con password',\n blocks: [\n p(`Se disattivato, il login con email e password viene rifiutato (PASSWORD_LOGIN_DISABLED). Restano validi i login con identità SSO attive.`),\n note('info', 'Utente solo SSO', `È l'opzione tipica per gli utenti federati: nessuna password locale, accesso solo dal provider.`),\n ],\n },\n 'user-2fa': {\n title: 'Codice via email (2FA)',\n blocks: [\n p(`Richiede un codice monouso inviato per email dopo la password o dopo il provider SSO. È per-utente e disattivata per impostazione predefinita.`),\n list([\n `Codice a 6 cifre, valido 10 minuti, 5 tentativi, massimo 3 invii.`,\n `Non sono token: fino alla verifica non esiste alcun JWT.`,\n ]),\n ],\n },\n 'user-passwordless': {\n title: 'Accesso con il solo codice email',\n blocks: [\n p(`Abilita il login senza password: si inserisce solo l'email e si usa il codice ricevuto. Richiede il codice email (2FA) attivo.`),\n note('warn', 'Non sono due fattori', `L'accesso con il solo codice è autenticazione tramite possesso della casella email: un solo fattore.`),\n ],\n },\n 'user-state': {\n title: 'Stato di registrazione',\n blocks: [\n table(\n ['Stato', 'Significato'],\n [\n ['Non definito / Inserito', 'Utente non confermato.'],\n ['Invitato (10)', 'Richiede il rinnovo della password.'],\n ['Confermato (20)', 'Utente valido, può accedere.'],\n ['Eliminato (50)', 'Soft delete: login bloccato.'],\n ['Bloccato (99)', 'Login bloccato.'],\n ],\n ),\n ],\n },\n\n // ------------------------------------------------------------------ ruoli\n 'role-description': {\n title: 'Descrizione del ruolo',\n blocks: [\n p(`Nome del ruolo (per esempio \"Gestore ordini\"). Deve avere almeno un menu selezionato: il salvataggio sostituisce integralmente i menu del ruolo.`),\n ],\n },\n 'role-level': {\n title: 'Livello di abilitazione',\n blocks: [\n table(\n ['Livello', 'Valore', 'Uso tipico'],\n [\n ['Lettura', '10', 'Sola consultazione.'],\n ['Scrittura', '20', 'Modifica dei dati.'],\n ['Legacy (30)', '30', 'Valore storico, mantenuto senza perdita.'],\n ],\n ),\n p(`Il controllo confronta tipoAbilitazione >= soglia: un livello 20 soddisfa anche una richiesta di livello 10.`),\n ],\n },\n\n // --------------------------------------------------------- catalogo menu\n 'menu-code': {\n title: 'Codice menu',\n blocks: [\n p(`Chiave tecnica del menu, stabile e senza spazi (per esempio ORDINI o MNU001). È la chiave primaria: non si modifica dopo la creazione.`),\n note('warn', 'Immutabile', `Il codice è referenziato da ruoli e grant: per cambiarlo, elimina e ricrea il menu.`),\n ],\n },\n 'menu-group-code': {\n title: 'Codice gruppo',\n blocks: [\n p(`Un solo carattere (per esempio A). Raggruppa i menu nella navigazione. Immutabile dopo la creazione.`),\n ],\n },\n 'menu-type-code': {\n title: 'Codice tipo menu',\n blocks: [\n p(`Un solo carattere (per esempio X). Classifica i menu (amministrazione, operativo, ...). Immutabile dopo la creazione.`),\n ],\n },\n 'menu-group-order': {\n title: 'Ordine del gruppo',\n blocks: [\n p(`Numero che determina l'ordine dei gruppi nella navigazione: valori più bassi compaiono prima.`),\n ],\n },\n 'menu-order': {\n title: 'Ordine del menu',\n blocks: [\n p(`Numero che determina l'ordine dei menu all'interno del gruppo: valori più bassi compaiono prima.`),\n ],\n },\n 'menu-page': {\n title: 'Pagina',\n blocks: [\n p(`Percorso o nome della pagina del frontend associata al menu (per esempio /ordini). È informativo: lo usano la UI host e i client.`),\n ],\n },\n 'menu-icon': {\n title: 'Icona',\n blocks: [\n p(`Identificatore dell'icona usato dalla UI host (per esempio fa-box o un nome concordato). Accessi non impone un set di icone.`),\n ],\n },\n 'menu-rifmenu': {\n title: 'Menu di riferimento',\n blocks: [\n p(`Codice di un altro menu che fa da padre, per costruire gerarchie di menu. Lascia vuoto per un menu di primo livello.`),\n ],\n },\n 'menu-note': {\n title: 'Nota',\n blocks: [\n p(`Nota descrittiva mostrata agli amministratori. Non incide sui permessi.`),\n ],\n },\n 'menu-enabled': {\n title: 'Menu abilitato',\n blocks: [\n p(`Se disattivato, il menu non compare nel catalogo attivo e non viene considerato nel calcolo dei grant. I collegamenti a ruoli e utenti restano.`),\n ],\n },\n 'menu-group-enabled': {\n title: 'Gruppo abilitato',\n blocks: [\n p(`Disattiva l'intero gruppo: i menu al suo interno non sono considerati attivi, ma restano configurabili per una riattivazione futura.`),\n ],\n },\n\n // ----------------------------------------------------------------- filtri\n 'filter-type-code': {\n title: 'Codice tipo filtro',\n blocks: [\n p(`Numero identificativo del tipo filtro. È la chiave primaria: non si modifica dopo la creazione.`),\n ],\n },\n 'filter-type-field': {\n title: 'Campo',\n blocks: [\n p(`Nome della colonna applicativa della tabella FILTRI a cui il tipo è associato (per esempio CODCLI). Deve esistere nello schema, altrimenti i filtri che lo usano vengono rifiutati.`),\n ],\n },\n 'filter-type-enabled': {\n title: 'Tipo filtro abilitato',\n blocks: [\n p(`Se disattivato, il tipo non è proposto ai client, ma i filtri già salvati restano nel database.`),\n ],\n },\n 'filters-json': {\n title: 'Filtro JSON',\n blocks: [\n p(`Rappresentazione JSON dei filtri applicativi dell'utente. Usa \"Carica\" per partire dalla struttura esistente, modifica i valori e poi \"Salva\".`),\n note('info', 'Campi ammessi', `Sono accettati solo i campi mappati nello schema (FILTRI_UTENTE_DB_MAPPING). Un campo non configurato viene rifiutato con un errore esplicito.`),\n ],\n },\n 'filters-user': {\n title: 'Utente',\n blocks: [\n p(`Utente a cui appartengono i filtri. Selezionalo prima di caricare o salvare: i filtri sono sempre per singolo utente.`),\n ],\n },\n\n // ------------------------------------------------------------ assegnazioni\n 'role-assignment': {\n title: 'Assegnazione dei ruoli',\n blocks: [\n p(`Il salvataggio SOSTITUISCE integralmente i ruoli dell'utente: la lista selezionata diventa l'unico set di ruoli.`),\n list([\n `Per aggiungere un ruolo, selezionalo mantenendo quelli già presenti.`,\n `Per rimuovere un ruolo, deselezionalo.`,\n `Non modificare i grant diretti: quelli si gestiscono nella scheda \"Grant diretti\".`,\n ]),\n note('warn', 'Non è incrementale', `Inviare solo il nuovo ruolo rimuove gli altri. Invia sempre la lista completa desiderata.`),\n ],\n },\n 'grant-direct': {\n title: 'Grant diretti',\n blocks: [\n p(`Permessi assegnati direttamente all'utente, indipendenti dai ruoli. Hanno precedenza sui permessi ereditati dai ruoli per lo stesso menu.`),\n list([\n `Imposta il livello per concedere l'accesso.`,\n `Scegli \"nessuno\" per rimuovere il grant diretto.`,\n `Il salvataggio sostituisce l'intero set di grant diretti.`,\n ]),\n ],\n },\n 'grant-level': {\n title: 'Livello di grant',\n blocks: [\n table(\n ['Voce', 'Valore', 'Significato'],\n [\n ['nessuno', '—', 'Nessun grant diretto per quel menu (il permesso può arrivare dai ruoli).'],\n ['Lettura', '10', 'Sola consultazione.'],\n ['Scrittura', '20', 'Modifica dei dati.'],\n ],\n ),\n p(`Il confronto è tipoAbilitazione >= soglia richiesta dalla rotta.`),\n ],\n },\n 'user-delete': {\n title: 'Imposta stato eliminato',\n blocks: [\n p(`Esegue un soft delete: imposta lo stato di registrazione a \"Eliminato\" (50). L'utente non può più accedere, ma i dati restano nel database.`),\n note('info', 'Reversibile', `Non è una cancellazione fisica: puoi ripristinare l'utente cambiando lo stato di registrazione.`),\n ],\n },\n\n // ----------------------------------------------------- selezioni di menu\n 'menu-group-select': {\n title: 'Gruppo',\n blocks: [\n p(`Gruppo di appartenenza del menu. Se l'elenco è vuoto, crea prima un gruppo in \"Menu e gruppi → Gruppi\".`),\n ],\n },\n 'menu-type-select': {\n title: 'Tipo menu',\n blocks: [\n p(`Classificazione del menu (amministrazione, operativo, ...). È facoltativa: se non serve, lascia \"Nessun tipo\". I tipi si gestiscono in \"Menu e gruppi → Tipi menu\".`),\n ],\n },\n\n // ------------------------------------------------------------------ login\n 'login-email': {\n title: 'Email o utente',\n blocks: [\n p(`Indirizzo email dell'utente Accessi. Se l'account è abilitato al solo codice, dopo questo passo riceverai un codice via email invece di inserire la password.`),\n ],\n },\n 'login-password': {\n title: 'Password',\n blocks: [\n p(`Password locale dell'utente. Non serve per gli account passwordless, che accedono con il solo codice email.`),\n ],\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/accessi-module/Console/help.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Contenuti di aiuto contestuale della console Accessi.\n *\n * Ogni voce spiega, in italiano corretto, che cosa inserire in un campo\n * tecnico e da dove prendere il valore. Sono mostrati come miniguida nel\n * tooltip aperto dall'icona \"i\" accanto al campo.\n */\nimport type { WikiBlock } from './wiki';\n\nexport interface HelpEntry {\n title: string;\n blocks: WikiBlock[];\n}\n\nconst p = (text: string): WikiBlock => ({ kind: 'p', text });\nconst list = (items: string[]): WikiBlock => ({ kind: 'list', items });\nconst code = (language: string, codeText: string, title?: string): WikiBlock => ({ kind: 'code', language, title, code: codeText });\nconst note = (tone: 'info' | 'ok' | 'warn' | 'danger', title: string, text: string): WikiBlock => ({ kind: 'callout', tone, title, text });\nconst table = (head: string[], rows: string[][]): WikiBlock => ({ kind: 'table', head, rows });\n\nexport const HELP: Record<string, HelpEntry> = {\n // ------------------------------------------------------------------ SSO\n 'sso-provider': {\n title: 'Provider SSO',\n blocks: [\n p(`La chiave del provider censito in Accessi. Deve coincidere esattamente con quella usata dal backend quando valida il login SSO e chiama FederatedAuthService.authenticate({ provider, subject }).`),\n p(`Se il backend usa la chiave azure-ad-acme-prod, qui va la stessa stringa: una differenza, anche di un carattere, fa fallire il collegamento.`),\n note('info', 'Chi crea il provider', `Prima si censisce il provider in \"SSO → Provider\" (superutente), poi lo si sceglie qui. Se l'elenco è vuoto, non ci sono provider attivi.`),\n ],\n },\n 'sso-subject': {\n title: 'ID utente del provider (subject)',\n blocks: [\n p(`È l'identificatore STABILE della persona nel provider, non l'email. Il backend lo ottiene dopo aver validato il token del provider e lo passa ad Accessi; Accessi non vede mai il token esterno.`),\n p(`Ecco il claim corretto per i provider più comuni:`),\n table(\n ['Provider', 'Claim da usare', 'Note'],\n [\n ['Azure AD / Entra ID', 'oid', 'Object ID. Non usare preferred_username né email.'],\n ['Google', 'sub', 'ID numerico di 21 cifre. Non l indirizzo email.'],\n ['Apple', 'sub', 'Identificatore stabile per la tua app; disponibile solo al primo login, va salvato subito.'],\n ['Okta', 'sub', 'Claim standard OIDC.'],\n ['Auth0', 'sub', 'Formato auth0|... oppure google-oauth2|... .'],\n ['Keycloak', 'sub', 'Claim standard OIDC.'],\n ['Microsoft personale (MSA)', 'sub', 'Account outlook/live.'],\n ['SAML 2.0', 'NameID', 'Identificatore persistente dell utente.'],\n ['OIDC generico', 'sub', 'Claim standard OIDC.'],\n ],\n ),\n note('danger', 'Mai usare l email', `L'email può cambiare e non è un identificatore stabile: usarla come subject crea duplicati e collegamenti errati.`),\n code('ts', [\n '// Nel backend, dopo aver validato il token del provider:',\n '// Azure AD / Entra ID',\n \"const subject = claims.oid;\",\n '// Google / Okta / Keycloak / OIDC generico',\n \"const subject = claims.sub;\",\n '// Apple (disponibile al primo login, salvalo subito)',\n \"const subject = claims.sub;\",\n '// SAML',\n \"const subject = assertion.nameId;\",\n ].join('\\n'), 'Come ricavare il subject'),\n ],\n },\n 'sso-note': {\n title: 'Nota',\n blocks: [\n p(`Nota interna per gli amministratori, utile a ricordare perché il collegamento esiste (per esempio \"migrato da AD il 2026-01-10\"). Non viene inviata al provider.`),\n ],\n },\n 'provider-key': {\n title: 'Chiave provider',\n blocks: [\n p(`Identificativo tecnico del provider, usato dal backend nel payload SSO. Scegli una chiave descrittiva e stabile, per esempio azure-ad-acme-prod o google-workspace-acme.`),\n note('warn', 'Immutabile', `Dopo il censimento la chiave non si modifica: identità e configurazione del backend la referenziano. Se serve cambiarla, disabilita il provider e creane uno nuovo.`),\n ],\n },\n 'provider-description': {\n title: 'Descrizione',\n blocks: [\n p(`Nome leggibile mostrato agli amministratori (per esempio \"Azure AD – Acme produzione\"). Non è la chiave tecnica: può contenere spazi.`),\n ],\n },\n 'provider-note': {\n title: 'Nota',\n blocks: [\n p(`Nota interna del catalogo. Non inserire segreti, issuer, client id o dati personali: quei valori restano nel backend.`),\n ],\n },\n 'provider-active': {\n title: 'Provider attivo',\n blocks: [\n p(`Se disattivato, il provider non accetta nuovi login e nuovi collegamenti; le identità e lo storico restano. Riattivalo quando il backend è pronto.`),\n ],\n },\n\n // --------------------------------------------------------- service token\n 'token-label': {\n title: 'Descrizione del token',\n blocks: [\n p(`Nome del token, mostrato agli amministratori (per esempio \"Integrazione IA – produzione\"). Non è un segreto.`),\n ],\n },\n 'token-scopes': {\n title: 'Scope',\n blocks: [\n p(`Etichette che il backend controlla per autorizzare le chiamate tecniche. Un token non è un superutente: abilita solo le rotte protette con ServiceTokenGuard e @RequireServiceTokenScopes.`),\n list([\n `Formato: lettere, numeri, ':', '_' o '-', massimo 40 caratteri.`,\n `Esempi: ia, chat, tools, report:read.`,\n `Separa gli scope con una virgola.`,\n ]),\n code('ts', [\n '@UseGuards(ServiceTokenGuard)',\n \"@RequireServiceTokenScopes('ia')\",\n '@Post(\"internal/ask\")',\n 'ask() { /* il token deve avere lo scope ia */ }',\n ].join('\\n'), 'Come li usa il backend'),\n note('warn', 'Scope vuoto', `Senza scope il token non soddisfa nessun @RequireServiceTokenScopes, ma passa comunque le rotte protette senza scope richiesto.`),\n ],\n },\n 'token-ttl': {\n title: 'Durata (giorni)',\n blocks: [\n p(`Giorni di validità del token. Alla scadenza il token smette di funzionare.`),\n note('danger', 'Nessuna scadenza', `Se lasci vuoto questo campo e non imposti una data, il token non scade mai: sconsigliato in produzione.`),\n ],\n },\n\n // ----------------------------------------------------------------- utenti\n 'user-email': {\n title: 'Email',\n blocks: [\n p(`Identificativo di accesso dell'utente e recapito per le email di reset o codici. Deve essere univoca.`),\n ],\n },\n 'user-name': {\n title: 'Nome e cognome',\n blocks: [\n p(`Dati anagrafici mostrati in console e restituiti nel profilo. Non incidono sull'autenticazione.`),\n ],\n },\n 'user-password-login': {\n title: 'Login con password',\n blocks: [\n p(`Se disattivato, il login con email e password viene rifiutato (PASSWORD_LOGIN_DISABLED). Restano validi i login con identità SSO attive.`),\n note('info', 'Utente solo SSO', `È l'opzione tipica per gli utenti federati: nessuna password locale, accesso solo dal provider.`),\n ],\n },\n 'user-2fa': {\n title: 'Codice via email (2FA)',\n blocks: [\n p(`Richiede un codice monouso inviato per email dopo la password o dopo il provider SSO. È per-utente e disattivata per impostazione predefinita.`),\n list([\n `Codice a 6 cifre, valido 10 minuti, 5 tentativi, massimo 3 invii.`,\n `Non sono token: fino alla verifica non esiste alcun JWT.`,\n ]),\n ],\n },\n 'user-passwordless': {\n title: 'Accesso con il solo codice email',\n blocks: [\n p(`Abilita il login senza password: si inserisce solo l'email e si usa il codice ricevuto. Richiede il codice email (2FA) attivo.`),\n note('warn', 'Non sono due fattori', `L'accesso con il solo codice è autenticazione tramite possesso della casella email: un solo fattore.`),\n ],\n },\n 'user-default-page': {\n title: 'Pagina di default',\n blocks: [\n p(`Percorso o codice della pagina verso cui indirizzare l'utente dopo il login. Esempio: /dashboard oppure la chiave di una pagina gestita dal frontend.`),\n p(`E una stringa libera (massimo 50 caratteri): la libreria la conserva e la restituisce nel profilo; l'instradamento effettivo lo decide il frontend.`),\n ],\n },\n 'user-state': {\n title: 'Stato di registrazione',\n blocks: [\n table(\n ['Stato', 'Significato'],\n [\n ['Non definito / Inserito', 'Utente non confermato.'],\n ['Invitato (10)', 'Richiede il rinnovo della password.'],\n ['Confermato (20)', 'Utente valido, può accedere.'],\n ['Eliminato (50)', 'Soft delete: login bloccato.'],\n ['Bloccato (99)', 'Login bloccato.'],\n ],\n ),\n ],\n },\n\n // ------------------------------------------------------------------ ruoli\n 'role-description': {\n title: 'Descrizione del ruolo',\n blocks: [\n p(`Nome del ruolo (per esempio \"Gestore ordini\"). Deve avere almeno un menu selezionato: il salvataggio sostituisce integralmente i menu del ruolo.`),\n ],\n },\n 'role-level': {\n title: 'Livello di abilitazione',\n blocks: [\n table(\n ['Livello', 'Valore', 'Uso tipico'],\n [\n ['Lettura', '10', 'Sola consultazione.'],\n ['Scrittura', '20', 'Modifica dei dati.'],\n ['Legacy (30)', '30', 'Valore storico, mantenuto senza perdita.'],\n ],\n ),\n p(`Il controllo confronta tipoAbilitazione >= soglia: un livello 20 soddisfa anche una richiesta di livello 10.`),\n ],\n },\n\n // --------------------------------------------------------- catalogo menu\n 'menu-code': {\n title: 'Codice menu',\n blocks: [\n p(`Chiave tecnica del menu, stabile e senza spazi (per esempio ORDINI o MNU001). È la chiave primaria: non si modifica dopo la creazione.`),\n note('warn', 'Immutabile', `Il codice è referenziato da ruoli e grant: per cambiarlo, elimina e ricrea il menu.`),\n ],\n },\n 'menu-group-code': {\n title: 'Codice gruppo',\n blocks: [\n p(`Un solo carattere (per esempio A). Raggruppa i menu nella navigazione. Immutabile dopo la creazione.`),\n ],\n },\n 'menu-type-code': {\n title: 'Codice tipo menu',\n blocks: [\n p(`Un solo carattere (per esempio X). Classifica i menu (amministrazione, operativo, ...). Immutabile dopo la creazione.`),\n ],\n },\n 'menu-group-order': {\n title: 'Ordine del gruppo',\n blocks: [\n p(`Numero che determina l'ordine dei gruppi nella navigazione: valori più bassi compaiono prima.`),\n ],\n },\n 'menu-order': {\n title: 'Ordine del menu',\n blocks: [\n p(`Numero che determina l'ordine dei menu all'interno del gruppo: valori più bassi compaiono prima.`),\n ],\n },\n 'menu-page': {\n title: 'Pagina',\n blocks: [\n p(`Percorso o nome della pagina del frontend associata al menu (per esempio /ordini). È informativo: lo usano la UI host e i client.`),\n ],\n },\n 'menu-icon': {\n title: 'Icona',\n blocks: [\n p(`Identificatore dell'icona usato dalla UI host (per esempio fa-box o un nome concordato). Accessi non impone un set di icone.`),\n ],\n },\n 'menu-rifmenu': {\n title: 'Menu di riferimento',\n blocks: [\n p(`Codice di un altro menu che fa da padre, per costruire gerarchie di menu. Lascia vuoto per un menu di primo livello.`),\n ],\n },\n 'menu-note': {\n title: 'Nota',\n blocks: [\n p(`Nota descrittiva mostrata agli amministratori. Non incide sui permessi.`),\n ],\n },\n 'menu-enabled': {\n title: 'Menu abilitato',\n blocks: [\n p(`Se disattivato, il menu non compare nel catalogo attivo e non viene considerato nel calcolo dei grant. I collegamenti a ruoli e utenti restano.`),\n ],\n },\n 'menu-group-enabled': {\n title: 'Gruppo abilitato',\n blocks: [\n p(`Disattiva l'intero gruppo: i menu al suo interno non sono considerati attivi, ma restano configurabili per una riattivazione futura.`),\n ],\n },\n\n // ----------------------------------------------------------------- filtri\n 'filter-type-code': {\n title: 'Codice tipo filtro',\n blocks: [\n p(`Numero identificativo del tipo filtro. È la chiave primaria: non si modifica dopo la creazione.`),\n ],\n },\n 'filter-type-field': {\n title: 'Campo',\n blocks: [\n p(`Nome della colonna applicativa della tabella FILTRI a cui il tipo è associato (per esempio CODCLI). Deve esistere nello schema, altrimenti i filtri che lo usano vengono rifiutati.`),\n ],\n },\n 'filter-type-enabled': {\n title: 'Tipo filtro abilitato',\n blocks: [\n p(`Se disattivato, il tipo non è proposto ai client, ma i filtri già salvati restano nel database.`),\n ],\n },\n 'filters-json': {\n title: 'Filtro JSON',\n blocks: [\n p(`Rappresentazione JSON dei filtri applicativi dell'utente. Usa \"Carica\" per partire dalla struttura esistente, modifica i valori e poi \"Salva\".`),\n note('info', 'Campi ammessi', `Sono accettati solo i campi mappati nello schema (FILTRI_UTENTE_DB_MAPPING). Un campo non configurato viene rifiutato con un errore esplicito.`),\n ],\n },\n 'filters-user': {\n title: 'Utente',\n blocks: [\n p(`Utente a cui appartengono i filtri. Selezionalo prima di caricare o salvare: i filtri sono sempre per singolo utente.`),\n ],\n },\n\n // ------------------------------------------------------------ assegnazioni\n 'role-assignment': {\n title: 'Assegnazione dei ruoli',\n blocks: [\n p(`Il salvataggio SOSTITUISCE integralmente i ruoli dell'utente: la lista selezionata diventa l'unico set di ruoli.`),\n list([\n `Per aggiungere un ruolo, selezionalo mantenendo quelli già presenti.`,\n `Per rimuovere un ruolo, deselezionalo.`,\n `Non modificare i grant diretti: quelli si gestiscono nella scheda \"Grant diretti\".`,\n ]),\n note('warn', 'Non è incrementale', `Inviare solo il nuovo ruolo rimuove gli altri. Invia sempre la lista completa desiderata.`),\n ],\n },\n 'grant-direct': {\n title: 'Grant diretti',\n blocks: [\n p(`Permessi assegnati direttamente all'utente, indipendenti dai ruoli. Hanno precedenza sui permessi ereditati dai ruoli per lo stesso menu.`),\n list([\n `Imposta il livello per concedere l'accesso.`,\n `Scegli \"nessuno\" per rimuovere il grant diretto.`,\n `Il salvataggio sostituisce l'intero set di grant diretti.`,\n ]),\n ],\n },\n 'grant-level': {\n title: 'Livello di grant',\n blocks: [\n table(\n ['Voce', 'Valore', 'Significato'],\n [\n ['nessuno', '—', 'Nessun grant diretto per quel menu (il permesso può arrivare dai ruoli).'],\n ['Lettura', '10', 'Sola consultazione.'],\n ['Scrittura', '20', 'Modifica dei dati.'],\n ],\n ),\n p(`Il confronto è tipoAbilitazione >= soglia richiesta dalla rotta.`),\n ],\n },\n 'user-delete': {\n title: 'Imposta stato eliminato',\n blocks: [\n p(`Esegue un soft delete: imposta lo stato di registrazione a \"Eliminato\" (50). L'utente non può più accedere, ma i dati restano nel database.`),\n note('info', 'Reversibile', `Non è una cancellazione fisica: puoi ripristinare l'utente cambiando lo stato di registrazione.`),\n ],\n },\n\n // ----------------------------------------------------- selezioni di menu\n 'menu-group-select': {\n title: 'Gruppo',\n blocks: [\n p(`Gruppo di appartenenza del menu. Se l'elenco è vuoto, crea prima un gruppo in \"Menu e gruppi → Gruppi\".`),\n ],\n },\n 'menu-type-select': {\n title: 'Tipo menu',\n blocks: [\n p(`Classificazione del menu (amministrazione, operativo, ...). È facoltativa: se non serve, lascia \"Nessun tipo\". I tipi si gestiscono in \"Menu e gruppi → Tipi menu\".`),\n ],\n },\n\n // ------------------------------------------------------------------ login\n 'login-email': {\n title: 'Email o utente',\n blocks: [\n p(`Indirizzo email dell'utente Accessi. Se l'account è abilitato al solo codice, dopo questo passo riceverai un codice via email invece di inserire la password.`),\n ],\n },\n 'login-password': {\n title: 'Password',\n blocks: [\n p(`Password locale dell'utente. Non serve per gli account passwordless, che accedono con il solo codice email.`),\n ],\n },\n};\n"]}
|
|
@@ -418,7 +418,7 @@ exports.WIKI_SECTIONS = [
|
|
|
418
418
|
' body: JSON.stringify({ email, flagSuper: true }), // ignorato o negato',
|
|
419
419
|
'});',
|
|
420
420
|
].join('\n'), 'Privilegi dal client'),
|
|
421
|
-
note('info', `Campi di dominio`, `UTENTI_CONFIG contiene
|
|
421
|
+
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.`),
|
|
422
422
|
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.`),
|
|
423
423
|
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.`),
|
|
424
424
|
],
|
|
@@ -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 ],\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('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.'],\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 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 solo i flag di dominio Accessi: FLGSUPER, FLGADMIN, FLG2FATT (2FA), FLGPWDLESS (passwordless) e FLGPASSWORD (login locale). I flag/colonne applicativi (FLGMOP, FLGPIANA, FLGADDETTI, FLGOSPITI, FLGPIANARFID, FLGCONTA, FLGCUBI, FLGCICLPASS, FLGDIPENDENTI, FLGINVENTARI, CAUMOV, NUMMAC, RAGSOCCLI, PAGDEF, 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.`),\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 ],\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, 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 ],\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('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.'],\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 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.`),\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 ],\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, 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"]}
|