[FEAT] Modello Organizzativo SGSI (ISO 27001/27017/27018) + SoA pre-popolato da NIS2

Nuovo modulo guidato in 6 step (cl. 4-10 + Statement of Applicability):
- migration 037 (isms_models/roles/soa/documents) + 038 (dataset 111 controlli:
  93 Annex A:2022 + 7 CLD/27017 + 11 PII/27018) + runner scripts/migrate-isms.php
- IsmsModelController (16 endpoint) registrato in index.php
- SoA pre-popolato dalle risposte Gap Analysis NIS2 (mapping iso27001_control)
- estensioni cloud condizionali 27017/27018 via flag uses_public_cloud/
  is_cloud_provider/processes_pii_in_cloud
- AIService::generateIsmsDocument + fonti ISO in nis2_sources.php
- frontend isms.html/isms.js + api client + sidebar + help + i18n IT/EN
- ingest KB ISO (scope SYSTEM, solo titoli/sintesi: no testo coperto da copyright)
- version.json 1.14.0; doc studio + deploy handoff

Strumento di supporto/pre-audit (non certificazione). Migration DA APPLICARE su host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-11 11:32:42 +02:00
parent 66d50cd2cf
commit 3f74165531
17 changed files with 2166 additions and 1 deletions
+118
View File
@@ -0,0 +1,118 @@
-- ============================================================================
-- Migration 037 - Modello Organizzativo SGSI (ISO/IEC 27001:2022)
-- ----------------------------------------------------------------------------
-- Introduce il modulo "Modello Organizzativo" per costruire un Sistema di
-- Gestione della Sicurezza delle Informazioni (SGSI/ISMS) secondo
-- ISO/IEC 27001:2022 (clausole 4-10) + Statement of Applicability (SoA) sui
-- controlli Annex A:2022, estendibile con le norme cloud ISO/IEC 27017:2015
-- e ISO/IEC 27018:2019 (controlli condizionali).
--
-- Tutte ADDITIVE: nessuna tabella esistente modificata. Il SoA tiene un
-- riferimento "morbido" ai compliance_controls via linked_control_id (INT, no
-- FK) per non vincolare lo schema esistente.
--
-- Dataset di riferimento dei controlli (93 Annex A + 7 CLD + PII): vedi
-- migration 038_iso27001_annex_controls.sql.
--
-- IDEMPOTENTE: CREATE TABLE IF NOT EXISTS. Applicare su host MySQL
-- mysql -h localhost nis2_agile_db < 037_isms_model.sql
-- oppure tramite il runner con la connessione corretta dell'app:
-- docker exec nis2-app php scripts/migrate-isms.php
-- NON usare `docker exec nis2-db` (DB secondario non-prod).
-- STATO: DA APPLICARE su host.
-- ============================================================================
-- Intestazione del SGSI: una riga per organization (un SGSI attivo per org).
CREATE TABLE IF NOT EXISTS isms_models (
id INT AUTO_INCREMENT PRIMARY KEY,
organization_id INT NOT NULL,
status ENUM('draft','active','under_review') DEFAULT 'draft',
-- Clausola 4 - Contesto dell'organizzazione
scope_statement TEXT NULL,
context_internal TEXT NULL,
context_external TEXT NULL,
interested_parties JSON NULL,
boundaries TEXT NULL,
exclusions TEXT NULL,
-- Clausola 6 - Pianificazione (metodologia + obiettivi)
risk_methodology TEXT NULL,
isms_objectives JSON NULL,
-- Flag estensioni cloud (attivano controlli condizionali nel SoA)
uses_public_cloud TINYINT(1) DEFAULT 0, -- abilita ISO 27017 (lato cliente)
is_cloud_provider TINYINT(1) DEFAULT 0, -- abilita ISO 27017 (lato fornitore)
processes_pii_in_cloud TINYINT(1) DEFAULT 0, -- abilita ISO 27018 (PII processor)
version VARCHAR(20) DEFAULT '1.0',
created_by INT NULL,
approved_by INT NULL,
approved_at DATETIME 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 (created_by) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_isms_org (organization_id),
INDEX idx_isms_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Ruoli e responsabilita SGSI (RACI) - clausola 5.3.
CREATE TABLE IF NOT EXISTS isms_roles (
id INT AUTO_INCREMENT PRIMARY KEY,
isms_model_id INT NOT NULL,
organization_id INT NOT NULL,
role_name VARCHAR(150) NOT NULL,
user_id INT NULL,
responsibility TEXT NULL,
raci ENUM('R','A','C','I') NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (isms_model_id) REFERENCES isms_models(id) ON DELETE CASCADE,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_isms_roles_model (isms_model_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Statement of Applicability: una riga per controllo applicabile/escluso.
CREATE TABLE IF NOT EXISTS isms_soa (
id INT AUTO_INCREMENT PRIMARY KEY,
isms_model_id INT NOT NULL,
organization_id INT NOT NULL,
control_code VARCHAR(20) NOT NULL, -- es. A.5.1, CLD.9.5.1, PII.1
standard ENUM('iso27001','iso27017','iso27018') DEFAULT 'iso27001',
applicable TINYINT(1) DEFAULT 1,
justification_inclusion TEXT NULL,
justification_exclusion TEXT NULL,
implementation_status ENUM('not_started','in_progress','implemented','verified') DEFAULT 'not_started',
implementation_pct INT DEFAULT 0,
-- true se lo stato iniziale e stato DERIVATO dalle risposte NIS2 (assessment Art.21)
derived_from_nis2 TINYINT(1) DEFAULT 0,
source_ref VARCHAR(255) NULL, -- es. "NIS2 Art.21.2.a (ART21A_001)"
linked_control_id INT NULL, -- soft-link a compliance_controls.id (no FK)
updated_by INT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (isms_model_id) REFERENCES isms_models(id) ON DELETE CASCADE,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL,
UNIQUE KEY uk_isms_soa_ctrl (isms_model_id, control_code),
INDEX idx_isms_soa_model (isms_model_id),
INDEX idx_isms_soa_standard (standard)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Documented information minima (clausola 7.5): policy/procedure, bozze AI.
CREATE TABLE IF NOT EXISTS isms_documents (
id INT AUTO_INCREMENT PRIMARY KEY,
isms_model_id INT NOT NULL,
organization_id INT NOT NULL,
doc_type VARCHAR(60) NOT NULL, -- es. policy_sgsi, procedura_incident, soa_export
title VARCHAR(255) NOT NULL,
status ENUM('draft','review','approved') DEFAULT 'draft',
ai_generated TINYINT(1) DEFAULT 0,
body_html LONGTEXT NULL,
version VARCHAR(20) DEFAULT '1.0',
linked_policy_id INT NULL, -- soft-link a policies.id (no FK)
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (isms_model_id) REFERENCES isms_models(id) ON DELETE CASCADE,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_isms_docs_model (isms_model_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;