enforce(RateLimiter::identifier(), 'login'); // تتبع المحاولات الفاشلة لكل حساب if ($redis && !empty($phone)) { $accountKey = "login_attempts:account:" . hash('sha256', $phone); $accountAttempts = (int) $redis->get($accountKey); if ($accountAttempts >= 5) { $ttl = $redis->ttl($accountKey); $waitMinutes = ceil($ttl / 60); jsonError("تم تعليق تسجيل الدخول لهذا الحساب مؤقتاً. يرجى المحاولة بعد {$waitMinutes} دقيقة."); exit; } } // البحث عن المشرف باستخدام بصمة الجهاز (Fingerprint Hash) $fpHash = hash('sha256', $fingerprint); // تسجيل محاولة تسجيل الدخول للتدقيق $loginAuditData = [ 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', 'fingerprint_hash' => $fpHash, 'phone_hash' => !empty($phone) ? hash('sha256', $phone) : null, 'timestamp' => date('Y-m-d H:i:s'), 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', 'result' => 'pending' ]; error_log("[LOGIN_AUDIT] " . json_encode($loginAuditData)); try { $con = Database::get('main'); $stmt = $con->prepare("SELECT * FROM adminUser WHERE fingerprint_hash = :fp LIMIT 1"); $stmt->execute([':fp' => $fpHash]); $admin = $stmt->fetch(PDO::FETCH_ASSOC); // إذا لم يتم العثور بالبصمة، نبحث بالـ ID المباشر أو بفك تشفير البيانات (AES-GCM Decryption in PHP) if (!$admin && !empty($phone)) { // 1. بحث مباشر بالـ ID المعياري Unencrypted $stmtId = $con->prepare("SELECT * FROM adminUser WHERE id = :id LIMIT 1"); $stmtId->execute([':id' => $phone]); $admin = $stmtId->fetch(PDO::FETCH_ASSOC); // 2. إذا لم يتم العثور بالـ ID، نفحص الحقول المشفّرة (email / phone / name) عبر فك التشفير if (!$admin) { $stmtAll = $con->query("SELECT * FROM adminUser"); while ($row = $stmtAll->fetch(PDO::FETCH_ASSOC)) { $decPhone = ($encryptionHelper && !empty($row['phone'])) ? $encryptionHelper->decryptData($row['phone']) : $row['phone']; $decEmail = ($encryptionHelper && !empty($row['email'])) ? $encryptionHelper->decryptData($row['email']) : $row['email']; $decName = ($encryptionHelper && !empty($row['name'])) ? $encryptionHelper->decryptData($row['name']) : $row['name']; if ($phone === $decPhone || $phone === $decEmail || $phone === $decName || $phone === $row['phone'] || $phone === $row['email']) { $admin = $row; break; } } } // عند إيجاد الحساب وتأكيد كلمة المرور، نقوم بتحديث وبث بصمة الجهاز للجلسة if ($admin && password_verify($password, $admin['password'])) { $encFpRaw = $encryptionHelper ? $encryptionHelper->encryptData($fingerprint) : $fingerprint; $updateStmt = $con->prepare("UPDATE adminUser SET fingerprint = :fp_raw, fingerprint_hash = :fp WHERE id = :id"); $updateStmt->execute([ ':fp_raw' => $encFpRaw, ':fp' => $fpHash, ':id' => $admin['id'] ]); $admin['fingerprint_hash'] = $fpHash; } } if ($admin) { // 1. التحقق من حالة الحساب if (isset($admin['status'])) { if ($admin['status'] === 'pending') { jsonError("حسابك قيد المراجعة حالياً. يرجى الانتظار للموافقة."); exit; } elseif ($admin['status'] === 'suspended') { jsonError("هذا الحساب معلق. يرجى التواصل مع المدير."); exit; } elseif ($admin['status'] === 'rejected') { jsonError("تم رفض طلب الانضمام لهذا الحساب."); exit; } } // 2. التحقق من كلمة المرور if (password_verify($password, $admin['password'])) { // إصدار JWT مباشرة عند نجاح كلمة المرور $jwtService = new JwtService($redis); $role = $admin['role'] ?? 'admin'; if ($redis) { $oldJti = $redis->get("active_jti:" . $admin['id']); if ($oldJti) { $jwtService->revokeToken($oldJti, 3600); } } $jwt = $jwtService->generateAccessToken($admin['id'], $role, $audience, $fingerprint); if ($encryptionHelper && !empty($admin['name'])) { $admin['name'] = $encryptionHelper->decryptData($admin['name']) ?: $admin['name']; } unset($admin['password']); printSuccess([ "message" => "Login successful", "admin" => $admin, "jwt" => $jwt, "expires_in" => 3600 ]); exit; } else { jsonError("كلمة المرور غير صحيحة."); } } else { jsonError("الحساب غير موجود. يرجى التأكد من اسم المستخدم أو البريد الإلكتروني وكلمة المرور."); } } catch (Throwable $e) { error_log("[Admin Login Throwable Error] " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString()); jsonError("حدث خطأ في السيرفر: " . $e->getMessage(), 500); }