death-notifier/src/main/php/com/fwdekker/deathnotifier/user/ValidatePasswordResetTokenA...

93 lines
3.6 KiB
PHP

<?php
namespace com\fwdekker\deathnotifier\user;
use com\fwdekker\deathnotifier\Action;
use com\fwdekker\deathnotifier\validator\IsValidCsrfTokenRule;
use com\fwdekker\deathnotifier\Util;
use com\fwdekker\deathnotifier\validator\IsEmailRule;
use com\fwdekker\deathnotifier\validator\InvalidInputException;
use com\fwdekker\deathnotifier\validator\IsStringRule;
use com\fwdekker\deathnotifier\validator\RuleSet;
/**
* Checks whether the given password reset token is valid.
*/
class ValidatePasswordResetTokenAction extends Action
{
/**
* @var UserList the list to validate the password reset token in
*/
private readonly UserList $user_list;
/**
* Constructs a new `ValidatePasswordResetTokenAction`.
*
* @param UserList $user_list the list to validate the password reset token in
*/
public function __construct(UserList $user_list)
{
$this->user_list = $user_list;
}
/**
* Checks whether the given password reset token is valid.
*
* 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"`: the email address to
* validate the password reset token of, `"reset_token": string`: the password reset token to validate
* @return null
* @throws InvalidInputException if no valid CSRF token is present, if no account with the given email address
* exists, if the reset token is invalid, or if the reset token has expired
*/
public function handle(array $inputs): mixed
{
(new RuleSet([
"token" => [new IsValidCsrfTokenRule()],
"email" => [new IsEmailRule()],
"reset_token" => [new IsStringRule()],
]))->check($inputs);
ValidatePasswordResetTokenAction::validate_token($this->user_list, $inputs["email"], $inputs["reset_token"]);
return null;
}
/**
* Checks whether the given password reset token is valid.
*
* @param UserList $user_list the list to validate the password reset token in
* @param string $email the email address to validate the password reset token of
* @param string $reset_token the password reset token to validate
* @return void
* @throws InvalidInputException if no account with the given email address exists, if the reset token is invalid,
* or if the reset token has expired
*/
public static function validate_token(UserList $user_list, string $email, string $reset_token): void
{
$user_data = $user_list->get_user_by_email($email);
if ($user_data === null)
throw new InvalidInputException("No user with that email address has been registered.");
if ($reset_token !== $user_data["password_reset_token"])
// TODO: Just tell the user why the link is invalid: Because no request exists, or because the token is wrong
// TODO: Also, tell the user what they can do to resolve this
throw new InvalidInputException(
"This password reset link is invalid. Maybe you already reset your password?"
);
$minutes_left = Util::minutes_until_interval_elapsed(
$user_data["password_reset_token_timestamp"],
UserList::MINUTES_VALID_PASSWORD_RESET
);
if ($minutes_left < 0) {
throw new InvalidInputException(
`This password reset link has expired. <a href="./">Return to the front page</a> and press the ` .
`"Forgot password?" button to request a new password reset link.`
);
}
}
}