Overview

Autenticazione & Autorizzazioni

Questa sezione raccoglie il flusso login e i livelli autorizzativi principali: UI di accesso, metadata (tabella/colonna/menu) e route-guard lato host Angular.

1) Login

Screen riferimento:

  • manual__auth_login__01.png: schermata login (username/password + azioni auth).

Captcha (abilitazione):

  • Il captcha viene abilitato quando entrambe le chiavi sono valorizzate:
  • captcha_public_key
  • captcha_private_key
  • La configurazione pre-login viene esposta da GET /api/Meta/AuthConfig (captchaEnabled, captchaSiteKey).

Registrazione (abilitazione):

  • La registrazione viene esposta in UI quando registrationEnabled = true nella AuthConfig.
  • Nel backend la registrazione e' spenta per default: si accende solo con AppSettings:registrationEnabled = true. default-role-id deve indicare un ruolo esistente senza diritti admin/superadmin, altrimenti la registrazione viene rifiutata.
  • In login compare il link Registrati; nel form register sono previsti username, email, password e (se attivo) captcha.

Recupero password:

  • In login e disponibile il link Password dimenticata?.
  • La richiesta reset usa MetaService.requestPasswordReset e invia email con link contenente token.
  • Chiavi minime da configurare per il flusso email:
  • email-host
  • email-port
  • email-ssl
  • email-user
  • email-pwd
  • email-sender-address-registration
  • site-url (base URL usata per costruire il link di reset).

AppSettings consigliati (estratto):

Snippet 1JSON
{
  "AppSettings": {
    "captcha_public_key": "your-public-key",
    "captcha_private_key": "your-private-key",

    "registrationEnabled": "true",
    "email-sender-address-registration": "no-reply@your-domain.tld",

Limite dei tentativi di login

MetaService.login controlla il limite prima di verificare credenziali e captcha. Oltre il limite risponde HTTP 429 errors.auth.login_rate_limited, con i secondi di attesa in retryAfterSeconds. La risposta non dice quale limite e' scattato, per non rivelare se lo username esiste.

  • Per utente (sempre attivo, non configurabile): 5 password sbagliate in 5 minuti sullo stesso username bloccano quell'utente per 1 minuto. Un login riuscito azzera il contatore. Protegge dal brute force sul singolo account.
  • Per IP (loginRateLimitPerIpPerMinute, dalla 1.7.16): numero massimo di tentativi al minuto, riusciti o no, dallo stesso IP; superato, l'IP e' bloccato per 1 minuto. Default 0 = spento (anche con un valore non numerico o negativo). Sulle installazioni esposte a internet impostalo, es. 30: e' l'unica difesa dal credential stuffing, cioe' molti username diversi provati dallo stesso IP, che il limite per utente non ferma.
  • IP esenti (loginRateLimitExemptIps, dalla 1.7.16): IP separati da virgola trattati come il loopback, quindi mai limitati ne' contati. Servono agli stress test che fanno login di centinaia di utenti dallo stesso IP (una VM VirtualBox in NAT vede l'host come 10.0.2.2). Il confronto e' esatto; la forma IPv4-mapped ::ffff:10.0.2.2 equivale a 10.0.2.2.
  • Loopback (127.0.0.1, ::1, ::ffff:127.x.x.x): mai limitato.
  • Le due chiavi si leggono a ogni login, quindi una modifica vale senza riavvio se la configurazione si ricarica da sola (altrimenti va riciclato l'application pool).
  • Fino alla 1.7.15 il limite per IP era fisso a 30 tentativi al minuto, con il solo loopback esente.
Snippet 2JSON
{
  "AppSettings": {
    "loginRateLimitPerIpPerMinute": "30",
    "loginRateLimitExemptIps": "10.0.2.2, 192.168.1.50"
  }
}

1bis) Modello flag admin (server + client)

Il framework distingue tre flag con granularita' crescente, valutati in sequenza dal piu' restrittivo al piu' permissivo:

FlagSorgente DBScopeUsato in
isSuperAdminruoli.superadmin (bit)Privilegio massimo: puo' modificare metadati di progettoGate server su tutte le mutazioni metadata (RawHelpers.checkAdmin) + UI-gate client (UserInfoService.isUserAdmin / isCurrentUserAdmin)
isRoleAdminruoli.admin (bit)Ruolo "Amministratore" nominale (es. seed id_ruolo=2)Solo fallback-grant per-route (non supera checkAdmin)
isAdmin (legacy)utenti.isAdmin (bit)Flag per-utente storicoSolo fallback-grant per-route (non supera checkAdmin)

Le due semantiche applicative:

  • Gate "mutazione metadata" (scaffolding, save boardcontent, edit colonne/tabelle metadata, ecc.):

- server: RawHelpers.checkAdmin(uid) — fallisce con AuthenticationException("Need administrative rights!") se !user.isSuperAdmin.

- client: UI nasconde/disabilita le azioni lette da UserInfoService.isUserAdmin(userLike) che ritorna userLike.isSuperAdmin.

- Gli endpoint coinvolti sono ~40.

  • Fallback-grant per-route (quando una tabella ha md_grant_by_default = false e nessun grant esplicito utente/ruolo/azienda):

- server: applyTableRestrictions — concede view/edit/insert/delete se user.hasAdminGrant, dove:

Snippet 3text
    hasAdminGrant = isSuperAdmin OR isRoleAdmin OR isAdmin

- La cascade e' permissiva per retrocompatibilita': un superadmin supera sempre, ma anche un ruolo admin=1 o un utente con il vecchio utenti.isAdmin=1 resta autorizzato.

Payload k-user (cookie / sessionStorage) post-login contiene entrambi i flag:

Snippet 4JSON
{
  "user_id": 100274,
  "user_name": "admin",
  "role": "Admin",
  "role_id": 1,
  "isAdmin": true,
  "isSuperAdmin": true,

Note operative:

  • Il flag legacy utenti.isAdmin resta popolato sul modello user ma non va piu' usato per decisioni di autorizzazione nuove: preferire sempre isSuperAdmin (gate) o hasAdminGrant (fallback permissivo).
  • Lato Angular host, il route-guard roleRouteCanMatchGuard usa la label role (stringa "Admin" / "Amministratore" / ecc.) per match con FRAMEWORK_ROUTE_ROLE_RULES. E' una decisione di routing applicativa, indipendente dai flag DB.

1ter) Chiamate AsmxProxy: login obbligatorio per default

Dalla 1.7.13 ogni metodo raggiungibile da /api/Meta/AsmxProxy/{servizio}.{metodo} richiede una sessione valida, salvo quelli dichiarati anonimi. Vale per MetaService, scaffolding e per i servizi dell'applicazione in WEB_UI_CRAFTER.ProjectData.Servizi.

  • [AsmxAnonymous] (namespace WEB_UI_CRAFTER.Helpers): metodo chiamabile senza login, come login, me, GetTranslation, registrationStatus.
  • [AsmxAdmin]: metodo riservato agli amministratori (ruolo superadmin). La classe scaffolding e' marcata per intero: tutti i suoi metodi, su ogni DBMS, sono riservati agli amministratori.
  • [AsmxFirstRun]: anonimo solo finche' firstRun e' true, poi riservato agli amministratori.
  • Il proxy invoca solo le classi di servizio: un nome di classe completo di altri namespace viene rifiutato.

Un servizio dell'applicazione che deve rispondere prima del login va marcato con [AsmxAnonymous]; senza, risponde 401 errors.auth.unauthenticated.

Sessione, logout e lista utenti

  • Con enableCookieAuthentication = true token, scadenza e sostituzione della sessione da parte di un nuovo login sono verificati lato server su tutti i provider, PostgreSQL e Oracle compresi.
  • Con enableCookieAuthentication = "signed" (dalla 1.7.21) il cookie k-user e' del server e firmato HMAC: piu' sessioni per utente come con false, ma un cookie senza firma valida, scaduto o modificato risponde 401 errors.auth.unauthenticated. Con false privilegi e ruolo dichiarati nel cookie sono confrontati col DB a ogni richiesta. Confronto delle tre modalita' e chiave cookie-signing-key in AppSettings.
  • Lo user_id del cookie k-user e' accettato solo se intero; un cookie illeggibile risponde 401 errors.auth.unauthenticated.
  • logoutSession, logout e logOutForce agiscono solo sulla sessione del chiamante; chiudere la sessione di un altro utente richiede diritti di amministratore.
  • La lista utenti non restituisce piu' token e IP della sessione: il codice client non deve contarci.

2) Metadati autorizzazioni: tabelle, colonne, menu

Screen ripresi dalla documentazione metadata esistente:

  • metadata-integration__metadata-editor-auth-table__desktop.png: autorizzazioni tabella.
  • metadata-integration__metadata-editor-auth-table-edit-popup__desktop.png: popup edit autorizzazione tabella.
  • metadata-integration__metadata-editor-auth-column__desktop.png: autorizzazioni colonna.
  • metadata-integration__metadata-editor-auth-column-edit-popup__desktop.png: popup edit autorizzazione colonna.
  • metadata-integration__metadata-menu-management__desktop.png: gestione menu metadata.
  • metadata-integration__metadata-menu-management-edit-popup__desktop.png: popup edit voce menu.

Metadati coinvolti:

  • _Metadati_Utenti_Autorizzazioni_Tabelle: permessi view/edit/insert/delete a livello route/tabella.
  • _Metadati_Utenti_Autorizzazioni_Colonne: permessi field-level (es. editabilita/required per ruolo o utente).
  • _metadati__menu: visibilita/navigazione voci menu in combinazione con i permessi route.
  • md_record_restriction_key_user_field_list (route): restrizione dei record per utente, ruolo o azienda per chi non e' admin. Vale in lettura (griglie, lookup, OData) e, dalla 1.7.21, anche in scrittura: updateRecord, deleteRecord e deleteRecordByID su una riga che la restrizione nasconde all'utente rispondono 403 errors.auth.route_read_forbidden (reason row_not_visible); prima la WHERE della UPDATE/DELETE conteneva solo la chiave. Stessa regola via OData (404): vedi OData.

3) Utilizzo route-guard (host project)

Obiettivo: bloccare route riservate in base ai ruoli utente e reindirizzare a /unauthorized.

File host da modificare (esempio WuicTest):

  • C:\src\Wuic\WuicTest\wwwroot\src\app\routing\route-role-map.ts
  • C:\src\Wuic\WuicTest\wwwroot\src\app\routing\role-route.guard.ts
  • C:\src\Wuic\WuicTest\wwwroot\src\app\wuic-bridges\routes.ts
  • C:\src\Wuic\WuicTest\wwwroot\src\app\app.routes.ts
  • C:\src\Wuic\WuicTest\wwwroot\src\app\component\unauthorized\unauthorized.component.ts

Esempio 1: mappa regole ruolo/route

Snippet 5ts
export const FRAMEWORK_ROUTE_ROLE_RULES: RouteRoleRule[] = [
  { key: 'designer', routePattern: 'designer', roles: ['admin'] },
  { key: 'workflow-designer', routePattern: 'workflow-designer', roles: ['admin'] },
  { key: 'dashboard', routePattern: ':route/dashboard', roles: ['admin'] },
  { key: 'report-designer', routePattern: ':route/report-designer', roles: ['admin'] }
];

Esempio 2: aggancio guard alle route

Snippet 6ts
{
  path: ':route/dashboard',
  loadComponent: () => import('wuic-framework-lib-src/component/designer/designer.route.component').then((m) => m.DesignerRouteComponent),
  canMatch: [menuRouteAccessCanMatchGuard, roleRouteCanMatchGuard],
  canActivate: [menuRouteAccessCanActivateGuard, roleRouteCanActivateGuard],
  data: { breadcrumbs: 'dashboard', roleRuleKey: 'dashboard' }
}

Esempio 3: redirect su pagina unauthorized

Snippet 7ts
return router.createUrlTree(['/unauthorized'], {
  queryParams: { from: attemptedPath || '/' }
});

Note operative:

  • roleRuleKey in route.data e preferibile quando la route contiene parametri dinamici.
  • In fallback, il guard puo usare il route.path per match diretto.
  • La pagina unauthorized deve essere registrata nelle route host e mostrare la route richiesta (queryParam from).

Screenshot

autenticazione-autorizzazioni / login main
autenticazione-autorizzazioni / login main