93 lines
3.6 KiB
PHP
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.`
|
|
);
|
|
}
|
|
}
|
|
}
|