[CORE] Initial project scaffold - NIS2 Agile Compliance Platform
Complete MVP implementation including: - PHP 8.4 backend with Front Controller pattern (80+ API endpoints) - Multi-tenant architecture with organization_id isolation - JWT authentication (HS256, 2h access + 7d refresh tokens) - 14 controllers: Auth, Organization, Assessment, Dashboard, Risk, Incident, Policy, SupplyChain, Training, Asset, Audit, Admin - AI Service integration (Anthropic Claude API) for gap analysis, risk suggestions, policy generation, incident classification - NIS2 gap analysis questionnaire (~80 questions, 10 categories) - MySQL schema (20 tables) with NIS2 Art. 21 compliance controls - NIS2 Art. 23 incident reporting workflow (24h/72h/30d) - Frontend: login, register, dashboard, assessment wizard, org setup - Docker configuration (PHP-FPM + Nginx + MySQL) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* NIS2 Agile - Asset Controller
|
||||
*
|
||||
* Inventario asset IT/OT, classificazione, dipendenze.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/BaseController.php';
|
||||
|
||||
class AssetController extends BaseController
|
||||
{
|
||||
public function list(): void
|
||||
{
|
||||
$this->requireOrgAccess();
|
||||
$pagination = $this->getPagination();
|
||||
|
||||
$where = 'organization_id = ?';
|
||||
$params = [$this->getCurrentOrgId()];
|
||||
|
||||
if ($this->hasParam('asset_type')) {
|
||||
$where .= ' AND asset_type = ?';
|
||||
$params[] = $this->getParam('asset_type');
|
||||
}
|
||||
if ($this->hasParam('criticality')) {
|
||||
$where .= ' AND criticality = ?';
|
||||
$params[] = $this->getParam('criticality');
|
||||
}
|
||||
if ($this->hasParam('status')) {
|
||||
$where .= ' AND status = ?';
|
||||
$params[] = $this->getParam('status');
|
||||
}
|
||||
|
||||
$total = Database::count('assets', $where, $params);
|
||||
$assets = Database::fetchAll(
|
||||
"SELECT a.*, u.full_name as owner_name
|
||||
FROM assets a
|
||||
LEFT JOIN users u ON u.id = a.owner_user_id
|
||||
WHERE a.{$where}
|
||||
ORDER BY a.criticality DESC, a.name
|
||||
LIMIT {$pagination['per_page']} OFFSET {$pagination['offset']}",
|
||||
$params
|
||||
);
|
||||
|
||||
$this->jsonPaginated($assets, $total, $pagination['page'], $pagination['per_page']);
|
||||
}
|
||||
|
||||
public function create(): void
|
||||
{
|
||||
$this->requireOrgRole(['org_admin', 'compliance_manager']);
|
||||
$this->validateRequired(['name', 'asset_type']);
|
||||
|
||||
$assetId = Database::insert('assets', [
|
||||
'organization_id' => $this->getCurrentOrgId(),
|
||||
'name' => trim($this->getParam('name')),
|
||||
'asset_type' => $this->getParam('asset_type'),
|
||||
'category' => $this->getParam('category'),
|
||||
'description' => $this->getParam('description'),
|
||||
'criticality' => $this->getParam('criticality', 'medium'),
|
||||
'owner_user_id' => $this->getParam('owner_user_id'),
|
||||
'location' => $this->getParam('location'),
|
||||
'ip_address' => $this->getParam('ip_address'),
|
||||
'vendor' => $this->getParam('vendor'),
|
||||
'version' => $this->getParam('version'),
|
||||
'serial_number' => $this->getParam('serial_number'),
|
||||
'purchase_date' => $this->getParam('purchase_date'),
|
||||
'warranty_expiry' => $this->getParam('warranty_expiry'),
|
||||
'dependencies' => $this->getParam('dependencies') ? json_encode($this->getParam('dependencies')) : null,
|
||||
]);
|
||||
|
||||
$this->logAudit('asset_created', 'asset', $assetId);
|
||||
$this->jsonSuccess(['id' => $assetId], 'Asset registrato', 201);
|
||||
}
|
||||
|
||||
public function get(int $id): void
|
||||
{
|
||||
$this->requireOrgAccess();
|
||||
|
||||
$asset = Database::fetchOne(
|
||||
'SELECT a.*, u.full_name as owner_name
|
||||
FROM assets a LEFT JOIN users u ON u.id = a.owner_user_id
|
||||
WHERE a.id = ? AND a.organization_id = ?',
|
||||
[$id, $this->getCurrentOrgId()]
|
||||
);
|
||||
|
||||
if (!$asset) {
|
||||
$this->jsonError('Asset non trovato', 404, 'ASSET_NOT_FOUND');
|
||||
}
|
||||
|
||||
$this->jsonSuccess($asset);
|
||||
}
|
||||
|
||||
public function update(int $id): void
|
||||
{
|
||||
$this->requireOrgRole(['org_admin', 'compliance_manager']);
|
||||
|
||||
$updates = [];
|
||||
$fields = ['name', 'asset_type', 'category', 'description', 'criticality',
|
||||
'owner_user_id', 'location', 'ip_address', 'vendor', 'version',
|
||||
'serial_number', 'purchase_date', 'warranty_expiry', 'status'];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($this->hasParam($field)) {
|
||||
$updates[$field] = $this->getParam($field);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasParam('dependencies')) {
|
||||
$updates['dependencies'] = json_encode($this->getParam('dependencies'));
|
||||
}
|
||||
|
||||
if (!empty($updates)) {
|
||||
Database::update('assets', $updates, 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]);
|
||||
$this->logAudit('asset_updated', 'asset', $id, $updates);
|
||||
}
|
||||
|
||||
$this->jsonSuccess($updates, 'Asset aggiornato');
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$this->requireOrgRole(['org_admin']);
|
||||
$deleted = Database::delete('assets', 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]);
|
||||
if ($deleted === 0) {
|
||||
$this->jsonError('Asset non trovato', 404, 'ASSET_NOT_FOUND');
|
||||
}
|
||||
$this->logAudit('asset_deleted', 'asset', $id);
|
||||
$this->jsonSuccess(null, 'Asset eliminato');
|
||||
}
|
||||
|
||||
public function dependencyMap(): void
|
||||
{
|
||||
$this->requireOrgAccess();
|
||||
|
||||
$assets = Database::fetchAll(
|
||||
'SELECT id, name, asset_type, criticality, dependencies
|
||||
FROM assets
|
||||
WHERE organization_id = ? AND status = "active"',
|
||||
[$this->getCurrentOrgId()]
|
||||
);
|
||||
|
||||
$nodes = [];
|
||||
$edges = [];
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$nodes[] = [
|
||||
'id' => $asset['id'],
|
||||
'label' => $asset['name'],
|
||||
'type' => $asset['asset_type'],
|
||||
'criticality' => $asset['criticality'],
|
||||
];
|
||||
|
||||
$deps = json_decode($asset['dependencies'] ?? '[]', true) ?: [];
|
||||
foreach ($deps as $depId) {
|
||||
$edges[] = ['from' => $asset['id'], 'to' => (int) $depId];
|
||||
}
|
||||
}
|
||||
|
||||
$this->jsonSuccess(['nodes' => $nodes, 'edges' => $edges]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user