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

143 lines
4.6 KiB
PHP

<?php
namespace com\fwdekker\deathnotifier\user;
use com\fwdekker\deathnotifier\Action;
use com\fwdekker\deathnotifier\ActionException;
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\validator\HasLengthRule;
use com\fwdekker\deathnotifier\validator\IsEmailRule;
use com\fwdekker\deathnotifier\validator\InvalidInputException;
use PDO;
/**
* Registers a new user.
*/
class RegisterAction extends Action
{
/**
* @var PDO the connection to register the user with
*/
private readonly PDO $conn;
/**
* @var UserManager the manager to register the user with
*/
private readonly UserManager $user_manager;
/**
* @var MailManager the manager to send emails with
*/
private readonly MailManager $mail_manager;
/**
* Constructs a new `RegisterAction`.
*
* @param PDO $conn the connection to register the user with
* @param UserManager $user_manager the manager to register the user with
* @param MailManager $mail_manager the manager to send emails with
*/
public function __construct(PDO $conn, UserManager $user_manager, MailManager $mail_manager)
{
parent::__construct(
require_logged_out: true,
require_valid_csrf_token: true,
rule_lists: [
"email" => [new IsEmailRule()],
"password" => [new HasLengthRule(UserManager::MIN_PASSWORD_LENGTH, UserManager::MAX_PASSWORD_LENGTH)],
],
);
$this->conn = $conn;
$this->user_manager = $user_manager;
$this->mail_manager = $mail_manager;
}
/**
* Registers a new user.
*
* @param array<int|string, mixed> $inputs `"email": string`: the email address to register, `"password": string`:
* the password to use for the new account
* @return null
* @throws InvalidInputException if the email address is already used
*/
function handle(array $inputs): mixed
{
Database::transaction($this->conn, function () use ($inputs) {
if ($this->user_manager->has_user_with_email($inputs["email"]))
throw new InvalidInputException("That email address already in use by someone else.", "email");
$uuid = $this->user_manager->add_user($inputs["email"], $inputs["password"]);
$user_data = $this->user_manager->get_user_by_uuid($uuid);
if ($user_data === null)
throw new ActionException("Failed to retrieve account data. Refresh the page and try again.");
$token = $user_data["email_verification_token"];
$this->mail_manager->queue_email(new RegisterEmail($inputs["email"], $token));
});
return null;
}
}
/**
* An email to be sent to a recently registered user, including instructions for email verification.
*/
class RegisterEmail extends Email
{
/**
* A string identifying the type of email.
*/
public const TYPE = "register";
/**
* @var string the token to verify the email address with
*/
public string $token;
/**
* Constructs a new `RegisterEmail`.
*
* @param string $recipient the intended recipient of the email
* @param string $token the token to verify the email address with
*/
public function __construct(string $recipient, string $token)
{
parent::__construct($this::TYPE, $recipient);
$this->token = $token;
}
public function get_subject(): string
{
return "Welcome to Death Notifier";
}
public function get_body(): string
{
$base_path = Config::get()["server"]["base_path"];
$verify_path = "$base_path?action=verify-email&email=" . rawurlencode($this->recipient) . "&token=$this->token";
return
"You have successfully created an account with Death Notifier. " .
"Welcome!" .
"\n\n" .
"Until you verify your email address, you will not receive any notifications. " .
"You can verify your email address by clicking the link below. " .
"This link will expire after " . UserManager::MINUTES_VALID_VERIFICATION . " minutes." .
"\n" .
"Verify: $verify_path" .
"\n\n" .
"If you did not create this account, you can delete the account. " .
"Go to the Death Notifier website, reset your password, log in, and delete the account." .
"\n\n" .
$base_path;
}
}