Files
server-manager/src/Services/AuditService.php

218 lines
6.2 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace ServerManager\Services;
use ServerManager\Core\Database;
use ServerManager\Core\Session;
use ServerManager\Core\Logger;
class AuditService
{
private Database $db;
private Logger $logger;
public function __construct()
{
$this->db = Database::getInstance();
$this->logger = Logger::getInstance();
}
public function log(
string $action,
string $entityType,
?int $entityId,
?array $details = null,
?array $metadata = null
): void {
$userId = Session::get('user_id');
$username = Session::get('username');
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
$now = date('Y-m-d H:i:s');
$this->db->insert('audit_logs', [
'user_id' => $userId,
'username' => $username ?? 'system',
'action' => $action,
'entity_type' => $entityType,
'entity_id' => $entityId,
'details' => $details ? json_encode($details, JSON_UNESCAPED_SLASHES) : null,
'ip_address' => $ip,
'user_agent' => $userAgent,
'created_at' => $now,
]);
$logMessage = sprintf(
'[AUDIT] %s | %s | %s:%s | User: %s (%d)',
$action,
$entityType,
$entityId ?? 'N/A',
$ip,
$username ?? 'system',
$userId ?? 0
);
$this->logger->info($logMessage);
if (in_array($action, ['credential_access', 'credential_decrypt', 'credential_use'])) {
$this->logger->warning("[CREDENTIAL_ACCESS] {$logMessage}");
}
}
public function logCredentialAccess(int $serverId, string $reason): void
{
$server = $this->db->fetch('SELECT name, ip_address FROM servers WHERE id = ?', [$serverId]);
$serverName = $server['name'] ?? 'Unknown';
$this->log(
'credential_access',
'server_credentials',
$serverId,
[
'server_name' => $serverName,
'ip_address' => $server['ip_address'] ?? 'unknown',
'reason' => $reason,
'timestamp' => date('c'),
]
);
}
public function logLogin(string $username, bool $success, ?string $reason = null): void
{
$this->log(
$success ? 'login_success' : 'login_failed',
'user',
null,
[
'username' => $username,
'success' => $success,
'reason' => $reason,
]
);
if (!$success) {
$this->logger->warning("[SECURITY] Failed login attempt for username: {$username}", [
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
]);
}
}
public function logServerAction(int $serverId, string $action, string $command): void
{
$this->log(
'server_action',
'server',
$serverId,
[
'action' => $action,
'command' => $command,
]
);
}
public function logUserManagement(int $userId, string $action): void
{
$this->log(
$action,
'user',
$userId,
[
'performed_by' => Session::get('username'),
]
);
}
public function getRecentActivity(int $limit = 20, ?int $userId = null): array
{
$sql = 'SELECT al.*, u.username as actor_name
FROM audit_logs al
LEFT JOIN users u ON al.user_id = u.id';
$params = [];
if ($userId !== null) {
$sql .= ' WHERE al.user_id = ?';
$params[] = $userId;
}
$sql .= ' ORDER BY al.created_at DESC LIMIT ?';
$params[] = $limit;
return $this->db->fetchAll($sql, $params);
}
public function getAuditLogs(int $page = 1, int $perPage = 50, array $filters = []): array
{
$where = [];
$params = [];
if (!empty($filters['action'])) {
$where[] = 'al.action = ?';
$params[] = $filters['action'];
}
if (!empty($filters['user_id'])) {
$where[] = 'al.user_id = ?';
$params[] = $filters['user_id'];
}
if (!empty($filters['username'])) {
$where[] = '(al.username LIKE ? OR u.username LIKE ?)';
$params[] = '%' . $filters['username'] . '%';
$params[] = '%' . $filters['username'] . '%';
}
if (!empty($filters['entity_type'])) {
$where[] = 'al.entity_type = ?';
$params[] = $filters['entity_type'];
}
if (!empty($filters['ip_address'])) {
$where[] = 'al.ip_address LIKE ?';
$params[] = '%' . $filters['ip_address'] . '%';
}
if (!empty($filters['details'])) {
$where[] = 'al.details LIKE ?';
$params[] = '%' . $filters['details'] . '%';
}
if (!empty($filters['date_from'])) {
$where[] = 'al.created_at >= ?';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'al.created_at <= ?';
$params[] = $filters['date_to'];
}
$whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
$offset = ($page - 1) * $perPage;
$total = $this->db->fetch(
"SELECT COUNT(*) as total FROM audit_logs al {$whereClause}",
$params
);
$logs = $this->db->fetchAll(
"SELECT al.*, u.username as actor_name
FROM audit_logs al
LEFT JOIN users u ON al.user_id = u.id
{$whereClause}
ORDER BY al.created_at DESC
LIMIT ? OFFSET ?",
array_merge($params, [$perPage, $offset])
);
return [
'data' => $logs,
'total' => (int) ($total['total'] ?? 0),
'page' => $page,
'per_page' => $perPage,
'total_pages' => ceil((int) ($total['total'] ?? 0) / $perPage),
];
}
}