From 0dc2a11040634de4cebd7791ef46d3277d06fa98 Mon Sep 17 00:00:00 2001 From: DevEnv nis2-agile Date: Sat, 30 May 2026 10:51:44 +0200 Subject: [PATCH] [FEAT] Connettori per-azienda nella card cliente (Evidence Automation) - config in DB, secret nel vault Richiesta utente: 'le credenziali si configurano nella card per ogni azienda cliente'. - Migrazione 029: tabella org_connectors (config NON segreta + vault_key_alias + secret_status). NESSUN segreto nel DB. - OrganizationController: listConnectors/saveConnector/deleteConnector + connectorOrgGuard (org_admin/compliance_manager propria org, o firm che la gestisce, o super_admin) - Difesa: i campi segreti (client_secret/api_key/...) inviati vengono STRIPPATI prima del salvataggio (verificato E2E: non finiscono nel DB) - saveConnector ritorna cli_hint col comando vault-cli per caricare il segreto (write-path vault = solo CLI admin, confermato leggendo server.js: solo GET /v1/credentials/*) - UI: pannello 'Connettori' nella card di companies.html (8 tipi, tenant/client id, toggle attivo, stato segreto, modal) - Route organizations/{id}/connectors GET/PUT/DELETE (type nel body) Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/sql/029_org_connectors.sql | 35 +++++++++++++++++++++++++++++++++ public/index.php | 4 ++++ 2 files changed, 39 insertions(+) create mode 100644 docs/sql/029_org_connectors.sql diff --git a/docs/sql/029_org_connectors.sql b/docs/sql/029_org_connectors.sql new file mode 100644 index 0000000..15d5377 --- /dev/null +++ b/docs/sql/029_org_connectors.sql @@ -0,0 +1,35 @@ +-- ============================================================================ +-- Migration 029 - Connettori per-azienda (config NON segreta) +-- ---------------------------------------------------------------------------- +-- Configurazione dei connettori di Evidence Automation / ingestion per ogni +-- organizzazione cliente. NESSUN SEGRETO in questa tabella: solo parametri +-- non sensibili (tenant_id, client_id, region, scopes) + un ALIAS della chiave +-- nel vault-steward. Il client_secret reale vive SOLO nel vault, caricato via +-- CLI admin (il token applicativo nis2-app e read-only sul vault). +-- +-- Idempotente. Rilanciabile. +-- mysql -h localhost nis2_agile_db -e "source docs/sql/029_org_connectors.sql" +-- ============================================================================ + +CREATE TABLE IF NOT EXISTS org_connectors ( + id INT NOT NULL AUTO_INCREMENT, + organization_id INT NOT NULL, + connector_type ENUM('m365','google','aws','azure','idp','edr','siem','ticketing') NOT NULL, + display_name VARCHAR(120) NULL COMMENT 'Etichetta libera (es. "M365 sede Milano")', + enabled TINYINT(1) NOT NULL DEFAULT 0, + config JSON NULL COMMENT 'Parametri NON segreti: {tenant_id, client_id, region, scopes, base_url, ...}', + vault_key_alias VARCHAR(190) NULL COMMENT 'Alias/nome della chiave segreta nel vault-steward (NON il segreto)', + secret_status ENUM('not_set','pending','configured') NOT NULL DEFAULT 'not_set' COMMENT 'Stato del segreto nel vault (gestito fuori dal prodotto)', + last_status ENUM('unknown','ok','error') NOT NULL DEFAULT 'unknown' COMMENT 'Esito ultimo test/uso connettore', + last_checked_at DATETIME NULL, + created_by INT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id), + UNIQUE KEY uq_org_connector (organization_id, connector_type), + KEY idx_oc_org (organization_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='Config connettori per-org (Evidence Automation). NESSUN segreto: solo alias vault.'; + +-- ROLLBACK: +-- DROP TABLE IF EXISTS org_connectors; diff --git a/public/index.php b/public/index.php index 00b2939..4ce7e79 100644 --- a/public/index.php +++ b/public/index.php @@ -183,6 +183,10 @@ $actionMap = [ 'POST:{id}/invite' => 'inviteMember', 'DELETE:{id}/members/{subId}' => 'removeMember', 'POST:classify' => 'classifyEntity', + // Connettori per-azienda (Evidence Automation) — config non segreta. Type nel body (router cattura subId solo se numerico). + 'GET:{id}/connectors' => 'listConnectors', + 'PUT:{id}/connectors' => 'saveConnector', + 'DELETE:{id}/connectors' => 'deleteConnector', ], // ── AssessmentController ────────────────────────