ServerManager project files

This commit is contained in:
2026-06-06 17:58:34 -04:00
parent ce1863d955
commit 9c0bc7f189
502 changed files with 87837 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace ServerManager\Middleware;
use ServerManager\Core\Session;
class AuthMiddleware
{
public function handle(): mixed
{
if (!Session::has('user_id')) {
Session::setFlash('error', 'You must be logged in to access this page.');
$this->redirect('/login');
}
if (Session::get('session_expiry') && time() > Session::get('session_expiry')) {
Session::destroy();
Session::setFlash('error', 'Your session has expired. Please log in again.');
$this->redirect('/login');
}
return null;
}
private function redirect(string $url): never
{
if (!headers_sent()) {
header("Location: {$url}");
} else {
echo '<script>window.location.href="' . $url . '";</script>';
}
exit;
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace ServerManager\Middleware;
use ServerManager\Core\Session;
use ServerManager\Core\Security;
class CSRFMiddleware
{
public function handle(): mixed
{
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
return null;
}
$token = $_POST['_csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
$security = new Security();
if (!$security->validateCSRFToken($token)) {
if ($this->isApiRequest()) {
http_response_code(419);
header('Content-Type: application/json');
echo json_encode(['error' => 'CSRF token mismatch']);
exit;
}
Session::setFlash('error', 'The form has expired. Please try again.');
header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '/'));
exit;
}
return null;
}
private function isApiRequest(): bool
{
return str_starts_with($_SERVER['REQUEST_URI'] ?? '', '/api/') ||
str_starts_with($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json');
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace ServerManager\Middleware;
use ServerManager\Core\Session;
use ServerManager\Core\Database;
use ServerManager\Core\App;
class RateLimitMiddleware
{
public function handle(): mixed
{
$config = App::getConfig()['rate_limit'];
$maxAttempts = $config['max_attempts'] ?? 5;
$decayMinutes = $config['decay_minutes'] ?? 15;
$ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
$key = 'rate_limit_' . $ip . '_' . ($_SERVER['REQUEST_URI'] ?? '/');
$attempts = Session::get($key, ['count' => 0, 'first_attempt' => 0]);
if ($attempts['first_attempt'] && (time() - $attempts['first_attempt']) > ($decayMinutes * 60)) {
$attempts = ['count' => 0, 'first_attempt' => 0];
}
$attempts['count']++;
if ($attempts['first_attempt'] === 0) {
$attempts['first_attempt'] = time();
}
Session::set($key, $attempts);
if ($attempts['count'] > $maxAttempts) {
$retryAfter = ($decayMinutes * 60) - (time() - $attempts['first_attempt']);
if ($this->isApiRequest()) {
http_response_code(429);
header('Content-Type: application/json');
header("Retry-After: {$retryAfter}");
echo json_encode([
'error' => 'Too many requests. Please try again later.',
'retry_after' => $retryAfter,
]);
exit;
}
http_response_code(429);
header("Retry-After: {$retryAfter}");
echo '<h1>429 Too Many Requests</h1><p>Please wait ' . ceil($retryAfter / 60) . ' minutes before trying again.</p>';
exit;
}
return null;
}
private function isApiRequest(): bool
{
return str_starts_with($_SERVER['REQUEST_URI'] ?? '', '/api/');
}
}

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace ServerManager\Middleware;
use ServerManager\Core\Session;
class RoleMiddleware
{
private string $requiredRole;
public function __construct(string $requiredRole = 'admin')
{
$this->requiredRole = $requiredRole;
}
public function handle(): mixed
{
$userRole = Session::get('user_role');
$roleHierarchy = [
'super_admin' => 3,
'admin' => 2,
'operator' => 1,
];
$requiredLevel = $roleHierarchy[$this->requiredRole] ?? 0;
$userLevel = $roleHierarchy[$userRole] ?? 0;
if ($userLevel < $requiredLevel) {
if ($this->isApiRequest()) {
http_response_code(403);
header('Content-Type: application/json');
echo json_encode(['error' => 'Forbidden: insufficient permissions']);
exit;
}
Session::setFlash('error', 'You do not have permission to access this area.');
header('Location: /dashboard');
exit;
}
return null;
}
private function isApiRequest(): bool
{
return str_starts_with($_SERVER['REQUEST_URI'] ?? '', '/api/') ||
str_starts_with($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json');
}
public static function superAdmin(): self
{
return new self('super_admin');
}
public static function admin(): self
{
return new self('admin');
}
public static function operator(): self
{
return new self('operator');
}
}