🚀 مُصادَق: الإطلاق الأولي للنظام المتكامل

This commit is contained in:
Hamza-Ayed
2026-05-03 00:59:39 +03:00
commit d0e538408d
43 changed files with 2554 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Core\{Request, Response};
use App\Services\Security\JwtService;
use Exception;
final class AuthMiddleware
{
public function __construct(private readonly JwtService $jwtService) {}
public function handle(Request $request, callable $next): mixed
{
$authHeader = $request->getHeader('Authorization');
if (!$authHeader || !str_starts_with($authHeader, 'Bearer ')) {
Response::error('يجب تسجيل الدخول للوصول إلى هذا المورد', 'UNAUTHORIZED', 401);
return null;
}
$token = substr($authHeader, 7);
try {
$decoded = $this->jwtService->verifyToken($token);
$request->user = (object) $decoded;
$request->tenantId = $decoded['tenant_id'] ?? null;
} catch (Exception $e) {
Response::error('جلسة العمل منتهية أو غير صالحة', 'UNAUTHORIZED', 401);
return null;
}
return $next($request);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Core\{Request, Response, Redis};
use App\Services\Security\HmacService;
use App\Core\Database;
final class HmacMiddleware
{
public function __construct(private readonly HmacService $hmac) {}
public function handle(Request $request, callable $next): mixed
{
$publicKey = $request->getHeader('X-Api-Key');
$signature = $request->getHeader('X-Signature');
$timestamp = $request->getHeader('X-Timestamp');
$nonce = $request->getHeader('X-Nonce');
if (!$publicKey || !$signature || !$timestamp || !$nonce) {
Response::error('بيانات التوقيع (HMAC) ناقصة', 'HMAC_MISSING', 401);
return null;
}
// 1. Lookup Secret by Public Key
$db = Database::getInstance();
$stmt = $db->prepare("SELECT secret_hash, tenant_id FROM api_keys WHERE public_key = ? AND is_active = 1 LIMIT 1");
$stmt->execute([$publicKey]);
$apiKey = $stmt->fetch();
if (!$apiKey) {
Response::error('مفتاح API غير صالح', 'HMAC_INVALID_KEY', 401);
return null;
}
// 2. Verify Signature
// Note: secret_hash in DB is the actual secret for signing
$isValid = $this->hmac->verify(
$apiKey['secret_hash'],
$request->getMethod(),
$request->getPath(),
$timestamp,
$nonce,
json_encode($request->getBody()),
$signature
);
if (!$isValid) {
Response::error('توقيع الطلب غير صحيح', 'HMAC_INVALID_SIGNATURE', 401);
return null;
}
// 3. Set context
$request->tenantId = $apiKey['tenant_id'];
return $next($request);
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Core\{Request, Response, Redis};
final class RateLimitMiddleware
{
/**
* @param int $limit Requests allowed
* @param int $window Seconds window
*/
public function handle(Request $request, callable $next, int $limit = 60, int $window = 60): mixed
{
$redis = Redis::getInstance();
$ip = $_SERVER['REMOTE_ADDR'];
$key = "ratelimit:" . md5($request->getPath() . "|" . $ip);
$current = $redis->get($key);
if ($current && (int)$current >= $limit) {
Response::error('لقد تجاوزت الحد المسموح من الطلبات، يرجى المحاولة لاحقاً', 'RATE_LIMIT_EXCEEDED', 429);
return null;
}
if (!$current) {
$redis->setex($key, $window, 1);
} else {
$redis->incr($key);
}
return $next($request);
}
}