-- ============================================================ -- NIS2 Agile - Migration 006: Security & Performance Improvements -- Data: 2026-02-20 -- Eseguire come: mysql -u root -p nis2_agile_db < 006_security_improvements.sql -- ============================================================ -- ── 1. Indici performance su incidents ──────────────────────────────────── -- Per query filtrate per org + scadenze NIS2 ALTER TABLE incidents ADD INDEX IF NOT EXISTS idx_inc_org_status (organization_id, status), ADD INDEX IF NOT EXISTS idx_inc_org_significant (organization_id, is_significant), ADD INDEX IF NOT EXISTS idx_inc_early_warning_due (organization_id, early_warning_due), ADD INDEX IF NOT EXISTS idx_inc_notification_due (organization_id, notification_due), ADD INDEX IF NOT EXISTS idx_inc_final_report_due (organization_id, final_report_due); -- ── 2. Indici performance su risks ──────────────────────────────────────── ALTER TABLE risks ADD INDEX IF NOT EXISTS idx_risks_org_status (organization_id, status), ADD INDEX IF NOT EXISTS idx_risks_score (organization_id, inherent_risk_score DESC); -- ── 3. Indici performance su audit_logs (immutabilità) ──────────────────── -- L'audit log deve essere append-only e ricercabile rapidamente ALTER TABLE audit_logs ADD INDEX IF NOT EXISTS idx_audit_org_created (organization_id, created_at DESC), ADD INDEX IF NOT EXISTS idx_audit_entity (entity_type, entity_id); -- ── 4. Trigger per rendere audit_log immutabile ─────────────────────────── -- Blocca UPDATE e DELETE sull'audit log (solo INSERT consentito) DROP TRIGGER IF EXISTS prevent_audit_log_update; CREATE TRIGGER prevent_audit_log_update BEFORE UPDATE ON audit_logs FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'audit_logs is append-only: UPDATE not permitted'; END; DROP TRIGGER IF EXISTS prevent_audit_log_delete; CREATE TRIGGER prevent_audit_log_delete BEFORE DELETE ON audit_logs FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'audit_logs is append-only: DELETE not permitted'; END; -- ── 5. Colonna deleted_at per soft delete su tabelle critiche ───────────── -- Permette di "eliminare" record senza perdere traccia storica -- Risks: soft delete ALTER TABLE risks ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP NULL DEFAULT NULL AFTER updated_at; ALTER TABLE risks ADD INDEX IF NOT EXISTS idx_risks_deleted (organization_id, deleted_at); -- Policies: soft delete ALTER TABLE policies ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP NULL DEFAULT NULL AFTER updated_at; ALTER TABLE policies ADD INDEX IF NOT EXISTS idx_policies_deleted (organization_id, deleted_at); -- Suppliers: soft delete ALTER TABLE suppliers ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP NULL DEFAULT NULL AFTER updated_at; -- ── 6. Indice su refresh_tokens per performance ─────────────────────────── ALTER TABLE refresh_tokens ADD INDEX IF NOT EXISTS idx_refresh_user_expires (user_id, expires_at); -- ── 7. Pulizia automatica refresh token scaduti ─────────────────────────── -- Evento schedulato (richiede event scheduler abilitato) DROP EVENT IF EXISTS cleanup_expired_refresh_tokens; CREATE EVENT IF NOT EXISTS cleanup_expired_refresh_tokens ON SCHEDULE EVERY 6 HOUR STARTS CURRENT_TIMESTAMP DO DELETE FROM refresh_tokens WHERE expires_at < NOW() - INTERVAL 1 DAY; -- ── 8. Verifica ─────────────────────────────────────────────────────────── SELECT 'Migration 006 completed successfully' AS status; -- Abilita event scheduler (da eseguire manualmente se necessario): -- SET GLOBAL event_scheduler = ON;