Files
server/app/Middleware/ApiMiddleware.php

129 lines
5.0 KiB
PHP

<?php
namespace App\Middleware;
use App\Utils\JWT;
use App\Utils\View;
class ApiMiddleware
{
public function handle()
{
$headers = getallheaders();
// Handle case-insensitive headers and Proxy fallbacks
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? '';
$machineId = $headers['X-Machine-ID'] ?? $headers['x-machine-id'] ?? $_SERVER['HTTP_X_MACHINE_ID'] ?? '';
if (!preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
View::json(['error' => 'Serial Key not provided'], 401);
return false;
}
$serialKey = $matches[1];
if (empty($machineId)) {
View::json(['error' => 'Machine ID not provided'], 400);
return false;
}
try {
$conn = \App\Config\Database::getInstance()->getConnection();
// Find server by Serial Key and join with client to check both statuses
$stmt = $conn->prepare("
SELECT s.id, s.machine_id, s.status, s.ip_v4, s.client_id, c.status as client_status
FROM servers s
JOIN clients c ON s.client_id = c.id
WHERE s.serial_key = :serial
");
$stmt->execute(['serial' => $serialKey]);
$server = $stmt->fetch();
if (!$server) {
$this->log(null, 'auth_failed', "Invalid Serial Key: $serialKey");
View::json(['error' => 'Invalid Serial Key'], 401);
return false;
}
// Get Client IP (Handle Proxy)
$clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
// If multiple IPs in X-Forwarded-For, take the first one
if (strpos($clientIp, ',') !== false) {
$clientIp = trim(explode(',', $clientIp)[0]);
}
// Validate IP
if ($server['ip_v4'] !== $clientIp) {
$this->log($server['id'], 'auth_blocked', "Invalid IP Address. Expected: {$server['ip_v4']}, Got: $clientIp");
View::json(['error' => 'Invalid IP Address'], 403);
return false;
}
if ($server['status'] !== 'active') {
$this->log($server['id'], 'auth_blocked', 'Server is inactive');
View::json(['error' => 'Server is inactive'], 403);
return false;
}
// Check if client is active
if ($server['client_status'] !== 'active') {
$this->log($server['id'], 'auth_blocked', 'Client is inactive');
View::json(['error' => 'Client is inactive'], 403);
return false;
}
// Bind Machine ID if first use
if (empty($server['machine_id'])) {
$update = $conn->prepare("UPDATE servers SET machine_id = :mid WHERE id = :id");
$update->execute(['mid' => $machineId, 'id' => $server['id']]);
$this->log($server['id'], 'bind_machine', "Machine ID bound: $machineId");
}
// Validate Machine ID
elseif ($server['machine_id'] !== $machineId) {
$this->log($server['id'], 'auth_blocked', "Invalid Machine ID. Expected: {$server['machine_id']}, Got: $machineId");
View::json(['error' => 'Serial Key already bound to another machine'], 403);
return false;
}
// Log Success (Optional: Log every sync might be too much, maybe only on change?
// For now, let's log sync to show activity as requested "verificar conexões")
$this->log($server['id'], 'sync', 'Agent connected');
// Update Last Seen
$updateLastSeen = $conn->prepare("UPDATE servers SET last_seen = NOW() WHERE id = :id");
$updateLastSeen->execute(['id' => $server['id']]);
return true;
} catch (\Exception $e) {
$this->log(null, 'error', 'System error: ' . $e->getMessage());
View::json(['error' => 'Authentication error'], 500);
return false;
}
}
private function log($serverId, $action, $message)
{
try {
$conn = \App\Config\Database::getInstance()->getConnection();
$stmt = $conn->prepare("INSERT INTO api_logs (server_id, action, message, ip_address) VALUES (:sid, :act, :msg, :ip)");
// Get Client IP (Handle Proxy)
$clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
// If multiple IPs in X-Forwarded-For, take the first one
if (strpos($clientIp, ',') !== false) {
$clientIp = trim(explode(',', $clientIp)[0]);
}
$stmt->execute([
'sid' => $serverId,
'act' => $action,
'msg' => $message,
'ip' => $clientIp
]);
} catch (\Exception $e) {
// Silent fail for logs
}
}
}