PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.7
Yatra – Travel Booking & Tour Operator Software v3.0.2.7
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / app / Ajax / LoginAjax.php

LoginAjax.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.7, at app/Ajax/LoginAjax.php

359 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Ajax;
6
7 use WP_REST_Request;
8 use WP_REST_Response;
9 use WP_Error;
10
11 /**
12 * Login AJAX Handler
13 *
14 * Handles AJAX login requests
15 */
16 class LoginAjax
17 {
18 /**
19 * Constructor - Initialize AJAX hooks
20 */
21 public function __construct()
22 {
23 // Register AJAX action for logged-in users
24 add_action('wp_ajax_yatra_ajax_login', [$this, 'handleAjaxLogin']);
25
26 // Register AJAX action for non-logged-in users
27 add_action('wp_ajax_nopriv_yatra_ajax_login', [$this, 'handleAjaxLogin']);
28 }
29
30 /**
31 * Handle AJAX login request
32 */
33 public function handleAjaxLogin(): void
34 {
35 // Rate limiting check
36 $this->checkRateLimit();
37
38 // Verify nonce with multiple checks
39 $nonce = $_POST['yatra_login_nonce'] ?? $_POST['nonce'] ?? '';
40 if (!wp_verify_nonce($nonce, 'yatra_login_action') && !wp_verify_nonce($nonce, 'yatra_login_nonce')) {
41 $this->logSecurityEvent('nonce_verification_failed', [
42 'nonce' => substr($nonce, 0, 8) . '...',
43 'ip' => $this->getClientIp()
44 ]);
45
46 wp_send_json_error([
47 'success' => false,
48 'code' => 'security_check_failed',
49 'message' => __('Security check failed. Please refresh the page and try again.', 'yatra')
50 ]);
51 }
52
53 // Validate request method
54 if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
55 wp_send_json_error([
56 'success' => false,
57 'code' => 'invalid_method',
58 'message' => __('Invalid request method.', 'yatra')
59 ]);
60 }
61
62 // Get and sanitize form data
63 $username = sanitize_user($_POST['log'] ?? $_POST['username'] ?? '');
64 $password = $_POST['pwd'] ?? $_POST['password'] ?? '';
65 $remember = isset($_POST['rememberme']) && $_POST['rememberme'] === 'forever';
66 $redirect_to = $this->validateRedirectUrl($_POST['redirect_to'] ?? '');
67
68 // Enhanced input validation
69 $validation_result = $this->validateLoginInput($username, $password);
70 if (!$validation_result['valid']) {
71 wp_send_json_error([
72 'success' => false,
73 'code' => $validation_result['code'],
74 'message' => $validation_result['message']
75 ]);
76 }
77
78 // Log login attempt (security)
79 $this->logLoginAttempt($username);
80
81 // Attempt authentication with security checks
82 $user = $this->authenticateUser($username, $password);
83
84 if (is_wp_error($user)) {
85 $this->handleAuthenticationError($user);
86 return; // Exit early
87 }
88
89 // Additional security checks
90 if (!$this->isUserAllowedToLogin($user)) {
91 wp_send_json_error([
92 'success' => false,
93 'code' => 'user_not_allowed',
94 'message' => __('Your account is not allowed to login. Please contact support.', 'yatra')
95 ]);
96 }
97
98 // Successful login - set up secure session
99 $this->setupUserSession($user, $remember);
100
101 // Prepare secure success response
102 $response_data = $this->prepareSuccessResponse($user, $redirect_to);
103
104 // Send success response
105 wp_send_json_success($response_data);
106 }
107
108 /**
109 * Check rate limiting for login attempts
110 */
111 private function checkRateLimit(): void
112 {
113 $ip = $this->getClientIp();
114 $transient_key = 'yatra_login_limit_' . md5($ip);
115 $attempts = get_transient($transient_key) ?: 0;
116
117 // Allow 5 attempts per 15 minutes
118 if ($attempts >= 5) {
119 $this->logSecurityEvent('rate_limit_exceeded', ['ip' => $ip]);
120 wp_send_json_error([
121 'success' => false,
122 'code' => 'rate_limit_exceeded',
123 'message' => __('Too many login attempts. Please try again in 15 minutes.', 'yatra')
124 ]);
125 }
126
127 // Increment counter
128 set_transient($transient_key, $attempts + 1, 15 * MINUTE_IN_SECONDS);
129 }
130
131 /**
132 * Validate login input
133 */
134 private function validateLoginInput(string $username, string $password): array
135 {
136 if (empty($username)) {
137 return [
138 'valid' => false,
139 'code' => 'empty_username',
140 'message' => __('Please enter your username or email.', 'yatra')
141 ];
142 }
143
144 if (empty($password)) {
145 return [
146 'valid' => false,
147 'code' => 'empty_password',
148 'message' => __('Please enter your password.', 'yatra')
149 ];
150 }
151
152 if (strlen($username) > 60) {
153 return [
154 'valid' => false,
155 'code' => 'invalid_username_length',
156 'message' => __('Username is too long.', 'yatra')
157 ];
158 }
159
160 if (strlen($password) > 72) {
161 return [
162 'valid' => false,
163 'code' => 'invalid_password_length',
164 'message' => __('Password is too long.', 'yatra')
165 ];
166 }
167
168 return ['valid' => true];
169 }
170
171 /**
172 * Authenticate user with enhanced security
173 */
174 private function authenticateUser(string $username, string $password)
175 {
176 // Use WordPress authentication with additional security
177 $user = wp_authenticate($username, $password);
178
179 if (is_wp_error($user)) {
180 return $user;
181 }
182
183 // Check if user is verified (if email verification is required)
184 if ($this->isEmailVerificationRequired() && !get_user_meta($user->ID, 'yatra_email_verified', true)) {
185 return new WP_Error('email_not_verified',
186 __('Your email address has not been verified. Please check your email.', 'yatra')
187 );
188 }
189
190 return $user;
191 }
192
193 /**
194 * Handle authentication errors
195 */
196 private function handleAuthenticationError(WP_Error $error): void
197 {
198 $error_code = $error->get_error_code();
199 $error_message = $error->get_error_message();
200
201 // Log security events
202 $this->logSecurityEvent('authentication_failed', [
203 'error_code' => $error_code,
204 'error_message' => $error_message
205 ]);
206
207 // Provide user-friendly error messages
208 if (in_array($error_code, ['invalid_username', 'incorrect_password'], true)) {
209 $error_message = __('Invalid username or password. Please try again.', 'yatra');
210 } elseif ($error_code === 'email_not_verified') {
211 $error_message = $error_message; // Use the specific message
212 } else {
213 $error_message = __('Login failed. Please try again.', 'yatra');
214 }
215
216 wp_send_json_error([
217 'success' => false,
218 'code' => $error_code,
219 'message' => $error_message
220 ]);
221 }
222
223 /**
224 * Setup secure user session
225 */
226 private function setupUserSession(\WP_User $user, bool $remember): void
227 {
228 wp_set_current_user($user->ID);
229 wp_set_auth_cookie($user->ID, $remember);
230
231 // Update user metadata
232 update_user_meta($user->ID, 'yatra_last_login', current_time('mysql'));
233 update_user_meta($user->ID, 'yatra_last_login_ip', $this->getClientIp());
234
235 // Clear failed login attempts
236 $transient_key = 'yatra_login_limit_' . md5($this->getClientIp());
237 delete_transient($transient_key);
238 }
239
240 /**
241 * Prepare secure success response
242 */
243 private function prepareSuccessResponse(\WP_User $user, string $redirect_to): array
244 {
245 // Apply filter for custom redirect logic
246 $redirect_url = apply_filters('yatra_login_redirect_url', $redirect_to, $user);
247
248 return [
249 'success' => true,
250 'message' => __('Login successful! Redirecting...', 'yatra'),
251 'user_id' => $user->ID,
252 'user_login' => $user->user_login,
253 'display_name' => $user->display_name,
254 'redirect_url' => esc_url($redirect_url),
255 'timestamp' => time()
256 ];
257 }
258
259 /**
260 * Validate redirect URL for security
261 */
262 private function validateRedirectUrl(string $redirect_url): string
263 {
264 if (empty($redirect_url)) {
265 return home_url('/my-account');
266 }
267
268 $redirect_url = sanitize_url($redirect_url);
269
270 // Validate URL is safe
271 if (!wp_http_validate_url($redirect_url)) {
272 return home_url('/my-account');
273 }
274
275 // Only allow redirects to same host
276 $redirect_host = parse_url($redirect_url, PHP_URL_HOST);
277 $site_host = parse_url(home_url(), PHP_URL_HOST);
278
279 if ($redirect_host !== $site_host) {
280 return home_url('/my-account');
281 }
282
283 return $redirect_url;
284 }
285
286 /**
287 * Check if user is allowed to login
288 */
289 private function isUserAllowedToLogin(\WP_User $user): bool
290 {
291 // Check if user is active
292 if (in_array('inactive', (array) $user->roles, true)) {
293 return false;
294 }
295
296 // Apply additional checks via filter
297 return apply_filters('yatra_user_allowed_to_login', true, $user);
298 }
299
300 /**
301 * Check if email verification is required
302 */
303 private function isEmailVerificationRequired(): bool
304 {
305 return apply_filters('yatra_require_email_verification', false);
306 }
307
308 /**
309 * Get client IP address
310 */
311 private function getClientIp(): string
312 {
313 $ip_keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];
314
315 foreach ($ip_keys as $key) {
316 if (!empty($_SERVER[$key])) {
317 $ips = explode(',', $_SERVER[$key]);
318 $ip = trim($ips[0]);
319 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
320 return $ip;
321 }
322 }
323 }
324
325 return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
326 }
327
328 /**
329 * Log security events
330 */
331 private function logSecurityEvent(string $event, array $data = []): void
332 {
333 if (!defined('WP_DEBUG') || !WP_DEBUG) {
334 return;
335 }
336
337 $log_data = array_merge([
338 'event' => $event,
339 'timestamp' => current_time('mysql'),
340 'ip' => $this->getClientIp(),
341 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
342 ], $data);
343
344 error_log('Yatra Login Security: ' . json_encode($log_data));
345 }
346
347 /**
348 * Log login attempts
349 */
350 private function logLoginAttempt(string $username): void
351 {
352 if (!defined('WP_DEBUG') || !WP_DEBUG) {
353 return;
354 }
355
356 error_log('Yatra Login Attempt: ' . $username . ' from IP: ' . $this->getClientIp());
357 }
358 }
359