PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.1.2
Fluent Support – Helpdesk & Customer Support Ticket System v2.1.2
2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 All 67 releases
← All changes | app/Http/Controllers/AuthController.php +31 -91 2.3.22.1.2 View file →
@@ -96,23 +96,8 @@
96 96 'message' => __('Please provide a valid verification code that was sent to your email address', 'fluent-support')
97 97 ], 422);
98 98 }
99 99
100 - // the code must still be unused and must not have been consumed by a prior request
101 - if (($logHash['status'] ?? '') !== 'issued') {
102 - wp_send_json([
103 - 'message' => __('Your verification code has already been used. Please try again', 'fluent-support')
104 - ], 422);
105 - }
106 -
107 - // records created before the email-binding fix (or any other legacy/malformed record)
108 - // have no bound email; treat them as invalid rather than proceeding with a null email
109 - if (empty($logHash['email'])) {
110 - wp_send_json([
111 - 'message' => __('Your verification code has expired. Please request a new one', 'fluent-support')
112 - ], 422);
113 - }
114 -
115 100 // check if it got expired or not
116 101 $validTill = $logHash['valid_till'] ?? '';
117 102 if (($logHash['used_count'] ?? 0) > 5 || ($validTill && strtotime($validTill) < current_time('timestamp'))) {
118 103 wp_send_json([
@@ -131,30 +116,14 @@
131 116 'message' => __('Please provide a valid verification code that was sent to your email address', 'fluent-support')
132 117 ], 422);
133 118 }
134 119
135 - // atomically consume the code: only succeeds if the record is still in the exact
136 - // state we just read, closing the race where two requests both pass the checks above
137 - $consumed = Meta::where('key', $logHash['login_hash'])
138 - ->where('object_type', 'fs_login_hashes')
139 - ->where('value', $logHashMeta->value)
140 - ->update([
141 - 'value' => maybe_serialize(array_merge($logHash, [
142 - 'used_count' => ($logHash['used_count'] ?? 0) + 1,
143 - 'status' => 'used',
144 - ]))
145 - ]);
120 + $logHash['used_count'] += 1;
121 + $logHash['status'] = 'used';
146 122
147 - if (!$consumed) {
148 - wp_send_json([
149 - 'message' => __('Your verification code has already been used. Please try again', 'fluent-support')
150 - ], 422);
151 - }
152 -
153 - // the email is now server-verified for this code; ignore whatever the client
154 - // submitted and use the address the code was actually issued to, so the signup
155 - // can never be completed against a different (e.g. victim's) email address
156 - $formData['email'] = $logHash['email'];
123 + Meta::where('key', $logHash['login_hash'])->update([
124 + 'value' => maybe_serialize($logHash)
125 + ]);
157 126 }
158 127
159 128 /*
160 129 * Action After validate user signup validation success
@@ -276,9 +245,9 @@
276 245 ], 429);
277 246 }
278 247
279 248 if (!$user) {
280 - $user = new \WP_Error('authentication_failed', __('Invalid username, email address or incorrect password.', 'fluent-support'));
249 + $user = new \WP_Error('authentication_failed', __('<strong>Error</strong>: Invalid username, email address or incorrect password.', 'fluent-support'));
281 250
282 251 do_action('wp_login_failed', $email, $user);
283 252 $this->incrementLoginAttempts($ipKey);
284 253 $this->incrementLoginAttempts($accountKey);
@@ -290,17 +259,8 @@
290 259 }
291 260
292 261 $twoFactorEnabled = Helper::getBusinessSettings('enable_two_fa');
293 262 if ('yes' === $twoFactorEnabled) {
294 - if (!wp_check_password($password, $user->user_pass, $user->ID)) {
295 - $this->incrementLoginAttempts($ipKey);
296 - $this->incrementLoginAttempts($accountKey);
297 -
298 - return $this->response([
299 - 'message' => __('Invalid username, email address or incorrect password.', 'fluent-support')
300 - ], 403);
301 - }
302 -
303 263 (new TwoFaHandler)->maybe2FaRedirect($user);
304 264 }
305 265
306 266 if (apply_filters('fluent_support_use_native_login', true)) {
@@ -335,9 +295,9 @@
335 295 $this->incrementLoginAttempts($ipKey);
336 296 $this->incrementLoginAttempts($accountKey);
337 297
338 298 return $this->response([
339 - 'message' => __('Invalid username, email address or incorrect password.', 'fluent-support')
299 + 'message' => __('<strong>Error</strong>: Invalid username, email address or incorrect password.', 'fluent-support')
340 300 ], 403);
341 301 }
342 302
343 303 private function incrementLoginAttempts($rateLimitKey)
@@ -490,18 +450,8 @@
490 450 'message' => 'Username or email is required'
491 451 ]);
492 452 }
493 453
494 - // IP bucket is a generous volumetric backstop (shared office/NAT IPs can have many
495 - // unrelated users). It runs before the account lookup so that probes for accounts
496 - // that don't exist are throttled too. Keyed on the IP only, so a 429 here reveals
497 - // nothing about whether any given account exists.
498 - if (Helper::hitRateLimit('fs_reset_pass_ip_' . wp_hash(Helper::getIp()), 20)) {
499 - return $this->sendError([
500 - 'message' => __('Too many password reset requests. Please try again after 15 minutes.', 'fluent-support')
501 - ], 429);
502 - }
503 -
504 454 $user_data = get_user_by('email', $usernameOrEmail);
505 455
506 456 if (!$user_data) {
507 457 $user_data = get_user_by('login', $usernameOrEmail);
@@ -507,9 +457,11 @@
507 457 $user_data = get_user_by('login', $usernameOrEmail);
508 458 }
509 459
510 460 if (!$user_data) {
511 - return $this->sendResetPassResponse();
461 + return $this->sendError([
462 + 'message' => __('Invalid username or email', 'fluent-support')
463 + ]);
512 464 }
513 465
514 466 $user_data = apply_filters('lostpassword_user_data', $user_data, $errors);
515 467
@@ -517,17 +469,24 @@
517 469
518 470 $errors = apply_filters('lostpassword_errors', $errors, $user_data);
519 471
520 472 if ($errors->has_errors()) {
521 - return $this->sendResetPassResponse();
473 + return $this->sendError([
474 + 'message' => $errors->get_error_message()
475 + ]);
522 476 }
523 477
524 478 if (!$user_data) {
525 - return $this->sendResetPassResponse();
479 + return $this->sendError([
480 + 'message' => __('<strong>Error</strong>: There is no account with that username or email address.', 'fluent-support')
481 + ]);
526 482 }
527 483
528 484 if (is_multisite() && !is_user_member_of_blog($user_data->ID, get_current_blog_id())) {
529 - return $this->sendResetPassResponse();
485 +
486 + return $this->sendError([
487 + 'message' => __('<strong>Error</strong>: Invalid username or email', 'fluent-support')
488 + ]);
530 489 }
531 490
532 491 // Redefining user_login ensures we return the right case in the email.
533 492 $user_login = $user_data->user_login;
@@ -535,13 +494,21 @@
535 494 do_action('retrieve_password', $user_login);
536 495
537 496 $allow = apply_filters('allow_password_reset', true, $user_data->ID);
538 497
539 - if (!$allow || is_wp_error($allow)) {
540 - return $this->sendResetPassResponse();
498 + if (!$allow) {
499 + return $this->sendError([
500 + 'message' => __('Password reset is not allowed for this user', 'fluent-support')
501 + ]);
541 502 }
542 503
504 + if (is_wp_error($allow)) {
505 + return $this->sendError([
506 + 'message' => $allow->get_error_message()
507 + ]);
508 + }
543 509
510 +
544 511 /*
545 512 * Filter reset password link text
546 513 *
547 514 * @since v1.5.7
@@ -549,21 +516,8 @@
549 516 */
550 517 // translators: %s is the site name
551 518 $linkText = apply_filters("fluent_support/reset_password_link", sprintf(__('Reset your password for %s', 'fluent-support'), get_bloginfo('name')));
552 519
553 - // Issuance cooldown. get_password_reset_key() rotates the stored key, invalidating
554 - // any link already sitting in the account owner's inbox, so an unthrottled caller
555 - // could deny password recovery indefinitely. Suppressing the duplicate issuance is
556 - // safe: reset mail only ever goes to the account owner, so whoever triggered the
557 - // first send has already put a working link in that inbox.
558 - $cooldownKey = 'fs_reset_pass_sent_' . wp_hash($user_data->ID);
559 -
560 - if (get_transient($cooldownKey)) {
561 - return $this->sendResetPassResponse();
562 - }
563 -
564 - set_transient($cooldownKey, 1, 5 * MINUTE_IN_SECONDS);
565 -
566 520 $resetUrl = add_query_arg([
567 521 'action' => 'rp',
568 522 'key' => get_password_reset_key($user_data),
569 523 'login' => rawurlencode($user_data->user_login)
@@ -601,24 +555,10 @@
601 555 $headers = array('Content-Type: text/html; charset=UTF-8');
602 556
603 557 wp_mail($user_data->user_email, $mailSubject, $message, $headers);
604 558
605 - return $this->sendResetPassResponse();
606 - }
607 -
608 - /**
609 - * Single response for every password reset outcome.
610 - *
611 - * Whether the account exists, is disallowed, is not a member of this site or is
612 - * inside the resend cooldown, the caller sees the same thing — otherwise the form
613 - * confirms which usernames and email addresses are real.
614 - *
615 - * @return mixed
616 - */
617 - protected function sendResetPassResponse()
618 - {
619 559 return $this->sendSuccess([
620 - 'message' => __('If an account matches that username or email, a password reset link has been sent. Please check your inbox, including the spam folder.', 'fluent-support')
560 + 'message' => __('Please check your email for the reset link', 'fluent-support')
621 561 ]);
622 562 }
623 563
624 564 /**