diff --git a/docs/DESIGN_MODULO_QUESTIONARI_FORNITORI.md b/docs/DESIGN_MODULO_QUESTIONARI_FORNITORI.md new file mode 100644 index 0000000..f84b695 --- /dev/null +++ b/docs/DESIGN_MODULO_QUESTIONARI_FORNITORI.md @@ -0,0 +1,159 @@ +# Design — Modulo Questionari Fornitori configurabile + +> Stato: **PROPOSTA DI DESIGN** (nessun codice scritto). Da approvare prima dell'implementazione. +> Autore: sessione 2026-05-31. Origine: richiesta utente (Cristiano Benassati). +> Riferimenti prodotto attuali: `SupplyChainController` (self-assessment a link-token già esistente, P3), `supplier-assessment.html`, tabella `supplier_questionnaires` (migrazione 027), `acn_requirements` (migrazione 031). + +--- + +## 1. Obiettivo + +Evolvere l'attuale self-assessment fornitori (link-token usa-e-getta, 8 domande fisse) in un **modulo questionari configurabile e ricorrente**, che copra 4 capacità richieste: + +1. **Anagrafica fornitore** con possibilità per il fornitore di compilare **online**. +2. **Questionari diversi per categoria fornitore** (es. cloud provider, MSP/MSSP, manutentore hardware, consulenza, logistica…). +3. **Domande configurabili ed estensibili** dall'azienda — non solo NIS2, ma anche per **procedure interne** (qualità, privacy, 231, ESG…). +4. **Gestione scadenze e ricorrenze**, con accesso del fornitore **in base al profilo registrato** e gestione degli **aggiornamenti** nel tempo. + +Valore: trasforma il modulo Supply Chain da "valutazione una tantum" a **due-diligence continua dei fornitori** — coerente con Art. 21.2(d) NIS2 e con le best practice (ENISA supply chain, ISO 28000). + +--- + +## 2. Cosa esiste già (base di partenza) + +| Componente esistente | Riuso | +|---|---| +| `SupplyChainController::sendQuestionnaire` (link-token + scadenza 30gg) | base per le "campagne" | +| `publicQuestionnaire` / `submitPublicQuestionnaire` (no-auth via token) | base per la compilazione online senza account | +| `supplier_questionnaires` (token_hash, status, score, expires_at) | da estendere con template_id e recurrence | +| `supplier-assessment.html` (pagina pubblica) | da rendere dinamica (render del template) | +| costante `QUESTIONNAIRE` (8 domande hardcoded) | da migrare in DB configurabile | + +**Gap da colmare:** le 8 domande sono hardcoded nel controller; non c'è categoria fornitore, né editor domande, né ricorrenza/scadenze gestite, né profilo fornitore persistente. + +--- + +## 3. Decisione architetturale chiave — accesso fornitore (ibrido) + +Richiesta utente: valutare **ibrido**. Proposta: + +### Modello consigliato: **token-per-campagna come default + account fornitore opzionale ("upgrade")** + +| Aspetto | Link-token (default) | Account fornitore (opzionale) | +|---|---|---| +| Onboarding fornitore | zero attrito: clic sul link email | registrazione/credenziali | +| Sicurezza | token sha256 + scadenza, single-purpose | superficie auth nuova da proteggere | +| Storico per il fornitore | no (lo vede solo l'azienda) | sì: portale con questionari passati/scadenze | +| Ricorrenza/aggiornamenti | nuovo link a ogni campagna | il fornitore rientra e aggiorna | +| Costo implementativo | basso (estende l'esistente) | alto (auth esterna, sessioni, recovery) | + +**Raccomandazione:** partire **token-per-campagna esteso** (Fase 1-2), e introdurre l'**account fornitore** solo in Fase 4, quando il valore lo giustifica. Un fornitore può "rivendicare" il proprio profilo (claim) partendo da un token valido → diventa account. Questo è l'ibrido: **token-first, account-on-demand**, senza costruire subito un IdP esterno. + +> Sicurezza account fornitore (se/quando attivato): utenti in tabella separata `supplier_users` (MAI in `users` interni), password Argon2id, rate-limit, scope limitato alla sola anagrafica/questionari del proprio fornitore, nessun accesso ai dati dell'organizzazione cliente. Valutare riuso del pattern token effimero invece di password. + +--- + +## 4. Schema dati proposto (migrazioni nuove, additive) + +``` +supplier_categories + id, organization_id (NULL = globale/sistema), name, slug, description, active + +questionnaire_templates + id, organization_id, category_id (FK supplier_categories, NULL = tutte), + name, version, scope ENUM('nis2','privacy','quality','231','esg','custom'), + recurrence_months (NULL = una tantum), is_active, created_by, created_at + -- versioning: come policy_versions, snapshot a ogni pubblicazione + +questionnaire_questions + id, template_id, order_index, code, text, + type ENUM('yes_no_partial','scale_1_5','single_choice','multi_choice','text','number','file'), + options JSON (per choice), weight, required, help_text + -- estensibili: l'azienda aggiunge domande per procedure interne + +questionnaire_campaigns + id, organization_id, supplier_id, template_id, template_version, + status ENUM('draft','sent','in_progress','completed','expired','overdue'), + token_hash, sent_to_email, sent_at, due_at, completed_at, + reminder_count, next_reminder_at, score, risk_level + +questionnaire_answers + id, campaign_id, question_id, answer_value, answer_text, file_ref, answered_at + +-- Fase 4 (opzionale): +supplier_users + id, supplier_id, email, password_hash, status, last_login_at, ... +``` + +`suppliers` esistente: aggiungere `category_id` (FK), `default_contact_email` già presente come `contact_email`. + +--- + +## 5. API proposte + +**Lato azienda (JWT):** +- `GET/POST /api/supply-chain/categories` — CRUD categorie fornitore +- `GET/POST/PUT /api/supply-chain/templates` — CRUD template + versioning +- `GET/POST/PUT/DELETE /api/supply-chain/templates/{id}/questions` — editor domande +- `POST /api/supply-chain/{supplierId}/campaigns` — avvia campagna (sceglie template per categoria, imposta scadenza/ricorrenza) +- `GET /api/supply-chain/campaigns` — cruscotto campagne con scadenze/stato + +**Lato fornitore (no-auth via token, evoluzione dell'esistente):** +- `GET /api/supply-chain/public-questionnaire?token=` — ritorna il **template dinamico** (domande dal DB, non più hardcoded) +- `POST /api/supply-chain/submit-public-questionnaire` — salva risposte multi-tipo + calcola score pesato + +**Scadenze (cron):** +- `scripts/supplier-questionnaire-cron.sh` (Europe/Rome): invia reminder, marca `overdue`, apre la campagna ricorrente alla scadenza `recurrence_months`. + +--- + +## 6. UI proposta + +- **supply-chain.html**: tab "Categorie" + "Template questionari" (editor domande drag&drop o lista ordinabile); sulla scheda fornitore, dropdown categoria + "Avvia campagna" (sceglie template, scadenza, ricorrenza). +- **supplier-assessment.html**: reso **dinamico** — renderizza qualsiasi template/tipi di domanda dal token. +- **Cruscotto scadenze**: lista campagne con semaforo (in regola / in scadenza / scaduta), reminder inviati. +- (Fase 4) **portale fornitore**: login separato, "i miei questionari", storico, prossime scadenze. + +--- + +## 7. Fasi incrementali (proposta) + +| Fase | Contenuto | Rischio | Valore | +|---|---|---|---| +| **1** | Categorie fornitore + template/domande configurabili in DB (migra le 8 domande hardcoded) + editor base lato azienda. `supplier-assessment.html` dinamico. | Basso (additivo) | Questionari per categoria + estensibili | +| **2** | Campagne con **scadenza + reminder** (cron) + cruscotto stato. Ricorrenza (`recurrence_months`). | Medio (cron) | Gestione scadenze/aggiornamenti | +| **3** | Tipi domanda avanzati (allegati, scale, multi-choice) + scoring configurabile per peso. | Medio | Flessibilità procedure interne | +| **4** | **Account fornitore** (portale persistente, claim da token). | Alto (auth esterna) | Self-service completo | + +Ogni fase è rilasciabile da sola. Le Fasi 1-2 coprono già l'80% della richiesta. + +--- + +## 8. Sicurezza / conformità (da rispettare) + +- Token: `sq_` + 20 byte random, hash SHA-256 in DB, scadenza, single-use sul completamento (già fatto, da mantenere). +- Endpoint pubblici: nessun dato di altre org accessibile dal token (no IDOR — già verificato sul modulo attuale). +- Dati fornitore = dati personali del referente → rispetto GDPR (minimizzazione, retention, diritto cancellazione). +- Account fornitore (Fase 4): tabella separata, mai mischiare con `users`; secret nel vault se servono integrazioni. +- Audit: ogni invio/compilazione loggato (riuso `logAudit`). + +--- + +## 9. Domande aperte per l'utente (prima di implementare) + +1. Le **categorie fornitore** sono libere (definite dall'azienda) o vuoi un set predefinito di partenza (cloud/MSP/hardware/consulenza/logistica)? +2. La **ricorrenza** tipica: annuale? configurabile per template? +3. I **reminder**: quanti e a che intervallo prima/dopo la scadenza? +4. Lo **scoring**: soglia "requisiti soddisfatti" come oggi (≥70) o configurabile per template? +5. Fase 4 account fornitore: serve davvero o il link-token ricorrente basta? + +--- + +## 10. Stima (indicativa, multi-sessione) + +- Fase 1: ~1 sessione (migrazione + CRUD template/domande + render dinamico). +- Fase 2: ~1 sessione (campagne + cron scadenze + cruscotto). +- Fase 3: ~0.5 sessione. +- Fase 4: ~1-2 sessioni (auth fornitore + portale, la più delicata). + +Nessuna di queste tocca i moduli esistenti in modo distruttivo: tutto **additivo**.