207 lines
6.9 KiB
PHP
207 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace com\fwdekker\deathnotifier\user;
|
|
|
|
use com\fwdekker\deathnotifier\Action;
|
|
use com\fwdekker\deathnotifier\Config;
|
|
use com\fwdekker\deathnotifier\mailer\Email;
|
|
use com\fwdekker\deathnotifier\mailer\EmailQueue;
|
|
use com\fwdekker\deathnotifier\tracking\TrackingList;
|
|
use com\fwdekker\deathnotifier\UnexpectedException;
|
|
use com\fwdekker\deathnotifier\validation\InvalidTypeException;
|
|
use com\fwdekker\deathnotifier\validation\InvalidValueException;
|
|
use com\fwdekker\deathnotifier\validation\IsEmailRule;
|
|
use com\fwdekker\deathnotifier\validation\IsValidCsrfTokenRule;
|
|
use com\fwdekker\deathnotifier\validation\RuleSet;
|
|
use com\fwdekker\deathnotifier\validation\LoginValidator;
|
|
|
|
|
|
/**
|
|
* Changes the user's email address and sends the user a verification link.
|
|
*
|
|
* @see ChangeEmailFromEmail
|
|
* @see ChangeEmailToEmail
|
|
*/
|
|
class ChangeEmailAction extends Action
|
|
{
|
|
/**
|
|
* @var UserList the list to change the user's email address in
|
|
*/
|
|
private readonly UserList $user_list;
|
|
/**
|
|
* @var EmailQueue the queue to add notifications to
|
|
*/
|
|
private readonly EmailQueue $email_queue;
|
|
|
|
|
|
/**
|
|
* Constructs a new `ChangeEmailAction`.
|
|
*
|
|
* @param UserList $user_list the list to change the user's email address in
|
|
* @param EmailQueue $email_queue the queue to add notifications to
|
|
*/
|
|
public function __construct(UserList $user_list, EmailQueue $email_queue)
|
|
{
|
|
$this->user_list = $user_list;
|
|
$this->email_queue = $email_queue;
|
|
}
|
|
|
|
|
|
/**
|
|
* Changes the user's email address and sends the user a verification link.
|
|
*
|
|
* Requires that the user is logged in and that a valid CSRF token is present.
|
|
*
|
|
* @param array<int|string, mixed> $inputs `"token": string`: a valid CSRF token, `"email": string`: the new email
|
|
* address
|
|
* @return null
|
|
* @throws InvalidTypeException if any of the inputs has the incorrect type
|
|
* @throws InvalidValueException if the user is not logged in, if no valid CSRF token is present, if the email
|
|
* address is invalid, if the email address is not different, or if the email address is already used
|
|
* @throws UnexpectedException if the current user has been deleted
|
|
* @noinspection PhpDocRedundantThrowsInspection can be thrown through {@see TrackingList::transaction()}
|
|
*/
|
|
public function handle(array $inputs): mixed
|
|
{
|
|
(new LoginValidator(validate_logged_in: true))->check($_SESSION);
|
|
(new RuleSet([
|
|
"token" => [new IsValidCsrfTokenRule()],
|
|
"email" => [new IsEmailRule()],
|
|
]))->check($inputs);
|
|
|
|
$this->user_list->transaction(function () use ($inputs) {
|
|
$user_data = $this->user_list->get_user_by_uuid($_SESSION["uuid"]);
|
|
if ($user_data === null)
|
|
throw new UnexpectedException("Failed to retrieve user data. Please refresh the page and try again.");
|
|
if ($inputs["email"] === $user_data["email"])
|
|
throw new InvalidValueException("That is already your email address.", "email");
|
|
if ($this->user_list->has_user_with_email($inputs["email"]))
|
|
throw new InvalidValueException("That email address is already in use by someone else.", "email");
|
|
|
|
$token = $this->user_list->set_email($_SESSION["uuid"], $inputs["email"]);
|
|
$this->email_queue->enqueue([
|
|
new ChangeEmailFromEmail($user_data["email"], $inputs["email"]),
|
|
new ChangeEmailToEmail($inputs["email"], $user_data["email"], $token)
|
|
]);
|
|
});
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* An email informing a user that their email has been changed and needs to be verified, sent to the user's old email
|
|
* address.
|
|
*
|
|
* @see ChangeEmailAction
|
|
*/
|
|
class ChangeEmailFromEmail extends Email
|
|
{
|
|
/**
|
|
* A string identifying the type of email.
|
|
*/
|
|
public const TYPE = "changed-email-from";
|
|
|
|
/**
|
|
* @var string the new email address
|
|
*/
|
|
public readonly string $new_email;
|
|
|
|
|
|
/**
|
|
* Constructs a new `ChangeEmailFromEmail`.
|
|
*
|
|
* @param string $recipient the intended recipient of this email
|
|
* @param string $new_email the new email address
|
|
*/
|
|
public function __construct(string $recipient, string $new_email)
|
|
{
|
|
parent::__construct($this::TYPE, $recipient);
|
|
|
|
$this->new_email = $new_email;
|
|
}
|
|
|
|
|
|
public function get_subject(): string
|
|
{
|
|
return "Your email address has been changed";
|
|
}
|
|
|
|
public function get_body(): string
|
|
{
|
|
$base_path = Config::get("server.base_path");
|
|
|
|
return
|
|
"You changed the email address of your Death Notifier account from $this->recipient to $this->new_email. " .
|
|
"Until you verify your email address, you will not receive any notifications on either email address. " .
|
|
"Check the inbox of $this->new_email for a verification link." .
|
|
"\n\n" .
|
|
"If you did not request a change of email address, log in at the Death Notifier website, change your " .
|
|
"password, and change your email address back." .
|
|
"\n\n" .
|
|
$base_path;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An email informing a user that their email has been changed and needs to be verified, sent to the user's new email
|
|
* address that needs to be verified.
|
|
*
|
|
* @see ChangeEmailAction
|
|
*/
|
|
class ChangeEmailToEmail extends Email
|
|
{
|
|
/**
|
|
* A string identifying the type of email.
|
|
*/
|
|
public const TYPE = "changed-email-to";
|
|
|
|
/**
|
|
* @var string the old email address
|
|
*/
|
|
public readonly string $old_email;
|
|
/**
|
|
* @var string the token to verify the email address with
|
|
*/
|
|
public readonly string $token;
|
|
|
|
|
|
/**
|
|
* Constructs a new `ChangeEmailEmail`.
|
|
*
|
|
* @param string $recipient the intended recipient of this email
|
|
* @param string $old_email the old email address
|
|
* @param string $token the token to verify the email address with
|
|
*/
|
|
public function __construct(string $recipient, string $old_email, string $token)
|
|
{
|
|
parent::__construct($this::TYPE, $recipient);
|
|
|
|
$this->old_email = $old_email;
|
|
$this->token = $token;
|
|
}
|
|
|
|
|
|
public function get_subject(): string
|
|
{
|
|
return "Verify your new email address";
|
|
}
|
|
|
|
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 changed the email address of your Death Notifier account from $this->old_email to $this->recipient. " .
|
|
"Until you verify your email address, you will not receive any notifications. " .
|
|
"You can verify your new email address by clicking the link below. " .
|
|
"This link will expire after " . UserList::MINUTES_VALID_VERIFICATION . " minutes." .
|
|
"\n" .
|
|
"Verify: $verify_path" .
|
|
"\n\n" .
|
|
$base_path;
|
|
}
|
|
}
|