emilsoftware-utilities 2.0.0-dev.31 → 2.0.0-dev.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/accessi-module/Console/console.js +19 -46
  2. package/dist/accessi-module/Console/console.js.map +1 -1
  3. package/dist/accessi-module/Console/generated/model/authenticatedUserPayloadDto.d.ts +1 -1
  4. package/dist/accessi-module/Console/generated/model/authenticatedUserPayloadDto.js.map +1 -1
  5. package/dist/accessi-module/Console/generated/model/createMenuGroupRequest.d.ts +0 -5
  6. package/dist/accessi-module/Console/generated/model/createMenuGroupRequest.js.map +1 -1
  7. package/dist/accessi-module/Console/generated/model/groupWithMenusEntity.d.ts +0 -2
  8. package/dist/accessi-module/Console/generated/model/groupWithMenusEntity.js.map +1 -1
  9. package/dist/accessi-module/Console/generated/model/menuEntity.d.ts +0 -2
  10. package/dist/accessi-module/Console/generated/model/menuEntity.js.map +1 -1
  11. package/dist/accessi-module/Console/generated/model/publicRegisterRequest.d.ts +0 -6
  12. package/dist/accessi-module/Console/generated/model/publicRegisterRequest.js.map +1 -1
  13. package/dist/accessi-module/Console/generated/model/registerRequest.d.ts +2 -8
  14. package/dist/accessi-module/Console/generated/model/registerRequest.js.map +1 -1
  15. package/dist/accessi-module/Console/generated/model/updateMenuGroupRequest.d.ts +0 -5
  16. package/dist/accessi-module/Console/generated/model/updateMenuGroupRequest.js.map +1 -1
  17. package/dist/accessi-module/Console/generated/model/userDto.d.ts +3 -33
  18. package/dist/accessi-module/Console/generated/model/userDto.js.map +1 -1
  19. package/dist/accessi-module/Console/wiki.js +2 -2
  20. package/dist/accessi-module/Console/wiki.js.map +1 -1
  21. package/dist/accessi-module/Controllers/ConfiguratorController.js +2 -2
  22. package/dist/accessi-module/Controllers/ConfiguratorController.js.map +1 -1
  23. package/dist/accessi-module/Dtos/AuthenticatedUserPayloadDto.d.ts +1 -1
  24. package/dist/accessi-module/Dtos/AuthenticatedUserPayloadDto.js +1 -1
  25. package/dist/accessi-module/Dtos/AuthenticatedUserPayloadDto.js.map +1 -1
  26. package/dist/accessi-module/Dtos/CatalogDtos.d.ts +0 -2
  27. package/dist/accessi-module/Dtos/CatalogDtos.js +0 -14
  28. package/dist/accessi-module/Dtos/CatalogDtos.js.map +1 -1
  29. package/dist/accessi-module/Dtos/GetGroupsWithMenusResponse.d.ts +0 -1
  30. package/dist/accessi-module/Dtos/GetGroupsWithMenusResponse.js +0 -8
  31. package/dist/accessi-module/Dtos/GetGroupsWithMenusResponse.js.map +1 -1
  32. package/dist/accessi-module/Dtos/GetMenusResponse.d.ts +0 -1
  33. package/dist/accessi-module/Dtos/GetMenusResponse.js +0 -8
  34. package/dist/accessi-module/Dtos/GetMenusResponse.js.map +1 -1
  35. package/dist/accessi-module/Dtos/PublicRegisterRequest.d.ts +1 -1
  36. package/dist/accessi-module/Dtos/PublicRegisterRequest.js +1 -1
  37. package/dist/accessi-module/Dtos/PublicRegisterRequest.js.map +1 -1
  38. package/dist/accessi-module/Dtos/RegisterRequest.d.ts +1 -4
  39. package/dist/accessi-module/Dtos/RegisterRequest.js +2 -31
  40. package/dist/accessi-module/Dtos/RegisterRequest.js.map +1 -1
  41. package/dist/accessi-module/Dtos/UserDto.d.ts +2 -17
  42. package/dist/accessi-module/Dtos/UserDto.js +4 -113
  43. package/dist/accessi-module/Dtos/UserDto.js.map +1 -1
  44. package/dist/accessi-module/Services/AuthService/AuthService.js +0 -2
  45. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  46. package/dist/accessi-module/Services/PermissionService/PermissionService.js +5 -13
  47. package/dist/accessi-module/Services/PermissionService/PermissionService.js.map +1 -1
  48. package/dist/accessi-module/Services/UserService/UserService.d.ts +1 -3
  49. package/dist/accessi-module/Services/UserService/UserService.js +19 -131
  50. package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
  51. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js +23 -0
  52. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js.map +1 -1
  53. package/dist/accessi-module/database-updates/accessiSchema.d.ts +23 -1
  54. package/dist/accessi-module/database-updates/accessiSchema.js +29 -12
  55. package/dist/accessi-module/database-updates/accessiSchema.js.map +1 -1
  56. package/dist/accessi-module/security/accessControl.d.ts +2 -2
  57. package/dist/accessi-module/security/accessControl.js +2 -2
  58. package/dist/accessi-module/security/accessControl.js.map +1 -1
  59. package/dist/accessi-module/security/authenticatedToken.d.ts +1 -1
  60. package/dist/accessi-module/security/authenticatedToken.js +2 -2
  61. package/dist/accessi-module/security/authenticatedToken.js.map +1 -1
  62. package/dist/emilsoftware-utilities-2.0.0-dev.33.tgz +0 -0
  63. package/package.json +1 -1
  64. package/dist/emilsoftware-utilities-2.0.0-dev.31.tgz +0 -0
@@ -1141,8 +1141,8 @@ POST /api/accessi/auth/confirm-reset-password/{token}
1141
1141
  " body: JSON.stringify({ email, flagSuper: true }), // ignorato o negato",
1142
1142
  "});"
1143
1143
  ].join("\n"), "Privilegi dal client"),
1144
- note("info", `Flag applicativi`, `Il profilo espone i flag UTENTI_CONFIG (FLGMOP, FLGPIANA, FLGADDETTI, FLGOSPITI, FLGPIANARFID, FLGCONTA, FLGCUBI, FLGCICLPASS, FLGDIPENDENTI, FLGINVENTARI), il codice causale CAUMOV e ENABLEIA (assistente IA). Sono modificabili solo da flussi privilegiati (allowPrivilegedFields/allowPrivilegedChanges) e visibili nella scheda utente della console.`),
1145
- note("info", `Gruppi menu e NOMECAMPO`, `Ogni gruppo menu puo dichiarare NOMECAMPO, la colonna UTENTI_CONFIG che ne regola l'abilitazione (es. FLGDIPENDENTI). get-groups-with-menus e get-menus espongono nomeCampo.`),
1144
+ 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.`),
1145
+ 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.`),
1146
1146
  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.`)
1147
1147
  ]
1148
1148
  },
@@ -1907,7 +1907,7 @@ function roleAssignmentEditor(roles, assignedRoles, groups) {
1907
1907
  const assigned = (assignedRoles ?? []).some((current) => current.codiceRuolo === role.codiceRuolo);
1908
1908
  const groupedMenus = groups.map((group) => ({ group, entries: group.menus.map((menu) => ({ menu, assignment: roleMenus.find((item) => item.codiceMenu === menu.codiceMenu) })).filter((entry) => entry.assignment) })).filter((entry) => entry.entries.length > 0);
1909
1909
  const missingMenus = roleMenus.filter((item) => !catalogMenus.has(item.codiceMenu));
1910
- return `<article class="role-assignment-card"><div class="role-assignment-heading"><label class="check"><input type="checkbox" name="role" value="${escapeHtml(role.codiceRuolo)}" ${assigned ? "checked" : ""}><span><strong>${escapeHtml(role.descrizioneRuolo)}</strong><small>Codice ruolo: ${escapeHtml(role.codiceRuolo)}</small></span></label><span class="role-menu-count">${roleMenus.length} menu</span></div><div class="role-permission-groups">${groupedMenus.map(({ group, entries }) => `<section class="role-permission-group"><div class="role-permission-group-heading"><strong>${escapeHtml(group.descrizioneGruppo)}</strong>${groupMetadata(group)}</div>${entries.map(({ menu, assignment }) => `<div class="role-permission-entry ${menu.enabled === false ? "is-disabled" : ""}"><div><strong>${escapeHtml(menu.descrizioneMenu)}</strong>${menuMetadata(menu)}</div><span class="permission-level">${escapeHtml(rolePermissionLevel(assignment?.tipoAbilitazione))}</span></div>`).join("")}</section>`).join("") || '<p class="form-help">Questo ruolo non contiene menu attivi nel catalogo.</p>'}${missingMenus.length ? `<section class="role-permission-group missing-role-menus"><strong>Menu non pi\xF9 presenti nel catalogo</strong>${missingMenus.map((menu) => `<div class="role-permission-entry"><div><strong>${escapeHtml(menu.codiceMenu)}</strong><p class="entity-note">Il menu \xE8 ancora associato al ruolo ma non \xE8 disponibile nel catalogo corrente.</p></div><span class="permission-level">${escapeHtml(rolePermissionLevel(menu.tipoAbilitazione))}</span></div>`).join("")}</section>` : ""}</div></article>`;
1910
+ return `<article class="role-assignment-card"><div class="role-assignment-heading"><label class="check"><input type="checkbox" name="role" value="${escapeHtml(role.codiceRuolo)}" ${assigned ? "checked" : ""}><span><strong>${escapeHtml(role.descrizioneRuolo)}</strong><small>Codice ruolo: ${escapeHtml(role.codiceRuolo)}</small></span></label><span class="role-menu-count">${roleMenus.length} menu</span></div><div class="role-permission-groups">${groupedMenus.map(({ group, entries }) => `<section class="role-permission-group"><div class="role-permission-group-heading"><strong>${escapeHtml(group.descrizioneGruppo)}</strong>${groupMetadata(group)}</div>${entries.map(({ menu, assignment }) => `<div class="role-permission-entry ${menu.enabled === false ? "is-disabled" : ""}"><div><strong>${escapeHtml(menu.descrizioneMenu)}</strong>${menuMetadata(menu)}</div><span class="permission-level">${escapeHtml(rolePermissionLevel(assignment?.tipoAbilitazione))}</span></div>`).join("")}</section>`).join("") || '<p class="form-help">Questo ruolo non contiene menu attivi nel catalogo.</p>'}${missingMenus.length ? `<section class="role-permission-group missing-role-menus"><strong>Menu non pi\xC3\xB9 presenti nel catalogo</strong>${missingMenus.map((menu) => `<div class="role-permission-entry"><div><strong>${escapeHtml(menu.codiceMenu)}</strong><p class="entity-note">Il menu \xC3\xA8 ancora associato al ruolo ma non \xC3\xA8 disponibile nel catalogo corrente.</p></div><span class="permission-level">${escapeHtml(rolePermissionLevel(menu.tipoAbilitazione))}</span></div>`).join("")}</section>` : ""}</div></article>`;
1911
1911
  }).join("")}</div><button>Salva ruoli</button></form>`;
1912
1912
  }
1913
1913
  function roleOverview(role, groups) {
@@ -1917,7 +1917,7 @@ function roleOverview(role, groups) {
1917
1917
  const tree = groups.map((group) => ({ group, entries: group.menus.filter((menu) => assignments.has(menu.codiceMenu)) })).filter((node) => node.entries.length > 0);
1918
1918
  const orphans = roleMenus.filter((item) => !knownMenus.has(item.codiceMenu));
1919
1919
  const groupNodes = tree.map(({ group, entries }) => `<li class="role-tree-group ${group.enabled === false ? "is-disabled" : ""}"><div class="role-tree-group-head"><strong>${escapeHtml(group.descrizioneGruppo)}</strong><span class="role-tree-count">${entries.length}</span></div><ul class="role-tree-menus">${entries.map((menu) => `<li class="role-tree-menu ${menu.enabled === false ? "is-disabled" : ""}"><span class="role-tree-menu-name">${escapeHtml(menu.descrizioneMenu)}</span><span class="permission-level">${escapeHtml(rolePermissionLevel(assignments.get(menu.codiceMenu)?.tipoAbilitazione))}</span></li>`).join("")}</ul></li>`).join("");
1920
- const orphanNode = orphans.length ? `<li class="role-tree-group is-orphan"><div class="role-tree-group-head"><strong>Menu non pi\xF9 nel catalogo</strong><span class="role-tree-count">${orphans.length}</span></div><ul class="role-tree-menus">${orphans.map((item) => `<li class="role-tree-menu"><span class="role-tree-menu-name"><code>${escapeHtml(item.codiceMenu)}</code></span><span class="permission-level">${escapeHtml(rolePermissionLevel(item.tipoAbilitazione))}</span></li>`).join("")}</ul></li>` : "";
1920
+ const orphanNode = orphans.length ? `<li class="role-tree-group is-orphan"><div class="role-tree-group-head"><strong>Menu non pi\xC3\xB9 nel catalogo</strong><span class="role-tree-count">${orphans.length}</span></div><ul class="role-tree-menus">${orphans.map((item) => `<li class="role-tree-menu"><span class="role-tree-menu-name"><code>${escapeHtml(item.codiceMenu)}</code></span><span class="permission-level">${escapeHtml(rolePermissionLevel(item.tipoAbilitazione))}</span></li>`).join("")}</ul></li>` : "";
1921
1921
  return `<article class="role-card">
1922
1922
  <div class="role-card-head">
1923
1923
  <div class="role-card-title">
@@ -2044,11 +2044,11 @@ function federatedIdentityFields(providers) {
2044
2044
  <label>Nota ${helpDot("sso-note")}<input name="note"><span class="form-help">Nota interna, non inviata al provider.</span></label>`;
2045
2045
  }
2046
2046
  function federatedIdentitySection(identities, providers) {
2047
- return `<section class="user-editor-section sso-identity-section" data-user-panel="sso" role="tabpanel" aria-labelledby="user-tab-sso"><div class="section-heading"><div><p class="eyebrow">Autenticazione esterna</p><h3>Identit\xE0 SSO</h3></div><span class="muted">Collegamenti verificati dal backend</span></div><p class="form-help">Disabilita conserva lo storico; elimina rimuove il collegamento (l'utente resta).</p><div class="sso-identity-list">${identities.map((identity) => `<article class="sso-identity-item ${identity.active ? "" : "is-disabled"}"><div class="sso-identity-data"><strong>${escapeHtml(identity.provider)}</strong><dl class="entity-metadata"><div><dt>Subject</dt><dd>${escapeHtml(identity.subject)}</dd></div><div><dt>Stato</dt><dd>${identity.active ? "Abilitata" : "Disabilitata"}</dd></div></dl>${identity.note ? `<p class="entity-note"><strong>Nota</strong>${escapeHtml(identity.note)}</p>` : ""}</div><div class="sso-identity-actions"><button type="button" class="secondary" data-identity-toggle="${escapeHtml(identity.identityKey)}" data-active="${String(!identity.active)}">${identity.active ? "Disabilita" : "Abilita"}</button><button type="button" class="icon-button danger-button" data-identity-delete="${escapeHtml(identity.identityKey)}" aria-label="Elimina definitivamente il collegamento SSO ${escapeHtml(identity.provider)}" title="Elimina definitivamente il collegamento SSO"><span aria-hidden="true">&#128465;</span></button></div></article>`).join("") || '<p class="muted">Nessun collegamento SSO.</p>'}</div>${providers.some((provider) => provider.active) ? `<form id="link-sso" class="sso-link-form">${federatedIdentityFields(providers)}<button>Collega SSO verificato</button></form>` : '<p class="form-help">Non ci sono provider SSO attivi. Censiscine uno nella sezione SSO prima di creare collegamenti.</p>'}</section>`;
2047
+ return `<section class="user-editor-section sso-identity-section" data-user-panel="sso" role="tabpanel" aria-labelledby="user-tab-sso"><div class="section-heading"><div><p class="eyebrow">Autenticazione esterna</p><h3>Identit\xC3\xA0 SSO</h3></div><span class="muted">Collegamenti verificati dal backend</span></div><p class="form-help">Disabilita conserva lo storico; elimina rimuove il collegamento (l'utente resta).</p><div class="sso-identity-list">${identities.map((identity) => `<article class="sso-identity-item ${identity.active ? "" : "is-disabled"}"><div class="sso-identity-data"><strong>${escapeHtml(identity.provider)}</strong><dl class="entity-metadata"><div><dt>Subject</dt><dd>${escapeHtml(identity.subject)}</dd></div><div><dt>Stato</dt><dd>${identity.active ? "Abilitata" : "Disabilitata"}</dd></div></dl>${identity.note ? `<p class="entity-note"><strong>Nota</strong>${escapeHtml(identity.note)}</p>` : ""}</div><div class="sso-identity-actions"><button type="button" class="secondary" data-identity-toggle="${escapeHtml(identity.identityKey)}" data-active="${String(!identity.active)}">${identity.active ? "Disabilita" : "Abilita"}</button><button type="button" class="icon-button danger-button" data-identity-delete="${escapeHtml(identity.identityKey)}" aria-label="Elimina definitivamente il collegamento SSO ${escapeHtml(identity.provider)}" title="Elimina definitivamente il collegamento SSO"><span aria-hidden="true">&#128465;</span></button></div></article>`).join("") || '<p class="muted">Nessun collegamento SSO.</p>'}</div>${providers.some((provider) => provider.active) ? `<form id="link-sso" class="sso-link-form">${federatedIdentityFields(providers)}<button>Collega SSO verificato</button></form>` : '<p class="form-help">Non ci sono provider SSO attivi. Censiscine uno nella sezione SSO prima di creare collegamenti.</p>'}</section>`;
2048
2048
  }
2049
2049
  function userDetailTabs(hasSso) {
2050
2050
  const tabs = [
2051
- ...hasSso ? [{ id: "sso", label: "Identit\xE0 SSO" }] : [],
2051
+ ...hasSso ? [{ id: "sso", label: "Identit\xC3\xA0 SSO" }] : [],
2052
2052
  { id: "profile", label: "Profilo e accesso" },
2053
2053
  { id: "roles", label: "Ruoli" },
2054
2054
  { id: "grants", label: "Grant diretti" }
@@ -2105,7 +2105,7 @@ function pageNumbers(current, total) {
2105
2105
  return result2;
2106
2106
  }
2107
2107
  function pagerMarkup(totalPages) {
2108
- const context = `<span class="pager-context">${usersTotal} utent${usersTotal === 1 ? "e" : "i"}${totalPages > 1 ? ` \xB7 pagina ${usersPage + 1} di ${totalPages}` : ""}</span>`;
2108
+ const context = `<span class="pager-context">${usersTotal} utent${usersTotal === 1 ? "e" : "i"}${totalPages > 1 ? ` \xC2\xB7 pagina ${usersPage + 1} di ${totalPages}` : ""}</span>`;
2109
2109
  if (totalPages <= 1) {
2110
2110
  return `<nav class="pager" aria-label="Paginazione utenti">${context}</nav>`;
2111
2111
  }
@@ -2280,35 +2280,14 @@ async function showUsers() {
2280
2280
  function authenticationPolicyFields(user) {
2281
2281
  return `<fieldset><legend>Verifica dell'accesso (opzionale)</legend>
2282
2282
  <label class="check"><input name="flagDueFattori" type="checkbox" ${user.flagDueFattori ? "checked" : ""}> Richiedi un codice via email (2FA) ${helpDot("user-2fa")}</label>
2283
- <p class="form-help">Se attiva, il codice \xE8 richiesto dopo password o SSO.</p>
2283
+ <p class="form-help">Se attiva, il codice \xC3\xA8 richiesto dopo password o SSO.</p>
2284
2284
  <label class="check"><input name="passwordlessLoginEnabled" type="checkbox" ${user.passwordlessLoginEnabled ? "checked" : ""} ${user.flagDueFattori ? "" : "disabled"}> Consenti accesso con il solo codice email ${helpDot("user-passwordless")}</label>
2285
2285
  <p class="form-help">Accesso senza password (richiede il codice email).</p>
2286
2286
  </fieldset>`;
2287
2287
  }
2288
- var APPLICATION_FLAGS = [
2289
- ["flagMop", "MOP"],
2290
- ["flagPiana", "Piana"],
2291
- ["flagAddetti", "Addetti"],
2292
- ["flagOspiti", "Ospiti"],
2293
- ["flagPianaRfid", "Piana RFID"],
2294
- ["flagConta", "Contabilit\xE0"],
2295
- ["flagCubi", "Cubi"],
2296
- ["flagCicliPass", "Cicli passivi"],
2297
- ["flagDipendenti", "Gestione dipendenti"],
2298
- ["flagInventari", "Gestione inventari"]
2299
- ];
2300
- function applicationFlagFields(user) {
2301
- const record = user;
2302
- const checks = APPLICATION_FLAGS.map(([key, label]) => `<label class="check"><input name="${key}" type="checkbox" ${record[key] ? "checked" : ""}> ${label}</label>`).join("");
2303
- return `<fieldset><legend>Abilitazioni applicative</legend>
2304
- ${checks}
2305
- <label>Codice causale <input name="caumov" value="${escapeHtml(String(record.caumov ?? ""))}"></label>
2306
- <label class="check"><input name="enableIa" type="checkbox" ${record.enableIa ? "checked" : ""}> Assistente IA (ENABLEIA)</label>
2307
- </fieldset>`;
2308
- }
2309
2288
  function showLocalUserForm() {
2310
2289
  render(`<button id="back">Indietro</button><h2>Nuovo utente locale</h2><p class="muted">Viene inviata l'e-mail per impostare la password.</p>
2311
- <form id="local-user"><label>Email ${helpDot("user-email")}<input name="email" type="email" required><span class="form-help">Identificativo di accesso; ricever\xE0 l'email per la password.</span></label><label>Nome ${helpDot("user-name")}<input name="nome"></label><label>Cognome ${helpDot("user-name")}<input name="cognome"></label><button>Crea utente</button></form>`);
2290
+ <form id="local-user"><label>Email ${helpDot("user-email")}<input name="email" type="email" required><span class="form-help">Identificativo di accesso; ricever\xC3\xA0 l'email per la password.</span></label><label>Nome ${helpDot("user-name")}<input name="nome"></label><label>Cognome ${helpDot("user-name")}<input name="cognome"></label><button>Crea utente</button></form>`);
2312
2291
  byId("back").onclick = () => show("users");
2313
2292
  const localUserForm = document.getElementById("local-user");
2314
2293
  if (localUserForm) localUserForm.onsubmit = (event) => {
@@ -2325,7 +2304,7 @@ async function showSsoUserForm() {
2325
2304
  if (!providers.some((provider) => provider.active)) {
2326
2305
  throw new Error("Prima di creare un utente SSO, censisci e abilita almeno un provider SSO.");
2327
2306
  }
2328
- render(`<button id="back">Indietro</button><h2>Nuovo utente SSO</h2><p class="muted">Salva solo collegamenti gi\xE0 verificati dal backend.</p>
2307
+ render(`<button id="back">Indietro</button><h2>Nuovo utente SSO</h2><p class="muted">Salva solo collegamenti gi\xC3\xA0 verificati dal backend.</p>
2329
2308
  <form id="sso-user"><label>Email ${helpDot("user-email")}<input name="email" type="email" required><span class="form-help">Email di contatto e identificativo Accessi dell'utente.</span></label><label>Nome ${helpDot("user-name")}<input name="nome"></label><label>Cognome ${helpDot("user-name")}<input name="cognome"></label>${federatedIdentityFields(providers)}<label class="check"><input name="passwordLoginEnabled" type="checkbox"> Abilita anche la password locale ${helpDot("user-password-login")}</label><span class="form-help">Se disattivato, l'utente accede solo via SSO.</span><button>Crea utente SSO</button></form>`);
2330
2309
  byId("back").onclick = () => show("ssoUsers");
2331
2310
  const ssoUserForm = document.getElementById("sso-user");
@@ -2348,10 +2327,10 @@ async function showSsoUserForm() {
2348
2327
  };
2349
2328
  }
2350
2329
  async function showSsoProviders() {
2351
- if (!federatedAuthenticationAvailable) throw new Error("SSO non \xE8 abilitato in questa istanza Accessi.");
2330
+ if (!federatedAuthenticationAvailable) throw new Error("SSO non \xC3\xA8 abilitato in questa istanza Accessi.");
2352
2331
  const providerList = result(await getFederatedProviders());
2353
2332
  render(`<div class="toolbar"><button id="new-provider">Nuovo provider</button><button id="reload-providers" class="secondary">Aggiorna</button></div>
2354
- <h2>Provider SSO</h2><p class="muted">La validazione di token, issuer e segreti resta nel backend. Un provider si elimina solo se nessuna identit\xE0 \xE8 collegata.</p>
2333
+ <h2>Provider SSO</h2><p class="muted">La validazione di token, issuer e segreti resta nel backend. Un provider si elimina solo se nessuna identit\xC3\xA0 \xC3\xA8 collegata.</p>
2355
2334
  <div class="menu-table-wrap"><table><thead><tr><th>Provider</th><th>Descrizione</th><th>Stato</th><th></th></tr></thead><tbody>${providerList.map((provider) => `<tr class="${provider.active ? "" : "is-disabled"}"><td><code>${escapeHtml(provider.provider)}</code></td><td>${escapeHtml(provider.description)}</td><td>${provider.active ? "Attivo" : "Disabilitato"}</td><td class="row-actions"><button data-provider="${escapeHtml(provider.provider)}">Gestisci</button></td></tr>`).join("") || '<tr><td colspan="4" class="muted">Nessun provider censito.</td></tr>'}</tbody></table></div>`);
2356
2335
  byId("new-provider").onclick = () => showProviderForm();
2357
2336
  byId("reload-providers").onclick = () => handleAction(showSsoProviders);
@@ -2360,11 +2339,11 @@ async function showSsoProviders() {
2360
2339
  });
2361
2340
  }
2362
2341
  async function showSsoUsers() {
2363
- if (!federatedAuthenticationAvailable) throw new Error("SSO non \xE8 abilitato in questa istanza Accessi.");
2342
+ if (!federatedAuthenticationAvailable) throw new Error("SSO non \xC3\xA8 abilitato in questa istanza Accessi.");
2364
2343
  const users = await loadUsers();
2365
2344
  render(`<div class="toolbar"><button id="new-sso">Nuovo utente SSO</button><button id="reload-sso-users" class="secondary">Aggiorna</button></div>
2366
- <h2>Utenti e identit\xE0 SSO</h2><p class="muted">Gestisci i collegamenti provider/subject gi\xE0 verificati dal backend.</p>
2367
- <div class="menu-table-wrap"><table><thead><tr><th>Utente</th><th>Accesso</th><th></th></tr></thead><tbody>${users.map(({ utente }) => `<tr><td>${escapeHtml(utente.email)}</td><td>${utente.passwordLoginEnabled === false ? "solo SSO" : "password e SSO"}</td><td class="row-actions"><button data-sso-user="${utente.codiceUtente}">Gestisci identit\xE0</button></td></tr>`).join("") || '<tr><td colspan="3" class="muted">Nessun utente.</td></tr>'}</tbody></table></div>`);
2345
+ <h2>Utenti e identit\xC3\xA0 SSO</h2><p class="muted">Gestisci i collegamenti provider/subject gi\xC3\xA0 verificati dal backend.</p>
2346
+ <div class="menu-table-wrap"><table><thead><tr><th>Utente</th><th>Accesso</th><th></th></tr></thead><tbody>${users.map(({ utente }) => `<tr><td>${escapeHtml(utente.email)}</td><td>${utente.passwordLoginEnabled === false ? "solo SSO" : "password e SSO"}</td><td class="row-actions"><button data-sso-user="${utente.codiceUtente}">Gestisci identit\xC3\xA0</button></td></tr>`).join("") || '<tr><td colspan="3" class="muted">Nessun utente.</td></tr>'}</tbody></table></div>`);
2368
2347
  byId("new-sso").onclick = () => handleAction(showSsoUserForm);
2369
2348
  byId("reload-sso-users").onclick = () => handleAction(showSsoUsers);
2370
2349
  document.querySelectorAll("[data-sso-user]").forEach((button) => {
@@ -2443,7 +2422,7 @@ async function showUser(rawCode) {
2443
2422
  const defaultUserTab = federatedAuthenticationAvailable ? "sso" : "profile";
2444
2423
  render(`<div class="user-action-bar"><button id="back" type="button" class="secondary">Indietro</button><label class="inline-field">Stato registrazione ${helpDot("user-state")}<select id="user-state">${registrationStateOptions(user.statoRegistrazione)}</select></label><button id="save-state" type="button" class="secondary">Aggiorna stato</button><button id="disable-user" type="button" class="danger-button">Imposta stato eliminato</button>${helpDot("user-delete")}</div><div class="page-header"><div><p class="eyebrow">Utente ${codiceUtente}</p><h2>${escapeHtml(user.email)}</h2></div><span class="muted">Gestione profilo, ruoli e autorizzazioni</span></div>${userDetailTabs(federatedAuthenticationAvailable)}<div class="user-editor">
2445
2424
  ${identitySection}
2446
- <form id="profile" data-user-panel="profile" role="tabpanel" aria-labelledby="user-tab-profile"><h3>Profilo e accesso</h3><label>Nome ${helpDot("user-name")}<input name="nome" value="${escapeHtml(user.nome)}"></label><label>Cognome ${helpDot("user-name")}<input name="cognome" value="${escapeHtml(user.cognome)}"></label><label>Email ${helpDot("user-email")}<input name="email" type="email" value="${escapeHtml(user.email)}" required><span class="form-help">Identificativo di accesso e recapito.</span></label>${federatedAuthenticationAvailable ? `<label class="check"><input name="passwordLoginEnabled" type="checkbox" ${user.passwordLoginEnabled !== false ? "checked" : ""}> Login con password ${helpDot("user-password-login")}</label><span class="form-help">Se disabilitato, il login con email e password restituisce un errore esplicito; restano valide le identit\xE0 SSO attive.</span>` : ""}${authenticationPolicyFields(user)}${applicationFlagFields(user)}<button>Salva</button></form>
2425
+ <form id="profile" data-user-panel="profile" role="tabpanel" aria-labelledby="user-tab-profile"><h3>Profilo e accesso</h3><label>Nome ${helpDot("user-name")}<input name="nome" value="${escapeHtml(user.nome)}"></label><label>Cognome ${helpDot("user-name")}<input name="cognome" value="${escapeHtml(user.cognome)}"></label><label>Email ${helpDot("user-email")}<input name="email" type="email" value="${escapeHtml(user.email)}" required><span class="form-help">Identificativo di accesso e recapito.</span></label>${federatedAuthenticationAvailable ? `<label class="check"><input name="passwordLoginEnabled" type="checkbox" ${user.passwordLoginEnabled !== false ? "checked" : ""}> Login con password ${helpDot("user-password-login")}</label><span class="form-help">Se disabilitato, il login con email e password restituisce un errore esplicito; restano valide le identit\xC3\xA0 SSO attive.</span>` : ""}${authenticationPolicyFields(user)}<button>Salva</button></form>
2447
2426
  ${roleAssignmentEditor(allRoles, userGrants.ruoli, menuGroups)}
2448
2427
  ${directGrantEditor(menuGroups, userGrants.abilitazioni)}
2449
2428
  </div>`);
@@ -2475,9 +2454,6 @@ async function showUser(rawCode) {
2475
2454
  handleAction(async () => {
2476
2455
  const values = formValues(eventForm(event));
2477
2456
  const fields = new FormData(eventForm(event));
2478
- const applicationFlags = Object.fromEntries(
2479
- APPLICATION_FLAGS.map(([key]) => [key, fields.has(key)])
2480
- );
2481
2457
  await updateUtente(codiceUtente, {
2482
2458
  codiceUtente,
2483
2459
  email: values.email ?? "",
@@ -2485,9 +2461,6 @@ async function showUser(rawCode) {
2485
2461
  cognome: values.cognome || void 0,
2486
2462
  flagDueFattori: fields.has("flagDueFattori"),
2487
2463
  passwordlessLoginEnabled: fields.has("passwordlessLoginEnabled"),
2488
- caumov: values.caumov || void 0,
2489
- enableIa: fields.has("enableIa"),
2490
- ...applicationFlags,
2491
2464
  ...federatedAuthenticationAvailable ? { passwordLoginEnabled: fields.has("passwordLoginEnabled") } : {}
2492
2465
  });
2493
2466
  if (codiceUtente === currentUser?.codiceUtente && (fields.has("flagDueFattori") !== Boolean(user.flagDueFattori) || fields.has("passwordlessLoginEnabled") !== Boolean(user.passwordlessLoginEnabled))) {
@@ -2543,7 +2516,7 @@ async function showUser(rawCode) {
2543
2516
  });
2544
2517
  document.querySelectorAll("[data-identity-delete]").forEach((button) => {
2545
2518
  button.onclick = () => handleAction(async () => {
2546
- if (!window.confirm("Eliminare definitivamente questo collegamento SSO? L\u2019utente Accessi non verr\xE0 eliminato.")) return;
2519
+ if (!window.confirm("Eliminare definitivamente questo collegamento SSO? L\xE2\u20AC\u2122utente Accessi non verr\xC3\xA0 eliminato.")) return;
2547
2520
  await deleteFederatedIdentityPermanently(codiceUtente, button.dataset.identityDelete ?? "");
2548
2521
  showNotice("Collegamento SSO eliminato definitivamente.");
2549
2522
  await showUser(String(codiceUtente));
@@ -2679,7 +2652,7 @@ async function showMenuItems() {
2679
2652
  async function showMenuTypes() {
2680
2653
  const menuTypes = result(await getMenuTypes());
2681
2654
  render(`<div class="toolbar"><button id="new-menu-type">Nuovo tipo menu</button><button id="reload-menu-types" class="secondary">Aggiorna</button></div>
2682
- <h2>Tipi menu</h2><p class="form-help">Classificano i menu (es. amministrazione, operativit\xE0). Un tipo si elimina solo se non \xE8 usato da alcun menu.</p>
2655
+ <h2>Tipi menu</h2><p class="form-help">Classificano i menu (es. amministrazione, operativit\xC3\xA0). Un tipo si elimina solo se non \xC3\xA8 usato da alcun menu.</p>
2683
2656
  ${menuTypeTable(menuTypes)}`);
2684
2657
  byId("new-menu-type").onclick = () => showMenuTypeForm();
2685
2658
  byId("reload-menu-types").onclick = () => handleAction(showMenuTypes);
@@ -2897,7 +2870,7 @@ async function showServiceTokens(includeRevoked = false) {
2897
2870
  });
2898
2871
  }
2899
2872
  function showServiceTokenForm() {
2900
- render(`<button id="back" type="button" class="secondary">Indietro</button><h2>Nuovo token di servizio</h2><form id="token-form"><label>Descrizione ${helpDot("token-label")}<input name="label" required maxlength="100"><span class="form-help">Identifica l'integrazione, ad esempio "IA - produzione".</span></label><label>Scope ${helpDot("token-scopes")}<input name="scopes" placeholder="ia, chat"><span class="form-help">Separati da virgola (lettere, numeri, ':', '_', '-'). Definiscono cosa pu\xF2 fare il token.</span></label><label>Durata in giorni ${helpDot("token-ttl")}<input name="ttlDays" type="number" min="1" step="1"><span class="form-help">Opzionale. Lascia vuoto per un token senza scadenza.</span></label><button>Crea token</button></form>`);
2873
+ render(`<button id="back" type="button" class="secondary">Indietro</button><h2>Nuovo token di servizio</h2><form id="token-form"><label>Descrizione ${helpDot("token-label")}<input name="label" required maxlength="100"><span class="form-help">Identifica l'integrazione, ad esempio "IA - produzione".</span></label><label>Scope ${helpDot("token-scopes")}<input name="scopes" placeholder="ia, chat"><span class="form-help">Separati da virgola (lettere, numeri, ':', '_', '-'). Definiscono cosa pu\xC3\xB2 fare il token.</span></label><label>Durata in giorni ${helpDot("token-ttl")}<input name="ttlDays" type="number" min="1" step="1"><span class="form-help">Opzionale. Lascia vuoto per un token senza scadenza.</span></label><button>Crea token</button></form>`);
2901
2874
  byId("back").onclick = () => void show("tokens");
2902
2875
  const form = document.getElementById("token-form");
2903
2876
  if (form) form.onsubmit = (event) => {