Storing the verification phone as a keyed HMAC fixed OTP lookups but broke every query that joined those tables back to the account, because phone_verification*.phone_number no longer holds the same value as driver.phone / passengers.phone. Six joins were affected, and four of them feed the `verified` flag that the rider and driver apps check at sign-in — so this was already failing under the current CBC mode, not only after a switch to GCM. Accounts now carry phone_key, computed exactly as otpPhoneKey() does, and the joins match on it. It is written at registration for both apps and populated for existing rows by the backfill. The backfill also covers the columns added for the remaining lookups: users.email_bidx/phone_bidx and driver.national_bidx, which were migrated but never populated, and honours a per-field prefix so phone_key reproduces otpPhoneKey's exact output. Insert column/value counts verified with a paren-aware parser after editing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
128 lines
5.6 KiB
PHP
128 lines
5.6 KiB
PHP
<?php
|
|
// loginUsingCredentialsWithoutGooglePassenger.php
|
|
// مسار مخصص لفاحصي التطبيق (الركاب) يعمل بدون JWT Interceptors
|
|
|
|
require_once __DIR__ . '/../../core/bootstrap.php';
|
|
|
|
$email = filterRequest("email");
|
|
$password = filterRequest("password");
|
|
$fingerprint = filterRequest('fingerPrint') ?? filterRequest('fingerprint');
|
|
$audience = filterRequest('aud') ?: 'siro_passenger';
|
|
|
|
// 1. تطبيق حد معدل الطلبات (Rate Limiting) للفاحصين: 3 محاولات بالدقيقة لكل IP
|
|
$rateLimiter = new RateLimiter($redis);
|
|
$rateLimiter->enforce(RateLimiter::identifier(), 'tester_login');
|
|
|
|
if (!$email || !$password) {
|
|
echo json_encode(["status" => "failure", "message" => "Email and password are required"]);
|
|
exit();
|
|
}
|
|
|
|
// 2. التحقق من أن الحساب مخصص للفحص فقط (isTest check)
|
|
$allowedTesterEmailsEnv = getenv('ALLOWED_TESTER_EMAILS') ?: '';
|
|
$allowedEmails = array_filter(array_map('trim', explode(',', $allowedTesterEmailsEnv)));
|
|
if (empty($allowedEmails)) {
|
|
$allowedEmails = [
|
|
'driver_tester@siromove.com',
|
|
'passenger_tester@siromove.com',
|
|
];
|
|
}
|
|
|
|
|
|
$cleanEmail = strtolower(trim($email));
|
|
$isTester = in_array($cleanEmail, $allowedEmails) ||
|
|
substr($cleanEmail, -13) === '@siromove.com' ||
|
|
str_contains($cleanEmail, 'tester') ||
|
|
str_contains($cleanEmail, 'reviewer');
|
|
|
|
try {
|
|
$con = Database::get('main');
|
|
|
|
// تشفير الإيميل للبحث في قاعدة البيانات
|
|
$encryptedEmail = $encryptionHelper->encryptData($email);
|
|
global $blindIndex;
|
|
$emailBidx = $blindIndex ? $blindIndex->index('passengers.email', $email) : null;
|
|
|
|
// Auto-seed/create tester passenger logic removed for security
|
|
|
|
$sql = "SELECT
|
|
p.*,
|
|
phone_verification_passenger.verified,
|
|
invitesToPassengers.isInstall,
|
|
invitesToPassengers.inviteCode,
|
|
invitesToPassengers.isGiftToken
|
|
FROM passengers p
|
|
LEFT JOIN phone_verification_passenger
|
|
ON phone_verification_passenger.phone_number = p.phone_key
|
|
LEFT JOIN invitesToPassengers
|
|
ON invitesToPassengers.inviterPassengerPhone = p.phone
|
|
WHERE p.email = :email OR (:email_bidx IS NOT NULL AND p.email_bidx = :email_bidx)
|
|
LIMIT 1";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->bindParam(':email', $encryptedEmail);
|
|
$stmt->bindParam(':email_bidx', $emailBidx);
|
|
$stmt->execute();
|
|
|
|
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($data) {
|
|
// فحص الباسورد
|
|
if (password_verify($password, $data['password'])) {
|
|
// التحقق من أن الحساب معلم كحساب فحص في قاعدة البيانات أو البيئة
|
|
$isTestInDb = (isset($data['is_test']) && $data['is_test'] == 1) || (isset($data['isTest']) && $data['isTest'] == 1);
|
|
if (!$isTestInDb && !$isTester) {
|
|
jsonError("Access denied. Not a tester account.");
|
|
exit();
|
|
}
|
|
// فك تشفير البيانات للرد
|
|
if(isset($data['phone'])) $data['phone'] = $encryptionHelper->decryptData($data['phone']);
|
|
if(isset($data['email'])) $data['email'] = $encryptionHelper->decryptData($data['email']);
|
|
if(isset($data['gender'])) $data['gender'] = $encryptionHelper->decryptData($data['gender']);
|
|
if(isset($data['birthdate'])) $data['birthdate'] = $encryptionHelper->decryptData($data['birthdate']);
|
|
if(isset($data['site'])) $data['site'] = $encryptionHelper->decryptData($data['site']);
|
|
if(isset($data['first_name'])) $data['first_name'] = $encryptionHelper->decryptData($data['first_name']);
|
|
if(isset($data['last_name'])) $data['last_name'] = $encryptionHelper->decryptData($data['last_name']);
|
|
if(isset($data['sosPhone'])) $data['sosPhone'] = $encryptionHelper->decryptData($data['sosPhone']);
|
|
if(isset($data['education'])) $data['education'] = $encryptionHelper->decryptData($data['education']);
|
|
if(isset($data['employmentType'])) $data['employmentType'] = $encryptionHelper->decryptData($data['employmentType']);
|
|
if(isset($data['maritalStatus'])) $data['maritalStatus'] = $encryptionHelper->decryptData($data['maritalStatus']);
|
|
|
|
// Force verified = 1 for the test user so the Rider app doesn't reject the login
|
|
if (isset($data['is_test']) && $data['is_test'] == 1) {
|
|
$data['verified'] = 1;
|
|
}
|
|
|
|
// توليد الـ JWT بصلاحية (tester) لتميزهم عن المستخدمين الفعليين
|
|
$jwtService = new JwtService($redis);
|
|
$jwt = $jwtService->generateAccessToken($data['id'], 'tester', $audience, $fingerprint);
|
|
|
|
echo json_encode([
|
|
"status" => "success",
|
|
"jwt" => $jwt,
|
|
"data" => [$data] // مطابق لنسق التطبيق الذي يتوقع مصفوفة
|
|
], JSON_UNESCAPED_UNICODE);
|
|
|
|
} else {
|
|
echo json_encode([
|
|
"status" => "failure",
|
|
"message" => "Invalid credentials"
|
|
]);
|
|
}
|
|
} else {
|
|
echo json_encode([
|
|
"status" => "failure",
|
|
"message" => "Invalid credentials"
|
|
]);
|
|
}
|
|
|
|
} catch (Throwable $e) {
|
|
error_log("Error in loginUsingCredentialsWithoutGooglePassenger: " . $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());
|
|
http_response_code(500);
|
|
echo json_encode([
|
|
"status" => "failure",
|
|
"message" => "Server error: " . $e->getMessage() . " in " . basename($e->getFile()) . " on line " . $e->getLine()
|
|
]);
|
|
}
|
|
exit();
|