2022-12-01 18:24:08 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace com\fwdekker\deathnotifier\user;
|
|
|
|
|
|
|
|
use com\fwdekker\deathnotifier\Action;
|
2022-12-03 14:59:53 +01:00
|
|
|
use com\fwdekker\deathnotifier\Config;
|
2022-12-04 23:56:49 +01:00
|
|
|
use com\fwdekker\deathnotifier\IllegalStateError;
|
2022-12-03 14:59:53 +01:00
|
|
|
use com\fwdekker\deathnotifier\mailer\Email;
|
2022-12-04 23:56:49 +01:00
|
|
|
use com\fwdekker\deathnotifier\mailer\EmailQueue;
|
|
|
|
use com\fwdekker\deathnotifier\validator\HasStringLengthRule;
|
2022-12-04 16:58:12 +01:00
|
|
|
use com\fwdekker\deathnotifier\validator\InvalidInputException;
|
2022-12-06 18:12:43 +01:00
|
|
|
use com\fwdekker\deathnotifier\validator\IsEmailRule;
|
|
|
|
use com\fwdekker\deathnotifier\validator\IsStringRule;
|
|
|
|
use com\fwdekker\deathnotifier\validator\IsValidCsrfTokenRule;
|
2022-12-04 23:56:49 +01:00
|
|
|
use com\fwdekker\deathnotifier\validator\RuleSet;
|
2022-12-01 18:24:08 +01:00
|
|
|
|
|
|
|
|
2022-12-03 14:59:53 +01:00
|
|
|
/**
|
|
|
|
* Resets the user's password after they forgot it.
|
2022-12-04 23:56:49 +01:00
|
|
|
*
|
|
|
|
* @see ResetPasswordEmail
|
2022-12-03 14:59:53 +01:00
|
|
|
*/
|
2022-12-01 18:24:08 +01:00
|
|
|
class ResetPasswordAction extends Action
|
|
|
|
{
|
2022-12-03 14:59:53 +01:00
|
|
|
/**
|
2022-12-04 23:56:49 +01:00
|
|
|
* @var UserList the list to reset the password in
|
2022-12-03 14:59:53 +01:00
|
|
|
*/
|
2022-12-04 23:56:49 +01:00
|
|
|
private readonly UserList $user_list;
|
2022-12-03 14:59:53 +01:00
|
|
|
/**
|
2022-12-04 23:56:49 +01:00
|
|
|
* @var EmailQueue the queue to add a notification to
|
2022-12-03 14:59:53 +01:00
|
|
|
*/
|
2022-12-04 23:56:49 +01:00
|
|
|
private readonly EmailQueue $email_queue;
|
2022-12-01 18:24:08 +01:00
|
|
|
|
|
|
|
|
2022-12-03 14:59:53 +01:00
|
|
|
/**
|
|
|
|
* Constructs a new `ResetPasswordAction`.
|
|
|
|
*
|
2022-12-04 23:56:49 +01:00
|
|
|
* @param UserList $user_list the list to reset the password in
|
|
|
|
* @param EmailQueue $email_queue the queue to add a notification to
|
2022-12-03 14:59:53 +01:00
|
|
|
*/
|
2022-12-04 23:56:49 +01:00
|
|
|
public function __construct(UserList $user_list, EmailQueue $email_queue)
|
2022-12-01 18:24:08 +01:00
|
|
|
{
|
2022-12-04 23:56:49 +01:00
|
|
|
$this->user_list = $user_list;
|
|
|
|
$this->email_queue = $email_queue;
|
2022-12-01 18:24:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-03 14:59:53 +01:00
|
|
|
/**
|
|
|
|
* Resets the user's password after they forgot it.
|
|
|
|
*
|
2022-12-04 23:56:49 +01:00
|
|
|
* Requires that a valid CSRF token is present. Does not require the user to be logged in or out.
|
|
|
|
*
|
|
|
|
* @param array<int|string, mixed> $inputs `"token": string`: a valid CSRF token, `"email": string`: the email
|
|
|
|
* address of the account to reset the password of, `"password": string`: the new password to set,
|
|
|
|
* `"reset_token": string`: the token to reset the password with
|
2022-12-03 14:59:53 +01:00
|
|
|
* @return null
|
2022-12-04 23:56:49 +01:00
|
|
|
* @throws InvalidInputException if no valid CSRF token is present, if no account with the given email address
|
|
|
|
* exists, if the password is too short or too long, if the reset token is invalid, or if the reset token has
|
|
|
|
* expired
|
2022-12-03 14:59:53 +01:00
|
|
|
*/
|
2022-12-04 23:56:49 +01:00
|
|
|
public function handle(array $inputs): mixed
|
2022-12-01 18:24:08 +01:00
|
|
|
{
|
2022-12-04 23:56:49 +01:00
|
|
|
(new RuleSet([
|
2022-12-06 18:12:43 +01:00
|
|
|
"token" => [new IsValidCsrfTokenRule()],
|
|
|
|
"email" => [new IsEmailRule()],
|
|
|
|
"password" => [new HasStringLengthRule(UserList::MIN_PASSWORD_LENGTH, UserList::MAX_PASSWORD_LENGTH)],
|
|
|
|
"reset_token" => [new IsStringRule()],
|
2022-12-04 23:56:49 +01:00
|
|
|
]))->check($inputs);
|
|
|
|
|
|
|
|
$this->user_list->transaction(function () use ($inputs) {
|
2022-12-06 18:12:43 +01:00
|
|
|
ValidatePasswordResetTokenAction::validate_token(
|
|
|
|
$this->user_list,
|
|
|
|
$inputs["email"],
|
|
|
|
$inputs["reset_token"]
|
|
|
|
);
|
2022-12-04 23:56:49 +01:00
|
|
|
|
|
|
|
$user_data = $this->user_list->get_user_by_email($inputs["email"]);
|
|
|
|
if ($user_data === null)
|
|
|
|
throw new IllegalStateError("User data is `null` despite previous validation.");
|
|
|
|
|
|
|
|
$this->user_list->set_password($user_data["uuid"], $inputs["password"]);
|
|
|
|
$this->email_queue->queue_email(new ResetPasswordEmail($inputs["email"]));
|
2022-12-03 14:59:53 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An email informing a user that their password has been reset.
|
2022-12-04 23:56:49 +01:00
|
|
|
*
|
|
|
|
* @see ResetPasswordAction
|
2022-12-03 14:59:53 +01:00
|
|
|
*/
|
|
|
|
class ResetPasswordEmail extends Email
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* A string identifying the type of email.
|
|
|
|
*/
|
|
|
|
public const TYPE = "reset-password";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a new `ChangedPasswordEmail`.
|
|
|
|
*
|
|
|
|
* @param string $recipient the intended recipient of the email
|
|
|
|
*/
|
|
|
|
public function __construct(string $recipient)
|
|
|
|
{
|
|
|
|
parent::__construct($this::TYPE, $recipient);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function get_subject(): string
|
|
|
|
{
|
|
|
|
return "Your password has been reset";
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get_body(): string
|
|
|
|
{
|
|
|
|
$base_path = Config::get()["server"]["base_path"];
|
|
|
|
|
|
|
|
return
|
|
|
|
"You requested a password reset and 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;
|
2022-12-01 18:24:08 +01:00
|
|
|
}
|
|
|
|
}
|