user_list = $user_list; $this->email_queue = $email_queue; } /** * Changes the user's password and notifies the user by email. * * Requires that the user is logged in and that a valid CSRF token is present. * * @param array $inputs `"token": string`: a valid CSRF token, `"password_old": string`: the old * password, `"password_new": string`: the new password * @return null * @throws InvalidTypeException if any of the inputs has the incorrect type * @throws InvalidValueException if the user is not logged in, if no valid CSRF token is present, if the old * password is incorrect, or if the new password is too short or too long * @throws UnexpectedException if the current user has been deleted * @noinspection PhpDocRedundantThrowsInspection can be thrown through {@see TrackingList::transaction()} */ public function handle(array $inputs): mixed { (new LoginValidator(validate_logged_in: true))->check($_SESSION); (new RuleSet([ "token" => [new IsValidCsrfTokenRule()], "password_old" => [new IsStringRule()], "password_new" => [new HasStringLengthRule(UserList::MIN_PASSWORD_LENGTH, UserList::MAX_PASSWORD_LENGTH)], ]))->check($inputs); $this->user_list->transaction(function () use ($inputs) { $user_data = $this->user_list->get_user_by_uuid($_SESSION["uuid"]); if ($user_data === null) throw new UnexpectedException("Failed to retrieve user data. Please refresh the page and try again."); if (!password_verify($inputs["password_old"], $user_data["password"])) throw new InvalidValueException("Incorrect old password.", "password_old"); $this->user_list->set_password($_SESSION["uuid"], $inputs["password_new"]); $this->email_queue->queue_emails([new ChangePasswordEmail($user_data["email"])]); }); return null; } } /** * An email informing a user that their password has been changed. * * @see ChangePasswordAction */ class ChangePasswordEmail extends Email { /** * A string identifying the type of email. */ public const TYPE = "changed-password"; /** * Constructs a new `ChangedPasswordEmail`. * * @param string $recipient the intended recipient of this email */ public function __construct(string $recipient) { parent::__construct($this::TYPE, $recipient); } public function get_subject(): string { return "Your password has been changed"; } public function get_body(): string { $base_path = Config::get("server.base_path"); return "You changed the password of your Death Notifier account." . "\n\n" . "If you did not change the password of your account, go to the Death Notifier website and use the forgot " . "password option to change your password back." . "\n\n" . $base_path; } }