diff --git a/docs/sql/006_security_improvements.sql b/docs/sql/006_security_improvements.sql index 63a78d2..0e2e015 100644 --- a/docs/sql/006_security_improvements.sql +++ b/docs/sql/006_security_improvements.sql @@ -1,24 +1,46 @@ -- ============================================================ -- NIS2 Agile - Migration 006: Security & Performance Improvements -- Data: 2026-02-20 --- Eseguire come: mysql -u root -p nis2_agile_db < 006_security_improvements.sql --- Compatibile: MySQL 8.0+ +-- Compatibile: MySQL 8.0 (senza CREATE INDEX IF NOT EXISTS) -- ============================================================ +-- Procedura helper: crea indice solo se non esiste +DROP PROCEDURE IF EXISTS create_index_if_not_exists; +DELIMITER // +CREATE PROCEDURE create_index_if_not_exists( + IN p_table VARCHAR(100), + IN p_index VARCHAR(100), + IN p_cols VARCHAR(500) +) +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.statistics + WHERE table_schema = DATABASE() + AND table_name = p_table + AND index_name = p_index + ) THEN + SET @sql = CONCAT('CREATE INDEX `', p_index, '` ON `', p_table, '` (', p_cols, ')'); + PREPARE stmt FROM @sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END // +DELIMITER ; + -- ── 1. Indici performance su incidents ──────────────────────────────────── -CREATE INDEX IF NOT EXISTS idx_inc_org_status ON incidents (organization_id, status); -CREATE INDEX IF NOT EXISTS idx_inc_org_significant ON incidents (organization_id, is_significant); -CREATE INDEX IF NOT EXISTS idx_inc_early_warning_due ON incidents (organization_id, early_warning_due); -CREATE INDEX IF NOT EXISTS idx_inc_notification_due ON incidents (organization_id, notification_due); -CREATE INDEX IF NOT EXISTS idx_inc_final_report_due ON incidents (organization_id, final_report_due); +CALL create_index_if_not_exists('incidents', 'idx_inc_org_status', 'organization_id, status'); +CALL create_index_if_not_exists('incidents', 'idx_inc_org_significant', 'organization_id, is_significant'); +CALL create_index_if_not_exists('incidents', 'idx_inc_early_warning_due','organization_id, early_warning_due'); +CALL create_index_if_not_exists('incidents', 'idx_inc_notification_due', 'organization_id, notification_due'); +CALL create_index_if_not_exists('incidents', 'idx_inc_final_report_due', 'organization_id, final_report_due'); -- ── 2. Indici performance su risks ──────────────────────────────────────── -CREATE INDEX IF NOT EXISTS idx_risks_org_status ON risks (organization_id, status); -CREATE INDEX IF NOT EXISTS idx_risks_score ON risks (organization_id, inherent_risk_score); +CALL create_index_if_not_exists('risks', 'idx_risks_org_status', 'organization_id, status'); +CALL create_index_if_not_exists('risks', 'idx_risks_score', 'organization_id, inherent_risk_score'); -- ── 3. Indici performance su audit_logs ─────────────────────────────────── -CREATE INDEX IF NOT EXISTS idx_audit_org_created ON audit_logs (organization_id, created_at); -CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_logs (entity_type, entity_id); +CALL create_index_if_not_exists('audit_logs', 'idx_audit_org_created', 'organization_id, created_at'); +CALL create_index_if_not_exists('audit_logs', 'idx_audit_entity', 'entity_type, entity_id'); -- ── 4. Trigger per rendere audit_log immutabile ─────────────────────────── DROP TRIGGER IF EXISTS prevent_audit_log_update; @@ -49,23 +71,15 @@ ALTER TABLE policies ALTER TABLE suppliers ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP NULL DEFAULT NULL AFTER updated_at; --- Indici per soft delete (escludono record eliminati nelle query standard) -CREATE INDEX IF NOT EXISTS idx_risks_deleted ON risks (organization_id, deleted_at); -CREATE INDEX IF NOT EXISTS idx_policies_deleted ON policies (organization_id, deleted_at); +-- Indici per le colonne deleted_at +CALL create_index_if_not_exists('risks', 'idx_risks_deleted', 'organization_id, deleted_at'); +CALL create_index_if_not_exists('policies', 'idx_policies_deleted', 'organization_id, deleted_at'); -- ── 6. Indice su refresh_tokens ─────────────────────────────────────────── -CREATE INDEX IF NOT EXISTS idx_refresh_user_expires ON refresh_tokens (user_id, expires_at); +CALL create_index_if_not_exists('refresh_tokens', 'idx_refresh_user_expires', 'user_id, expires_at'); --- ── 7. Evento pulizia refresh token scaduti ─────────────────────────────── -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; +-- ── 7. Pulizia procedura temporanea ─────────────────────────────────────── +DROP PROCEDURE IF EXISTS create_index_if_not_exists; -- ── 8. Verifica ─────────────────────────────────────────────────────────── SELECT 'Migration 006 completed successfully' AS status; - --- Nota: per abilitare event scheduler (richiede SUPER): --- SET GLOBAL event_scheduler = ON;