130 lines
4.4 KiB
PHP
130 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace com\fwdekker\deathnotifier\user;
|
|
|
|
use com\fwdekker\deathnotifier\Action;
|
|
use com\fwdekker\deathnotifier\Config;
|
|
use com\fwdekker\deathnotifier\mailer\Email;
|
|
use com\fwdekker\deathnotifier\mailer\EmailQueue;
|
|
use com\fwdekker\deathnotifier\UnexpectedException;
|
|
use com\fwdekker\deathnotifier\validation\HasStringLengthRule;
|
|
use com\fwdekker\deathnotifier\validation\InvalidTypeException;
|
|
use com\fwdekker\deathnotifier\validation\InvalidValueException;
|
|
use com\fwdekker\deathnotifier\validation\IsStringRule;
|
|
use com\fwdekker\deathnotifier\validation\IsValidCsrfTokenRule;
|
|
use com\fwdekker\deathnotifier\validation\RuleSet;
|
|
use com\fwdekker\deathnotifier\validation\LoginValidator;
|
|
|
|
|
|
/**
|
|
* Changes the user's password and notifies the user by email.
|
|
*
|
|
* @see ChangePasswordEmail
|
|
*/
|
|
class ChangePasswordAction extends Action
|
|
{
|
|
/**
|
|
* @var UserList the list to change the user's password in
|
|
*/
|
|
private readonly UserList $user_list;
|
|
/**
|
|
* @var EmailQueue the queue to add a notification to
|
|
*/
|
|
private readonly EmailQueue $email_queue;
|
|
|
|
|
|
/**
|
|
* Constructs a new `ChangePasswordAction`.
|
|
*
|
|
* @param UserList $user_list the list to change the user's password in
|
|
* @param EmailQueue $email_queue the queue to add a notification to
|
|
*/
|
|
public function __construct(UserList $user_list, EmailQueue $email_queue)
|
|
{
|
|
$this->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<int|string, mixed> $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->enqueue([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;
|
|
}
|
|
}
|