142 lines
4.1 KiB
PHP
142 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace com\fwdekker\deathnotifier\user;
|
|
|
|
use com\fwdekker\deathnotifier\Action;
|
|
use com\fwdekker\deathnotifier\ActionMethod;
|
|
use com\fwdekker\deathnotifier\Config;
|
|
use com\fwdekker\deathnotifier\Database;
|
|
use com\fwdekker\deathnotifier\mailer\Email;
|
|
use com\fwdekker\deathnotifier\mailer\MailManager;
|
|
use com\fwdekker\deathnotifier\Util;
|
|
use com\fwdekker\deathnotifier\validator\IsEmailRule;
|
|
use com\fwdekker\deathnotifier\validator\ValidationException;
|
|
use PDO;
|
|
|
|
|
|
/**
|
|
* Sends a password reset email.
|
|
*/
|
|
class SendPasswordResetAction extends Action
|
|
{
|
|
/**
|
|
* @var PDO the connection to send the password reset with
|
|
*/
|
|
private readonly PDO $conn;
|
|
/**
|
|
* @var UserManager the manager to send the password reset with
|
|
*/
|
|
private readonly UserManager $user_manager;
|
|
/**
|
|
* @var MailManager the manager to send emails with
|
|
*/
|
|
private readonly MailManager $mail_manager;
|
|
|
|
|
|
/**
|
|
* Constructs a new `SendPasswordResetAction`.
|
|
*
|
|
* @param PDO $conn the connection to send the password reset with
|
|
* @param UserManager $user_manager the manager to send the password reset with
|
|
* @param MailManager $mail_manager the manager to send emails with
|
|
*/
|
|
public function __construct(PDO $conn, UserManager $user_manager, MailManager $mail_manager)
|
|
{
|
|
parent::__construct(
|
|
ActionMethod::POST,
|
|
"send-password-reset",
|
|
require_valid_csrf_token: true,
|
|
rule_lists: ["email" => [new IsEmailRule()]],
|
|
);
|
|
|
|
$this->conn = $conn;
|
|
$this->user_manager = $user_manager;
|
|
$this->mail_manager = $mail_manager;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sends a password reset email.
|
|
*
|
|
* @return null
|
|
* @throws ValidationException if another password reset email was sent too recently
|
|
*/
|
|
function handle(): mixed
|
|
{
|
|
Database::transaction($this->conn, function () {
|
|
$user_data = $this->user_manager->get_user_by_email($_POST["email"]);
|
|
|
|
$minutes_left = Util::minutes_until_interval_elapsed(
|
|
$user_data["password_reset_token_timestamp"],
|
|
UserManager::MINUTES_BETWEEN_PASSWORD_RESETS
|
|
);
|
|
if ($minutes_left > 0) {
|
|
throw new ValidationException(
|
|
"A password reset email was sent recently. " .
|
|
"Please wait $minutes_left more minute(s) before requesting a new email."
|
|
);
|
|
}
|
|
|
|
$token = $this->user_manager->register_password_reset($_POST["email"]);
|
|
$this->mail_manager->queue_email(new SendPasswordResetEmail($_POST["email"], $token));
|
|
});
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* An email to help a user reset their password.
|
|
*/
|
|
class SendPasswordResetEmail extends Email
|
|
{
|
|
/**
|
|
* A string identifying the type of email.
|
|
*/
|
|
public const TYPE = "reset-password";
|
|
|
|
/**
|
|
* @var string the token to reset the password with
|
|
*/
|
|
public string $token;
|
|
|
|
|
|
/**
|
|
* Constructs a new `ResetPasswordEmail`.
|
|
*
|
|
* @param string $recipient the intended recipient of the email
|
|
* @param string $token the token to reset the password with
|
|
*/
|
|
public function __construct(string $recipient, string $token)
|
|
{
|
|
parent::__construct($this::TYPE, $recipient);
|
|
|
|
$this->token = $token;
|
|
}
|
|
|
|
|
|
public function get_subject(): string
|
|
{
|
|
return "Reset your password";
|
|
}
|
|
|
|
public function get_body(): string
|
|
{
|
|
$base_path = Config::get()["server"]["base_path"];
|
|
$verify_path =
|
|
"$base_path?action=reset-password&email=" . rawurlencode($this->recipient) . "&token=$this->token";
|
|
|
|
return
|
|
"You requested a password reset link for your Death Notifier account. " .
|
|
"You can choose a new password by clicking the link below. " .
|
|
"This link expires after " . UserManager::MINUTES_VALID_PASSWORD_RESET . " minutes." .
|
|
"\n" .
|
|
"Reset password: $verify_path" .
|
|
"\n\n" .
|
|
"If you did not request a new password, you can safely ignore this message." .
|
|
"\n\n" .
|
|
$base_path;
|
|
}
|
|
}
|