Passa al contenuto principale

Claims e policy

Inventario delle lettere autorizzative e dei claim custom in uso in TrainingHub, con il rispettivo significato e i punti di codice che li referenziano.

Cosa fa

TrainingHub usa KSet + un policy provider custom (KSet.Auth.Engines.LettersClaimsPolicyProvider, in D:\repos\dev3sd\Kset) che valuta le policy ASP.NET come richieste di lettere sul claim base associato all'utente. Questa pagina è la mappa unica di:

  • quali lettere (R, C, U, ...) sono effettivamente in uso e cosa significano;
  • quali claim custom esistono al di fuori del pattern auto-generato dal CRUD;
  • quali combinazioni ruolo × claim vengono seedate dal database.

Quando aggiungi una nuova lettera o un claim non-CRUD, aggiorna questa pagina in contestuale al PR.

Modello

I suffissi _X vivono in due posti diversi con ruoli diversi:

DoveEsempioCosa contiene
Codice / config[Authorize(Policy = "page-edu-foo_RU")], <AuthorizeView Policy="page-edu-foo_S">, oss.menu.authPolicy = 'page-edu-foo_M'Le lettere richieste per accedere alla risorsa.
DB — kset.claims.slugpage-edu-fooSolo il nome base. Mai con suffisso _X.
DB — kset.rolesClaims.authorizations'RCUD', 'RS', NULL (= tutte le lettere)Le lettere granted a un ruolo per quel claim.

LettersClaimsPolicyProvider:

  1. Trasforma il claim utente in "{claim.slug}_{rolesClaims.authorizations}" (trim _ finale se authorizations è null).
  2. Estrae la base (parte prima dell'ultimo _) sia dalla policy richiesta che dal claim utente e fa il join su quella.
  3. Se entrambi hanno _, verifica che le lettere richieste siano sottoinsieme di quelle granted.
  4. Se solo uno dei due contiene _, la policy passa (clausola permissiva XOR).

Conseguenza pratica della (4): mettere il suffisso direttamente in claims.slug "funziona" per coincidenza ma rompe il modello. Vedi CLAUDE.md § Convenzioni.

Lettere in uso

LetteraSemanticaOrigineEsempi d'uso
RRead — accesso alla pagina/grid in lettura.CRUD generato + pagine custom.page-job-workers_R (WorkerProfile.razor), page-reg-companies_R (CompanyProfile.razor), page-edu-vw_trainingExpirations_R (menu Scadenze formazione).
CCreate — inserimento di nuovi record.CRUD generato.page-edu-courseSessions_C, page-edu-priorTrainings_C (quick action su WorkerProfile).
UUpdate — modifica record esistente.CRUD generato.page-job-workers_U (quick action "Modifica anagrafica").
DDelete — cancellazione record.CRUD generato.Tutti i CRUD generati.
MMenu — visibilità voce nel menu laterale.Convention oss.menu.authPolicy.page-edu-teachers_M, page-edu-trainingTopics_M, page-iso-certifications_M, ecc. (vedi Scripts/menu-update.sql).
SSend / azione custom — gating di un'azione non-CRUD specifica della pagina.Uso manuale.page-teacher-letters_S (firma lettera in LetterDetail.razor), page-teacher-attendance_S (upload PDF registro in Attendance.razor), page-edu-sessionPlanner_S (avvio wizard Nuova sessione).

Note di interpretazione.

  • R/C/U/D sono prodotte automaticamente dal CRUD generator: ogni pagina CRUD generata espone tutte e quattro le lettere per la propria entità.
  • M è il gating del link nel menu, non della pagina: separare _M da _R consente di mostrare/nascondere la voce indipendentemente dall'accesso alla pagina (es. dare R ad un ruolo ma tenere il link fuori dal menu finché non ottiene anche M).
  • S è semantica: il policy provider non sa che "S = send". È convenzione applicativa: tutte le azioni non-CRUD passano per _S. Se serve discriminare più azioni custom sulla stessa entità, vedi Anti-pattern sotto.

Nessuna altra lettera è in uso al 2026-05-11. Le lettere sono case-sensitive nel confronto del policy provider, quindi mantenere maiuscole anche nelle nuove introduzioni.

Claim custom (non auto-generate da CRUD)

Le claim auto-generate seguono page-<schema>-<table> dove <schema> corrisponde a uno schema del DB (edu, job, reg, inv, iso, ...). Le claim qui sotto non corrispondono a un'entità CRUD: hanno schema o slug "virtuale" e vanno mantenute manualmente.

Slug (in kset.claims)Schema "virtuale"Cosa gatingPunti d'uso
page-dashboard-compliancedashboardCruscotto conformità aziende (no CRUD sotto).Components/Pages/ComplianceDashboard/CompanyProspect.razor, CompaniesTrainingOverview.razor (_R).
page-import-training-coursesimportWizard import corsi pregressi da Excel.Components/Pages/Import/TrainingCoursesImport.razor (_R).
page-import-prior-trainingsimportWizard import formazioni storiche da Excel.Components/Pages/Import/PriorTrainingsImport.razor (_R).
page-teacher-dashboardteacherArea docente — home.Components/Pages/TeacherArea/Dashboard.razor (_R).
page-teacher-calendarteacherArea docente — calendario impegni.Components/Pages/TeacherArea/Calendar.razor (_R).
page-teacher-lettersteacherArea docente — lettere d'incarico (lettura + firma).Letters.razor, LetterDetail.razor (_R, _S per firma).
page-teacher-attendanceteacherArea docente — registro presenze (lettura + chiusura + upload).Attendance.razor (_R, _U chiusura, _S upload PDF).
page-edu-sessionPlanneredu (virtuale)Avvio wizard "Nuova sessione" dal menu top-level.Voce menu new_session (menu-add-training-requests.sql, _S).
page-appointments-calendarappointmentsCalendario globale appuntamenti formativi (non è CRUD diretto).Voce menu appointments_calendar (menu-update.sql, _R).

I seed di queste claim vivono in TrainingHub.Database/Scripts/seed-policies-*.sql. Ogni nuovo claim custom deve essere accompagnato da uno script di seed idempotente.

Ruoli seedati e loro grant

RuoloClaimauthorizations grantedSorgente seed
teacherpage-teacher-dashboardRseed-policies-teacher-area.sql
teacherpage-teacher-calendarRidem
teacherpage-teacher-lettersRSidem (R lettura, S firma)
teacherpage-teacher-attendanceRUSidem (R lettura, U chiusura registro, S upload PDF)

Tutti gli altri ruoli (admin, operatori, ecc.) vengono creati e popolati a mano via UI di Role Claims Assignment.

Anti-pattern noti

1. Suffisso _X nello slug di kset.claims

-- ❌ SBAGLIATO
INSERT INTO [kset].[claims] ([slug]) VALUES (N'page-teacher-letters_S');

-- ✅ CORRETTO
INSERT INTO [kset].[claims] ([slug]) VALUES (N'page-teacher-letters');
INSERT INTO [kset].[rolesClaims] ([roleId], [claimId], [authorizations])
VALUES (@teacherRoleId, @claimId, N'RS');

Errore "trascinato" su più sessioni: vedi commento di testata su seed-policies-teacher-area.sql e seed-policies-edu-sessions-redesign-2026-05-08.sql per il cleanup automatico.

2. Suffisso semantico multi-parola (_S_StartWizard, _S_RemoveAndQueue)

Presenti in:

  • Components/CRUD/edu/TrainingRequest.razor:74page-edu-trainingRequests_S_StartWizard
  • Components/CRUD/edu/WorkerTrainingDetail.razor:277page-edu-workerTrainingDetails_S_RemoveAndQueue

Il policy provider prende le lettere dopo l'ultimo _: in questi casi diventa StartWizard / RemoveAndQueue, lettere maiuscole e minuscole che non sono lettere autorizzative reali. Funziona oggi solo per via della clausola XOR permissiva (utenti con claim senza _ passano comunque). Da rifattorizzare: una sola azione _S per entità è gestibile; per due azioni distinte servono o due claim differenti (page-edu-trainingRequests-startWizard come claim a sé) o due lettere differenti (ma siamo limitati al pool ASCII maiuscole).

3. Una claim per ogni lettera (anziché una claim base + lettere granted)

-- ❌ SBAGLIATO — esplode il numero di righe in claims
('page-edu-foo_R'), ('page-edu-foo_C'), ('page-edu-foo_U'), ('page-edu-foo_D')

-- ✅ CORRETTO — una sola claim, lettere in rolesClaims.authorizations
('page-edu-foo')

Come aggiungere un nuovo claim custom

  1. Decidi se è davvero non-CRUD (un'entità CRUD generata espone già R/C/U/D automaticamente).
  2. Crea uno script Scripts/seed-policies-<feature>.sql idempotente:
    • INSERT kset.claims con slug base.
    • INSERT kset.rolesClaims per i ruoli interessati, con la stringa di lettere granted.
  3. Usa la policy in codice con il suffisso corretto: [Authorize(Policy = "page-...-foo_R")], AuthorizeView, oss.menu.authPolicy.
  4. Aggiorna la sezione Claim custom di questa pagina.

Come aggiungere una nuova lettera

  1. Verifica che la semantica non sia già coperta da R/C/U/D/S/M. In particolare: _S è il catch-all per qualsiasi azione non-CRUD; introdurre una nuova lettera ha senso solo se serve discriminare più azioni custom sulla stessa entità per ruoli diversi.
  2. Sceglie una lettera maiuscola non in uso (lookup nella tabella Lettere in uso sopra).
  3. Documenta la nuova lettera prima di iniziare ad usarla.
  4. Concedi la lettera ai ruoli interessati via UI Role Claims Assignment o via seed.

Riferimenti codice

  • D:\repos\dev3sd\Kset\KSet.Auth\Engines\LettersClaimsPolicyProvider.cs — logica di match policy → claim utente.
  • D:\repos\dev3sd\Kset\KSet.Auth\Engines\ClaimsTransformationBase.cs — costruzione del claim utente da kset.claims + kset.rolesClaims.
  • TrainingHub.Database/Scripts/seed-policies-*.sql — seed dei claim custom e dei grant per ruolo.
  • TrainingHub.Database/Scripts/menu-*.sql — uso di oss.menu.authPolicy con suffisso _M/_R.
  • CLAUDE.md § Convenzioni — promemoria sul modello policy vs claim.