sendErrorResponse(405, __('Method not allowed', 'yatra')); return false; } // Security: Rate limiting for login page access if (!$this->checkRateLimit()) { $this->sendErrorResponse(429, __('Too many requests', 'yatra')); return false; } // Security: Check if user is already logged in and redirect securely if (is_user_logged_in()) { $current_user = wp_get_current_user(); $redirect_url = apply_filters('yatra_login_redirect_url', home_url('/' . \Yatra\Services\SettingsService::getAccountBase()), $current_user); // Use safe redirect to prevent open redirects wp_safe_redirect($redirect_url, 302); exit; } // Security: Validate nonce if present (for form submissions) if (isset($_GET['_wpnonce']) && !wp_verify_nonce($_GET['_wpnonce'], 'yatra_login_page')) { $this->sendErrorResponse(403, __('Security check failed', 'yatra')); return false; } // Load the login page template with error handling $template_path = YATRA_PLUGIN_PATH . 'templates/login-page.php'; if (!file_exists($template_path)) { // Fallback to shortcode if template is missing return $this->handleFallback(); } try { // Set up WordPress environment $this->setupWordPressEnvironment(); // Security headers $this->setSecurityHeaders(); // Include the template include $template_path; exit; } catch (\Exception $e) { return $this->handleFallback(); } } /** * Get the route pattern for this handler */ public function getPattern(): string { return '^login/?$'; } /** * Get the route name for this handler */ public function getName(): string { return 'login'; } /** * Check rate limiting for login page access */ private function checkRateLimit(): bool { $ip = $this->getClientIp(); $transient_key = 'yatra_login_page_limit_' . md5($ip); $attempts = get_transient($transient_key) ?: 0; // Allow 30 requests per 5 minutes if ($attempts >= 30) { return false; } set_transient($transient_key, $attempts + 1, 5 * MINUTE_IN_SECONDS); return true; } /** * Setup WordPress environment for the login page */ private function setupWordPressEnvironment(): void { global $wp_query; // Prevent 404 $wp_query->is_404 = false; $wp_query->is_page = true; $wp_query->is_singular = true; // Set proper headers status_header(200); // Set page title and metadata $wp_query->set('page_title', __('Login', 'yatra')); $wp_query->set('meta_description', __('Login to your Yatra account', 'yatra')); // Set up post data for compatibility $wp_query->set('post', (object) [ 'ID' => 0, 'post_title' => __('Login', 'yatra'), 'post_content' => '', 'post_type' => 'page', 'post_status' => 'publish' ]); } /** * Set security headers */ private function setSecurityHeaders(): void { if (!headers_sent()) { header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: SAMEORIGIN'); header('Referrer-Policy: strict-origin-when-cross-origin'); header('Content-Security-Policy: "default-src \'self\'; script-src \'self\' \'unsafe-inline\'; style-src \'self\' \'unsafe-inline\'; img-src \'self\' data: https:; font-src \'self\' data:; connect-src \'self\'"'); } } /** * Handle fallback when template is not available */ private function handleFallback(): bool { // Fallback to shortcode rendering add_filter('template_include', function($template) { return get_template_directory() . '/page.php'; }); // Create a virtual page add_filter('the_content', function($content) { return do_shortcode('[yatra_login]'); }); return true; } /** * Send error response */ private function sendErrorResponse(int $code, string $message): void { if (!headers_sent()) { status_header($code); header('Content-Type: text/html; charset=' . get_bloginfo('charset')); } wp_die( esc_html($message), esc_html__('Error', 'yatra'), ['response' => $code] ); } /** * Get client IP address */ private function getClientIp(): string { $ip_keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR']; foreach ($ip_keys as $key) { if (!empty($_SERVER[$key])) { $ips = explode(',', $_SERVER[$key]); $ip = trim($ips[0]); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { return $ip; } } } return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } }