Script scripts/seed_supplier_template.php: crea il template predefinito NIS2 base (is_default, pass_threshold 70) + 26 domande dal JSON canonico docs/supplier-portal/template-nis2-base.questions.json, mappate alle misure ACN GV.SC (Allegato 2 Det. 164179/2025) con nis2_ref/vuln_flag/high_criticality_only. Idempotente: template per nome, domande per question_code. Applicato a org 129 (Agile Technology, dogfooding): template id=1, 26 domande. Re-run verificato: 0 inserite, 26 gia presenti. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
101 lines
4.0 KiB
PHP
101 lines
4.0 KiB
PHP
<?php
|
|
/**
|
|
* Seed idempotente del template "NIS2 base" (questionario fornitori GV.SC) per una org.
|
|
*
|
|
* Uso (host): docker exec nis2-app php /var/www/nis2-agile/public/_seed_tpl.php <org_id>
|
|
* (scripts/ NON e bind-mountato nel container: copiare in public/ per l'esecuzione,
|
|
* oppure eseguire via CLI host con il giusto include path).
|
|
*
|
|
* Sorgente domande: docs/supplier-portal/template-nis2-base.questions.json (26 domande,
|
|
* mappate alle misure ACN GV.SC, Allegato 2 Det. 164179/2025).
|
|
*
|
|
* Idempotente: se esiste gia un template is_default con name "NIS2 base" per l'org,
|
|
* non duplica (verifica per nome) e salta le domande gia presenti (per question_code).
|
|
*/
|
|
|
|
$ROOT = '/var/www/nis2-agile';
|
|
require $ROOT . '/application/config/env.php';
|
|
require $ROOT . '/application/config/config.php';
|
|
require $ROOT . '/application/config/database.php';
|
|
|
|
$orgId = (int) ($argv[1] ?? 0);
|
|
if ($orgId <= 0) {
|
|
fwrite(STDERR, "ERRORE: org_id mancante. Uso: php seed_supplier_template.php <org_id>\n");
|
|
exit(1);
|
|
}
|
|
|
|
$org = Database::fetchOne('SELECT id, name FROM organizations WHERE id = ?', [$orgId]);
|
|
if (!$org) {
|
|
fwrite(STDERR, "ERRORE: organizzazione {$orgId} non trovata.\n");
|
|
exit(1);
|
|
}
|
|
|
|
$jsonPath = $ROOT . '/docs/supplier-portal/template-nis2-base.questions.json';
|
|
if (!is_file($jsonPath)) {
|
|
fwrite(STDERR, "ERRORE: file domande non trovato: {$jsonPath}\n");
|
|
exit(1);
|
|
}
|
|
$data = json_decode((string) file_get_contents($jsonPath), true);
|
|
$questions = $data['questions'] ?? [];
|
|
if (empty($questions)) {
|
|
fwrite(STDERR, "ERRORE: nessuna domanda nel JSON.\n");
|
|
exit(1);
|
|
}
|
|
|
|
// 1. Template (idempotente per nome)
|
|
$tplName = 'NIS2 base - Sicurezza catena di fornitura';
|
|
$tpl = Database::fetchOne(
|
|
'SELECT id FROM questionnaire_templates WHERE organization_id = ? AND name = ?',
|
|
[$orgId, $tplName]
|
|
);
|
|
if ($tpl) {
|
|
$tplId = (int) $tpl['id'];
|
|
echo "Template gia presente (id={$tplId}) per org {$orgId} ({$org['name']}).\n";
|
|
} else {
|
|
$tplId = Database::insert('questionnaire_templates', [
|
|
'organization_id' => $orgId,
|
|
'category_id' => null,
|
|
'name' => $tplName,
|
|
'description' => 'Template predefinito mappato alle misure ACN GV.SC (Allegato 2, Det. 164179/2025) e Art. 21.2(d)/21.3 Dir. (UE) 2022/2555. 26 domande, scoring per-vulnerabilita.',
|
|
'current_version' => '1.0',
|
|
'status' => 'active',
|
|
'pass_threshold' => 70,
|
|
'is_default' => 1,
|
|
'created_by' => null,
|
|
]);
|
|
echo "Template creato (id={$tplId}) per org {$orgId} ({$org['name']}).\n";
|
|
}
|
|
|
|
// 2. Domande (idempotente per question_code)
|
|
$inserted = 0; $skipped = 0;
|
|
foreach ($questions as $q) {
|
|
$code = $q['code'] ?? null;
|
|
if ($code === null) { continue; }
|
|
$exists = Database::fetchOne(
|
|
'SELECT id FROM questionnaire_questions WHERE template_id = ? AND question_code = ?',
|
|
[$tplId, $code]
|
|
);
|
|
if ($exists) { $skipped++; continue; }
|
|
|
|
Database::insert('questionnaire_questions', [
|
|
'template_id' => $tplId,
|
|
'organization_id' => $orgId,
|
|
'question_code' => $code,
|
|
'question_text' => $q['text'] ?? '',
|
|
'question_type' => $q['type'] ?? 'yes_no_partial',
|
|
'options' => isset($q['options']) && $q['options'] !== null
|
|
? json_encode($q['options'], JSON_UNESCAPED_UNICODE) : null,
|
|
'weight' => (float) ($q['weight'] ?? 1),
|
|
'is_required' => !empty($q['required']) ? 1 : 0,
|
|
'order_index' => (int) ($q['order_index'] ?? 0),
|
|
'nis2_ref' => $q['nis2_ref'] ?? null,
|
|
'vuln_flag' => $q['vuln_flag'] ?? null,
|
|
'high_criticality_only' => !empty($q['high_criticality_only']) ? 1 : 0,
|
|
]);
|
|
$inserted++;
|
|
}
|
|
|
|
$total = Database::fetchOne('SELECT COUNT(*) AS n FROM questionnaire_questions WHERE template_id = ?', [$tplId]);
|
|
echo "Domande: inserite={$inserted}, gia presenti={$skipped}, totale nel template={$total['n']}.\n";
|
|
echo "OK.\n";
|