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), ]; } }