File manager - Edit - /home/webapp69.cm.in.th/u69319090028/Shop/app/Services/AuthService.php
Back
<?php /** * AuthService * Handles business logic for authentication, security, and session management */ require_once __DIR__ . '/../Models/User.php'; require_once __DIR__ . '/../Models/LoginAttempt.php'; require_once __DIR__ . '/../Models/LoginHistory.php'; require_once __DIR__ . '/../Models/UserSession.php'; require_once __DIR__ . '/../Models/PasswordResetToken.php'; require_once __DIR__ . '/../Models/UserAgreement.php'; require_once __DIR__ . '/../Models/ActivityLog.php'; require_once __DIR__ . '/../Helpers/Validator.php'; require_once __DIR__ . '/../Helpers/Session.php'; require_once __DIR__ . '/../Helpers/Security.php'; require_once __DIR__ . '/../Helpers/Auth.php'; require_once __DIR__ . '/../Helpers/Language.php'; class AuthService { private User $userModel; private LoginAttempt $attemptModel; private LoginHistory $historyModel; private UserSession $sessionModel; private PasswordResetToken $resetTokenModel; private UserAgreement $agreementModel; private ActivityLog $activityLog; public function __construct() { $this->userModel = new User(); $this->attemptModel = new LoginAttempt(); $this->historyModel = new LoginHistory(); $this->sessionModel = new UserSession(); $this->resetTokenModel = new PasswordResetToken(); $this->agreementModel = new UserAgreement(); $this->activityLog = new ActivityLog(); } /** * Register a new user with Customer role */ public function register(array $data): array { $validator = Validator::make($data, [ 'username' => 'required|username|unique:users,username', 'email' => 'required|email|unique:users,email', 'phone' => 'required|phone|unique:users,phone', 'first_name' => 'required|max:100', 'last_name' => 'max:100', 'password' => 'required|min:8', 'confirm_password' => 'required|matches:password', 'terms_accepted' => 'accepted' ]); if (!$validator->validate()) { return ['success' => false, 'errors' => $validator->errors()]; } // Clean phone number (digits only) $cleanPhone = preg_replace('/[^0-9]/', '', (string)$data['phone']); $passwordHash = password_hash($data['password'], PASSWORD_BCRYPT, ['cost' => 12]); $userId = $this->userModel->insert([ 'username' => trim($data['username']), 'email' => trim(strtolower($data['email'])), 'phone' => $cleanPhone, 'password' => $passwordHash, 'first_name' => trim($data['first_name']), 'last_name' => trim($data['last_name'] ?? ''), 'profile_image' => null, 'status' => 'active', 'is_temp_password' => 0, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]); // Assign Customer role by default $this->userModel->assignRole($userId, 'customer'); // Record agreement acceptance $agreement = $this->agreementModel->getLatestActive('customer'); if ($agreement) { $this->agreementModel->recordAcceptance($userId, (int)$agreement['id'], 'customer', $agreement['version']); } // Log registration activity $this->activityLog->record($userId, 'user_register', 'user', $userId, 'User registered account'); return ['success' => true, 'user_id' => $userId]; } /** * Authenticate user with Username or Email + Password */ public function authenticate(string $identifier, string $password, bool $rememberMe = false): array { $identifier = trim($identifier); $clientIp = Security::getClientIp(); if (empty($identifier) || empty($password)) { return ['success' => false, 'message' => __('invalid_credentials')]; } // Check Brute Force Lockout [$isLocked, $lockRemainingMinutes] = $this->attemptModel->checkLockout($identifier, $clientIp); if ($isLocked) { return [ 'success' => false, 'message' => __('account_locked', ['minutes' => $lockRemainingMinutes]) ]; } $user = $this->userModel->findByUsernameOrEmail($identifier); if (!$user || !password_verify($password, $user['password'])) { $isNowLocked = $this->attemptModel->recordFailure($identifier, $clientIp); if ($user) { $this->historyModel->record((int)$user['id'], $isNowLocked ? 'locked' : 'failed'); } if ($isNowLocked) { return [ 'success' => false, 'message' => __('account_locked', ['minutes' => 15]) ]; } return ['success' => false, 'message' => __('invalid_credentials')]; } // Check Account Status if ($user['status'] === 'suspended') { $this->historyModel->record((int)$user['id'], 'failed'); return ['success' => false, 'message' => __('account_suspended')]; } if ($user['status'] === 'deleted' || !empty($user['deleted_at'])) { return ['success' => false, 'message' => __('account_deleted')]; } // Clear failed attempts upon successful authentication $this->attemptModel->clearAttempts($identifier, $clientIp); // Regenerate PHP session to prevent session fixation Session::regenerate(); Session::set('user_id', (int)$user['id']); Session::set('username', $user['username']); Session::set('email', $user['email']); // Update last login $this->userModel->updateLastLogin((int)$user['id']); // Record successful login in history $this->historyModel->record((int)$user['id'], 'success'); // Handle Remember Me token if ($rememberMe) { $token = $this->sessionModel->createRememberSession((int)$user['id']); if (!headers_sent()) { setcookie( 'cargoo_remember', $token, time() + 2592000, // 30 days '/', '', isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on', true // HttpOnly ); } } // Clear cached auth helper Auth::clearCache(); return [ 'success' => true, 'user' => $user, 'roles' => $this->userModel->getUserRoles((int)$user['id']) ]; } /** * Check Remember-Me Cookie on boot */ public function checkRememberCookie(): void { if (!Auth::check() && !empty($_COOKIE['cargoo_remember'])) { $rawToken = $_COOKIE['cargoo_remember']; $session = $this->sessionModel->findValidSession($rawToken); if ($session) { $user = $this->userModel->findById((int)$session['user_id']); if ($user && $user['status'] === 'active') { Session::regenerate(); Session::set('user_id', (int)$user['id']); Session::set('username', $user['username']); Session::set('email', $user['email']); $this->userModel->updateLastLogin((int)$user['id']); } else { $this->sessionModel->removeSessionByToken($rawToken); setcookie('cargoo_remember', '', time() - 3600, '/'); } } else { setcookie('cargoo_remember', '', time() - 3600, '/'); } } } /** * Logout */ public function logout(): void { if (!empty($_COOKIE['cargoo_remember'])) { $this->sessionModel->removeSessionByToken($_COOKIE['cargoo_remember']); setcookie('cargoo_remember', '', time() - 3600, '/'); } Auth::logout(); } /** * Request Password Reset Link */ public function requestPasswordReset(string $identifier): array { $identifier = trim($identifier); $user = $this->userModel->findByUsernameOrEmail($identifier); // Security rule: Do not reveal if user exists to avoid user enumeration $genericSuccess = [ 'success' => true, 'message' => __('reset_link_sent') ]; if (!$user || $user['status'] !== 'active') { return $genericSuccess; } $token = $this->resetTokenModel->createToken((int)$user['id']); $resetUrl = Url::to('/reset-password?token=' . $token); // In production this sends an email. For local/demo environment, we also save token in flash message for testing $genericSuccess['reset_link'] = $resetUrl; $genericSuccess['token'] = $token; $this->activityLog->record((int)$user['id'], 'password_reset_requested', 'user', (int)$user['id'], 'Requested password reset token'); return $genericSuccess; } /** * Verify and Reset Password using Token */ public function resetPassword(string $token, string $newPassword, string $confirmPassword): array { $validator = Validator::make([ 'password' => $newPassword, 'confirm_password' => $confirmPassword ], [ 'password' => 'required|min:8', 'confirm_password' => 'required|matches:password' ]); if (!$validator->validate()) { return ['success' => false, 'errors' => $validator->errors()]; } $tokenRecord = $this->resetTokenModel->verifyToken($token); if (!$tokenRecord) { return ['success' => false, 'message' => __('invalid_or_expired_token')]; } $userId = (int)$tokenRecord['user_id']; $newHash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 12]); $this->userModel->update($userId, [ 'password' => $newHash, 'is_temp_password' => 0, 'updated_at' => date('Y-m-d H:i:s') ]); $this->resetTokenModel->markAsUsed((int)$tokenRecord['id']); // Invalidate all active sessions for this user $this->sessionModel->removeAllForUser($userId); $this->activityLog->record($userId, 'password_reset_completed', 'user', $userId, 'Password reset via secure token'); return ['success' => true, 'message' => __('password_reset_success')]; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.23 |
proxy
|
phpinfo
|
Settings