From 312608de930dffc287d40bfcdc1264ac513d233a Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 1 May 2026 00:47:30 +0300 Subject: [PATCH] admin 13 --- Admin/auth/approve_admin.php | 48 +++++++++++++++++++++ Admin/auth/list_pending.php | 33 ++++++++++++++ Admin/auth/login.php | 55 +++++++++++++++++------- Admin/auth/loginWallet.php | 26 +++++++---- Admin/auth/migrate_db.php | 28 ++++++++++++ Admin/auth/register.php | 59 +++++++++++++++++++++++++ Admin/auth/verify_login.php | 83 ++++++++++++++++++++++++++++++++++++ Admin/dashbord.php | 7 +++ ggg.php | 78 +++++++++++++++++++++++++++++++++ 9 files changed, 394 insertions(+), 23 deletions(-) create mode 100644 Admin/auth/approve_admin.php create mode 100644 Admin/auth/list_pending.php create mode 100644 Admin/auth/migrate_db.php create mode 100644 Admin/auth/register.php create mode 100644 Admin/auth/verify_login.php create mode 100644 ggg.php diff --git a/Admin/auth/approve_admin.php b/Admin/auth/approve_admin.php new file mode 100644 index 0000000..1f0d20e --- /dev/null +++ b/Admin/auth/approve_admin.php @@ -0,0 +1,48 @@ + 'Forbidden. Super Admin access required.']); + exit; +} + +$targetId = filterRequest('admin_id'); +$action = filterRequest('action'); // approved, rejected, suspended + +if (empty($targetId) || empty($action)) { + jsonError("Admin ID and action are required."); + exit; +} + +if (!in_array($action, ['approved', 'rejected', 'suspended'])) { + jsonError("Invalid action."); + exit; +} + +try { + $con = Database::get('main'); + + $sql = "UPDATE adminUser SET status = :status, approved_by = :by, approved_at = NOW() WHERE id = :id"; + $stmt = $con->prepare($sql); + $stmt->execute([ + ':status' => $action, + ':by' => $user_id, // السوبر أدمن الحالي + ':id' => $targetId + ]); + + if ($stmt->rowCount() > 0) { + printSuccess(null, "Admin status updated to $action."); + } else { + jsonError("Admin not found or status already updated."); + } + +} catch (Exception $e) { + error_log("[Approve Admin Error] " . $e->getMessage()); + jsonError("Server Error: " . $e->getMessage()); +} diff --git a/Admin/auth/list_pending.php b/Admin/auth/list_pending.php new file mode 100644 index 0000000..6e4af20 --- /dev/null +++ b/Admin/auth/list_pending.php @@ -0,0 +1,33 @@ + 'Forbidden. Super Admin access required.']); + exit; +} + +try { + $con = Database::get('main'); + + $stmt = $con->prepare("SELECT id, name, phone, created_at FROM adminUser WHERE status = 'pending' ORDER BY created_at DESC"); + $stmt->execute(); + $pending = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // فك تشفير الأسماء + foreach ($pending as &$admin) { + $admin['name'] = $encryptionHelper->decryptData($admin['name']) ?: $admin['name']; + } + + printSuccess($pending); + +} catch (Exception $e) { + error_log("[List Pending Admins Error] " . $e->getMessage()); + jsonError("Server Error: " . $e->getMessage()); +} diff --git a/Admin/auth/login.php b/Admin/auth/login.php index 4957fb1..38aae4e 100755 --- a/Admin/auth/login.php +++ b/Admin/auth/login.php @@ -24,25 +24,50 @@ try { $admin = $stmt->fetch(PDO::FETCH_ASSOC); if ($admin) { - // التحقق من كلمة المرور الهاش + // 1. التحقق من حالة الحساب + 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'])) { - // فك تشفير الاسم للعرض في التطبيق - $admin['name'] = $encryptionHelper->decryptData($admin['name']) ?: $admin['name']; - unset($admin['password']); - - $jwtService = new JwtService($redis); - $role = $admin['role'] ?? 'admin'; + // 3. توليد رمز تحقق OTP وإرساله عبر WhatsApp + $otp = rand(10000, 99999); + $phone = $admin['phone'] ?? ''; // تأكد من وجود حقل الهاتف في الجدول - // توليد توكن الدخول مع ربطه ببصمة الجهاز - $jwt = $jwtService->generateAccessToken($admin['id'], $role, $audience, $fingerprint); + if (empty($phone)) { + // Fallback للأرقام المسموح لها إذا لم يكن الرقم مسجلاً في الجدول + // (قد نحتاج لتحسين هذه النقطة لاحقاً) + jsonError("رقم الهاتف غير مسجل لهذا الحساب. يرجى مراجعة الإدارة."); + exit; + } - printSuccess([ - "message" => "Login successful", - "admin" => $admin, - "jwt" => $jwt, - "expires_in" => 3600 - ]); + $messageBody = "رمز التحقق الخاص بك للدخول إلى لوحة الإدارة هو: $otp"; + $success = sendWhatsAppFromServer($phone, $messageBody); + + if ($success) { + // حفظ الرمز في قاعدة البيانات للتحقق لاحقاً + $stmt = $con->prepare("INSERT INTO token_verification_admin (phone_number, token, expiration_time) + VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 10 MINUTE)) + ON DUPLICATE KEY UPDATE token = VALUES(token), expiration_time = VALUES(expiration_time)"); + $stmt->execute([$phone, $otp]); + + printSuccess([ + "status" => "otp_required", + "message" => "تم إرسال رمز التحقق إلى WhatsApp الخاص بك.", + "phone" => $phone + ]); + } else { + jsonError("فشل في إرسال رمز التحقق عبر WhatsApp."); + } } else { jsonError("كلمة المرور غير صحيحة."); } diff --git a/Admin/auth/loginWallet.php b/Admin/auth/loginWallet.php index d50061c..05edf31 100644 --- a/Admin/auth/loginWallet.php +++ b/Admin/auth/loginWallet.php @@ -14,19 +14,17 @@ use Firebase\JWT\JWT; $jwtService = new JwtService($redis ?? null); $admin = $jwtService->authenticate(); -if ($admin->role !== 'admin') { +if ($admin->role !== 'admin' && $admin->role !== 'super_admin') { jsonError("Unauthorized. Admin access required."); exit; } try { // جلب المفتاح المشترك لسيرفر المحفظة - // الأولوية لملف المفتاح المخصص للمدفوعات إن وجد، وإلا نستخدم الـ env $payKeyPath = '/home/intaleq-api/.secret_key_pay'; $payKey = file_exists($payKeyPath) ? trim(file_get_contents($payKeyPath)) : getenv('SECRET_KEY_PAY'); if (empty($payKey)) { - // Fallback للمفتاح الرئيسي إذا لم يتوفر مفتاح خاص بالدفع (يجب التأكد من تطابقه مع سيرفر المحفظة) $payKey = trim(@file_get_contents('/home/intaleq-api/.secret_key')); } @@ -39,21 +37,34 @@ try { $audience = 'Tripz-Wallet'; $hmacSecret = getenv('SECRET_KEY_HMAC') ?: ''; - $ttl = 3600; // ساعة واحدة + $ttl = 600; // 10 دقائق $iat = time(); $exp = $iat + $ttl; + $jti = bin2hex(random_bytes(16)); // محتوى التوكن (Payload) $payload = [ 'iss' => $issuer, 'aud' => $audience, 'user_id' => $admin->user_id, - 'role' => 'admin', + 'role' => $admin->role, // استخدام الـ role الحالي (admin أو super_admin) 'iat' => $iat, 'exp' => $exp, - 'jti' => bin2hex(random_bytes(16)) + 'jti' => $jti ]; + // إلغاء التوكن القديم إذا وجد في Redis + if ($redis) { + $oldJtiKey = "wallet_jti:" . $admin->user_id; + $oldJti = $redis->get($oldJtiKey); + if ($oldJti) { + // إضافة التوكن القديم للقائمة السوداء + $redis->setex("jwt:blacklist:$oldJti", $ttl + 60, '1'); + } + // تخزين الـ JTI الجديد + $redis->setex($oldJtiKey, $ttl, $jti); + } + // إضافة بصمة الجهاز للتوكن لزيادة الأمان $fpHeader = $_SERVER['HTTP_X_DEVICE_FP'] ?? null; $fpPepper = getenv('FP_PEPPER'); @@ -64,8 +75,7 @@ try { // توليد التوكن $jwt = JWT::encode($payload, $payKey, 'HS256'); - // حساب الـ HMAC Hash المطلوب لسيرفر المحفظة للتحقق - // بناءً على authenticateJWT المرسل: hash_hmac('sha256', $userId, $hmacSecret) + // حساب الـ HMAC Hash المطلوب لسيرفر المحفظة $hmacHash = hash_hmac('sha256', (string)$admin->user_id, $hmacSecret); printSuccess([ diff --git a/Admin/auth/migrate_db.php b/Admin/auth/migrate_db.php new file mode 100644 index 0000000..d5cbbcc --- /dev/null +++ b/Admin/auth/migrate_db.php @@ -0,0 +1,28 @@ +query("SHOW COLUMNS FROM adminUser LIKE 'status'"); + if ($check->rowCount() == 0) { + $sql = "ALTER TABLE adminUser + ADD COLUMN status ENUM('pending', 'approved', 'suspended', 'rejected') NOT NULL DEFAULT 'pending' AFTER role, + ADD COLUMN phone VARCHAR(50) DEFAULT NULL AFTER name, + ADD COLUMN email VARCHAR(255) DEFAULT NULL AFTER phone, + ADD COLUMN approved_by VARCHAR(64) DEFAULT NULL AFTER status, + ADD COLUMN approved_at DATETIME DEFAULT NULL AFTER approved_by"; + + $con->exec($sql); + + // Update existing admins to approved and super_admin + $con->exec("UPDATE adminUser SET status = 'approved', role = 'super_admin' WHERE id IS NOT NULL"); + + echo json_encode(["status" => "success", "message" => "Migration completed successfully."]); + } else { + echo json_encode(["status" => "success", "message" => "Columns already exist."]); + } +} catch (Exception $e) { + echo json_encode(["status" => "error", "message" => $e->getMessage()]); +} diff --git a/Admin/auth/register.php b/Admin/auth/register.php new file mode 100644 index 0000000..693d862 --- /dev/null +++ b/Admin/auth/register.php @@ -0,0 +1,59 @@ +prepare("SELECT id FROM adminUser WHERE phone = ? OR fingerprint_hash = ? LIMIT 1"); + $check->execute([$phone, $fpHash]); + + if ($check->rowCount() > 0) { + jsonError("هذا الحساب أو الجهاز مسجل مسبقاً."); + exit; + } + + // 2. تجهيز البيانات + $id = bin2hex(random_bytes(16)); + $hashedPassword = password_hash($password, PASSWORD_DEFAULT); + $encName = $encryptionHelper->encryptData($name); + $encFp = $encryptionHelper->encryptData($fingerprint); + + // 3. الإدخال في قاعدة البيانات (الحالة الافتراضية هي pending) + $sql = "INSERT INTO adminUser (id, name, phone, password, fingerprint, fingerprint_hash, role, status, created_at) + VALUES (:id, :name, :phone, :pass, :fp, :fp_hash, 'admin', 'pending', NOW())"; + + $stmt = $con->prepare($sql); + $stmt->execute([ + ':id' => $id, + ':name' => $encName, + ':phone' => $phone, + ':pass' => $hashedPassword, + ':fp' => $encFp, + ':fp_hash' => $fpHash + ]); + + printSuccess([ + "status" => "pending", + "message" => "تم تقديم طلب التسجيل بنجاح. يرجى انتظار موافقة الإدارة." + ]); + +} catch (Exception $e) { + error_log("[Admin Register Error] " . $e->getMessage()); + jsonError("خطأ في السيرفر: " . $e->getMessage()); +} diff --git a/Admin/auth/verify_login.php b/Admin/auth/verify_login.php new file mode 100644 index 0000000..ecc4524 --- /dev/null +++ b/Admin/auth/verify_login.php @@ -0,0 +1,83 @@ +prepare("SELECT * FROM token_verification_admin + WHERE phone_number = ? AND token = ? + AND expiration_time >= NOW()"); +$stmt->execute([$phone, $otp]); + + if ($stmt->rowCount() === 0) { + jsonError("رمز التحقق غير صالح أو منتهي الصلاحية."); + exit; + } + + // حذف الرمز بعد استخدامه لمرة واحدة + $con->prepare("DELETE FROM token_verification_admin WHERE phone_number = ?")->execute([$phone]); + + // 2. جلب بيانات المسؤول + $stmt = $con->prepare("SELECT * FROM adminUser WHERE phone = ? LIMIT 1"); + $stmt->execute([$phone]); + $admin = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$admin) { + jsonError("المسؤول غير موجود."); + exit; + } + + // 3. التحقق من البصمة (اختياري لكن مفضل لزيادة الأمان) + $fpHash = hash('sha256', $fingerprint); + if ($admin['fingerprint_hash'] !== $fpHash) { + // إذا كانت البصمة مختلفة، ربما يحاول تسجيل الدخول من جهاز آخر + // يمكننا إما المنع أو السماح وتحديث البصمة (حسب السياسة) + // هنا سنقوم بالمنع لزيادة الأمان + jsonError("عذراً، لا يمكن تسجيل الدخول من هذا الجهاز."); + exit; + } + + // 4. إصدار التوكن النهائي + $jwtService = new JwtService($redis); + $role = $admin['role'] ?? 'admin'; + + // إلغاء التوكن القديم إذا وجد في Redis (Token Revocation) + if ($redis) { + $oldJti = $redis->get("active_jti:" . $admin['id']); + if ($oldJti) { + $jwtService->revokeToken($oldJti, 3600); + } + } + + $jwt = $jwtService->generateAccessToken($admin['id'], $role, $audience, $fingerprint); + + // فك تشفير الاسم للعرض + $admin['name'] = $encryptionHelper->decryptData($admin['name']) ?: $admin['name']; + unset($admin['password']); + + printSuccess([ + "message" => "Login successful", + "admin" => $admin, + "jwt" => $jwt, + "expires_in" => 3600 + ]); + +} catch (Exception $e) { + error_log("[Admin Verify OTP Error] " . $e->getMessage()); + jsonError("خطأ في السيرفر: " . $e->getMessage()); +} diff --git a/Admin/dashbord.php b/Admin/dashbord.php index fac1598..96f1e29 100644 --- a/Admin/dashbord.php +++ b/Admin/dashbord.php @@ -1,6 +1,13 @@ 'Unauthorized access. Admin role required.']); + exit; +} + $sql = " SELECT -- العدادات العامة diff --git a/ggg.php b/ggg.php new file mode 100644 index 0000000..9030112 --- /dev/null +++ b/ggg.php @@ -0,0 +1,78 @@ + 'error', + 'message' => 'Access denied for this admin phone.', + ]); + exit; +} + +// 3) التحقق من بقية المدخلات (action + text) +$action = $data['action'] ?? ''; +$text = trim($data['text'] ?? ''); + +if ($text === '' || ($action !== 'encrypt' && $action !== 'decrypt')) { + http_response_code(400); + echo json_encode([ + 'status' => 'error', + 'message' => 'Invalid input: need action=encrypt|decrypt and non-empty text.', + ]); + exit; +} + +// 4) تنفيذ التشفير / الفك +try { + // require_once __DIR__ . '/encrypt_decrypt.php'; + + if ($action === 'encrypt') { + $result = $encryptionHelper->encryptData($text); + } else { // decrypt + $result = $encryptionHelper->decryptData($text); + } + + echo json_encode([ + 'status' => 'success', + 'action' => $action, + 'result' => (string) $result, + ]); +} catch (Exception $e) { + http_response_code(500); + echo json_encode([ + 'status' => 'error', + 'message' => 'Operation failed.', + ]); +} \ No newline at end of file