isLoggedIn()) throw new UserFacingException('You are already logged in.'); // Verifying an existing reset key? if (isset($_GET['step'], $_GET['email'], $_GET['key']) && $_GET['step'] == 2) $this->verifyResetKey(); else $this->requestResetKey(); } private function requestResetKey() { parent::__construct('Reset password - ' . SITE_TITLE); $form = new ForgotPasswordForm(); $this->page->adopt($form); // Have they submitted an email address yet? if (isset($_POST['emailaddress']) && preg_match('~^.+@.+\.[a-z]+$~', trim($_POST['emailaddress']))) { $user = Member::fromEmailAddress($_POST['emailaddress']); if (!$user) { $form->adopt(new Alert('Invalid email address', 'The email address you provided could not be found in our system. Please try again.', 'danger')); return; } if (Authentication::getResetTimeOut($user->getUserId()) > 0) { // Update the reset time-out to prevent hammering $resetTimeOut = Authentication::updateResetTimeOut($user->getUserId()); // Present it to the user in a readable way if ($resetTimeOut > 3600) $timeOut = sprintf('%d hours', ceil($resetTimeOut / 3600)); elseif ($resetTimeOut > 60) $timeOut = sprintf('%d minutes', ceil($resetTimeOut / 60)); else $timeOut = sprintf('%d seconds', $resetTimeOut); $form->adopt(new Alert('Password reset token already sent', 'We already sent a password reset token to this email address recently. ' . 'If no email was received, please wait ' . $timeOut . ' to try again.', 'error')); return; } Authentication::setResetKey($user->getUserId()); Email::resetMail($user->getUserId()); // Show the success message $this->page->clear(); $box = new DummyBox('An email has been sent'); $box->adopt(new Alert('', 'We have sent an email to ' . $_POST['emailaddress'] . ' containing details on how to reset your password.', 'success')); $this->page->adopt($box); } } private function verifyResetKey() { $email = rawurldecode($_GET['email']); $user = Member::fromEmailAddress($email); if (!$user) throw new UserFacingException('Invalid email address. Please make sure you copied the full link in the email you received.'); $key = $_GET['key']; if (!Authentication::checkResetKey($user->getUserId(), $key)) throw new UserFacingException('Invalid reset token. Please make sure you copied the full link in the email you received. Note: you cannot use the same token twice.'); parent::__construct('Reset password - ' . SITE_TITLE); $form = new PasswordResetForm($email, $key); $this->page->adopt($form); // Are they trying to set something already? if (isset($_POST['password1'], $_POST['password2'])) { $missing = []; if (strlen($_POST['password1']) < 6 || !preg_match('~[^A-z]~', $_POST['password1'])) $missing[] = 'Please fill in a password that is at least six characters long and contains at least one non-alphabetic character (e.g. a number or symbol).'; if ($_POST['password1'] != $_POST['password2']) $missing[] = 'The passwords you entered do not match.'; // So, are we good to go? if (empty($missing)) { Authentication::updatePassword($user->getUserId(), Authentication::computeHash($_POST['password1'])); // Consume token, ensuring it isn't used again Authentication::consumeResetKey($user->getUserId()); $_SESSION['login_msg'] = ['Your password has been reset', 'You can now use the form below to log in to your account.', 'success']; header('Location: ' . BASEURL . '/login/'); exit; } else $form->adopt(new Alert('Some fields require your attention', '', 'danger')); } } }