[FEAT] A4 Fase 4.3 — Matrice RACI + link m2m: raci_assignments + procedure_inventory/procedure_risk/inventory_risk/risk_measure (mig.043), RaciController org-scoped (anti-IDOR, validazione object/measure), raci.html+raci.js (Bootstrap Italia/AGID), routing+sidebar+api+help+i18n. Build+review adversariale via workflow (0 finding critical/major). Cache-buster ?v=20260619.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-15 22:44:08 +02:00
co-authored by Claude Opus 4.8
parent 05dfba6fcd
commit b4d47d58a2
44 changed files with 1154 additions and 111 deletions
+116
View File
@@ -0,0 +1,116 @@
-- ============================================================================
-- Migration 043 — A4 Fase 4.3: Matrice RACI + link molti-a-molti tra
-- inventario (assets), procedure (policies), rischi (risks) e misure ACN.
-- ----------------------------------------------------------------------------
-- 5 tabelle ADDITIVE. Idempotente (CREATE TABLE IF NOT EXISTS). Applicare con la
-- STESSA connessione PDO dell'app (container nis2-db via TCP+TLS), via runner.
--
-- Ancoraggio normativo (docs/DESIGN_A4_RELATIONAL.md): la matrice RACI collega i
-- RUOLI dell'organigramma (mig.041, org_roles) agli OGGETTI di compliance
-- (inventario/procedure/rischi/fornitori) con responsabilita R/A/C/I.
-- - GV.RR-02 (NIST CSF 2.0): ruoli, responsabilita e poteri stabiliti/comunicati
-- - ID.AM (asset management): inventario dei sistemi rilevanti
-- - GV.PO (policy/governance): procedure
-- - art. 24 D.Lgs. 138/2024 + ID.RA (risk assessment): rischi
--
-- NOTE strutturali (verificate sul codice reale):
-- - 'inventario' nel prodotto = tabella `assets` (mig.001 riga 363).
-- - 'procedure' = tabella `policies` (mig.001 riga 256).
-- - rischi = `risks` (riga 141); fornitori = `suppliers` (riga 286).
-- - org_roles = mig.041 (id + organization_id).
-- - misure ACN = catalogo JSON application/data/acn_measures.json (campo 'code',
-- es. GV.OC-04) — NON e una tabella DB: 'risk_measure.measure_code' NON ha FK.
-- - raci_assignments.object_id e POLIMORFICO (punta a assets/policies/risks/
-- suppliers a seconda di object_type) -> NON puo avere FK; l'esistenza e
-- verificata in PHP (RaciController::assertObjectExists), org-scoped.
--
-- Multi-tenancy: ogni tabella ha organization_id NOT NULL (FK organizations,
-- ON DELETE CASCADE) e ogni query del controller filtra organization_id.
-- ============================================================================
-- Assegnazione RACI: per ogni (ruolo, oggetto) una sola responsabilita R/A/C/I.
-- object_id e polimorfico (vedi nota sopra) -> nessuna FK su object_id.
CREATE TABLE IF NOT EXISTS raci_assignments (
id INT AUTO_INCREMENT PRIMARY KEY,
organization_id INT NOT NULL,
role_id INT NOT NULL,
object_type ENUM('inventory','procedure','risk','supplier') NOT NULL,
object_id INT NOT NULL,
raci ENUM('R','A','C','I') NOT NULL,
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (role_id) REFERENCES org_roles(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_raci (role_id, object_type, object_id),
INDEX idx_raci_org (organization_id),
INDEX idx_raci_object (object_type, object_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Link m2m: procedura (policy) <-> inventario (asset).
CREATE TABLE IF NOT EXISTS procedure_inventory (
id INT AUTO_INCREMENT PRIMARY KEY,
organization_id INT NOT NULL,
policy_id INT NOT NULL,
asset_id INT NOT NULL,
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE,
FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_proc_inv (policy_id, asset_id),
INDEX idx_pi_org (organization_id),
INDEX idx_pi_asset (asset_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Link m2m: procedura (policy) <-> rischio (risk).
CREATE TABLE IF NOT EXISTS procedure_risk (
id INT AUTO_INCREMENT PRIMARY KEY,
organization_id INT NOT NULL,
policy_id INT NOT NULL,
risk_id INT NOT NULL,
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE,
FOREIGN KEY (risk_id) REFERENCES risks(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_proc_risk (policy_id, risk_id),
INDEX idx_pr_org (organization_id),
INDEX idx_pr_risk (risk_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Link m2m: inventario (asset) <-> rischio (risk).
CREATE TABLE IF NOT EXISTS inventory_risk (
id INT AUTO_INCREMENT PRIMARY KEY,
organization_id INT NOT NULL,
asset_id INT NOT NULL,
risk_id INT NOT NULL,
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE CASCADE,
FOREIGN KEY (risk_id) REFERENCES risks(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_inv_risk (asset_id, risk_id),
INDEX idx_ir_org (organization_id),
INDEX idx_ir_risk (risk_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Link m2m: rischio (risk) <-> misura ACN (codice del catalogo JSON, non FK).
CREATE TABLE IF NOT EXISTS risk_measure (
id INT AUTO_INCREMENT PRIMARY KEY,
organization_id INT NOT NULL,
risk_id INT NOT NULL,
measure_code VARCHAR(20) NOT NULL,
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (risk_id) REFERENCES risks(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_risk_measure (risk_id, measure_code),
INDEX idx_rm_org (organization_id),
INDEX idx_rm_measure (measure_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;