0) try { $userInfo = User::byId($userId); } catch(UserNotFoundException $ex) { url_redirect('auth-forgot'); return; } $notices = []; $ipAddress = $_SERVER['REMOTE_ADDR']; $siteIsPrivate = $cfg->getBoolean('private.enable'); $canResetPassword = $siteIsPrivate ? $cfg->getBoolean('private.allow_password_reset', true) : true; $remainingAttempts = UserLoginAttempt::remaining($ipAddress); while($canResetPassword) { if(!empty($reset) && $userId > 0) { if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } $verificationCode = !empty($reset['verification']) && is_string($reset['verification']) ? $reset['verification'] : ''; try { $tokenInfo = UserRecoveryToken::byToken($verificationCode); } catch(UserRecoveryTokenNotFoundException $ex) { unset($tokenInfo); } if(empty($tokenInfo) || !$tokenInfo->isValid() || $tokenInfo->getUserId() !== $userInfo->getId()) { $notices[] = 'Invalid verification code!'; break; } $password = !empty($reset['password']) && is_array($reset['password']) ? $reset['password'] : []; $passwordNew = !empty($password['new']) && is_string($password['new']) ? $password['new'] : ''; $passwordConfirm = !empty($password['confirm']) && is_string($password['confirm']) ? $password['confirm'] : ''; if(empty($passwordNew) || empty($passwordConfirm) || $passwordNew !== $passwordConfirm) { $notices[] = "Password confirmation failed!"; break; } if(User::validatePassword($passwordNew) !== '') { $notices[] = 'Your password is too weak!'; break; } // also disables two factor auth to prevent getting locked out of account entirely // this behaviour should really be replaced with recovery keys... $userInfo->setPassword($passwordNew) ->removeTOTPKey() ->save(); $msz->createAuditLog('PASSWORD_RESET', [], $userInfo); $tokenInfo->invalidate(); url_redirect('auth-login', ['redirect' => '/']); return; } if(!empty($forgot)) { if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } if(empty($forgot['email']) || !is_string($forgot['email'])) { $notices[] = "You didn't supply an e-mail address."; break; } if($remainingAttempts < 1) { $notices[] = "There are too many failed login attempts from your IP address, please try again later."; break; } try { $forgotUser = User::byEMailAddress($forgot['email']); } catch(UserNotFoundException $ex) { unset($forgotUser); } if(empty($forgotUser) || $forgotUser->isDeleted()) { $notices[] = "This e-mail address is not registered with us."; break; } try { $tokenInfo = UserRecoveryToken::byUserAndRemoteAddress($forgotUser, $ipAddress); } catch(UserRecoveryTokenNotFoundException $ex) { $tokenInfo = UserRecoveryToken::create($forgotUser, $ipAddress); $recoveryMessage = Mailer::template('password-recovery', [ 'username' => $forgotUser->getUsername(), 'token' => $tokenInfo->getToken(), ]); $recoveryMail = Mailer::sendMessage( [$forgotUser->getEMailAddress() => $forgotUser->getUsername()], $recoveryMessage['subject'], $recoveryMessage['message'] ); if(!$recoveryMail) { $notices[] = "Failed to send reset email, please contact the administrator."; $tokenInfo->invalidate(); break; } } url_redirect('auth-reset', ['user' => $forgotUser->getId()]); return; } break; } Template::render(isset($userInfo) ? 'auth.password_reset' : 'auth.password_forgot', [ 'password_notices' => $notices, 'password_email' => !empty($forget['email']) && is_string($forget['email']) ? $forget['email'] : '', 'password_attempts_remaining' => $remainingAttempts, 'password_user' => $userInfo ?? null, 'password_verification' => $verificationCode ?? '', ]);