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 × claimvengono 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:
| Dove | Esempio | Cosa 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.slug | page-edu-foo | Solo 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:
- Trasforma il claim utente in
"{claim.slug}_{rolesClaims.authorizations}"(trim_finale seauthorizationsè null). - Estrae la base (parte prima dell'ultimo
_) sia dalla policy richiesta che dal claim utente e fa il join su quella. - Se entrambi hanno
_, verifica che le lettere richieste siano sottoinsieme di quelle granted. - 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
| Lettera | Semantica | Origine | Esempi d'uso |
|---|---|---|---|
R | Read — 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). |
C | Create — inserimento di nuovi record. | CRUD generato. | page-edu-courseSessions_C, page-edu-priorTrainings_C (quick action su WorkerProfile). |
U | Update — modifica record esistente. | CRUD generato. | page-job-workers_U (quick action "Modifica anagrafica"). |
D | Delete — cancellazione record. | CRUD generato. | Tutti i CRUD generati. |
M | Menu — 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). |
S | Send / 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/Dsono 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_Mda_Rconsente 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 gating | Punti d'uso |
|---|---|---|---|
page-dashboard-compliance | dashboard | Cruscotto conformità aziende (no CRUD sotto). | Components/Pages/ComplianceDashboard/CompanyProspect.razor, CompaniesTrainingOverview.razor (_R). |
page-import-training-courses | import | Wizard import corsi pregressi da Excel. | Components/Pages/Import/TrainingCoursesImport.razor (_R). |
page-import-prior-trainings | import | Wizard import formazioni storiche da Excel. | Components/Pages/Import/PriorTrainingsImport.razor (_R). |
page-teacher-dashboard | teacher | Area docente — home. | Components/Pages/TeacherArea/Dashboard.razor (_R). |
page-teacher-calendar | teacher | Area docente — calendario impegni. | Components/Pages/TeacherArea/Calendar.razor (_R). |
page-teacher-letters | teacher | Area docente — lettere d'incarico (lettura + firma). | Letters.razor, LetterDetail.razor (_R, _S per firma). |
page-teacher-attendance | teacher | Area docente — registro presenze (lettura + chiusura + upload). | Attendance.razor (_R, _U chiusura, _S upload PDF). |
page-edu-sessionPlanner | edu (virtuale) | Avvio wizard "Nuova sessione" dal menu top-level. | Voce menu new_session (menu-add-training-requests.sql, _S). |
page-appointments-calendar | appointments | Calendario 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
| Ruolo | Claim | authorizations granted | Sorgente seed |
|---|---|---|---|
teacher | page-teacher-dashboard | R | seed-policies-teacher-area.sql |
teacher | page-teacher-calendar | R | idem |
teacher | page-teacher-letters | RS | idem (R lettura, S firma) |
teacher | page-teacher-attendance | RUS | idem (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:74—page-edu-trainingRequests_S_StartWizardComponents/CRUD/edu/WorkerTrainingDetail.razor:277—page-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
- Decidi se è davvero non-CRUD (un'entità CRUD generata espone già
R/C/U/Dautomaticamente). - Crea uno script
Scripts/seed-policies-<feature>.sqlidempotente:- INSERT
kset.claimscon slug base. - INSERT
kset.rolesClaimsper i ruoli interessati, con la stringa di lettere granted.
- INSERT
- Usa la policy in codice con il suffisso corretto:
[Authorize(Policy = "page-...-foo_R")],AuthorizeView,oss.menu.authPolicy. - Aggiorna la sezione Claim custom di questa pagina.
Come aggiungere una nuova lettera
- 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. - Sceglie una lettera maiuscola non in uso (lookup nella tabella Lettere in uso sopra).
- Documenta la nuova lettera prima di iniziare ad usarla.
- 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 dakset.claims+kset.rolesClaims.TrainingHub.Database/Scripts/seed-policies-*.sql— seed dei claim custom e dei grant per ruolo.TrainingHub.Database/Scripts/menu-*.sql— uso dioss.menu.authPolicycon suffisso_M/_R.CLAUDE.md§ Convenzioni — promemoria sul modello policy vs claim.