NIS2 Agile come Provider di Compliance

NIS2 Agile espone una Services API REST che permette a sistemi esterni di leggere score di conformità, rischi, incidenti e controlli in tempo reale. Auth via API Key dedicata, rate limiting 100 req/ora, firma HMAC-SHA256 per webhook outbound.

Provider Disponibili
Disponibile

231 Agile (lg231)

Integrazione compliance D.Lgs. 231/2001 + NIS2. Incidenti cyber → OdV, rischi IT → registro 231, score NIS2 nel dashboard aziendale.

Guida integrazione →
Pianificato

AllRisk Agile

Feed rischi cyber per piattaforma ERM unificata. Risk score NIS2 mappato su categorie ISO 31000.

Pianificato

SustainAI Agile

Cybersecurity come pillar ESG. Score NIS2 nel report di sostenibilità (CSRD, GRI 418).

Disponibile

SIEM / SOC Esterni

Webhook su incident.created e risk.created_high verso qualsiasi endpoint HTTPS (Splunk, Elastic, PagerDuty…).

Endpoint disponibili
Base URL: https://nis2.agile.software/api/services
Auth: Header X-API-Key: nis2_xxxx — oppure Authorization: Bearer nis2_xxxx
MetodoEndpointScopeDescrizione
POST/tokenread:allToken exchange: API Key → JWT 15 min. lg231 usa questo JWT per le chiamate successive.
POST/ssosso:login (o read:all)SSO federato: passa email + ruolo + responsabilità → JWT NIS2 2h. Utente creato se non esiste. Auditato.
GET/statusHealth check piattaforma, versione, DB. Nessuna auth.
GET/compliance-summaryread:complianceScore NIS2 aggregato (0-100), domain scores Art.21, rischi aperti, incidenti
GET/risks/feedread:risksRegistro rischi con livello ISO 27005, status, area. Filtri: ?level=high,critical&status=open
GET/incidents/feedread:incidentsIncidenti Art.23: severity, status, notifiche CSIRT, deadline 72h, overdue
GET/controls/statusread:complianceStato controlli per dominio: implemented / partial / missing
GET/assets/criticalread:assetsAsset critici con tipo, criticità, dipendenze mappate
GET/suppliers/riskread:suppliersFornitori: risk_level, ultimo assessment, is_flagged
GET/policies/approvedread:policiesPolicy approvate: titolo, area, data, versione
GET/openapi.jsonSpecifica OpenAPI 3.0 in JSON
Esempio risposta /compliance-summary
// GET /api/services/compliance-summary // X-API-Key: nis2_Abc123... { "success": true, "data": { "organization": { "id": 5, "name": "Azienda XYZ", "sector": "energia", "nis2_entity_type": "essential" }, "compliance": { "overall_score": 73, "label": "Parziale", "domain_scores": [ { "domain": "Gestione del rischio", "score": 80, "status": "compliant" }, { "domain": "Sicurezza supply chain", "score": 45, "status": "partial" } ] }, "risks": { "total": 12, "open": 8, "high_critical": 3 }, "incidents": { "total": 2, "open": 1, "significant": 1, "overdue": 0 } } }
Headers risposta
HeaderValore
X-NIS2-API-Version1.0
X-NIS2-Org-IdID organizzazione (intero)
X-RateLimit-Limit100 (req/ora per chiave)
X-RateLimit-RemainingRichieste rimaste nella finestra
Sistema Inviti / Licenze B2B: NIS2 Agile genera token di invito che abilitano il provisioning automatico di organizzazioni e utenti. L'e-commerce o il partner riceve l'invito, lo consegna al cliente finale (es. lg231), che lo usa per attivarsi automaticamente. L'accesso si interrompe alla scadenza dell'invito.
Flusso completo
1. GENERAZIONE NIS2 Admin (super_admin) POST /api/invites → { token: "inv_abc123...", expires_at, plan, max_uses } Il token è visibile UNA SOLA VOLTA. Solo hash SHA-256 in DB. 2. DISTRIBUZIONE NIS2 → E-commerce → lg231 NIS2 Admin passa inv_abc123... all'e-commerce E-commerce lo consegna al cliente con l'ordine lg231 lo riceve (es. in metadata ordine) 3. VALIDAZIONE lg231 prima del provisioning GET /api/invites/validate?token=inv_abc123... → { valid: true, plan: "professional", expires_at, remaining_uses, plan_features } 4. PROVISIONING lg231 attiva automaticamente POST /api/services/provision (body include "invite_token": "inv_abc123...") → { org_id, api_key, access_token, license_expires_at, temp_password } NIS2 marca l'invito come usato (used_count++) 5. SCADENZA Accesso automaticamente revocato Quando expires_at raggiunto: - API key rimane ma licenza_expires_at è nel passato - GET /api/invites/validate → { valid: false, code: "EXPIRED" } - Rinnovo: nuovo invito dal NIS2 Admin → nuovo provisioning
API Inviti (solo super_admin)
MetodoEndpointAuthDescrizione
POST/api/invites/createJWT super_adminGenera 1..50 inviti in batch
GET/api/invites/listJWT super_adminLista con filtri status/channel, auto-scade pending
GET/api/invites/{id}JWT super_adminDettaglio singolo + org usante
DELETE/api/invites/{id}JWT super_adminRevoca (non cancella — solo status=revoked)
POST/api/invites/{id}/regenerateJWT super_adminNuovo token, stessa configurazione
GET/api/invites/validate?token=inv_...Anteprima pubblica: piano, scadenza, usi rimasti
Generare un invito (NIS2 Admin)
POST https://nis2.agile.software/api/invites/create Authorization: Bearer {jwt_super_admin} Content-Type: application/json { "plan": "professional", // essentials | professional | enterprise "duration_months": 12, // durata licenza dopo attivazione "invite_expires_days": 30, // giorni di validità dell'invito stesso "max_uses": 1, // 1=one-shot, N=batch (es: 10 per reseller) "label": "lg231 Partner Q1 2026", "channel": "lg231", // lg231 | ecommerce | direct | manual "issued_to": "partner@lg231.it", "notes": "Ordine OPP-2026-042" } // Risposta — token visibile UNA SOLA VOLTA: { "invites": [{ "id": 7, "token": "inv_a1b2c3d4e5f6...", ← SALVARE SUBITO, non recuperabile "plan": "professional", "expires_at": "2026-04-06 12:00:00", "invite_url": "https://nis2.agile.software/onboarding.html?invite=inv_..." }], "warning": "Salva i token subito — non saranno più visibili in chiaro." }
Provisioning con invito (lg231 → NIS2)
// lg231 usa l'invite_token ricevuto per attivare automaticamente il cliente: POST https://nis2.agile.software/api/services/provision Content-Type: application/json { "invite_token": "inv_a1b2c3d4...", ← sostituisce X-Provision-Secret "company": { "ragione_sociale": "Acme S.r.l.", "partita_iva": "02345678901", "ateco_code": "62.01.00", "sector": "ict" }, "admin": { "email": "ciso@acme.it", "first_name": "Marco", "last_name": "Rossi" }, "caller": { "system": "lg231", "company_id": 142, "callback_url": "https://lg231.agile.software/api/integrations/nis2-callback" } } // Risposta (il piano e la durata vengono dall'invito, non dal body): { "provisioned": true, "org_id": 8, "api_key": "nis2_abcdef...", ← salvare in lg231 metadata "access_token": "eyJ...", ← JWT 2h per apertura immediata "temp_password": "NIS2_xxxxxx", ← cambio obbligatorio al primo login "invite_plan": "professional", "license_expires_at": "2027-03-07 12:00:00", "dashboard_url": "https://nis2.agile.software/dashboard.html" }
Validazione preventiva (opzionale)
# lg231 può validare l'invito prima del provisioning: curl "https://nis2.agile.software/api/invites/validate?token=inv_a1b2c3..." // Se valido: { "valid": true, "plan": "professional", "duration_months": 12, "expires_at": "2026-04-06T12:00:00", "remaining_uses": 1, "plan_features": ["Policy Management + AI", "Supply Chain Assessment", ...] } // Se scaduto: { "valid": false, "error": "Invito scaduto il 2026-03-01 00:00:00", "code": "EXPIRED" } // Se già usato: { "valid": false, "error": "Invito già utilizzato", "code": "ALREADY_USED" }
Gestione ciclo di vita
Stato invitoSignificatoAzione
pendingValido, non ancora usatoUsabile per provision
usedRaggiunto max_usesProvisioning bloccato
expiredexpires_at superatoProvisioning bloccato, rinnovare
revokedRevocato da adminProvisioning bloccato permanente
Sicurezza token: il token inv_ viene restituito in chiaro UNA SOLA VOLTA alla creazione. Nel DB è conservato solo il hash SHA-256. Il token_prefix (es: inv_a1b2c3...) è visibile nell'admin panel per identificazione visiva.
Per l'agente Claude di lg231: questo tab descrive esattamente cosa implementare in lg231 Agile per consumare NIS2 come provider di compliance cybersecurity.
Perché integrare 231 con NIS2
Evento NIS2Rilevanza D.Lgs. 231/2001
Incidente cybersecurity (Art.23 NIS2)Reato presupposto art.24-bis — criminalità informatica
Fornitore IT flaggato alto rischioRischio complicità in reati informatici via supply chain
Policy sicurezza non approvataGap nel MOG — Modello Organizzativo e di Gestione
Score NIS2 < 40%Indicatore OdV: carenza controlli, escalation consigliata
Whistleblowing sicurezzaPotenziale escalation verso OdV 231
1. Aggiungere NIS2 al provider-config (company-ms)

File: services/company-ms/public/index.php — route PATCH /companies/{id}/provider-config

// Aggiungere ai $allowed: $allowed = [ 'certisource_pat', 'anthropic_api_key', 'openai_api_key', // ── Nuovo provider NIS2 ── 'nis2_api_key', // chiave da NIS2 → Settings → API Keys 'nis2_org_id', // ID org NIS2 (dal URL dashboard o /api/services/status) 'nis2_enabled', // bool: abilitare pull automatico ];
2. NIS2Client (shared lib o inline)
class Nis2Client { const BASE_URL = 'https://nis2.agile.software/api/services'; public static function get(string $endpoint, string $apiKey, array $query = []): array { $url = self::BASE_URL . $endpoint; if ($query) $url .= '?' . http_build_query($query); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'X-API-Key: ' . $apiKey, 'Accept: application/json', 'X-Caller: lg231-agile/1.0', ], ]); $body = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $code === 200 && $body ? ['success' => true, 'data' => json_decode($body, true)] : ['success' => false, 'http_code' => $code]; } }
3. Widget score NIS2 nella company view
// In company detail view PHP/JS: $nis2Key = $meta['nis2_api_key'] ?? null; if ($nis2Key) { $summary = Nis2Client::get('/compliance-summary', $nis2Key); // Mostrare gauge score + badge essential/important + rischi high // Link: https://nis2.agile.software/dashboard.html }
4. Escalation incidenti → OdV (monitoring-ms)
// Job periodico (cron o hook) nel monitoring-ms: $incidents = Nis2Client::get('/incidents/feed', $apiKey, ['significant' => 1, 'status' => 'open']); foreach (($incidents['data']['incidents'] ?? []) as $inc) { // Crea odv_activity: // type: 'cyber_incident_notifica' // description: 'Incidente NIS2 significativo: ' . $inc['title'] // priority: $inc['overdue'] ? 'urgent' : 'high' // source: 'nis2_agile' // reference_id: $inc['id'] }
3b. SSO federato — apertura diretta NIS2 da lg231
Quando un utente lg231 clicca "Apri NIS2" dal suo dashboard, lg231 chiama POST /api/services/sso e redirige l'utente su NIS2 già autenticato, portando con sé ruolo e responsabilità. Ogni accesso SSO è tracciato nell'audit trail.
// In lg231, quando l'utente clicca "Apri NIS2 Agile": $ssoResp = Nis2Client::post('/sso', $apiKey, [ 'user_email' => $currentUser['email'], 'user_name' => $currentUser['first_name'] . ' ' . $currentUser['last_name'], 'user_role' => 'compliance_manager', // mappa da ruolo lg231 'caller_system' => 'lg231', 'caller_user_id' => $currentUser['id'], 'responsibilities' => [ ['area' => 'MOG 231', 'scope' => 'art.24-bis criminalità informatica'], ['area' => 'OdV', 'scope' => 'monitoraggio cyber risk'], ], ]); if ($ssoResp['success']) { $jwt = $ssoResp['data']['data']['token']; $redirectUrl = $ssoResp['data']['data']['redirect_url']; // Redirect con token nel fragment (sicuro, non nel server log) header('Location: ' . $redirectUrl . '#sso_token=' . urlencode($jwt)); }
NIS2 lato frontend: in dashboard.html aggiungere:
const ssoToken = location.hash.match(/#sso_token=([^&]+)/)?.[1];
if (ssoToken) { localStorage.setItem('nis2_token', ssoToken); location.hash = ''; }
5. Provisioning automatico dal portale lg231
Quando un cliente lg231 acquista una licenza NIS2 Agile, lg231 riceve un invite_token dall'e-commerce. Con questo token lg231 chiama POST /api/services/provision passando i dati aziendali del cliente. NIS2 crea automaticamente org, admin e API key. Il piano e la durata sono definiti dall'invito (non modificabili dal chiamante).
// Salva in lg231 companies.metadata dopo provisioning: $meta['nis2_api_key'] = $response['api_key']; $meta['nis2_org_id'] = $response['org_id']; $meta['nis2_invite_id'] = $response['invite_id']; $meta['nis2_license_plan'] = $response['invite_plan']; $meta['nis2_expires_at'] = $response['license_expires_at']; $meta['nis2_enabled'] = true; // Controlla scadenza prima di ogni chiamata: if (strtotime($meta['nis2_expires_at'] ?? '1970-01-01') < time()) { // licenza scaduta → mostrare banner rinnovo, non chiamare NIS2 return ['success' => false, 'reason' => 'license_expired']; }
Checklist implementazione lg231
Nota CORS: tutte le chiamate a NIS2 devono avvenire server-to-server (PHP cURL), non da browser. NIS2 non accetta origini lg231 in CORS per sicurezza.
NIS2 invia notifiche push su eventi tramite webhook HTTPS firmati HMAC-SHA256. Il sistema esterno riceve la notifica in tempo reale senza polling.
Eventi disponibili
incident.created Nuovo incidente di sicurezza registrato → odv_activity
incident.deadline_warning Scadenza Art.23 (24h/72h) entro 4 ore → alert urgente OdV
risk.created_high Nuovo rischio HIGH o CRITICAL creato → risk register 231
compliance.score_changed Variazione score NIS2 > 5% → aggiorna widget
policy.approved Nuova policy di sicurezza approvata → aggiorna MOG ref
supplier.risk_flagged Fornitore IT flaggato alto rischio → supply chain 231
Registrare una subscription
POST https://nis2.agile.software/api/webhooks/subscriptions Authorization: Bearer {jwt_token} Content-Type: application/json { "url": "https://lg231.agile.software/api/integrations/nis2-webhook", "events": ["incident.created", "incident.deadline_warning", "risk.created_high"], "description": "lg231 Agile integration" } // Risposta: { "id": 1, "secret": "whsec_abc123..." } // Salvare il secret in companies.metadata.nis2_webhook_secret
Verifica firma HMAC (endpoint ricevente)
$secret = $meta['nis2_webhook_secret']; $body = file_get_contents('php://input'); $expected = 'sha256=' . hash_hmac('sha256', $body, $secret); $received = $_SERVER['HTTP_X_NIS2_SIGNATURE'] ?? ''; if (!hash_equals($expected, $received)) { http_response_code(401); exit; } $event = $_SERVER['HTTP_X_NIS2_EVENT'] ?? ''; $payload = json_decode($body, true); switch ($event) { case 'incident.created': // crea odv_activity... break; case 'risk.created_high': // aggiungi a risk register... break; }
Creare la prima API Key
  1. Login su NIS2 Agile con ruolo org_admin o superiore
  2. Vai su Settings → API Keys (sezione Integrazioni)
  3. Click "Nuova API Key" → nome descrittivo (es: "lg231 Integration") → scope: read:all
  4. Copia la chiave — formato nis2_XXXX... — visibile una sola volta
  5. Nota l'Organization ID dal URL del dashboard (es: /dashboard.html dopo login)
  6. In lg231: salva via PATCH /companies/{id}/provider-config con nis2_api_key e nis2_org_id
Test rapido (curl)
# 1. Verifica senza auth curl https://nis2.agile.software/api/services/status # 2. Compliance summary curl -H "X-API-Key: nis2_TUA_CHIAVE" \ https://nis2.agile.software/api/services/compliance-summary | python3 -m json.tool # 3. Incidenti significativi aperti curl -H "X-API-Key: nis2_TUA_CHIAVE" \ "https://nis2.agile.software/api/services/incidents/feed?significant=1&status=open" # 4. Rischi high/critical curl -H "X-API-Key: nis2_TUA_CHIAVE" \ "https://nis2.agile.software/api/services/risks/feed?level=high,critical"
Contatti e riferimenti
App NIS2 Agilehttps://nis2.agile.software
Specifiche OpenAPIGET /api/services/openapi.json
Test Runnertest-runner.php
Amministratorecristiano.benassati@gmail.com
Documento tecnico lg231docs/integration/lg231-nis2-integration.md