user_list = $user_list; $this->email_queue = $email_queue; } /** * Registers a new user. * * Requires that the user is logged out and that a valid CSRF token is present. * * @param array $inputs `"token": string`: a valid CSRF token, `"email": string`: the email * address to register the account under, `"password": string`: the password to use for the new account * @return null * @throws InvalidTypeException if any of the inputs has the incorrect type * @throws InvalidValueException if the user is logged in, if no valid CSRF token is present, if the email address * is invalid, if the email address is already in use, or if the password is too short or too long */ public function handle(array $inputs): mixed { (new LoginValidator(validate_logged_out: true))->check($_SESSION); (new RuleSet([ "token" => [new IsValidCsrfTokenRule()], "email" => [new IsEmailRule()], "password" => [new HasStringLengthRule(UserList::MIN_PASSWORD_LENGTH, UserList::MAX_PASSWORD_LENGTH)], "captcha" => [new EqualsRule( "pride", "Enter the word \"pride\" to prove that you are probably not a robot." )], ]))->check($inputs); $this->user_list->transaction(function () use ($inputs) { if ($this->user_list->has_user_with_email($inputs["email"])) throw new InvalidValueException("That email address already in use.", "email"); $token = $this->user_list->add_user($inputs["email"], $inputs["password"]); $this->email_queue->enqueue([new RegisterEmail($inputs["email"], $token)]); }); return null; } } /** * An email to be sent to a recently registered user, including instructions for email verification. * * @see RegisterAction */ 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 this 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 " . UserList::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; } }