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

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 (43) hide show
  1. package/dist/accessi-module/Console/console.js +19 -39
  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/publicRegisterRequest.d.ts +0 -4
  6. package/dist/accessi-module/Console/generated/model/publicRegisterRequest.js.map +1 -1
  7. package/dist/accessi-module/Console/generated/model/registerRequest.d.ts +2 -6
  8. package/dist/accessi-module/Console/generated/model/registerRequest.js.map +1 -1
  9. package/dist/accessi-module/Console/generated/model/userDto.d.ts +3 -29
  10. package/dist/accessi-module/Console/generated/model/userDto.js.map +1 -1
  11. package/dist/accessi-module/Console/wiki.js +2 -2
  12. package/dist/accessi-module/Console/wiki.js.map +1 -1
  13. package/dist/accessi-module/Controllers/ConfiguratorController.js +2 -2
  14. package/dist/accessi-module/Controllers/ConfiguratorController.js.map +1 -1
  15. package/dist/accessi-module/Dtos/AuthenticatedUserPayloadDto.d.ts +1 -1
  16. package/dist/accessi-module/Dtos/AuthenticatedUserPayloadDto.js +1 -1
  17. package/dist/accessi-module/Dtos/AuthenticatedUserPayloadDto.js.map +1 -1
  18. package/dist/accessi-module/Dtos/PublicRegisterRequest.d.ts +1 -1
  19. package/dist/accessi-module/Dtos/PublicRegisterRequest.js +1 -1
  20. package/dist/accessi-module/Dtos/PublicRegisterRequest.js.map +1 -1
  21. package/dist/accessi-module/Dtos/RegisterRequest.d.ts +1 -3
  22. package/dist/accessi-module/Dtos/RegisterRequest.js +2 -22
  23. package/dist/accessi-module/Dtos/RegisterRequest.js.map +1 -1
  24. package/dist/accessi-module/Dtos/UserDto.d.ts +2 -15
  25. package/dist/accessi-module/Dtos/UserDto.js +4 -97
  26. package/dist/accessi-module/Dtos/UserDto.js.map +1 -1
  27. package/dist/accessi-module/Services/UserService/UserService.d.ts +1 -2
  28. package/dist/accessi-module/Services/UserService/UserService.js +19 -108
  29. package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
  30. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js +16 -0
  31. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js.map +1 -1
  32. package/dist/accessi-module/database-updates/accessiSchema.d.ts +12 -1
  33. package/dist/accessi-module/database-updates/accessiSchema.js +18 -9
  34. package/dist/accessi-module/database-updates/accessiSchema.js.map +1 -1
  35. package/dist/accessi-module/security/accessControl.d.ts +2 -2
  36. package/dist/accessi-module/security/accessControl.js +2 -2
  37. package/dist/accessi-module/security/accessControl.js.map +1 -1
  38. package/dist/accessi-module/security/authenticatedToken.d.ts +1 -1
  39. package/dist/accessi-module/security/authenticatedToken.js +2 -2
  40. package/dist/accessi-module/security/authenticatedToken.js.map +1 -1
  41. package/dist/emilsoftware-utilities-2.0.0-dev.32.tgz +0 -0
  42. package/package.json +1 -1
  43. 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 applicativi legacy (FLGMOP, FLGPIANA, FLGADDETTI, FLGOSPITI, FLGPIANARFID, FLGCONTA, FLGCUBI, FLGCICLPASS, FLGDIPENDENTI, FLGINVENTARI) e le colonne applicative (CAUMOV, NUMMAC, RAGSOCCLI) sono stati rimossi dallo schema: i backend devono gestirli in tabelle proprie. ENABLEIA resta per l'assistente IA.`),
1145
+ note("info", `Gruppi menu e NOMECAMPO`, `Ogni gruppo menu puo dichiarare NOMECAMPO. get-groups-with-menus e get-menus espongono nomeCampo. Dopo la pulizia dello schema NOMECAMPO non deve piu puntare a colonne applicative rimosse.`),
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,20 @@ 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
2288
  function applicationFlagFields(user) {
2301
2289
  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
2290
  return `<fieldset><legend>Abilitazioni applicative</legend>
2304
- ${checks}
2305
- <label>Codice causale <input name="caumov" value="${escapeHtml(String(record.caumov ?? ""))}"></label>
2306
2291
  <label class="check"><input name="enableIa" type="checkbox" ${record.enableIa ? "checked" : ""}> Assistente IA (ENABLEIA)</label>
2307
2292
  </fieldset>`;
2308
2293
  }
2309
2294
  function showLocalUserForm() {
2310
2295
  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>`);
2296
+ <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
2297
  byId("back").onclick = () => show("users");
2313
2298
  const localUserForm = document.getElementById("local-user");
2314
2299
  if (localUserForm) localUserForm.onsubmit = (event) => {
@@ -2325,7 +2310,7 @@ async function showSsoUserForm() {
2325
2310
  if (!providers.some((provider) => provider.active)) {
2326
2311
  throw new Error("Prima di creare un utente SSO, censisci e abilita almeno un provider SSO.");
2327
2312
  }
2328
- render(`<button id="back">Indietro</button><h2>Nuovo utente SSO</h2><p class="muted">Salva solo collegamenti gi\xE0 verificati dal backend.</p>
2313
+ 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
2314
  <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
2315
  byId("back").onclick = () => show("ssoUsers");
2331
2316
  const ssoUserForm = document.getElementById("sso-user");
@@ -2348,10 +2333,10 @@ async function showSsoUserForm() {
2348
2333
  };
2349
2334
  }
2350
2335
  async function showSsoProviders() {
2351
- if (!federatedAuthenticationAvailable) throw new Error("SSO non \xE8 abilitato in questa istanza Accessi.");
2336
+ if (!federatedAuthenticationAvailable) throw new Error("SSO non \xC3\xA8 abilitato in questa istanza Accessi.");
2352
2337
  const providerList = result(await getFederatedProviders());
2353
2338
  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>
2339
+ <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
2340
  <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
2341
  byId("new-provider").onclick = () => showProviderForm();
2357
2342
  byId("reload-providers").onclick = () => handleAction(showSsoProviders);
@@ -2360,11 +2345,11 @@ async function showSsoProviders() {
2360
2345
  });
2361
2346
  }
2362
2347
  async function showSsoUsers() {
2363
- if (!federatedAuthenticationAvailable) throw new Error("SSO non \xE8 abilitato in questa istanza Accessi.");
2348
+ if (!federatedAuthenticationAvailable) throw new Error("SSO non \xC3\xA8 abilitato in questa istanza Accessi.");
2364
2349
  const users = await loadUsers();
2365
2350
  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>`);
2351
+ <h2>Utenti e identit\xC3\xA0 SSO</h2><p class="muted">Gestisci i collegamenti provider/subject gi\xC3\xA0 verificati dal backend.</p>
2352
+ <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
2353
  byId("new-sso").onclick = () => handleAction(showSsoUserForm);
2369
2354
  byId("reload-sso-users").onclick = () => handleAction(showSsoUsers);
2370
2355
  document.querySelectorAll("[data-sso-user]").forEach((button) => {
@@ -2443,7 +2428,7 @@ async function showUser(rawCode) {
2443
2428
  const defaultUserTab = federatedAuthenticationAvailable ? "sso" : "profile";
2444
2429
  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
2430
  ${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>
2431
+ <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)}${applicationFlagFields(user)}<button>Salva</button></form>
2447
2432
  ${roleAssignmentEditor(allRoles, userGrants.ruoli, menuGroups)}
2448
2433
  ${directGrantEditor(menuGroups, userGrants.abilitazioni)}
2449
2434
  </div>`);
@@ -2475,9 +2460,6 @@ async function showUser(rawCode) {
2475
2460
  handleAction(async () => {
2476
2461
  const values = formValues(eventForm(event));
2477
2462
  const fields = new FormData(eventForm(event));
2478
- const applicationFlags = Object.fromEntries(
2479
- APPLICATION_FLAGS.map(([key]) => [key, fields.has(key)])
2480
- );
2481
2463
  await updateUtente(codiceUtente, {
2482
2464
  codiceUtente,
2483
2465
  email: values.email ?? "",
@@ -2485,9 +2467,7 @@ async function showUser(rawCode) {
2485
2467
  cognome: values.cognome || void 0,
2486
2468
  flagDueFattori: fields.has("flagDueFattori"),
2487
2469
  passwordlessLoginEnabled: fields.has("passwordlessLoginEnabled"),
2488
- caumov: values.caumov || void 0,
2489
2470
  enableIa: fields.has("enableIa"),
2490
- ...applicationFlags,
2491
2471
  ...federatedAuthenticationAvailable ? { passwordLoginEnabled: fields.has("passwordLoginEnabled") } : {}
2492
2472
  });
2493
2473
  if (codiceUtente === currentUser?.codiceUtente && (fields.has("flagDueFattori") !== Boolean(user.flagDueFattori) || fields.has("passwordlessLoginEnabled") !== Boolean(user.passwordlessLoginEnabled))) {
@@ -2543,7 +2523,7 @@ async function showUser(rawCode) {
2543
2523
  });
2544
2524
  document.querySelectorAll("[data-identity-delete]").forEach((button) => {
2545
2525
  button.onclick = () => handleAction(async () => {
2546
- if (!window.confirm("Eliminare definitivamente questo collegamento SSO? L\u2019utente Accessi non verr\xE0 eliminato.")) return;
2526
+ if (!window.confirm("Eliminare definitivamente questo collegamento SSO? L\xE2\u20AC\u2122utente Accessi non verr\xC3\xA0 eliminato.")) return;
2547
2527
  await deleteFederatedIdentityPermanently(codiceUtente, button.dataset.identityDelete ?? "");
2548
2528
  showNotice("Collegamento SSO eliminato definitivamente.");
2549
2529
  await showUser(String(codiceUtente));
@@ -2679,7 +2659,7 @@ async function showMenuItems() {
2679
2659
  async function showMenuTypes() {
2680
2660
  const menuTypes = result(await getMenuTypes());
2681
2661
  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>
2662
+ <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
2663
  ${menuTypeTable(menuTypes)}`);
2684
2664
  byId("new-menu-type").onclick = () => showMenuTypeForm();
2685
2665
  byId("reload-menu-types").onclick = () => handleAction(showMenuTypes);
@@ -2897,7 +2877,7 @@ async function showServiceTokens(includeRevoked = false) {
2897
2877
  });
2898
2878
  }
2899
2879
  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>`);
2880
+ 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
2881
  byId("back").onclick = () => void show("tokens");
2902
2882
  const form = document.getElementById("token-form");
2903
2883
  if (form) form.onsubmit = (event) => {