userModel = new UserModel(); } /** * Handle login request */ public function login() { // Check if already authenticated if (Security::isAdminAuthenticated()) { $this->redirectWithMessage('/', 'Already logged in as admin'); return; } // Handle POST login request if ($_SERVER['REQUEST_METHOD'] === 'POST') { return $this->processLogin(); } // Show login form $this->showLoginForm(); } /** * Process login form submission */ private function processLogin() { // Validate required fields if (!isset($_POST['username']) || !isset($_POST['password'])) { $this->showLoginForm('Please provide username and password'); return; } // Validate input format $validation = Validation::validateAdminLogin($_POST['username'], $_POST['password']); if (!$validation['valid']) { Security::logSecurityEvent('login_validation_failed', [ 'username' => substr($_POST['username'], 0, 50), 'error' => $validation['message'] ]); $this->showLoginForm($validation['message']); return; } $username = $validation['data']['username']; $password = $_POST['password']; // Track login attempts for rate limiting $failedAttempts = $_SESSION['login_attempts'] ?? 0; if ($failedAttempts >= 5) { Security::logSecurityEvent('login_brute_force_attempt', [ 'username' => $username, 'ip' => Security::getClientIP() ]); $this->showLoginForm('Too many failed attempts. Please try again later.', true); return; } // Attempt authentication if (Security::authenticateAdmin($username, $password)) { // Success - clear failed attempts and log unset($_SESSION['login_attempts']); Security::logSecurityEvent('admin_login_success', ['username' => $username]); // Update user record if using database tracking $userId = $_SESSION['user_id'] ?? Security::generateSecureUserId(); $_SESSION['user_id'] = $userId; $this->userModel->createOrUpdate($userId, [ 'nickname' => 'Admin', 'is_admin' => true ]); // Redirect to dashboard or referer $redirectUrl = $_GET['redirect'] ?? '/'; header("Location: {$redirectUrl}"); exit; } else { // Failed login $_SESSION['login_attempts'] = $failedAttempts + 1; Security::logSecurityEvent('admin_login_failed', [ 'username' => $username, 'attempts' => $_SESSION['login_attempts'] ]); $remaining = 5 - $_SESSION['login_attempts']; $message = "Invalid credentials. {$remaining} attempts remaining."; $this->showLoginForm($message, true); } } /** * Handle logout */ public function logout() { // Get username before logout for logging $username = $_SESSION['admin_username'] ?? 'unknown'; Security::logoutAdmin(); Security::logSecurityEvent('admin_logout_success', ['username' => $username]); // Destroy session completely session_destroy(); $this->redirectWithMessage('/login', 'Successfully logged out'); } /** * Check current admin authentication status (API endpoint) */ public function status() { header('Content-Type: application/json'); $isAuthenticated = Security::isAdminAuthenticated(); $response = [ 'authenticated' => $isAuthenticated, 'user_id' => $_SESSION['user_id'] ?? null, 'username' => $isAuthenticated ? ($_SESSION['admin_username'] ?? 'admin') : null, 'login_time' => $isAuthenticated ? ($_SESSION['admin_login_time'] ?? null) : null, 'session_expires' => $isAuthenticated ? $this->calculateSessionExpiry() : null ]; echo json_encode($response); } /** * Calculate when current session expires */ private function calculateSessionExpiry() { $timeout = Config::get('admin.session_timeout', 3600); $loginTime = $_SESSION['admin_login_time'] ?? 0; return $loginTime + $timeout; } /** * Show login form */ private function showLoginForm($error = null, $isWarning = false) { // Generate new CSRF token $csrfToken = Security::generateCSRFToken(); // Get branding/styling from config $appName = Config::get('app.name', 'Application'); $loginTitle = Config::get('admin.login_title', 'Admin Login'); $primaryColor = Config::get('ui.primary_color', '#2d572c'); // Determine redirect URL $redirect = isset($_GET['redirect']) ? htmlspecialchars($_GET['redirect'], ENT_QUOTES) : '/'; $errorHtml = ''; if ($error) { $alertClass = $isWarning ? 'alert-warning' : 'alert-danger'; $errorHtml = " "; } echo " {$loginTitle} - {$appName}

{$appName}

{$loginTitle}

{$errorHtml}
← Back to application
"; } /** * Redirect with flash message via session */ private function redirectWithMessage($url, $message, $type = 'info') { $_SESSION['flash_message'] = ['text' => $message, 'type' => $type]; header("Location: {$url}"); exit; } }