From af795975721d12feeb8b10a83a3f56e128a4a00e Mon Sep 17 00:00:00 2001
From: "Florine W. Dekker"
Date: Mon, 29 Aug 2022 01:09:33 +0200
Subject: [PATCH] Clean up server-side by, like, a lot
---
.gitignore | 2 +-
Gruntfile.js | 2 +-
composer.json | 2 +-
composer.lock | Bin 12142 -> 12142 bytes
package-lock.json | Bin 225103 -> 225103 bytes
package.json | 2 +-
src/main/api.php | 265 +++++++++++++----------------
src/main/index.html | 10 +-
src/main/php/Database.php | 14 ++
src/main/php/Mailer.php | 2 +
src/main/php/Mediawiki.php | 4 +-
src/main/php/Response.php | 3 +
src/main/php/TrackingManager.php | 83 ++++-----
src/main/php/UserManager.php | 195 +++++++--------------
src/main/php/Util.php | 96 +++++++++++
src/main/php/Validator.php | 280 +++++++++++++++++++++++++++++++
16 files changed, 623 insertions(+), 337 deletions(-)
create mode 100644 src/main/php/Util.php
create mode 100644 src/main/php/Validator.php
diff --git a/.gitignore b/.gitignore
index a589b70..920d449 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,7 +119,7 @@ dist
## Composer
composer.phar
-/vendor/
+vendor/
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
diff --git a/Gruntfile.js b/Gruntfile.js
index d9d7f6d..f05f367 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -8,7 +8,7 @@ module.exports = grunt => {
},
copy: {
composer: {
- files: [{expand: true, cwd: "./", src: "vendor/**", dest: "dist/", flatten: false}]
+ files: [{expand: true, cwd: "./vendor/", src: "**", dest: "dist/.vendor/", flatten: false}]
},
css: {
files: [{expand: true, cwd: "src/main/", src: "**/*.css", dest: "dist/", flatten: true}]
diff --git a/composer.json b/composer.json
index e2aacee..f303587 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"name": "fwdekker/death-notifier",
"description": "Get notified when a famous person dies.",
- "version": "0.0.23",
+ "version": "0.0.24",
"type": "project",
"license": "MIT",
"homepage": "https://git.fwdekker.com/tools/death-notifier",
diff --git a/composer.lock b/composer.lock
index 254d81d278cf0abaacf7c3c8f3b62d7728d27514..f337106b61f5570149118f75160860087e277c09 100644
GIT binary patch
delta 45
zcmaDC_bzUOFr$L8sfDq*rKO>{WooKPN}8!*qKTPBig{{Evaw~Nsj<;!V@7K|089rB
ANdN!<
delta 45
zcmaDC_bzUOFrz}ExrLdznNeD@nT2_ZrJ+%xnPpO{X;P|@QF3y!nVH#UV@7K|095)7
Aq5uE@
diff --git a/package-lock.json b/package-lock.json
index 7f5b6e620f6275becb7942e5e27cfb698694c458..7f1220dbe27fb9fd567cd4d0cab98be57b0620ba 100644
GIT binary patch
delta 32
ocmX?qkN5mN-U+6RCKJu3F`7(fpushHandler(new StreamHandler($config["logger"]["file"], $config["logger"]["level"]));
-ErrorHandler::register($logger);
-
-// Connect to database
-$db_exists = file_exists($config["database"]["filename"]);
+// Preamble
+$_POST = Util::parse_post();
+$config = Util::read_config() ?? Util::http_exit(500);
+$logger = Util::create_logger($config["logger"]);
$conn = Database::connect($config["database"]["filename"]);
-// Instantiate utility classes
-$mediawiki = new Mediawiki($logger->withName("Mediawiki"));
-$user_manager = new UserManager($conn);
-$tracking_manager = new TrackingManager($mediawiki, $conn);
$mailer = new Mailer($logger->withName("Mailer"), $config, $conn);
+$mediawiki = new Mediawiki($logger->withName("Mediawiki"));
+$user_manager = new UserManager($conn, $mailer);
+$tracking_manager = new TrackingManager($conn, $mailer, $mediawiki);
-// Create db if it does not exist
-if (!$db_exists) {
- $logger->warning("Database does not exist. Creating new database at '{$config["database"]["filename"]}'.");
-
+if (Database::is_empty($conn)) {
+ $logger->info("Database does not exist. Creating new database at '{$config["database"]["filename"]}'.");
+ $mailer->install();
$user_manager->install();
$tracking_manager->install();
- $mailer->install();
-}
-
-// Start session
-/**
- * Generates a CSRF token.
- *
- * @return string the generated CSRF token
- */
-function generate_csrf_token(Logger $logger): string
-{
- try {
- return bin2hex(random_bytes(32));
- } catch (Exception $exception) {
- $logger->emergency("Failed to generate token.", ["cause" => $exception]);
- http_response_code(500);
- exit();
- }
}
session_start();
-if (!isset($_SESSION["token"]))
- $_SESSION["token"] = generate_csrf_token($logger);
-
-// Read JSON from POST
-$post_input = file_get_contents("php://input");
-if ($post_input !== false) $_POST = json_decode($post_input, associative: true);
+$_SESSION["token"] = $_SESSION["token"] ?? Util::generate_csrf_token($logger);
-/// Define validation helpers
-/**
- * Validates the user's CSRF token by comparing `$_POST["token"]` against `$_SESSION["token"]`.
- *
- * @return Response|null `null` if the user's CSRF token is valid, or an error response if either value is not set or if
- * the two are not equal
- */
-function validate_csrf(): ?Response
-{
- if (!(isset($_POST["token"]) && isset($_SESSION["token"]) && hash_equals($_POST["token"], $_SESSION["token"])))
- return new Response(
- payload: ["target" => null, "message" => "Invalid CSRF token. Try refreshing the page."],
- satisfied: false
- );
-
- return null;
-}
-
-/**
- * Validates that the user is logged out by checking that `$_SESSION["uuid"]` is not set.
- *
- * @return Response|null `null` if the user is logged out, or an error response otherwise
- */
-function validate_logged_out(): ?Response
-{
- if (isset($_SESSION["uuid"]))
- return new Response(payload: ["target" => null, "message" => "User is already logged in."], satisfied: false);
-
- return null;
-}
-
-/**
- * Validates that the user is logged in by checking that `$_SESSION["uuid"]` is set.
- *
- * @return Response|null `null` if the user is logged in, or an error response otherwise
- */
-function validate_logged_in(): ?Response
-{
- if (!isset($_SESSION["uuid"]))
- return new Response(payload: ["target" => null, "message" => "User is not logged in."], satisfied: false);
-
- return null;
-}
-
-/**
- * Validates that each of `arguments` is set.
- *
- * @param mixed ...$arguments the arguments to check
- * @return Response|null `null` if all `arguments` are set, or an error response otherwise
- */
-function validate_has_arguments(mixed ...$arguments): ?Response
-{
- foreach (func_get_args() as $argument) {
- if (!isset($argument))
- return new Response(payload: ["target" => null, "message" => "Missing argument."], satisfied: false);
- if (!is_string($argument))
- return new Response(payload: ["target" => null, "message" => "Invalid argument type."], satisfied: false);
- }
-
- return null;
-}
-
-
-/// Process request
+// Process request
$response = null;
if (isset($_POST["action"])) {
// POST requests; alter state
switch ($_POST["action"]) {
case "register":
- $response = validate_csrf()
- ?? validate_logged_out()
- ?? validate_has_arguments($_POST["email"], $_POST["password"], $_POST["password_confirm"])
- ?? $user_manager->register($mailer, $_POST["email"], $_POST["password"], $_POST["password_confirm"]);
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsNotSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new EqualsRule($_SESSION["token"])],
+ "email" => [new IsSetRule(), new EmailRule()],
+ "password" => [
+ new IsSetRule(),
+ new LengthRule(UserManager::MIN_PASSWORD_LENGTH, UserManager::MAX_PASSWORD_LENGTH)
+ ],
+ "password_confirm" => [new IsSetRule()],
+ ])
+ ?? $user_manager->register($_POST["email"], $_POST["password"], $_POST["password_confirm"]);
break;
case "login":
- $response = validate_csrf()
- ?? validate_logged_out()
- ?? validate_has_arguments($_POST["email"], $_POST["password"]);
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsNotSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new EqualsRule($_SESSION["token"])],
+ "email" => [new IsSetRule(), new EmailRule()],
+ "password" => [
+ new IsSetRule(),
+ new LengthRule(UserManager::MIN_PASSWORD_LENGTH, UserManager::MAX_PASSWORD_LENGTH)
+ ],
+ ]);
if ($response !== null) break;
[$response, $uuid] = $user_manager->check_login($_POST["email"], $_POST["password"]);
if ($response->satisfied) $_SESSION["uuid"] = $uuid;
break;
case "logout":
- $response = validate_csrf() ?? validate_logged_in();
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST, ["token" => [new EqualsRule($_SESSION["token"])]]);
if ($response !== null) break;
session_destroy();
session_start();
- $_SESSION["token"] = generate_csrf_token($logger);
+ $_SESSION["token"] = Util::generate_csrf_token($logger) ?? Util::http_exit(500);
$response = new Response(payload: null, satisfied: true);
break;
case "update-email":
- $response = validate_csrf()
- ?? validate_logged_in()
- ?? validate_has_arguments($_POST["email"])
- ?? $user_manager->set_email($_SESSION["uuid"], $_POST["email"], $mailer);
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new EqualsRule($_SESSION["token"])],
+ "email" => [new IsSetRule(), new EmailRule()],
+ ])
+ ?? $user_manager->set_email($_SESSION["uuid"], $_POST["email"]);
break;
case "verify-email":
- $response = validate_has_arguments($_POST["email"], $_POST["token"])
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new IsSetRule()],
+ "email" => [new IsSetRule(), new EmailRule()],
+ ])
?? $user_manager->verify_email($_POST["email"], $_POST["token"]);
break;
case "resend-verify-email":
- $response = validate_csrf()
- ?? validate_logged_in()
- ?? $user_manager->resend_verify_email($_SESSION["uuid"], $mailer);
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST, ["token" => [new EqualsRule($_SESSION["token"])]])
+ ?? $user_manager->resend_verify_email($_SESSION["uuid"]);
break;
case "update-password":
- $response = validate_csrf()
- ?? validate_logged_in()
- ?? validate_has_arguments($_POST["password_old"], $_POST["password_new"], $_POST["password_confirm"])
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new EqualsRule($_SESSION["token"])],
+ "password_old" => [
+ new IsSetRule(),
+ new LengthRule(UserManager::MIN_PASSWORD_LENGTH, UserManager::MAX_PASSWORD_LENGTH)
+ ],
+ "password_new" => [
+ new IsSetRule(),
+ new LengthRule(UserManager::MIN_PASSWORD_LENGTH, UserManager::MAX_PASSWORD_LENGTH)
+ ],
+ "password_confirm" => [new IsSetRule()],
+ ])
?? $user_manager->set_password(
$_SESSION["uuid"],
$_POST["password_old"],
@@ -194,20 +140,37 @@ if (isset($_POST["action"])) {
);
break;
case "user-delete":
- $response = validate_csrf()
- ?? validate_logged_in()
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST, ["token" => [new EqualsRule($_SESSION["token"])]])
?? $user_manager->delete($_SESSION["uuid"]);
break;
case "add-tracking":
- $response = validate_csrf()
- ?? validate_logged_in()
- ?? validate_has_arguments($_POST["person_name"])
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new EqualsRule($_SESSION["token"])],
+ "person_name" => [
+ new IsSetRule(),
+ new LengthRule(TrackingManager::MIN_TITLE_LENGTH, TrackingManager::MAX_TITLE_LENGTH),
+ new IsNotBlankRule()
+ ],
+ ])
?? $tracking_manager->add_tracking($_SESSION["uuid"], $_POST["person_name"]);
break;
case "remove-tracking":
- $response = validate_csrf()
- ?? validate_logged_in()
- ?? validate_has_arguments($_POST["person_name"])
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? Validator::validate_inputs($_POST,
+ [
+ "token" => [new EqualsRule($_SESSION["token"])],
+ "person_name" => [
+ new IsSetRule(),
+ new LengthRule(TrackingManager::MIN_TITLE_LENGTH, TrackingManager::MAX_TITLE_LENGTH),
+ new IsNotBlankRule()
+ ],
+ ])
?? $tracking_manager->remove_tracking($_SESSION["uuid"], $_POST["person_name"]);
break;
default:
@@ -230,14 +193,18 @@ if (isset($_POST["action"])) {
if (!$response->satisfied) {
session_destroy();
session_start();
- $_SESSION["token"] = generate_csrf_token($logger);
+ $_SESSION["token"] = Util::generate_csrf_token($logger) ?? Util::http_exit(500);
}
break;
case "get-user-data":
- $response = validate_logged_in() ?? $user_manager->get_user_data($_SESSION["uuid"]);
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? $user_manager->get_user_data($_SESSION["uuid"]);
break;
case "list-trackings":
- $response = validate_logged_in() ?? $tracking_manager->list_trackings($_SESSION["uuid"]);
+ $response =
+ Validator::validate_inputs($_SESSION, ["uuid" => [new IsSetRule()]])
+ ?? $tracking_manager->list_trackings($_SESSION["uuid"]);
break;
default:
$response = new Response(
@@ -255,7 +222,7 @@ if (isset($_POST["action"])) {
switch ($argv[1]) {
case "update-all-trackings":
$logger->info("Updating all trackings.");
- $tracking_manager->update_trackings($mailer, $tracking_manager->list_all_unique_person_names());
+ $tracking_manager->update_trackings($tracking_manager->list_all_unique_person_names());
exit("Successfully updated all trackings.");
case "process-email-queue":
$logger->info("Processing email queue.");
diff --git a/src/main/index.html b/src/main/index.html
index 3cbefd1..39fdc03 100644
--- a/src/main/index.html
+++ b/src/main/index.html
@@ -91,7 +91,7 @@
@@ -119,7 +119,7 @@