ServerManager project files
This commit is contained in:
36
src/Middleware/AuthMiddleware.php
Executable file
36
src/Middleware/AuthMiddleware.php
Executable 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;
|
||||
}
|
||||
}
|
||||
43
src/Middleware/CSRFMiddleware.php
Executable file
43
src/Middleware/CSRFMiddleware.php
Executable 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');
|
||||
}
|
||||
}
|
||||
63
src/Middleware/RateLimitMiddleware.php
Executable file
63
src/Middleware/RateLimitMiddleware.php
Executable 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/');
|
||||
}
|
||||
}
|
||||
67
src/Middleware/RoleMiddleware.php
Executable file
67
src/Middleware/RoleMiddleware.php
Executable 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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user