diff --git a/IdentityProvider_DataModel.md b/IdentityProvider_DataModel.md new file mode 100644 index 00000000..52e16090 --- /dev/null +++ b/IdentityProvider_DataModel.md @@ -0,0 +1,88 @@ +# Modello dati e contratti — Identity Provider PoliNetwork + +**Documenti collegati:** [`PRD_Admin_Dashboard_PoliNetwork.md`](./PRD_Admin_Dashboard_PoliNetwork.md) §2.6 · [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) · [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md) +**Versione:** 2.0 +**Data:** 12 ottobre 2026 + +## 0. Stato e responsabilità + +Il modello implementato vive nella repository autonoma `auth`, nel proprio database Postgres/Drizzle. Questa revisione segue il codice integrato agli SHA del PRD §20; non certifica migrazioni o configurazioni di produzione. + +La precedente proposta `capability_grant`, `institutional_email_link`, `institutional_email_domain` non è stata adottata. I nomi di endpoint proposti (`authorization.getCapabilities`, `rbac.grantCapability`, ecc.) non sono contratti disponibili. Non creare queste tabelle per integrare un nuovo consumer: usare OIDC/OAuth, i permessi IdP e il contratto della RFC v3. + +## 1. Schema implementato in auth + +Fonti: `src/db/auth-schema.ts`, `src/db/evidence.ts`, `src/db/rbac.ts`, `src/db/access-snapshot.ts`. + +### 1.1 Identità ed evidenze + +- **`user` e `account`:** identità locale e collegamenti provider; ownership del collegamento necessaria prima di usare un'evidenza. Matching di email non unisce utenti automaticamente. +- **`identity_evidence`:** PK `(issuer, subject)`, `provider_id`, `external_id`, lista `states`, `valid_until`, `telegram_id`, `telegram_username`, `email`. Indice univoco parziale su `telegram_id` non nullo. Username/email sono dati di visualizzazione; l'autorizzazione usa identificativi verificati, non lo username. +- **`student_verification_challenge`:** PK `user_id` con FK a `user`, `email` univoca, `code_hash`, `attempts`, `expires_at`, `last_sent_at`. Hash HMAC, TTL 10 minuti, cinque tentativi e cooldown 60 secondi. Dominio ammesso: solo `mail.polimi.it`. Alla conferma viene creato l'account `polimi-email` e la relativa evidenza Student. +- **Account/session/passkey e tabelle OAuth:** gestite dall'IdP e dal provider. Socio/Direttivo provengono dai gruppi Entra, ricontrollati per autorizzare; l'evidenza memorizzata non prolunga la freschezza della verifica. + +Queste tabelle non includono un registro di quote annuali, numeri associativi o censimento organizzativo. + +### 1.2 RBAC globale + +| Tabella | Significato | +|---|---| +| `permission` | Chiave stabile univoca, nome/descrizione, flag managed | +| `permission_implication` | Permesso → permesso implicato; transitiva, senza cicli | +| `role` | Bundle con chiave univoca, flag managed e possibile `source_state` | +| `role_parent` | Ruolo → ruolo ereditato; transitiva, senza cicli | +| `role_permission` | Collegamenti tra ruoli e permessi | +| `user_role` | Assegnazione manuale utente→ruolo con `assigned_by`, `assigned_at`; i ruoli gestiti non sono assegnazioni manuali | +| `rbac_audit_event` | Attore, operazione, target, prima/dopo JSON, data; append-only, scritto nella transazione della mutazione | + +Nel modello implementato non ci sono campi `level`, `scope_type`, `scope_value`, revoca temporale del grant o assegnazione diretta di permessi a utenti come prevedeva la proposta originaria. La rimozione di un'assegnazione viene tracciata nell'audit. Gli scope di OAuth delimitano il client, non il corso/team dei record. + +I ruoli gestiti sono Master Admin (wildcard da configurazione), Socio/Direttivo (Entra), Student (email verificata). Il permesso `membership:read` non implementa da solo l'area tesseramento o i rinnovi. Implicazioni e ruoli personalizzati possono concedere accesso senza costituire prova dello stato associativo: distinguere `states`, `roles` e `permissions`. + +### 1.3 Snapshot e notifiche + +L'IdP pubblica snapshot versionati/proiettati per client e notifiche da outbox; il backend effettua anche polling. La relazione Telegram→identità deriva dai collegamenti verificati, non da import di vecchi codici. Per campi wire, validità per fonte e comportamento in caso di outage si segue la RFC v3 §§5–7 e il codice, evitando un secondo schema divergente in questo PRD. + +## 2. Contratti implementati fra servizi + +- **OIDC persone:** Authorization Code/PKCE e refresh token. I consumer verificano issuer, audience, firma e claim; `polinetwork:identity` espone stati, ruoli, permessi e Telegram ID. Per API backend l'autorità corrente viene verificata tramite snapshot, non soltanto dai claim di un vecchio ID token. +- **OAuth servizi:** client credentials con `private_key_jwt`, risorsa/audience e scope assegnati al client. Token personali e di servizio hanno tipi attore distinti. +- **Backend tRPC:** `me.access` restituisce l'accesso corrente della persona. Ogni procedura ha policy per tipo attore, scope e permesso. Il bot può usare `X-PN-Actor: telegram:` solo con il relativo scope act-as. +- **Snapshot IdP:** lettura di servizio autenticata, proiezione configurata per client e notifiche interne; il backend conserva in `common_idp_state` l'ultimo snapshot/chiavi validi per un uso limitato nel tempo. +- **Dashboard BFF:** sessione in Redis, token solo sul server; le funzioni server applicano middleware e inoltrano bearer token al backend. + +La compatibilità legacy senza token resta configurabile nel backend; un token invalido viene rifiutato e non ripiega sul legacy. Le nuove procedure IdP, incluso lo storico giornaliero utenti Telegram, non accettano chiamate anonime. + +## 3. Dati backend implementati per la transizione + +- **Audit Telegram:** attore IdP (`actor_sub`), ID Telegram opzionale, client, `basis`, eventuale review e chiave di idempotenza; le azioni automatiche/native Telegram sono distinte da quelle autorizzate per permesso IdP. +- **Grant:** `tg_grants_v2` per i caller token, con attori IdP; il backend considera anche i grant legacy fino al cleanup. Non dedurre dai nuovi campi che la vecchia tabella sia già eliminata. +- **Contenuti:** colonne `*_by_sub` per l'autore IdP; non costituiscono un audit unificato prima/dopo di ogni modifica. +- **Microsoft 365:** lettura directory, workflow dedicato di creazione e membership dei gruppi tramite Graph, con permessi separati. Non è lo schema del registro associativo PoliNetwork. + +## 4. Proposta residua: registro associativo e claim + +Lo schema definitivo del registro del PRD §3.1 va deciso prima delle FK. Conservare un identificativo associativo univoco, stati e annualità storicizzati, consensi versionati e collegamento opzionale all'identità IdP; nessuna dipendenza obbligatoria da account Entra/Telegram. + +Una possibile futura tabella **`membership_claim`** (non implementata) conserva: + +| Campo logico | Requisito proposto | +|---|---| +| ID | Identificativo univoco del claim | +| Record socio / numero associativo | Riferimento al registro futuro | +| Email destinatario | Indirizzo verificato dal processo di iscrizione | +| Hash codice | Mai codice in chiaro | +| Scadenza e tentativi | TTL e limite da definire; uso singolo e invalidazione del precedente codice | +| Identità richiedente e data claim | `user.id` dell'IdP, valorizzato solo dopo verifica e controllo conflitti | +| Creazione / attore emissione | Tracciabilità dell'emissione e dei re-invii | + +Il claim deve collegare il record alla persona senza duplicare l'identità, rifiutare conflitti e concedere solo accesso ai propri dati. Emissione come effetto dell'approvazione o azione manuale, re-invio, TTL e riconciliazione con lo stato Socio Entra restano aperti. Non sono disponibili oggi endpoint `identity.membershipClaim.*`. + +## 5. Modello ancora da definire + +1. Schema Anagrafica Soci/Censimento, annualità, collegamento fra database e retention dei dati. +2. Scope corso/team, relazioni di responsabilità e policy dei record lato backend: il RBAC globale non li rappresenta. +3. Consensi/firme e testo versionato, audit amministrativo comune oltre al solo RBAC/moderazione. +4. Catalogo IdP dei nuovi moduli: sostituire le chiavi descrittive della roadmap con permessi esatti condivisi tra UI e backend. +5. Modalità sicura di claim e merge ownership-verified, che non esiste nel semplice collegamento account corrente. +6. Conferma operativa delle fasi 5–6: backup e rimozione delle tabelle legacy restano separati da questa revisione documentale. diff --git a/IdentityProvider_Design.md b/IdentityProvider_Design.md new file mode 100644 index 00000000..64a18e67 --- /dev/null +++ b/IdentityProvider_Design.md @@ -0,0 +1,87 @@ +# Design — Identity Provider PoliNetwork + +**Documenti collegati:** [`PRD_Admin_Dashboard_PoliNetwork.md`](./PRD_Admin_Dashboard_PoliNetwork.md) §2.6 · [`PRD_Classificazione_Feature.md`](./PRD_Classificazione_Feature.md) #0 · [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md) · [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md) +**Versione:** 2.0 +**Data:** 12 ottobre 2026 +**Base tecnica implementata:** repository autonoma `auth`, Better Auth e `@better-auth/oauth-provider` 1.7.2, Drizzle/Postgres. + +## 0. Baseline e fonti + +Questa revisione descrive il codice integrato, non certifica il deploy o il completamento della migrazione operativa. SHA e fonti per repository sono nel PRD §20. Per il contratto interservizio si segue la [RFC IdP–Telegram v3](https://github.com/PoliNetworkOrg/auth/blob/7ae7521f895c342fe33ee77264f4f9ef1531055c/docs/idp-telegram-integration-rfc-v3.md), con le successive modifiche Azure descritte nel codice dei consumer. + +La proposta precedente di estendere il Better Auth del backend mantenendo OTP email, link Telegram a codice e `capability_grant` è superata. L'IdP è autonomo; admin è un consumer OIDC. Il backend conserva temporaneamente l'autenticazione e i dati legacy per la transizione. + +## 1. Identità e collegamenti implementati + +L'identità locale è `user.id`; gli account esterni sono identificati per issuer/subject verificati. Email uguali non fanno un merge automatico di due persone. I collegamenti possono avere indirizzi diversi. + +- **Google e PoliNetwork Entra:** login e registrazione. Entra è vincolato al tenant PoliNetwork e richiede `acct: 0`: gli account guest sono rifiutati. +- **Passkey:** registrabili dopo il login e utilizzabili per gli accessi successivi. Il server impedisce di scollegare l'ultimo account Google/Entra anche se rimangono passkey o account di verifica. +- **Telegram:** collegamento opzionale nell'account tramite OIDC ufficiale, PKCE e ID token RS256; non è un login diretto. L'ID Telegram numerico è salvato in `identity_evidence` con unicità; lo username è metadato aggiornabile per visualizzazione, non una chiave di autorizzazione. Collegare Telegram non assegna alcun ruolo. +- **Student:** prova del possesso di un indirizzo `@mail.polimi.it` tramite codice email. Socio, Student, Direttivo e Telegram collegato sono fatti indipendenti. +- **Socio/Direttivo:** oggi derivano dai gruppi configurati del tenant Entra PoliNetwork; non dal pagamento della quota o da un codice di claim del registro associativo. + +L'account personale gestisce collegamenti, passkey e sessioni. La directory amministrativa IdP mostra gli stati individuali e i metodi di accesso; la gestione dei ruoli ha permessi separati. Questa directory non è l'Anagrafica Soci o il Censimento Admin/Team del PRD. + +## 2. Verifica Polimi implementata + +Sono ammessi solo indirizzi del dominio esatto `mail.polimi.it`, non `polimi.it` e non un'allowlist di domini amministrabile. + +Il codice ha sei cifre, TTL di 10 minuti, massimo cinque tentativi e cooldown di reinvio di 60 secondi su utente e destinatario. Il database salva l'HMAC del codice. La verifica riuscita crea il collegamento `polimi-email` e l'evidenza Student con durata `STUDENT_VERIFICATION_TTL_DAYS`. L'invio usa Microsoft Graph e `AZURE_EMAIL_SENDER`. + +Questa verifica attesta il possesso della casella, non corso/anno, tesseramento, quota o consenso privacy. L'estensione ad altri domini resta una decisione di prodotto. + +## 3. Socio: stato corrente e collegamento futuro al registro + +Lo stato IdP `socio` deriva dalla membership Entra del gruppo Soci. Non prova autonomamente che la quota annuale sia pagata e non fornisce numero associativo, scadenze o storico. + +Resta necessario il registro indipendente del PRD §3.1, accessibile anche senza account Microsoft 365. La proposta di claim via codice resta **da progettare e implementare**, insieme alla riconciliazione con Entra: + +1. Un operatore approva l'iscrizione e genera il numero associativo. +2. Il sistema invia all'email di iscrizione un codice di claim con hash, TTL e limite tentativi. +3. La persona accede all'IdP con un metodo disponibile e dimostra il possesso del codice. +4. Il registro viene collegato al suo `user.id`, senza richiedere email PoliNetwork o Telegram. +5. L'accesso alla propria scheda, ai rinnovi e ai consensi è limitato al proprio record. + +Invio automatico o manuale, TTL, re-invio a un'altra email e conflitti tra claim/Entra sono decisioni aperte. `membership_claim` e i relativi endpoint non esistono oggi. Il claim di un registro non deve essere confuso con l'ereditarietà del ruolo gestito Socio. + +## 4. RBAC implementato e residuo + +L'IdP salva ruoli, permessi, assegnazioni utente→ruolo, ereditarietà dei ruoli e implicazioni dei permessi. Non implementa una tabella di grant individuali con `level`, `scopeType` e `scopeValue`. + +I ruoli gestiti sono `master-admin`, `socio`, `direttivo`, `student`. Master Admin è un wildcard derivato dalla configurazione di bootstrap/gruppo Entra; Socio e Direttivo sono verificati tramite Graph, con cache di autorizzazione di 60 secondi e diniego se la verifica fallisce. I ruoli personalizzati sono assegnabili con `idp:roles:assign`, separato da `idp:roles:write`. La delega non può eccedere i permessi correnti dell'operatore. Le mutazioni RBAC sono registrate atomicamente in `rbac_audit_event`, append-only. + +Admin richiede `admin:access` e i permessi puntuali dei moduli; il backend li verifica indipendentemente. I ruoli Telegram legacy non vengono mappati automaticamente, e il veto globale `creator` non è riprodotto. Il catalogo iniziale non assegna i permessi Telegram ai ruoli ordinari: le assegnazioni vanno approvate e riconciliate. + +**Residuo:** scope corso/team verificato sui record anche dal backend; permessi per Anagrafica Soci, rinnovi, consensi e onboarding. Il modello globale attuale non può garantire la vista Capo Admin limitata al proprio ambito. + +## 5. OIDC e comunicazione fra servizi + +- **polinet.cc:** login OIDC implementato, consumer autonomo; non è necessario condividere cookie. +- **admin:** Authorization Code con PKCE/state/nonce; verifica degli ID token, token mantenuti nelle sessioni Redis del BFF e bearer token verso backend. Il browser riceve dati utente e permessi, non i token. Refresh coordinato per sessione e logout sono parte del contratto BFF. +- **web:** token di servizio client credentials con `backend:public:read` per le letture server→backend. Non è ancora un login OIDC personale degli studenti sul sito; alcune ricerche gruppi restano anonime durante la transizione. +- **telegram:** token di servizio e attori Telegram isolati (`X-PN-Actor`); modalità shadow/enforce e outbox audit. Il bot non conserva refresh token personali degli admin. +- **backend:** verifica token/scope/tipo attore e permessi correnti tramite snapshot IdP, aggiornato da notifiche e polling; conserva ultimo snapshot/chiavi validi per disponibilità limitata. + +Le applicazioni e le risorse OAuth sono amministrate nell'IdP, con client di servizio e interattivi, rotazione chiavi/segreti e gestione degli scope. Scope del client e permesso della persona sono controlli distinti. + +## 6. Migrazione ancora da verificare operativamente + +Le fasi di implementazione 0–4 della RFC hanno codice nei consumer. Non si assume concluso il provisioning o il cutover perché i commit sono integrati: + +- riconciliare persone/ruoli legacy e collegamenti Telegram verificati nell'IdP; +- provisionare risorse, client e chiavi separate, e verificare i flussi nell'ambiente di destinazione; +- confrontare shadow/enforce del bot, poi effettuare il cutover coordinato; +- chiudere il percorso anonimo (`LEGACY_ANONYMOUS=deny`) e rimuovere input/API legacy secondo fase 5; +- eseguire backup, cleanup dei dati/auth legacy e hardening di rete secondo fase 6. + +Il backend conserva ancora i grant legacy e `tg_grants_v2`; un grant attivo in una delle due fonti conta durante la transizione. Cleanup e migrazioni distruttive non sono attività di questa PR documentale. + +## 7. Decisioni residue + +1. Fonte e riconciliazione dello status associativo pagante rispetto al ruolo Socio Entra. +2. Modalità di claim del registro, invio/re-invio dei codici e gestione dei conflitti di identità. +3. Scope corso/team e catalogo dei nuovi moduli, con enforcement nel backend. +4. Primo flusso del sito pubblico che richiede login personale. +5. Eventuale estensione dei domini di verifica studente. +6. Evidenze operative del completamento del cutover e cleanup, senza dedurle dagli SHA applicativi. diff --git a/PRD_Admin_Dashboard_PoliNetwork.md b/PRD_Admin_Dashboard_PoliNetwork.md new file mode 100644 index 00000000..5d792f3b --- /dev/null +++ b/PRD_Admin_Dashboard_PoliNetwork.md @@ -0,0 +1,379 @@ +# PRD — Admin Dashboard PoliNetwork + +**Documento di prodotto per il Team IT** +**Versione:** 2.3 +**Data:** 14 ottobre 2026 +**Stato:** Bozza per revisione, allineata al codice integrato al 12 ottobre 2026 + +**Documenti di dettaglio:** [`PRD_Classificazione_Feature.md`](./PRD_Classificazione_Feature.md) (priorità e dipendenze) · [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md) (capacità e ruoli) · [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) (Identity Provider) · [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md) (tabelle e contratti) · [`Persone_Volontari_Soci.md`](./Persone_Volontari_Soci.md) (persone, censimento volontari, Libro Soci e viste per ruolo) + +## 0. Scopo + +Documento di riferimento per le funzionalità della Admin Dashboard di PoliNetwork. Le funzionalità interne (anagrafica soci, censimento admin/team, ruoli, governance, team) hanno priorità sulle aree pensate per soggetti esterni (associazioni partner, aziende, alloggi). + +### 0.1 Stato verificato e confini della verifica + +La baseline è il codice dei branch remoti predefiniti dopo `git fetch --all --prune`, non i checkout locali o i branch di feature. «Implementato» significa presente nel codice integrato: questa revisione non certifica deploy, configurazione dei client, migrazione dei dati o collaudo in produzione. Le fonti e gli SHA verificati sono in §20. + +- **Implementato:** IdP autonomo in `auth`, collegamenti di identità, eRBACo globale, directory utenti IdP, amministrazione applicazioni OAuth/OIDC, login OIDC/BFF della dashboard e controlli per procedura nel backend. +- **Implementato con transizione ancora prevista:** token di servizio per sito e bot, snapshot di autorizzazione, audit degli attori IdP e grant v2. Nel codice restano compatibilità anonima configurabile, modalità shadow/enforce del bot e dati/API legacy; non si assume concluso il cutover delle fasi 5–6 della RFC. +- **Implementato in admin:** overview con conteggi e andamento della creazione degli utenti Telegram, gestione Telegram/WhatsApp, contenuti (FAQ, guide, progetti, associazioni, etichette), segnalazioni link, directory Microsoft 365 e membership dei gruppi con permessi separati. +- **Roadmap residua:** Anagrafica Soci indipendente da Entra, Censimento Admin/Team, rinnovi, firma privacy e consensi versionati, scope corso/team, area Capo Admin e onboarding associativo; inoltre ricerca globale e audit amministrativo unificato. + +La directory Microsoft 365 e il ruolo IdP `socio` derivato da Entra **non costituiscono** il registro associativo paganti/storico richiesto da §3.1. Il sito usa un token di servizio verso il backend: questo non equivale a un login personale degli studenti sul sito. + +--- + +## 1. Persone, ruoli e gerarchie + +### 1.1 Ruoli Telegram + +I ruoli Telegram legacy (`admin`, `hr`, `president`, `direttivo`, `creator`, `owner` e `web` nell'inventario della RFC) servono alla riconciliazione della migrazione; non sono più il contratto di accesso della dashboard migrata. + +L'accesso richiede `admin:access`; ogni lettura riservata o mutazione richiede il proprio permesso IdP, verificato anche dal backend. Telegram non è obbligatorio per accedere. I vecchi titolari vanno riconciliati con ruoli e permessi IdP, senza assegnazioni automatiche basate sul nome legacy. + +L'IdP implementa i ruoli gestiti `master-admin`, `socio`, `direttivo`, `student`, oltre a ruoli personalizzati. `master-admin` ha tutti i permessi; gli altri ruoli hanno i permessi configurati. Il vecchio veto globale `creator` è sostituito dall'assenza dei permessi pertinenti, non da un'esclusione incondizionata. Dettagli in [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md). + +### 1.2 Socio, Admin e membro di team — categorie indipendenti + +Socio, Admin e membro di un team interno (IT, Design & Social, International, HR, Events & Partnerships, ...) sono **tre categorie indipendenti**, non una gerarchia annidata: + +- **Socio**: chi risulta iscritto e in regola con la quota associativa (Anagrafica Soci, §3). +- **Admin**: chi ha un ruolo operativo/organizzativo su PoliNetwork. +- **Membro di team**: chi fa parte di un team interno (§7). + +Un admin può essere socio o no; un socio può essere admin o no; un membro di un team può essere admin, socio, entrambi o nessuno dei due: le tre categorie si intersecano liberamente e nessuna implica le altre. **Il Socio resta la categoria più rilevante per l'associazione**: è chi la costituisce formalmente. Admin e membro di team sono categorie organizzative/operative e non sostituiscono né presuppongono lo status di socio. + +- Un **Socio** è un record dell'Anagrafica Soci (§3), indipendente dall'essere admin o membro di team. +- Un **Admin** è una persona con un ruolo organizzativo (Admin, Capo Admin, Direttivo, Presidente, ...) tracciato dal Censimento Admin/Team (§3), con un'identità Telegram collegata se opera sui canali PoliNetwork. Non deve necessariamente essere anche Socio. +- Un **Capo Admin** è un Admin con uno scope aggiuntivo (es. corso di studi) e visibilità limitata al proprio ambito. + +### 1.3 Gerarchia organizzativa + +``` +Owner / Presidente / Direttivo — responsabilità organizzative (permessi IdP espliciti) + │ + Capo Admin (per corso/ambito) — visibilità e azioni limitate al proprio ambito + │ + Admin — operativo +``` + +Lo status di Socio e l'appartenenza a un team interno sono assi indipendenti (§1.2): possono coesistere con qualunque punto della gerarchia sopra, o con nessuno. + +Il Direttivo assegna e revoca il ruolo di Capo Admin (periodicità da definire). L'admin "semplice" deve ottenere accesso alla dashboard, con permessi determinati dal proprio scope. Il modello attuale IdP è globale: vanno progettati lo scope corso/team e la sua verifica nel backend prima di attivare la vista Capo Admin (§19). Un ruolo personalizzato da solo non limita i record visibili. + +Una persona admin appartiene a **un solo corso di studi** come dato anagrafico personale, ma può essere admin/Capo Admin con **scope su gruppi di corsi diversi** contemporaneamente: il corso di appartenenza personale e lo scope di responsabilità sono due campi distinti. + +--- + +## 2. Fondamenta + +### 2.1 Autorizzazione per capacità (RBAC granulare) + +**Implementato:** ruoli e permessi globali in eRBACo (`auth`), con ereditarietà dei ruoli e implicazioni dei permessi. La dashboard legge `me.access` tramite BFF, filtra navigazione/azioni e applica middleware; il backend verifica token, scope del client e permesso della persona per procedura. Le assegnazioni e l'amministrazione RBAC vivono nell'IdP, con delega limitata ai permessi posseduti. + +**Da realizzare:** autorizzazione sui nuovi registri soci/censimento, scoping corso/team e permessi dei flussi futuri. Il catalogo implementato usa chiavi come `tg:users:read` e `web:content:write`, non la proposta `capability_grant` con chiave e livello separati. Le azioni ad alto impatto devono mostrare il permesso richiesto e richiedere conferma esplicita. Catalogo attuale e requisiti futuri sono distinti in [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md). + +### 2.2 Audit amministrativo unificato + +**Stato parziale:** l’IdP ha un audit RBAC append-only con prima/dopo; il backend registra attori IdP nelle azioni di moderazione, nei grant e nelle scritture dei contenuti. Questo non copre ancora un unico storico amministrativo per tutti i moduli. + +**Requisito residuo:** ogni mutazione amministrativa deve produrre una voce di audit con: attore, ruolo al momento dell'azione, timestamp, oggetto modificato, valori prima/dopo, motivo (obbligatorio per azioni sensibili), esito. Precondizione per esporre dati personali dell'Anagrafica/Censimento a più ruoli. + +#### 2.2.1 Registro consultabile delle modifiche a ruoli, permessi e persone + +**Da realizzare — priorità High, interna:** rendere lo storico leggibile da interfaccia anche a chi non è del Team IT, senza dover consultare database o log tecnici. Viene prima delle aree associazioni partner, eventi esterni, aziende e alloggi (§12–§15). La registrazione RBAC già presente è una base da riusare; non equivale alla disponibilità di questa vista. + +- Vista cronologica paginata, con ricerca e filtri per attore, persona/oggetto interessato, tipo di azione e intervallo di date; consultabile nella dashboard dai ruoli autorizzati alla lettura dell'audit. +- Copertura iniziale: assegnazione e revoca di ruoli alle persone, modifica dei permessi di un ruolo, modifica delle definizioni/implicazioni dei permessi e rimozione di account o record amministrativi. Le modifiche fatte nell'IdP devono essere consultabili anche se non sono state avviate dalla dashboard. +- Ogni riga deve spiegare **chi ha fatto cosa, a chi/su cosa e quando**, in linguaggio comprensibile: «Gabriele ha tolto il ruolo HR a Lucia», «Gabriele ha rimosso il permesso di gestione gruppi dal ruolo Moderatori». Il dettaglio mostra prima/dopo, motivo quando richiesto ed esito, distinguendo una rimozione di ruolo dalla cancellazione di una persona. +- Sono tracciate anche le azioni di operatori delegati **non Owner/Master Admin**: ogni modifica resta attribuita al suo attore. La lettura dell'audit è un permesso distinto dalla scrittura dei ruoli, assegnabile a chi deve verificare le modifiche; il catalogo e i destinatari vanno definiti. +- Gli eventi non sono modificabili o cancellabili dall'interfaccia. La vista non deve esporre segreti o ripristinare dati personali già eliminati: usa gli identificativi e i metadati conservati dall'audit quando nomi/dettagli non sono più disponibili. + +**Criteri di accettazione:** un revisore autorizzato può trovare la revoca di un ruolo a una persona, identificare l'operatore anche se delegato, confrontare i permessi prima/dopo una modifica e ricostruire una rimozione con i dati ancora disponibili, senza accesso tecnico al database. Un revisore privo dei permessi di scrittura può consultare lo storico senza poter modificare i ruoli. + +### 2.3 Ricerca globale + +**Da realizzare:** command palette che cerca trasversalmente soci, utenti Telegram, gruppi, membri Azure, FAQ e contenuti. Le ricerche presenti sono di sezione (inclusa ricerca utenti Telegram per ID), non una ricerca globale. + +### 2.4 Home come centro operativo + +**Implementato:** nuova shell e overview con conteggi di gruppi/utenti Telegram, segnalazioni aperte e grant attivi secondo i permessi; grafico basato sulle date di creazione dei record utenti Telegram (non membri attivi o audience dei gruppi). + +**Da realizzare:** indicatori su soci in scadenza, account non riconciliati, grant in scadenza, contenuti da pubblicare e attività recenti, con apertura della lista filtrata corrispondente. + +### 2.5 Gestione dei consensi e firma della privacy policy in dashboard + +**Da realizzare:** raccolta associativa dei consensi e firma del testo versionato; il consenso OAuth delle applicazioni IdP è un flusso distinto. + +Oggi le autorizzazioni privacy vengono raccolte tramite vari form esterni inviati caso per caso: le domande possono cambiare o diventare obsolete nel tempo, e la perdita di un form comporta la perdita della possibilità di dimostrare/recuperare il consenso raccolto con quella versione. + +- La firma della privacy policy (e di altre informative) avviene **dentro il flusso della dashboard** (iscrizione socio, §3.1; onboarding/censimento admin, §8), non più solo tramite form esterni scollegati. +- Ogni consenso è legato a una **versione specifica del testo firmato**, con data e revoca tracciabili: un cambiamento futuro del testo non invalida né sovrascrive lo storico dei consensi già raccolti. +- La firma passa per l'audit unificato (§2.2). +- Richiede l'Identity Provider (§2.6) per autenticare con certezza chi sta firmando. + +### 2.6 Identity Provider PoliNetwork + +**Implementato in una repository autonoma `auth`**, con Better Auth e `@better-auth/oauth-provider` 1.7.2, Postgres/Drizzle; non è un'estensione del server Better Auth legacy del backend. + +- Login/registrazione via Google o PoliNetwork Entra, passkey aggiungibili dopo il login; non è previsto il vecchio login email OTP. Entra ammette membri del tenant, respingendo guest e token senza `acct: 0`. +- Telegram si collega nell'account IdP tramite OIDC ufficiale, non tramite il codice legacy del bot; il collegamento non assegna ruoli. +- Verifica studente via codice email solo per `@mail.polimi.it`; `@polimi.it` non è oggi ammesso. Socio e Direttivo sono evidenze da gruppi Entra PoliNetwork, indipendenti da Telegram e Student. +- OIDC già integrato in `polinet.cc` e nella dashboard admin. `web` usa OAuth client credentials per leggere dal backend: il login personale sul sito resta un'integrazione futura. +- Directory utenti, ruoli/permessi, applicazioni (client di servizio e interattivi), chiavi e risorse sono amministrati nell'IdP. L'account personale gestisce identità collegate, passkey e sessioni. + +**Da realizzare:** collegamento al registro associativo indipendente da Entra, flussi di censimento/onboarding e firma documenti, e futuro accesso esterno multi-tenant. La base di autenticazione esiste; non implica che questi flussi siano già disponibili. Design e contratti correnti in [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) e [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md). + +--- + +## 3. Anagrafica Soci e Censimento Admin/Team + +**Da realizzare:** i due registri descritti qui non sono le directory utenti IdP o Microsoft 365. + +Due registri distinti, coerenti con l'indipendenza delle categorie di §1.2: + +- **Anagrafica Soci**: registro dei **soci attivi e paganti**, con **storico** dei soci passati. Per un socio l'azione ricorrente è il **rinnovo della quota associativa** (§6), non una rilevazione periodica di interesse. +- **Censimento Admin/Team**: rilevazione rivolta ad **admin e membri dei team**, il cui scopo è **rinnovare l'interesse/la disponibilità** a continuare il proprio ruolo — non una quota. Si integra con l'Onboarding (§8). + +Una persona può comparire in entrambi, in uno solo, o in nessuno dei due. + +### 3.1 Anagrafica Soci — campi + +| Blocco | Campi | Note | +|---|---|---| +| Identità | nome, cognome, email, telefono (opzionale) | | +| Identificativo | ID interno, numero associativo univoco | proprietà di questo registro (database PoliNetwork); Azure lo referenzia se presente | +| Stato | attivo pagante, sospeso, scaduto, ex socio | | +| Iscrizione | data ingresso, anno associativo (iscrizione annuale), data scadenza, stato rinnovo | | +| Profilo associativo | corso di studi, anno di corso, sede, competenze/interessi (opzionali) | | +| Consensi | consenso privacy policy, fonte, data, versione firmata, revoca | firma diretta in dashboard, §2.5 | + +Alcuni campi sono obbligatori, altri opzionali (l'elenco puntuale, incluso se serva il codice fiscale, resta da definire). HR può leggere l'Anagrafica Soci; il dettaglio dei permessi granulari campo per campo resta da definire. + +Vincoli: numero associativo univoco; storicizzazione degli stati (non sovrascrittura: i soci scaduti/ex soci restano nello storico); nessuna cancellazione bulk senza anteprima e conferma; ogni lettura/scrittura sensibile passa per l'audit (§2.2). + +Flusso: richiesta/iscrizione → verifica dati → deduplica → firma privacy policy → approvazione → numero socio → invio del codice di claim all'email di iscrizione (il socio lo usa per collegare il tesseramento alla propria identità, §2.6) → collegamento opzionale a Telegram/Azure → rinnovo annuale della quota (§6) → storico. + +### 3.2 Censimento Admin/Team — campi + +Rivolto a chi ha un ruolo organizzativo (Admin, Capo Admin, Direttivo, membro di team), indipendentemente dall'essere anche Socio. + +| Blocco | Campi | Note | +|---|---|---| +| Identità | nome, cognome, email, telefono | | +| Relazioni | ruolo organizzativo (§1.3), team (§7), Capo Admin/responsabile di riferimento | | +| Integrazioni | Telegram ID/username, Azure user ID, ultimo sync | collega senza duplicare | +| Rinnovo interesse | data ultima conferma, prossima scadenza conferma, esito | sostituisce il concetto di "quota" per questa popolazione | +| Consensi | consenso privacy policy, fonte, data, versione firmata | §2.5 | + +Flusso: rilevazione periodica (periodicità da definire, es. legata all'anno accademico) → conferma interesse/disponibilità tramite il flusso di Onboarding/Censimento in dashboard (§8) → aggiornamento stato → storico. + +### 3.3 Permessi + +**Requisito futuro, chiavi da definire nel catalogo IdP:** creazione/modifica per Direttivo/Owner/President inizialmente; lettura assegnabile anche a HR in sola lettura. Un operatore può creare un socio o un record del Censimento Admin/Team senza dover prima creare un account Azure. + +--- + +## 4. Governance e Direttivo + +Non verrà realizzata come area dedicata separata. La composizione del Direttivo, il ruolo organizzativo e l'incarico sono coperti dal Censimento Admin/Team (§3.2) e dalla gerarchia ruoli (§1.3). + +--- + +## 5. Dashboard Admin e Capo Admin + +**Da realizzare:** vista organizzativa con scope sui record. + +- Vista Capo Admin: elenco degli admin del proprio corso di studi con nome, cognome, anno di corso, data di ingresso, flag rappresentante, altre associazioni di appartenenza, telefono, username/contatto Telegram, link rapido WhatsApp/Telegram. +- Filtri: nome/cognome, anno, rappresentanza, altre associazioni. +- Sezione link ai gruppi Telegram di competenza del Capo Admin. +- Permessi: il Capo Admin vede **solo** i dati e i gruppi del proprio ambito (scoping, §2.1). + +Il modello dati separa il corso personale (Censimento Admin/Team, §3.2) dallo/dagli scope assegnati come Capo Admin (§1.3). + +--- + +## 6. Gestione Soci e Rinnovi + +**Da realizzare:** flusso associativo di ricevute e rinnovi. + +Il pagamento della quota resta un **bonifico bancario fuori dashboard**; il flusso di verifica del pagamento è in-dashboard: + +- Il socio può caricare la ricevuta del bonifico in dashboard; il caricamento avvia una richiesta di approvazione. +- Il Direttivo/ruolo autorizzato approva la ricevuta caricata, oppure segna il rinnovo come effettuato manualmente se la ricevuta non viene caricata. +- **Ricevuta automatizzata**: quando un rinnovo viene approvato, la dashboard genera e invia automaticamente la ricevuta al socio. +- Per i rinnovi del Direttivo verso l'associazione stessa, la ricevuta viene generata/automatizzata allo stesso modo; quando è richiesta una firma, il flusso notifica il Presidente, che deve firmarla. +- Reminder via email poco prima della scadenza. +- Vista Direttivo sullo stato dei soci: da verificare, ricevuta caricata in attesa di approvazione, pagamento effettuato, pagamento non effettuato. +- Azioni: "Approva ricevuta"/"Segna come pagato" (aggiorna stato + genera ricevuta + email di conferma), "Invia reminder". +- Storico delle azioni (chi ha approvato/segnato pagato, reminder inviati, ricevute generate), tracciato nell'audit (§2.2). +- Resta da decidere se il permesso di segnare come pagato/approvare ricevute sia riservato al solo Direttivo o esteso a un futuro ruolo Finance dedicato. + +--- + +## 7. Aree Team interni + +**Da realizzare:** struttura dei team e separazione degli accessi. + +Ogni team (IT, Design & Social, International, HR, Events & Partnerships, ...) è un'entità del Censimento Admin/Team (§3.2, blocco "Relazioni"), con pagina/area indipendente e permesso dedicato (§2.1). Obiettivo iniziale: struttura, ruoli e separazione degli accessi, non le funzionalità specifiche di ciascun team. + +--- + +## 8. Onboarding e Censimento Admin — flusso self-service in dashboard + +**Da realizzare:** il login IdP esiste, il flusso organizzativo seguente resta un requisito. + +Sia il censimento di nuovi candidati admin, sia il censimento periodico di admin già attivi, avvengono tramite un **flusso automatizzato della dashboard**, attivabile tramite un link o una mail inviata alla persona — non più tramite Telegram e scambio di messaggi manuale. + +- Autenticazione preventiva obbligatoria sull'Identity Provider PoliNetwork (§2.6) prima di poter proseguire nel flusso — identifica la persona e determina il tipo di candidatura/censimento applicabile. +- Una volta autenticato, l'utente accede a un'area personale dove può: firmare i documenti richiesti (§2.5), consultare le proprie statistiche, vedere il proprio Capo Admin di riferimento. +- Permessi differenziati per ruolo: ogni membro di PoliNetwork ha un livello di accesso diverso in base a cosa fa/è, ma tutti hanno accesso a qualcosa nella propria area. +- Richieste che risalgono la gerarchia: un admin può presentare dalla propria area la richiesta di diventare admin di un determinato gruppo, e la richiesta arriva al proprio Capo Admin di riferimento per l'approvazione. + +Ambito: link/mail di attivazione → autenticazione (Identity Provider) → candidatura o conferma censimento periodico → firma documenti → colloquio/approvazione (per i nuovi) → creazione/aggiornamento profilo (Censimento Admin/Team) → assegnazione corso/ruoli/team → passaggi di ingresso operativo. Il dettaglio del processo (candidatura, colloquio, criteri) resta da definire con HR e Team IT. + +--- + +## 9. Email di compleanno + +**Da realizzare.** + +Riguarda solo i soci. Dipende dall'Anagrafica Soci (data di nascita) e da un motore email. + +--- + +## 10. FAQ pubbliche + +**Implementato:** categorie e CRUD FAQ bilingue IT/EN, ricerca di sezione e modifica inline, con `web:content:write` sulle mutazioni. + +**Da realizzare:** riordino drag&drop e workflow bozza/pubblicata; lo schema backend attuale non ha un campo stato di pubblicazione o ordinamento delle FAQ. + +--- + +## 11. Miglioramenti alle aree esistenti + +- **Telegram grants**: storico dei grant terminati/interrotti; reminder sulle scadenze imminenti. +- **Telegram groups**: creazione/import di gruppi dalla dashboard, indicatori di salute (gruppo senza owner, invito rotto), statistiche. +- **Microsoft 365/Azure — implementato:** directory in sola lettura (`azure:members:read`), creazione tramite workflow dedicato (`azure:members:create`), lettura gruppi (`azure:groups:read`) e aggiunta/rimozione di utenti esistenti dai gruppi (`azure:groups:write`; il picker richiede anche lettura directory). Nessuna modifica generica di utenti/gruppi. **Residuo:** gestione licenze dalla UI e access review periodico sui gruppi. +- **Guide**: workflow bozza/pubblicazione, anteprima PDF, storico versioni invece di sola sostituzione. + +--- + +## 12. Area Associazioni Partner + +**Da realizzare:** accesso esterno e workflow di approvazione. Il CRUD interno delle pagine associazioni è già disponibile e non equivale a questa area. + +- Accesso e gestione account per decine di associazioni: si può creare l'account a **uno o più referenti** della stessa associazione fin dal MVP, e i referenti possono **nominare un successore trasferendo l'ownership** del proprio account. +- Richieste di pubblicazione nei gruppi Telegram: le associazioni presentano la richiesta dalla propria pagina/area e PoliNetwork approva. Le richieste possono riguardare più gruppi contemporaneamente; il flusso dovrà mostrare l'elenco completo dei gruppi tra cui scegliere. Resta da chiarire se serva anche l'integrazione WhatsApp. +- Gestione della pagina pubblica dell'associazione con flusso di richiesta/approvazione, al posto dell'attuale CRUD diretto di PoliNetwork. +- Eventuale sistema a crediti: logica ancora non definita. +- Eventi delle associazioni / "PoliTamTam" (§13). + +Richiede l'Identity Provider PoliNetwork (§2.6) per l'accesso esterno multi-tenant. + +--- + +## 13. Eventi delle associazioni — PoliTamTam + +**Da realizzare.** + +Le associazioni partner inseriscono gli eventi dalla propria pagina/area; PoliNetwork può inoltre aggiungere propri eventi, o eventualmente eventi per conto di altre associazioni. Ogni evento richiede approvazione prima di comparire pubblicamente — nessuna pubblicazione diretta. + +--- + +## 14. Area Aziende + +Da progettare in dettaglio prima dello sviluppo. + +--- + +## 15. Area proprietari di casa / Bacheca casa e coinquilini + +**Da realizzare.** + +Riguarda esclusivamente soggetti esterni (proprietari, cercatori di stanza). + +--- + +## 16. Newsletter + +**Da realizzare.** + +Dipende dall'Anagrafica Soci (segmentazione destinatari) ed eventualmente dagli eventi (§13) come fonte di contenuti. + +--- + +## 17. Vincoli + +- Nessuna azione distruttiva su più righe senza anteprima e conferma esplicita. +- Minimizzazione dei dati: ogni campo su soci/admin ha uno scopo dichiarato, un responsabile e un'ipotesi di retention; dati sensibili (es. codice fiscale) solo se realmente necessari. +- Audit prima di esporre dati personali a più ruoli. +- **Requisito residuo:** interfaccia amministrativa bilingue IT/EN con preferenza impostabile per singolo utente. I contenuti FAQ/guide bilingui non equivalgono a una UI amministrativa localizzata, oggi prevalentemente in inglese. + +--- + +## 18. Fuori scope + +- Non ridefinisce lo statuto, gli organi sociali o le regole di voto dell'associazione. +- Non decide se e come integrare un gateway di pagamento (Stripe, altro): il pagamento resta un bonifico bancario eseguito fuori dashboard. È invece in scope la verifica del pagamento in dashboard (upload ricevuta, approvazione, ricevuta automatizzata, §6). +- Non propone una contabilità completa (fatture, bilanci). + +--- + +## 19. Decisioni aperte + +**§1 — Ruoli e gerarchie** +1. Come estendere il RBAC globale IdP con scope corso/team e controlli sui record nel backend per Capo Admin e Team Lead? La precedente proposta `capability_grant.scopeType` non è implementata. +2. Con quale periodicità viene rinnovato il ruolo di Capo Admin (es. legato all'anno accademico)? + +**§2 — Fondamenta** +3. **Decisione architetturale attuata:** IdP autonomo, dashboard OIDC/BFF. Restano da verificare operativamente riconciliazione e cutover coordinato (`LEGACY_ANONYMOUS=deny`, bot enforce), poi rimozione di API/tabelle legacy secondo RFC fasi 5–6. +4. Il consenso privacy raccolto in dashboard (§2.5) sostituisce integralmente i form esterni oggi in uso, o convive con essi durante una fase di transizione? + +**§3 — Anagrafica Soci e Censimento Admin/Team** +5. Quali dati sono realmente necessari per il tesseramento (es. il codice fiscale è richiesto o va escluso)? +6. Un ruolo HR read-only può leggere tutti i campi del socio, o alcuni campi (es. dati di contatto personali) devono essere mascherati anche per HR? +7. Con quale periodicità si svolge il Censimento Admin/Team — es. una volta per anno accademico, o legato a un altro evento? + +**§5 — Dashboard Admin e Capo Admin** +8. Come si definisce "corso di studi" nel sistema (elenco chiuso dei corsi del Politecnico, testo libero, altro)? + +**§6 — Gestione Soci e Rinnovi** +9. Chi ha il permesso di segnare come pagato/approvare ricevute: solo Direttivo, o anche un ruolo Finance dedicato non ancora esistente? + +**§8 — Onboarding e Censimento Admin** +10. Il processo di candidatura/colloquio va definito nel dettaglio con HR e Team IT (criteri, colloquio, tempistiche). + +**§12 — Area Associazioni Partner** +11. Le richieste di pubblicazione riguardano solo Telegram, o resta necessaria anche l'integrazione WhatsApp? +12. Il sistema a crediti per le pubblicazioni resta previsto, e con quale logica di ricarica/consumo? + +**Identity Provider** (da [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) §7) +13. Il codice di claim del tesseramento viene generato in automatico all'approvazione dell'iscrizione, o con un'azione manuale di chi approva? +14. Se l'email di iscrizione è diversa da quella dell'identità PoliNetwork, è permesso re-inviare il codice a un nuovo indirizzo, e chi lo autorizza? +15. Chi approva la riconciliazione dei titolari legacy e assegna i ruoli personalizzati IdP prima del cutover? Le assegnazioni usano `user_role`, non `capability_grant`. +16. **Risolta nel codice:** provider OAuth/OIDC 1.7.2, registrazione applicazioni e client di servizio implementati. Restano provisioning e collaudo dei consumer nell’ambiente di destinazione. +17. **Ordine reale aggiornato:** polinet.cc e admin hanno già il login OIDC; web usa un token di servizio. Quali funzionalità del sito richiederanno il login personale, e quando? +18. Se estendere la verifica studente oltre il solo dominio attualmente ammesso `@mail.polimi.it` (in particolare `@polimi.it`): modifica da progettare, non una configurazione già disponibile. +19. Come riconciliare lo stato `socio` attuale da Entra con il futuro registro associativo paganti/storico e il claim del tesseramento, senza confondere account Microsoft 365 e iscrizione annuale? + +--- + +## 20. Fonti della revisione (12 ottobre 2026) + +Riferimenti al codice verificato, fissati allo SHA dopo il fetch: + +- **admin** (origin/main): [27a09690](https://github.com/PoliNetworkOrg/admin/tree/27a0969052cf8e846f46642e2c74f6f24f1d3eb1). Login OIDC/BFF e controlli (`src/server/permissions.ts`); shell e overview (`src/features/dashboard/`); FAQ e Microsoft 365 (`src/features/faqs/`, `src/features/azure/`). +- **auth** (origin/main): [7ae7521f](https://github.com/PoliNetworkOrg/auth/tree/7ae7521f895c342fe33ee77264f4f9ef1531055c). Identità e RBAC (`src/db/evidence.ts`, `src/db/rbac.ts`); contratti e migrazione (`docs/idp-telegram-integration-rfc-v3.md`). Le frasi introduttive di README/RFC che descrivono caller non ancora migrati sono precedenti ai commit dei consumer: lo stato qui riportato segue il codice di ciascuno. +- **backend** (origin/main): [84da4cf7](https://github.com/PoliNetworkOrg/backend/tree/84da4cf716210aae371e2aae2de829c651222c6b). Policy (`src/idp/policies.ts`), attori/audit/grant v2, directory e membership Azure, `tg.users.getCreationHistory`; compatibilità legacy documentata nel README. +- **telegram** (origin/main): [cca89904](https://github.com/PoliNetworkOrg/telegram/tree/cca89904267103576285db31e1b646dddd7798ae). Migrazione fase 4b, token/attori, shadow/enforce e audit outbox; README e `docs/idp-phase4b-handoff.md` distinguono implementazione e attività operative. +- **web** (origin/main): [d6b801e7](https://github.com/PoliNetworkOrg/web/tree/d6b801e7a8c8b0728fe073e52a5b45af9c439b29). Fase 4a: token di servizio in `src/lib/backend.ts`; guide, contatti e segnalazioni link. Non risulta introdotto un login personale sul sito. +- **polinet.cc** (origin/main): [d8803264](https://github.com/PoliNetworkOrg/polinet.cc/tree/d88032645cb098128f4187d23840bbb85a9fcdca). Login OIDC già integrato; ultima correzione al callback configurato. Le feature su branch non integrati (alias, analytics) non sono considerate completate. +- **aule** (origin/main): [da941567](https://github.com/PoliNetworkOrg/aule/tree/da941567474cabf6f14ae469a47e044a0cfdd600). PoliAule v2, campus multipli, stanze non prenotabili e orari sconosciuti; progetto separato, senza nuovi requisiti implementati per la dashboard admin. +- **polinetwork.github.io** (origin/master): [a7195b8c](https://github.com/PoliNetworkOrg/polinetwork.github.io/tree/a7195b8cef1bae55f7f281fbf2a3341bd22244e5). Documenti associativi aggiornati e rimando al nuovo sito gruppi; nessun registro soci o flusso rinnovi applicativo. +- **skills** (origin/main): [ed5a52e0](https://github.com/PoliNetworkOrg/skills/tree/ed5a52e021320f69b07745741e38a97fadb57e1e). Cataloghi/template e miglioramenti alle slide; tooling, non funzionalità della dashboard. +- **slides-polinetwork** (origin/main): [bd3953ca](https://github.com/viganogabriele/slides-polinetwork/tree/bd3953ca19cbdf086553d37afaeb6feefc14c187). Aggiornamenti alle presentazioni; materiale di comunicazione, non prova di feature applicative. +- **manifesti:** fetch non riuscito (`Repository not found`); disponibile solo la baseline locale `ba45063` del 30 agosto 2026, documenti di architettura e integrità della pubblicazione. Nessuna conclusione sulle modifiche remote successive. + +I checkout e le modifiche locali preesistenti non sono stati aggiornati o inclusi in questa revisione. La PR #68 resta dedicata alla documentazione; non porta nel proprio branch il codice applicativo recente di main. diff --git a/PRD_Classificazione_Feature.md b/PRD_Classificazione_Feature.md new file mode 100644 index 00000000..19186193 --- /dev/null +++ b/PRD_Classificazione_Feature.md @@ -0,0 +1,53 @@ +# Classificazione feature — Admin Dashboard PoliNetwork + +**Documenti collegati:** [`PRD_Admin_Dashboard_PoliNetwork.md`](./PRD_Admin_Dashboard_PoliNetwork.md) · [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md) · [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) · [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md) +**Versione:** 1.3 +**Data:** 14 ottobre 2026 + +## 0. Come leggere questa tabella + +Una riga per ogni sezione principale del PRD (§1–§16), con #0 per l'IdP e #2a per il registro consultabile delle modifiche amministrative. #2a dettaglia una parte di #2, senza duplicarne il lavoro. Le sezioni §17–§20 (Vincoli, Fuori scope, Decisioni aperte, Fonti) sono requisiti trasversali o meta-contenuti, non feature autonome, e non sono classificate. + +- **Priorità** — Low / Medium / High. Criterio guida: gli **strumenti e i dati interni** (anagrafica soci, censimento admin/team, ruoli, governance, team interni) vengono prima delle aree **accessorie** o rivolte a **soggetti esterni** (associazioni partner, aziende, alloggi). Questo riflette esplicitamente l'ordine dato nello Scopo del PRD (§0). +- **Difficoltà** — scala numerica 1–5 (1 = banale, 5 = molto complessa). Tiene conto di: quante parti del sistema tocca, se richiede nuove infrastrutture (es. Identity Provider), se dipende da decisioni ancora aperte (§19 del PRD), se è un flusso multi-step con approvazioni. +- **Stato** — riferito al codice integrato, non al deploy. Le priorità e difficoltà delle righe parziali riguardano il lavoro residuo. Fonti nel PRD §20. +- **Dipende da** — altre righe di questa tabella che devono esistere prima (o in parallelo) perché la feature abbia senso. + +--- + +## 1. Tabella di classificazione + +| # | Feature (breve) | Sezione PRD | Priorità | Difficoltà (1-5) | Dipende da | Stato | Note | +|---|---|---|---|---|---|---|---| +| 0 | Identity Provider PoliNetwork | [§2.6 Identity Provider PoliNetwork](./PRD_Admin_Dashboard_PoliNetwork.md#26-identity-provider-polinetwork) · design dedicato: [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) · tabelle e contratti: [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md) | High | 5 | — | Parziale | IdP autonomo e consumer admin/polinet.cc implementati; web usa token di servizio. Residui: cutover/cleanup, login personale sito, collegamento registro associativo. Non estendere il Better Auth legacy. | +| 1 | Ruoli e gerarchia | [§1 Persone, ruoli e gerarchie](./PRD_Admin_Dashboard_PoliNetwork.md#1-persone-ruoli-e-gerarchie) | High | 4 | #0 | Parziale | Ruoli/permessi globali e gerarchie implementati in eRBACo. Residui: preset organizzativi approvati, scope corso/team e riconciliazione legacy. | +| 2 | Fondamenta piattaforma (resto) | [§2 Fondamenta](./PRD_Admin_Dashboard_PoliNetwork.md#2-fondamenta) | High | 4 | #0, #1 | Parziale | Controlli BFF/backend, audit RBAC e overview implementati. Residui: audit unificato, ricerca globale, consensi/firma e indicatori associativi. | +| 2a | Registro consultabile delle modifiche amministrative | [§2.2.1 Registro consultabile](./PRD_Admin_Dashboard_PoliNetwork.md#221-registro-consultabile-delle-modifiche-a-ruoli-permessi-e-persone) | High | 3 | #0, #1; riusa l'audit esistente | Da realizzare | Vista leggibile e filtrabile di modifiche a ruoli/permessi e rimozioni, con attore, target, data e prima/dopo. Include operatori delegati non Owner; lettura separata dalla scrittura. Priorità interna, prima di #12–#15; non richiede di attendere tutte le altre fondamenta di #2. | +| 3 | Anagrafica soci e censimento | [§3 Anagrafica Soci e Censimento Admin/Team](./PRD_Admin_Dashboard_PoliNetwork.md#3-anagrafica-soci-e-censimento-adminteam) | High | 4 | #0, #1, #2 | Da realizzare | Registri associativi distinti da directory IdP/Microsoft 365. Lo stato Socio da Entra non certifica pagamento o rinnovo annuale. | +| 4 | Governance/Direttivo | [§4 Governance e Direttivo](./PRD_Admin_Dashboard_PoliNetwork.md#4-governance-e-direttivo) | Low | 1 | #3 | Nessuna area separata | Coperta dai registri e ruoli futuri; non completata solo perché esiste il ruolo IdP Direttivo. | +| 5 | Vista Capo Admin | [§5 Dashboard Admin e Capo Admin](./PRD_Admin_Dashboard_PoliNetwork.md#5-dashboard-admin-e-capo-admin) | High | 3 | #1, #3 | Da realizzare | Richiede scoping sui record anche nel backend; il RBAC globale non basta. | +| 6 | Rinnovi e pagamenti soci | [§6 Gestione Soci e Rinnovi](./PRD_Admin_Dashboard_PoliNetwork.md#6-gestione-soci-e-rinnovi) | High | 4 | #3 | Da realizzare | Upload ricevuta, approvazione, generazione ricevuta e reminder richiedono il registro associativo. | +| 7 | Aree team interni | [§7 Aree Team interni](./PRD_Admin_Dashboard_PoliNetwork.md#7-aree-team-interni) | Medium | 2 | #3 | Da realizzare | Struttura team e scope dedicati; nessuna area applicativa già implementata. | +| 8 | Onboarding self-service | [§8 Onboarding e Censimento Admin](./PRD_Admin_Dashboard_PoliNetwork.md#8-onboarding-e-censimento-admin---flusso-self-service-in-dashboard) | High | 5 | #0, #2, #3 | Da realizzare | Autenticazione disponibile; firma, candidature/censimento, colloqui e assegnazione organizzativa restano da costruire. | +| 9 | Email di compleanno | [§9 Email di compleanno](./PRD_Admin_Dashboard_PoliNetwork.md#9-email-di-compleanno) | Medium | 2 | #3 | Da realizzare | Richiede data di nascita nel registro e automazione email. | +| 10 | FAQ pubbliche | [§10 FAQ pubbliche](./PRD_Admin_Dashboard_PoliNetwork.md#10-faq-pubbliche) | Low | 2 | — | Parziale | CRUD/categorie IT/EN, ricerca e inline editing implementati. Residui: riordino drag&drop e bozza/pubblicata; non segnare l’intero requisito come fatto. | +| 11 | Miglioramenti aree esistenti | [§11 Miglioramenti alle aree esistenti](./PRD_Admin_Dashboard_PoliNetwork.md#11-miglioramenti-alle-aree-esistenti) | Medium | 3 | — | Parziale | Microsoft 365 directory e membership gruppi con permessi separati implementati, più statistiche utenti Telegram. Residui: licenze/access review, salute/import gruppi, reminder grant, versioni/workflow guide. | +| 12 | Area associazioni partner | [§12 Area Associazioni Partner](./PRD_Admin_Dashboard_PoliNetwork.md#12-area-associazioni-partner) | Low | 5 | #0 (Identity Provider, accesso multi-tenant) | Da realizzare | Il CRUD interno delle pagine associazioni esiste; non equivale ad accesso esterno multi-tenant, referenti/ownership e richieste approvate. | +| 13 | Eventi PoliTamTam | [§13 Eventi delle associazioni — PoliTamTam](./PRD_Admin_Dashboard_PoliNetwork.md#13-eventi-delle-associazioni--politamtam) | Low | 3 | #12 | Da realizzare | Inserimento e approvazione eventi, dopo l’area partner. | +| 14 | Area aziende | [§14 Area Aziende](./PRD_Admin_Dashboard_PoliNetwork.md#14-area-aziende) | Low | — | — | Da progettare | Scope ancora da definire. | +| 15 | Bacheca casa | [§15 Area proprietari di casa / Bacheca casa e coinquilini](./PRD_Admin_Dashboard_PoliNetwork.md#15-area-proprietari-di-casa--bacheca-casa-e-coinquilini) | Low | 3 | — | Da realizzare | Area rivolta a soggetti esterni. | +| 16 | Newsletter | [§16 Newsletter](./PRD_Admin_Dashboard_PoliNetwork.md#16-newsletter) | Low | 2 | #3, opz. #13 | Da realizzare | Segmentazione su registro associativo e invio newsletter. | + +--- + +## 2. Lettura rapida per fase + +**Fondamenta già disponibili:** IdP e OIDC admin/polinet.cc, RBAC globale, controlli per procedura, audit RBAC e overview. Non occorre ricostruirle per iniziare i moduli interni; prima di dichiarare conclusa la migrazione serve il collaudo operativo/cutover (#0). + +**Prima fase — lavoro interno residuo (High):** riconciliazione ruoli e scoping (#1), registro consultabile delle modifiche amministrative (#2a, riusando l'audit RBAC già presente), audit/consensi (#2), registri soci/censimento (#3), poi rinnovi (#6), vista Capo Admin (#5) e onboarding (#8). #2a viene prima delle aree associazioni, eventi esterni, aziende e alloggi (#12–#15). La ricerca globale non deve essere confusa con un prerequisito tecnico già disponibile. + +**Seconda fase — consolidamento interno (Medium):** team (#7), email compleanno (#9), miglioramenti Telegram/Microsoft 365/guide (#11). + +**Terza fase — accessorie ed esterne (Low):** completamento FAQ (#10), newsletter (#16), area partner (#12), eventi (#13), aziende (#14), bacheca casa (#15). Il CRUD FAQ e il CRUD interno associazioni esistenti non completano i rispettivi workflow futuri. + +L'ordine mantiene la priorità sui dati e strumenti interni richiesta dal PRD §0. Le dipendenze in tabella indicano le basi necessarie, anche quando queste sono già implementate in parte. diff --git a/Persone_Volontari_Soci.md b/Persone_Volontari_Soci.md new file mode 100644 index 00000000..95a8c22a --- /dev/null +++ b/Persone_Volontari_Soci.md @@ -0,0 +1,518 @@ +# Persone, Volontari e Libro Soci — specifica + +**Documenti collegati:** [`PRD_Admin_Dashboard_PoliNetwork.md`](./PRD_Admin_Dashboard_PoliNetwork.md) §1.2, §3, §5, §7, §8 · [`RBAC_Ruoli_Permessi.md`](./RBAC_Ruoli_Permessi.md) · [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md) +**Versione:** 0.1 +**Data:** 14 ottobre 2026 +**Stato:** Proposta da revisionare con Direttivo, HR e Team IT. Nulla di quanto descritto qui è implementato, salvo dove indicato come «oggi». +**Repository coinvolte:** `admin` (dashboard), `backend` (dati e API), `auth` (IdP) + +Questo documento specifica la sezione **Persone** della dashboard: Libro Soci, censimento di admin e membri dei team, viste per ruolo, permessi e collegamento con gli account IdP. Raffina i §3, §5, §7 e §8 del PRD; dove i due documenti divergono, vale questo. + +--- + +## 0. In breve + +1. **La scheda della persona vive nel backend ed esiste anche senza account.** Le circa 150 persone del censimento vengono importate e gestite anche se non accederanno mai all'IdP. +2. **L'account IdP si collega alla scheda dopo, con una verifica**: Telegram verificato, invito personale o conferma di un operatore. Non si crea mai una seconda scheda e lo storico resta sulla stessa persona. +3. **Socio, Admin e Membro di team sono tre ruoli indipendenti della stessa persona.** Ognuno ha i propri dati: le informazioni chieste a un admin non sono quelle chieste a un membro del team o a un socio. +4. **L'IdP decide le capacità globali** (es. «vedere tutti gli admin»). **Il backend decide le visibilità limitate** a partire dagli incarichi registrati nella scheda: chi è Team Lead di IT vede IT, chi è Capo Admin di un ambito vede gli admin di quell'ambito. +5. **Il Libro Soci diventa un registro PoliNetwork nel backend.** Microsoft 365 resta l'account tecnico del socio, collegato al registro. + +--- + +## 1. Contesto + +### 1.1 Fonti di dati oggi + +| Fonte | Cosa contiene | Limite | +|---|---|---| +| Microsoft 365 / Entra (pagina «Members» della dashboard) | Account `@polinetwork.org` dei soci, gruppo Soci, numero socio nel campo `employeeId`. La PR admin #95 / backend #70 / auth #53 aggiunge il cambio del numero socio (`azure:members:write`). | È una directory di account, non un libro soci: niente storico, stati, annualità o quote. | +| Modulo Volontari (Microsoft Forms, anno 26/27) | Conferma o abbandono, tipologia (Group Admin / Membro Team), anagrafica, corso, sede, rappresentanza, altre associazioni, gruppi, disponibilità a Capo Admin, consensi privacy e foto/video. | Dati in un foglio esterno, una riga per risposta. Non è collegato alle persone né ai gruppi. | +| Backend `tg_users`, `tg_group_admins` | Utenti Telegram visti dal bot e admin registrati per ciascun gruppo. | Identificati dal Telegram ID, non da una persona. | +| IdP `auth` | Chi si è registrato, account collegati (Google, Entra, Telegram, email Polimi), ruoli e permessi. | Contiene solo chi si registra. Il backend riceve dallo snapshot solo chi possiede almeno un permesso della propria proiezione (RFC v3 §5.2). | + +### 1.2 Il problema + +L'IdP mostra chi si è registrato, ma il censimento riguarda persone che in gran parte non si registreranno. Se l'IdP fosse l'anagrafica, queste persone mancherebbero; se le si aggiungesse a mano come «utenti», chi si registra dopo risulterebbe una persona diversa. Serve quindi un'anagrafica propria a cui collegare l'account, quando esiste. + +--- + +## 2. Principi + +### 2.1 Persona e account sono distinti + +- **Persona** (backend): chi è, come contattarla, quali ruoli ha in PoliNetwork, con lo storico. Esiste dal momento dell'import o dell'inserimento. +- **Account IdP** (auth): come la persona si autentica e quali identità esterne possiede. È facoltativo. +- Una persona ha **al massimo un account IdP collegato**, e un account è collegato **al massimo a una persona** (vincolo univoco su entrambi i lati). + +### 2.2 Fonte di verità per ciascun dato + +| Dato | Fonte di verità | Note | +|---|---|---| +| Anagrafica, contatti, corso, sede | Scheda persona (backend) | Aggiornata dal censimento o dall'operatore. | +| Stato di socio, numero socio, date di ammissione e uscita | Libro Soci (backend) | Entra riceve il numero socio come copia (`employeeId`). | +| Incarichi (admin, team, Team Lead, Capo Admin) | Tabelle organizzative (backend) | Assegnati da chi ha il permesso, sempre in audit. | +| Gruppi Telegram amministrati | `tg_group_admins` (backend, dal bot) | Ricondotti alla persona tramite Telegram ID. Quelli scritti nel modulo sono una dichiarazione. | +| Identità verificate (Telegram ID, email) | IdP | Usate per collegare l'account. | +| Permessi globali | IdP (RBAC) | Nessun permesso deriva dall'essere censiti. | + +### 2.3 Dichiarato e verificato + +Alcune domande del modulo duplicano dati di cui esiste una fonte certa. La risposta viene conservata come dichiarazione e confrontata con la fonte; non la sostituisce. + +- «Sei associato a PoliNetwork?» → confronto con il Libro Soci. Le discrepanze finiscono in una lista da verificare. +- «Elenca i gruppi in cui sei admin» → confronto con `tg_group_admins`. +- «Tag Telegram» → lo username può cambiare, quindi serve solo a suggerire il Telegram ID (§6.3). + +--- + +## 3. Glossario + +| Termine | Significato | +|---|---| +| **Persona** | Scheda nel backend con ID interno stabile (`P…`). | +| **Account** | Utente IdP (`sub`), collegabile a una persona. | +| **Socio** | Persona presente nel Libro Soci con un numero socio. | +| **Admin** | Volontario che amministra gruppi PoliNetwork (tipologia «Group Admin» del modulo). | +| **Capo Admin** | Admin referente per un ambito (corso o insieme di gruppi), nominato dal Direttivo. | +| **Team** | Team interno: IT, HR, Design & Social, International, Events & Partnerships. L'elenco è configurabile. | +| **Membro di team** | Persona con un'appartenenza attiva a un team. | +| **Team Lead** | Membro di team con ruolo di responsabile del proprio team. | +| **Campagna di censimento** | Rilevazione per anno accademico (es. 2026/27), con versione del modulo e dell'informativa. | +| **Risposta** | Ciò che una persona ha risposto in una campagna: conferma o abbandono, dati dichiarati, consensi. | + +--- + +## 4. Modello dati (backend) + +Le nuove tabelle seguono la convenzione `createTable` con nuovi prefissi. I nomi sono indicativi. I campi di contatto vanno cifrati a riposo come già avviene per i nomi in `tg_users`. Ogni tabella ha `timeColumns` e, per le scritture da dashboard, l'autore IdP (`*_by_sub`) come nelle tabelle web attuali. + +### 4.1 Persone + +**`people_persons`** + +| Campo | Tipo | Note | +|---|---|---| +| `id` | uuid PK | ID interno stabile, mostrato come `P…` | +| `first_name`, `last_name` | text | | +| `personal_email` | text, cifrato | Email personale; diversa dall'eventuale `@polinetwork.org` | +| `phone` | text, cifrato | Formato E.164 | +| `telegram_id` | bigint, univoco se presente | Valorizzato solo se verificato (§5) o confermato da un operatore | +| `telegram_username` | text | Solo visualizzazione | +| `study_course` | text oppure FK a un elenco corsi | Decisione aperta (PRD §19 punto 8) | +| `study_year` | enum | `1T`, `2T`, `3T+`, `1M`, `2M+`, `graduated` | +| `campus` | enum | `leonardo`, `bovisa`, `cremona`, `piacenza`, `mantova`, `lecco`, `external` | +| `other_associations` | text | Altre associazioni o liste | +| `student_representative` | bool + `representative_bodies` text | CCS, Senato, CdA, … | +| `profile_as_of` | FK campagna | Campagna da cui provengono corso, anno e sede | +| `idp_sub` | text, univoco se presente | Account collegato | +| `linked_at`, `link_method`, `linked_by_sub` | | `link_method`: `telegram`, `invite`, `operator` | +| `archived_at` | timestamp | Archiviazione, non cancellazione | + +**`people_link_invites`**: `id`, `person_id`, `email`, `token_hash`, `expires_at`, `used_at`, `used_by_sub`, `created_by_sub`. Token monouso; il nuovo invito invalida il precedente. + +**`people_consents`**: `person_id`, `document` (`privacy-volontari`, `foto-video`, `privacy-soci`, …), `document_version`, `granted` bool, `source` (`msforms_import`, `dashboard`, `operator`), `recorded_at`, `revoked_at`. Righe solo aggiunte: una revoca è una nuova riga, non una modifica. + +### 4.2 Organizzazione + +**`org_admin_profiles`**: una riga per persona che è o è stata admin. + +| Campo | Note | +|---|---| +| `person_id` | PK e FK | +| `status` | `active`, `paused`, `ended` | +| `volunteering_since` | Data indicativa dichiarata («Da quanto collabori») | +| `ended_on`, `end_reason` | `degree`, `personal`, `other` + testo | +| `available_as_capo_admin` | bool, dichiarato | +| `requested_groups` | text: gruppi in cui vorrebbe entrare (dichiarato) | +| `hr_notes` | Note interne, visibili solo con `people:notes:read` | + +I gruppi effettivamente amministrati **non** si salvano qui: si leggono da `tg_group_admins` con `people_persons.telegram_id = tg_group_admins.user_id`. + +**`org_capo_admin_scopes`**: `person_id`, `group_label_id` (FK `common_group_labels`) oppure `group_id` (FK `tg_groups`), `starts_on`, `ends_on`, `assigned_by_sub`. Un Capo Admin può avere più ambiti, distinti dal proprio corso personale (PRD §1.3). + +**`org_teams`**: `id`, `slug` (`it`, `hr`, `design-social`, `international`, `events-partnerships`), `name_it`, `name_en`, `active`. + +**`org_team_memberships`** + +| Campo | Note | +|---|---| +| `person_id`, `team_id` | Una persona può stare in più team | +| `role` | `member`, `lead` | +| `started_on`, `ended_on`, `end_reason` | Storico: chi esce non viene cancellato | +| `team_fields` | jsonb validato dallo schema del team (§4.4) | + +### 4.3 Censimento + +**`census_campaigns`**: `id`, `academic_year` (`2026/27`), `title`, `status` (`draft`, `open`, `closed`), `opens_at`, `closes_at`, `form_version`, `privacy_document_version`. + +**`census_responses`** + +| Campo | Note | +|---|---| +| `campaign_id`, `person_id` | Una risposta valida per persona e campagna; le correzioni sostituiscono la precedente e restano in audit | +| `volunteer_type` | `admin`, `team` | +| `team_id` | Per `team`; il modulo attuale non lo chiede (§6.4) | +| `continues` | bool | +| `end_reason` | Se `continues = false` | +| `answers` | jsonb: copia integrale delle risposte, utile come prova di cosa è stato dichiarato | +| `source` | `msforms_import`, `dashboard`, `operator` | +| `external_id` | ID della risposta Forms, per reimport idempotenti | +| `submitted_at`, `submitted_by_sub` | | + +### 4.4 Campi specifici per team + +Ogni team può avere campi propri, definiti nel backend come schema zod versionato per `slug` e validati su `org_team_memberships.team_fields`. Gli esempi vanno confermati con ciascun Team Lead: + +| Team | Campi di esempio | +|---|---| +| IT | Username GitHub, aree (frontend, backend, bot, infrastruttura), repository seguite | +| Design & Social | Strumenti, portfolio, canali gestiti | +| HR | Ambiti (colloqui, onboarding, censimento) | +| International | Lingue, paese di provenienza | +| Events & Partnerships | Eventi seguiti, contatti partner di riferimento | + +All'inizio lo schema è nel codice: modificarlo richiede una PR, ma garantisce validazione e tipi. Un editor di campi gestibile dai Lead si valuterà quando i campi saranno stabili. + +### 4.5 Libro Soci + +**`members_register`** + +| Campo | Note | +|---|---| +| `person_id` | FK, univoco | +| `member_number` | Intero univoco: il numero socio | +| `status` | `active`, `suspended`, `expired`, `former` | +| `admitted_on`, `ended_on`, `end_reason` | | +| `azure_user_id` | Account Microsoft 365, se esiste | + +**`members_years`**: `member_id`, `association_year`, `fee_status` (`due`, `receipt_uploaded`, `paid`, `unpaid`), `paid_on`, `receipt_ref`, `approved_by_sub`. Serve ai rinnovi del PRD §6. + +Lo stato non si sovrascrive: ogni cambio è in audit e le annualità restano. Il numero socio si cambia nel registro, poi si sincronizza su Entra (`employeeId`). La procedura `azure.members.setAssocNumber` della PR backend #70 diventa la parte «sincronizza su Entra» di questo flusso. + +### 4.6 Audit + +Ogni mutazione delle tabelle sopra e ogni export scrivono una voce con attore (`sub`), azione, oggetto, valori prima e dopo, motivo (obbligatorio per collegamenti, scollegamenti, nomine e archiviazioni) e data. È la base dell'audit unificato del PRD §2.2: queste tabelle non vanno esposte a più ruoli prima che esista. + +--- + +## 5. Account e collegamento + +### 5.1 Stati della scheda + +| Stato | Significato | +|---|---| +| **Non collegato** | La persona esiste solo nel backend. Condizione normale per chi non accede. | +| **Invito inviato** | Esiste un invito valido non ancora usato. | +| **Collegato** | `idp_sub` valorizzato. | +| **Da verificare** | Un tentativo di collegamento ha trovato un conflitto, ad esempio Telegram ID già su un'altra scheda o account già collegato. | + +### 5.2 Metodi di collegamento + +1. **Telegram verificato (automatico).** La persona entra nell'IdP e collega Telegram con l'OIDC ufficiale. Al primo accesso alla propria area, il backend chiede all'IdP il Telegram ID verificato del `sub`. Se coincide con il `telegram_id` di **una sola** scheda non collegata, propone il collegamento; la persona conferma. +2. **Invito personale.** Dalla scheda, un operatore invia un link monouso all'email personale raccolta. La persona entra nell'IdP con qualsiasi metodo, conferma «Sono io» e la scheda si collega al suo `sub`. Scadenza proposta: 14 giorni. +3. **Operatore.** Chi ha `people:links:manage` collega a mano un account a una scheda, con motivo obbligatorio. Serve per i conflitti e per chi non ha Telegram verificato né email valida. + +**Regole:** + +- Nome ed email uguali producono un **suggerimento**, mai un collegamento automatico. +- Lo username Telegram non basta: conta solo il Telegram ID verificato. +- Una scheda già collegata a un altro account va scollegata da un operatore prima di collegarne uno nuovo. +- Lo scollegamento non cancella nulla: la persona torna «Non collegato». +- Registrarsi all'IdP **non** crea una scheda, non rende socio e non assegna permessi. Chi entra senza scheda vede un messaggio con le istruzioni e può chiedere l'inserimento (§7.8). + +### 5.3 Cosa manca nell'IdP + +Oggi lo snapshot verso il backend include solo i soggetti con almeno un permesso della proiezione `backend` (RFC v3 §5.2). Per un volontario senza permessi il backend conosce il `sub` dal token, ma non il suo Telegram ID verificato. Serve uno di questi interventi in `auth`: + +- **(Raccomandato) Endpoint interno di lookup**: `GET /api/internal/subjects/{sub}`, accessibile solo al client `backend` con scope `idp:subjects:read`. Restituisce `telegramId`, email verificate e nome visualizzato di **un** soggetto. Il backend lo chiama solo durante il collegamento. +- **Alternativa**: includere nella proiezione `backend` anche i soggetti con Telegram collegato e nessun permesso. È più semplice, ma espone al backend l'intera lista dei registrati. + +Non si usano i claim inoltrati dalla dashboard: il backend deve ottenere il dato verificato dall'IdP. + +--- + +## 6. Import del censimento esistente + +### 6.1 Flusso + +1. HR esporta le risposte di Forms (xlsx/csv) e le carica in **Persone → Censimenti → Importa**, scegliendo la campagna (es. 2026/27). +2. Il backend analizza il file e mostra un'**anteprima**: nuove persone, persone già esistenti con dati da aggiornare, duplicati interni al file, righe non valide e discrepanze (§2.3). +3. HR risolve i duplicati scegliendo «stessa persona» oppure «persone diverse». +4. Con conferma esplicita, il backend crea o aggiorna le schede, le risposte, i profili admin e le appartenenze ai team, e registra i consensi con `source = msforms_import` e la versione dell'informativa della campagna. +5. Reimportare lo stesso file non duplica nulla: le risposte sono identificate da `external_id`. + +Nessuna riga viene eliminata. Le risposte «NO» chiudono il profilo admin o l'appartenenza con il motivo indicato, senza cancellare la persona. + +### 6.2 Corrispondenza tra domande e dati + +| Domanda del modulo (IT / EN) | Destinazione | +|---|---| +| 2/3 Vuoi continuare | `census_responses.continues` | +| 4, 23, 31, 39 Tipologia | `census_responses.volunteer_type`; crea o aggiorna `org_admin_profiles` oppure `org_team_memberships` | +| Nome, Cognome, Telefono, Email personale | `people_persons` | +| Tag Telegram | `telegram_username` e suggerimento del Telegram ID (§6.3) | +| Corso, Anno, Sede | `people_persons`, con `profile_as_of` = campagna | +| Altre associazioni, Rappresentante, Dove | `people_persons` | +| Da quanto collabori | `org_admin_profiles.volunteering_since` oppure `org_team_memberships.started_on` | +| Sei associato? | Solo in `answers`, confrontato con il Libro Soci | +| Altri gruppi desiderati | `org_admin_profiles.requested_groups` | +| Disponibile come Capo Admin | `org_admin_profiles.available_as_capo_admin` | +| Gruppi in cui sei admin (ramo NO) | Solo in `answers`, confrontato con `tg_group_admins` | +| Motivo dell'uscita (ramo NO) | `end_reason` | +| Consenso privacy, Consenso foto/video | `people_consents` | +| Spazio libero | Solo in `answers`, visibile con `census:responses:read` | + +### 6.3 Telegram + +Per ogni username importato, il backend cerca in `tg_users` un utente con quello username. Se lo trova, lo **propone** come Telegram ID; HR lo conferma singolarmente o in blocco dall'anteprima. Senza conferma, `telegram_id` resta vuoto. Il Telegram ID confermato abilita il collegamento automatico (§5.2) e il calcolo dei gruppi amministrati. + +### 6.4 Modifiche suggerite al modulo per le prossime campagne + +- Chi sceglie «Membro Team» non indica **quale team**: aggiungere la domanda, con selezione multipla. +- Rendere coerenti gli obblighi tra le versioni: il Tag Telegram è obbligatorio solo nel ramo NO in inglese, e «Altri gruppi» solo nel ramo SÌ in inglese. +- Chiedere il **Telegram ID** è inutile: lo fornisce il collegamento all'IdP. +- Dalla seconda campagna in dashboard (§7.7), le domande sui dati già noti diventano una conferma dei dati precompilati. + +--- + +## 7. Sezione «Persone» della dashboard + +### 7.1 Navigazione + +``` +Persone +├── Panoramica tutte le persone, con indicatori Socio / Admin / Team / Account +├── Libro Soci registro soci, stati, annualità +├── Admin tutti gli admin +├── Team tutti i team e tutti i membri +│ └── pagina del singolo team (IT, HR, …) +├── Censimenti campagne, risposte, import, solleciti +└── Collegamenti schede da verificare e inviti in corso + +Capo Admin admin e gruppi del mio ambito (solo Capo Admin) + +La mia area la mia scheda, i miei team, il censimento, consensi, account +``` + +Ogni voce compare solo a chi ha il permesso o l'incarico corrispondente (§8, §9). La pagina «Members» di Microsoft 365 resta in **Microsoft 365** come strumento tecnico degli account; dal Libro Soci si apre l'account collegato. + +### 7.2 Panoramica (vista totale) + +Per Direttivo e HR. Una riga per persona, con le colonne comuni: nome, cognome, indicatori **Socio** (n°), **Admin** (attivo/ex), **Team** (elenco), **Capo Admin**, **Account** (collegato/non collegato), ultima risposta al censimento. Filtri su ciascun indicatore, corso, sede e stato. La scheda di dettaglio ha schede separate (Anagrafica, Socio, Admin, Team, Censimenti, Consensi, Account, Storico), ciascuna visibile secondo i permessi. + +### 7.3 Libro Soci + +Colonne: n° socio, nome, cognome, stato, data di ammissione, quota dell'anno corrente, email PoliNetwork, account collegato. Azioni (con `members:write`): nuovo socio, cambio stato, cambio numero (poi sincronizzato su Entra), registrazione dell'uscita. Export con `members:export`, in audit. + +Il primo popolamento importa da Entra gli utenti del gruppo Soci con il loro `employeeId`, li associa a una scheda persona esistente o ne crea una nuova, e segnala gli utenti senza numero e i numeri duplicati. + +### 7.4 Admin + +Colonne: nome, cognome, corso, anno, sede, attivo da, gruppi amministrati (da `tg_group_admins`), gruppi richiesti, rappresentante, altre associazioni, disponibile come Capo Admin, Capo Admin di riferimento, socio sì/no, stato del censimento, account collegato. Filtri come nel PRD §5, più «senza gruppi», «dichiarati socio ma non nel Libro Soci» e «disponibili come Capo Admin». + +### 7.5 Team + +- **Tutti i team**: un riquadro per team (Lead, numero di membri, risposte al censimento mancanti) e una tabella unica di tutti i membri con colonna «Team». +- **Pagina del team** (`/dashboard/persone/team/`): membri attivi ed ex, ruolo, attivo da, campi specifici del team (§4.4), stato del censimento, consenso foto/video. + +### 7.6 Capo Admin + +Per chi ha un ambito attivo in `org_capo_admin_scopes`: + +- admin che amministrano almeno un gruppo del proprio ambito, con i campi del PRD §5: nome, cognome, anno, attivo da, rappresentante, altre associazioni, telefono, Telegram con link rapido; +- gruppi dell'ambito, segnalando quelli senza admin; +- richieste di ingresso in gruppi dell'ambito (dal campo «gruppi desiderati» e, in futuro, dalle richieste del PRD §8). + +### 7.7 Censimenti + +- Elenco delle campagne e loro stato. +- Per ciascuna campagna: persone attese (admin e membri attivi alla data di apertura), risposte arrivate, «continua», «lascia», «nessuna risposta». +- Azioni: importa da Forms, invia solleciti a chi non ha risposto (email e, se collegato, Telegram), chiudi la campagna. +- **Fase successiva**: il modulo compilato direttamente in dashboard da «La mia area», con dati precompilati, informativa versionata e consensi registrati alla firma (PRD §2.5). A quel punto Forms va dismesso. + +### 7.8 La mia area + +Per ogni account collegato a una scheda: + +- la propria scheda, con richiesta di correzione dei dati; +- i propri ruoli: socio (numero e stato), admin (gruppi), team; +- la risposta alla campagna aperta; +- i propri consensi, con possibilità di revoca; +- il proprio Capo Admin e il proprio Team Lead; +- per chi è membro di team, la rubrica del proprio team (§8). + +Chi è registrato ma non ha una scheda vede «Non risulti tra i volontari o i soci» e può inviare una richiesta, che arriva in **Persone → Collegamenti**. + +--- + +## 8. Viste per ruolo + +| Chi | Cosa vede | Cosa può fare | +|---|---|---| +| **Direttivo / Presidente** | Tutte le viste, tutti i campi | Tutto; unici a nominare Team Lead e Capo Admin e a scrivere il Libro Soci | +| **HR** | Panoramica, Admin, Team, Censimenti, Collegamenti; Libro Soci in sola lettura | Gestire schede e censimenti, importare, sollecitare, collegare account | +| **Capo Admin** | Vista Capo Admin del proprio ambito | Vedere contatti e stato degli admin dell'ambito; in futuro approvare richieste di ingresso | +| **Team Lead** | Pagina del proprio team con tutti i campi, contatti inclusi | Aggiungere al team persone esistenti o nuove, modificare i campi del team, chiudere un'appartenenza (con motivo) | +| **Membro di team** | Rubrica del proprio team: nome, ruolo, Telegram, email PoliNetwork | Nessuna modifica altrui | +| **Admin** | La mia area | Compilare il censimento, gestire i propri consensi | +| **Socio non volontario** | La mia area, sezione socio | In futuro: rinnovo e ricevute (PRD §6) | +| **Team Design & Social** | Oltre al resto, il consenso foto/video di chi compare nei contenuti | Nessuna | +| **Volontario non registrato** | Nulla | Riceve inviti e solleciti; resta gestito da HR | + +Un ruolo non esclude l'altro: chi è Team Lead di IT e Capo Admin vede entrambe le viste, ciascuna limitata al proprio ambito. + +### 8.1 Visibilità dei campi + +| Campo | Direttivo | HR | Capo Admin (ambito) | Team Lead (team) | Membro (team) | Sé stesso | +|---|---|---|---|---|---|---| +| Nome, cognome, Telegram | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Email personale, telefono | ✓ | ✓ | ✓ | ✓ | — | ✓ | +| Corso, anno, sede | ✓ | ✓ | ✓ | ✓ | — | ✓ | +| Rappresentanza, altre associazioni | ✓ | ✓ | ✓ | — | — | ✓ | +| Campi del team | ✓ | ✓ | — | ✓ | sola lettura dei campi pubblici del team | ✓ | +| Stato socio | ✓ | ✓ | — | — | — | ✓ | +| Note HR | ✓ | ✓ | — | — | — | — | +| Risposte integrali del censimento | ✓ | ✓ | — | stato risposta | — | ✓ | +| Consensi | ✓ | ✓ | — | foto/video | — | ✓ | + +Il PRD §19 punto 6 resta aperto: se HR non deve vedere i contatti dei soci, si toglie `people:contacts:read` dal ruolo HR senza cambiare il modello. + +--- + +## 9. Permessi + +### 9.1 Nuovi permessi IdP (globali) + +Chiavi esatte, da registrare in `auth` con una migrazione come `0016`/`0020`, aggiungendo il prefisso `people:` (e gli altri) alla proiezione `backend`. Nessuna implicazione automatica salvo dove indicato. + +| Permesso | Consente | Ruolo suggerito | +|---|---|---| +| `people:read` | Panoramica, Admin, Team (tutti), schede: campi comuni | Direttivo, HR | +| `people:contacts:read` | Email personale e telefono nelle viste globali | Direttivo, HR | +| `people:notes:read` | Note HR e risposte integrali | Direttivo, HR | +| `people:write` | Creare, modificare e archiviare schede, profili admin e appartenenze | Direttivo, HR | +| `people:links:manage` | Collegare e scollegare account, inviare inviti, risolvere conflitti | Direttivo, HR | +| `people:assignments:manage` | Nominare e revocare Team Lead e ambiti Capo Admin | Direttivo | +| `people:export` | Export dei dati personali | Direttivo | +| `census:manage` | Campagne, import, solleciti | Direttivo, HR | +| `members:read` | Libro Soci | Direttivo, HR | +| `members:write` | Scrivere il Libro Soci | Direttivo | +| `members:export` | Export del Libro Soci | Direttivo | + +Implicazioni proposte: `people:write` → `people:read`; `members:write` → `members:read`. Restano da approvare, coerenti con la pratica attuale di dichiararle nell'IdP. + +I permessi `azure:members:*` restano dedicati agli account Microsoft 365 e non danno accesso al Libro Soci. + +### 9.2 Accessi derivati dagli incarichi (nel backend) + +Non sono permessi IdP: il backend li calcola dalla scheda collegata al `sub` del chiamante. + +| Condizione nel backend | Accesso | +|---|---| +| Scheda collegata | La mia area: solo la propria scheda | +| Appartenenza attiva a un team | Rubrica del team | +| `role = lead` attivo in un team | Lettura e scrittura del proprio team (§8) | +| Ambito attivo in `org_capo_admin_scopes` | Vista Capo Admin limitata all'ambito | + +Così HR o il Direttivo nominano un Lead in un solo passaggio, senza dover assegnare anche un ruolo nell'IdP, e la revoca dell'incarico toglie l'accesso. Poiché chi nomina un Lead gli dà accesso a dati personali, la nomina richiede `people:assignments:manage` ed è sempre in audit con motivo. + +Gli accessi globali (§9.1) e quelli derivati si sommano. + +### 9.3 Accesso alla dashboard + +Oggi `/dashboard` richiede `admin:access`. Proposta: + +- **`/dashboard/persone/*`** e le altre sezioni operative continuano a richiedere `admin:access` più il permesso della pagina. +- **`/dashboard/me/*`** (La mia area), la pagina del proprio team e la vista Capo Admin richiedono solo una **scheda collegata**. La shell mostra soltanto queste voci a chi non ha `admin:access`. +- Il gate di `src/routes/dashboard.tsx` diventa «`admin:access` **oppure** scheda collegata». `getDashboardAccess` restituisce anche gli incarichi (`people.me.access`) per costruire la navigazione. + +--- + +## 10. API backend (bozza) + +Nuova policy accanto a `dashboard(permission)` in `src/idp/policies.ts`: + +```ts +/** Signed-in dashboard users with a linked person record; the procedure checks the record relation itself. */ +export const linkedPerson: Policy = { user: { scope: SCOPE.admin, linkedPerson: true } } +``` + +Il middleware risolve `sub → person` e lo mette nel contesto; restituisce 403 se la scheda non c'è. Le procedure con accesso derivato controllano la relazione (Lead del team X, ambito Y) nella query stessa, non solo in un controllo preliminare, così una lista non può includere righe fuori ambito. + +| Router | Procedure | Policy | +|---|---|---| +| `people.persons` | `list`, `get`, `create`, `update`, `archive` | `people:read` / `people:write` | +| `people.links` | `suggest`, `invite`, `link`, `unlink`, `pending` | `people:links:manage` | +| `people.assignments` | `setTeamLead`, `setCapoAdminScope`, `end…` | `people:assignments:manage` | +| `people.admins` | `list` | `people:read` | +| `people.teams` | `list`, `get(slug)`, `members(slug)` | `people:read` | +| `people.census` | `campaigns`, `responses`, `previewImport`, `commitImport`, `remind` | `census:manage` | +| `members` | `list`, `get`, `create`, `setStatus`, `setNumber`, `importFromEntra` | `members:read` / `members:write` | +| `people.me` | `access`, `profile`, `requestChange`, `consents`, `revokeConsent`, `respond`, `acceptInvite`, `linkByTelegram` | `linkedPerson` (salvo `acceptInvite` e `linkByTelegram`: utente autenticato senza scheda) | +| `people.myTeam` | `roster`, `update…` (Lead) | `linkedPerson` + relazione | +| `people.capoAdmin` | `admins`, `groups` | `linkedPerson` + ambito | + +`members.setNumber` scrive il registro e poi chiama la sincronizzazione Entra già presente (`setAssocNumber`). Se Entra rifiuta, il registro torna al numero precedente e l'errore viene mostrato. + +--- + +## 11. Lavoro per repository + +### `auth` + +- Migrazione con i permessi del §9.1 e relative implicazioni; aggiunta dei nuovi prefissi alla proiezione `backend`. +- Endpoint interno di lookup del soggetto (§5.3), con scope dedicato e test di autorizzazione. +- Ruolo personalizzato **HR** e permessi aggiuntivi al ruolo gestito `direttivo`, configurati dall'interfaccia RBAC: configurazione, non codice. + +### `backend` + +- Schema e migrazioni del §4, con cifratura dei contatti e audit. +- Router del §10 e policy `linkedPerson`. +- Import Forms con anteprima e commit idempotente; import da Entra per il Libro Soci. +- Join persona ↔ `tg_group_admins` ↔ `tg_groups` ↔ `common_group_labels` per gruppi amministrati e ambiti Capo Admin. +- Inviti e solleciti tramite il mailer esistente (`src/emails`). + +### `admin` + +- Sezione Persone (§7) con le tabelle e i filtri già in uso nelle altre pagine. +- Nuovi permessi in `src/server/permissions.ts` e gate del §9.3. +- La mia area, pagina team, vista Capo Admin. +- Wizard di import con anteprima e conferma (regola AGENTS.md: nessuna azione su più righe senza conferma). + +--- + +## 12. Fasi di rilascio + +| Fase | Contenuto | Valore per chi lo usa | +|---|---|---| +| **1. Anagrafica e import** | Schema persone/team/admin, import della campagna 2026/27, Panoramica, Admin, Team per Direttivo e HR | Il censimento è in dashboard; nessuno deve registrarsi | +| **2. Libro Soci** | Registro soci, import da Entra, cambio numero tramite registro | Libro soci aggiornato e consultabile | +| **3. Collegamento account** | Lookup IdP, inviti, collegamento da operatore, La mia area in lettura | Chi accede trova i propri dati | +| **4. Viste delegate** | Team Lead, membro di team, Capo Admin, nomine | Lead e Capo Admin lavorano in autonomia | +| **5. Censimento in dashboard** | Campagna 2027/28 compilata in dashboard, consensi versionati, solleciti | Forms dismesso | +| **6. Rinnovi** | Quote annuali e ricevute (PRD §6) | — | + +La fase 1 non dipende dall'IdP e si può avviare subito. Le fasi 1 e 2 possono procedere in parallelo. + +--- + +## 13. Privacy + +- Base: informativa volontari (`polinet.cc/privacy-volontari`, `/privacy-volunteers`) nella versione accettata nel modulo. Ogni campagna registra la versione dell'informativa. +- Il consenso foto/video è facoltativo e revocabile; la revoca va resa visibile a Design & Social. +- **Retention da decidere**: proposta di archiviare i volontari usciti dopo 12 mesi dalla fine dell'incarico, conservando nome, ruoli e date; contatti e risposte vanno cancellati. I dati del Libro Soci seguono gli obblighi associativi. +- Gli export sono soggetti a permesso e audit. + +--- + +## 14. Decisioni aperte + +1. **Corso di studi e ambiti Capo Admin**: elenco chiuso o testo libero (PRD §19 punto 8)? Gli ambiti coincidono con le etichette dei gruppi? +2. **Lookup IdP** (§5.3): endpoint dedicato o proiezione estesa? +3. **Accesso alla dashboard senza `admin:access`** (§9.3): va bene aprire la shell a chi ha solo una scheda collegata, o La mia area deve essere un'applicazione separata? +4. **Campi specifici dei team** (§4.4): da definire con ciascun Lead. +5. **Contatti visibili a HR e ai Team Lead** (§8.1, PRD §19 punto 6). +6. **Retention** dei volontari usciti (§13). +7. **Team Lead e nuove persone**: un Lead può creare una scheda nuova o solo aggiungere persone già censite? +8. **Membri di team e Libro Soci**: un membro di team deve sapere chi del proprio team è socio? Proposta: no. diff --git a/RBAC_Ruoli_Permessi.md b/RBAC_Ruoli_Permessi.md new file mode 100644 index 00000000..e7a31810 --- /dev/null +++ b/RBAC_Ruoli_Permessi.md @@ -0,0 +1,116 @@ +# Ruoli e permessi — Admin Dashboard PoliNetwork + +**Documenti collegati:** [`PRD_Admin_Dashboard_PoliNetwork.md`](./PRD_Admin_Dashboard_PoliNetwork.md) §1, §2.1 · [`IdentityProvider_Design.md`](./IdentityProvider_Design.md) §4 (RBAC) · [`IdentityProvider_DataModel.md`](./IdentityProvider_DataModel.md) (schema implementato e proposte residue) +**Versione:** 2.0 +**Data:** 12 ottobre 2026 +**Esclusioni:** pagine di caricamento annunci (§15 Bacheca casa e coinquilini), non coperte da capacità dedicate. + +## 0. Contratto implementato + +L'IdP `auth` è la fonte dei ruoli e permessi globali, con assegnazioni tramite `user_role`, ereditarietà dei ruoli e implicazioni dei permessi. Il backend controlla scope del client e permessi della persona per procedura; admin legge `me.access` e verifica gli stessi permessi nel BFF. Fonti fissate agli SHA in PRD §20. + +L'accesso a `/dashboard` richiede `admin:access`: Home/Overview e Account **della dashboard** non sono accessibili a qualunque persona autenticata. L'account personale IdP è distinto. Telegram collegato non è obbligatorio e non assegna ruoli. I nomi legacy sono inventario da riconciliare, non preset applicati automaticamente; il veto globale `creator` è superato. + +### 0.1 Permessi verificati dalla dashboard + +| Permesso | Ambito implementato | +|---|---| +| `admin:access` | Accesso dashboard e letture di dati pubblici | +| `tg:users:read`, `tg:messages:read`, `tg:audit:read` | Utenti, messaggi e audit Telegram | +| `tg:groups:manage` | Gestione gruppi Telegram | +| `tg:grants:read`, `tg:grants:manage` | Lettura e gestione grant | +| `wa:groups:manage` | Gruppi WhatsApp | +| `groups:labels:write` | Scrittura etichette comuni ai gruppi | +| `web:content:write` | Mutazioni FAQ, guide, progetti, associazioni e pubblicazione gruppi per etichetta | +| `web:reports:manage` | Gestione segnalazioni link | +| `azure:members:read`, `azure:members:create` | Directory Microsoft 365 in sola lettura e workflow dedicato di creazione | +| `azure:groups:read`, `azure:groups:write` | Lettura gruppi e aggiunta/rimozione di utenti esistenti; il picker richiede anche `azure:members:read` | + +Sono chiavi esatte: l'interfaccia non deduce `read` da `write` per prefisso. Le implicazioni sono risolte dall'IdP. Il catalogo per la moderazione bot è più ampio (`tg:ban`, `tg:trusted`, `tg:immune`, ecc.): si segue la RFC v3, non la proposta a chiave+livello sotto. + +### 0.2 Ruoli gestiti e amministrazione + +- `master-admin`: wildcard su tutti i permessi, derivato dall'allowlist di bootstrap o dal gruppo Entra configurato; non assegnabile come un ruolo personalizzato, non ereditabile. +- `socio`, `direttivo`: appartenenza derivata dai gruppi Entra; nessuna equivalenza automatica con quota pagata/storico associativo. `socio` ha il permesso iniziale `membership:read`. +- `student`: verifica della mail `@mail.polimi.it`, con permesso iniziale `student:verified`. +- Ruoli personalizzati: bundle assegnabili, con ereditarietà. Presidente, HR, Capo Admin e ruoli funzionali non hanno i preset di prodotto sotto assegnati automaticamente. + +La gestione usa `idp:permissions:read/write`, `idp:roles:read/write/assign`, `idp:people:read`, `idp:users:read/delete`, `idp:applications:read/write`. Modificare ruoli e assegnarli sono permessi distinti; la delega è limitata ai permessi correnti dell'operatore. Le mutazioni RBAC sono auditate atomicamente. Le implicazioni esistono: la vecchia regola «nessuna capacità implica le altre» non descrive l'implementazione. + +Lo scoping corso/team non è implementato. Va progettato insieme al backend prima di esporre viste Capo Admin o Team Lead su dati di altre persone. + +--- + +## 1. Requisiti di capacità per i moduli futuri (proposta) + +Questa tabella conserva i requisiti di prodotto non ancora implementati. Le chiavi e i livelli sono descrittivi, **non** endpoint o permessi registrati oggi; vanno tradotti nel catalogo IdP. La precedente tabella `capability_grant` non è stata adottata. + +| Capacità | Pagine coperte | Livelli | +|---|---|---| +| `members` | Anagrafica Soci (§3.1) | read / write | +| `members.sensitive` | Campi sensibili anagrafica (contatti personali, eventuale codice fiscale) | read / write — sotto-permesso di `members`, non implicito | +| `admin_census` | Censimento Admin/Team (§3.2) | read / write | +| `capoadmin_view` | Vista Capo Admin (§5) | read, **sempre scoped** al proprio corso/ambito | +| `renewals` | Gestione Soci e Rinnovi (§6): coda ricevute, approvazione, reminder | read / approve | +| `teams` | Aree Team interni (§7) | read / write, **scoped** al team | +| `onboarding` | Onboarding self-service, richieste in ingresso (§8) | read / approve | +| `associations.accounts` | Gestione account/referenti associazioni partner (§12) | read / write | +| `associations.requests` | Approvazione richieste pubblicazione e pagina pubblica associazioni (§12) | read / approve | +| `events.politamtam` | Approvazione eventi PoliTamTam (§13) | read / approve | +| `companies` | Area Aziende (§14) — placeholder, da definire con lo scope | read / write | +| `newsletter` | Composizione/invio newsletter (§16) | write / send | +| `membership.self` | Propria scheda in Anagrafica Soci: dati, storico, stato rinnovo (§3.1) | read (solo il proprio record) | +| `membership.self.renew` | Caricamento propria ricevuta di bonifico, stato della propria richiesta di rinnovo (§6) | write (solo sul proprio record) | +| `membership.self.consents` | Firma/consultazione dei propri consensi privacy (§2.5) | read / write (solo i propri) | + +Per nuove aree personali soci/onboarding va deciso se estendere la dashboard o un'applicazione separata: oggi `admin:access` resta richiesto per l'admin. + +--- + +## 2. Preset organizzativi desiderati (proposta, §1.3 del PRD) + +| Ruolo | Capacità di default | Note | +|---|---|---| +| **Owner** | Amministrazione completa secondo una delega approvata | Distinguere responsabilità organizzativa e bootstrap Master Admin; nessun mapping automatico. | +| **Direttivo** | Permessi dei nuovi registri e processi associativi da approvare | Il ruolo gestito esiste; accesso dashboard e deleghe operative restano espliciti nel catalogo IdP. | +| **Presidente** | Come Direttivo | Più la firma esplicita dei rinnovi Direttivo→associazione quando richiesta (§6). | +| **Capo Admin** | `capoadmin_view` (scoped), `admin_census` read (scoped), lettura utenti/gruppi Telegram con futuro scope sui record, `onboarding` approve (solo per richieste che risalgono a lui, §8) | Tutto **scoped** al proprio corso/ambito — mai visibilità globale. | +| **Admin (operativo)** | Solo Account personale + propria area Onboarding | Nessuna capacità di lettura/scrittura su altri moduli finché non riceve capacità funzionali aggiuntive. | +| **HR** | `members` read, `admin_census` **read/write**, `onboarding` **read/write (approve)** | Scrittura su Censimento Admin/Team e su Onboarding (colloqui, candidature — coerente con PRD §8, dove il processo di candidatura va definito "con HR e Team IT"). Resta **sola lettura** sull'Anagrafica Soci, come esplicitamente richiesto dal PRD §3.3 — il dettaglio su `members.sensitive` resta comunque una decisione aperta (PRD §19 punto 6). | +| **Socio** | `membership.self`, `membership.self.renew`, `membership.self.consents` | **Non è un livello della gerarchia amministrativa**: è l'asse indipendente definito dal PRD §1.2. Lo includo qui perché, in termini di accesso alla dashboard, un Socio senza alcun ruolo admin ha comunque capacità concrete e proprie (vedere la propria scheda, caricare la ricevuta di rinnovo, firmare i consensi) — più di un Admin operativo "nudo" senza capacità funzionali aggiuntive. Una persona può essere Socio e Admin insieme: le capacità si sommano, non si sostituiscono (§1.2 del PRD). | + +Questi preset non sono il catalogo runtime. In particolare Owner non è automaticamente Master Admin; Direttivo/Presidente non ricevono automaticamente tutti i permessi; Socio non dispone ancora dei flussi `membership.self*`. La precedente esclusione globale `creator` non si applica al percorso IdP. + +--- + +## 3. Preset funzionali desiderati (proposta) + +| Ruolo | Capacità | Tipicamente assegnato a | +|---|---|---| +| **Content Editor** | `web:content:write` per il CRUD attuale; permessi publish distinti e newsletter da progettare | Team Design & Social | +| **Telegram Moderator** | Permessi `tg:*` approvati secondo le azioni del catalogo RFC, senza un write generico | Team IT / admin operativi con delega specifica | +| **Azure Manager** | `azure:members:read/create`, `azure:groups:read/write`; licenze/access review da progettare | Team IT | +| **Membership Officer** ("Finance", PRD §19 punto 9) | `renewals` approve | Direttivo di default; ruolo dedicato se PoliNetwork decide di scorporarlo (decisione ancora aperta nel PRD) | +| **Team Lead** | `teams` write, **scoped al proprio team** | Responsabile di uno dei team interni (IT, Design & Social, International, HR, Events & Partnerships) | +| **Association Liaison** | `associations.accounts` write, `associations.requests` approve, `events.politamtam` approve | Team Events & Partnerships | +| **Onboarding Reviewer** | `onboarding` approve (non scoped) | Direttivo/HR per i colloqui, o delegabile | +| **Auditor** | `tg:audit:read` per lo storico Telegram; lettura audit unificato da progettare | Direttivo/Owner di default; assegnabile a chi deve verificare la conformità senza avere capacità di scrittura altrove | + +--- + +## 4. Regole trasversali + +- **Conferma esplicita obbligatoria** (PRD §2.1) su ogni azione ad alto impatto: cancellazioni, assegnazione/revoca ruoli, rimozione da gruppi, export dati personali, amministrazione sensibile tramite i permessi `idp:*` pertinenti. +- **Scoping**: `capoadmin_view`, `admin_census` (per Capo Admin) e `teams` (per Team Lead) non sono mai capacità globali — portano sempre un `scopeType`/`scopeValue` (corso, team). Sono requisiti da implementare: il modello globale IdP attuale non contiene questi campi. +- **Separazione dei nuovi dati:** non concedere implicitamente campi sensibili o visibilità su altri team/corsi. Le eventuali implicazioni del nuovo catalogo vanno deliberate nell’IdP, che già supporta implicazioni transitive. +- **Le capacità `self`** (`membership.self*`) sono requisiti futuri per il solo proprio record: nel disegno proposto derivano dal collegamento dell'identità al proprio record Socio (claim tramite codice, `IdentityProvider_Design.md` §3) — chiunque abbia claim-ato un tesseramento le ottiene sul proprio record, mai su quello di altri. +- **Audit obbligatorio** prima di esporre dati personali a più ruoli (PRD §17) — vale in particolare per `members.sensitive` e `admin_census`. + +--- + +## 5. Decisioni aperte + +1. `members.sensitive` — quali campi rientrano davvero (codice fiscale sì/no è ancora aperto nel PRD, §19 punto 5) e se HR li vede o restano mascherati anche per HR (PRD §19 punto 6). +2. Se "Membership Officer/Finance" diventa un ruolo reale distinto o resta capacità del solo Direttivo (PRD §19 punto 9). +3. Se `onboarding` approve per le candidature nuove (non i rinnovi di interesse) richiede un ruolo dedicato (es. HR + un membro del Direttivo insieme) invece che essere una capacità singola. +4. Se `companies` (Area Aziende, §14) merita un ruolo funzionale proprio — dipende dallo scope, ancora da progettare.