pushHandler(new StreamHandler($config["logger"]["file"], $config["logger"]["level"])); ErrorHandler::register($logger); // Instantiate utility classes $user_manager = new UserManager($logger->withName("UserManager"), $config["database"]["filename"]); $tracking_manager = new TrackingManager($logger->withName("TrackingManager"), $config["database"]["filename"]); $mailer = new Mailer($logger->withName("Mailer"), $config["mail"]); $mediawiki = new Mediawiki($logger->withName("Mediawiki")); // Create db if it does not exist if (!file_exists($config["database"]["filename"])) { $logger->warning("Database does not exist. Creating new database."); // Create database (new SQLite3($config["database"]["filename"]))->close(); // Populate database $user_manager->install(); $tracking_manager->install(); } // Start session /** * Generates a CSRF token. * * @return string the generated CSRF token */ function generate_csrf_token(): string { try { return bin2hex(random_bytes(32)); } catch (Exception $exception) { $log->emergency("Failed to generate token.", [$exception]); http_response_code(500); exit(); } } session_start(); if (!isset($_SESSION["token"])) $_SESSION["token"] = generate_csrf_token(); // Read JSON from POST $_POST = json_decode(file_get_contents("php://input"), associative: true); /// 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("Invalid CSRF token. Try refreshing the page.", 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("Used is already logged in.", 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("Used is not logged in.", 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("Missing argument.", false); return null; } /// Process request $response = null; if (isset($_POST["action"])) { // Process POST switch ($_POST["action"]) { case "register": $response = validate_csrf() ?? validate_logged_out() ?? validate_has_arguments($_POST["email"], $_POST["password"], $_POST["password_confirm"]) ?? $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"]); if ($response !== null) break; [$response, $uuid] = $user_manager->check_login($_POST["email"], $_POST["password"]); if ($uuid !== null) $_SESSION["uuid"] = $uuid; break; case "logout": $response = validate_csrf() ?? validate_logged_in(); if ($response !== null) break; session_destroy(); session_start(); $_SESSION["token"] = generate_csrf_token(); $response = new Response(null, true); break; case "update-email": $response = validate_csrf() ?? validate_logged_in() ?? validate_has_arguments($_POST["email"]) ?? $user_manager->set_email($_SESSION["uuid"], $_POST["email"]); break; case "update-password": $response = validate_csrf() ?? validate_logged_in() ?? validate_has_arguments($_POST["password_old"], $_POST["password_new"], $_POST["password_confirm"]) ?? $user_manager->set_password( $_SESSION["uuid"], $_POST["password_old"], $_POST["password_new"], $_POST["password_confirm"] ); break; case "user-delete": $response = validate_csrf() ?? validate_logged_in() ?? $user_manager->delete($_SESSION["uuid"]); break; case "add-tracking": $response = validate_csrf() ?? validate_logged_in() ?? validate_has_arguments($_POST["person_name"]) ?? $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"]) ?? $tracking_manager->remove_tracking($_SESSION["uuid"], $_POST["person_name"]); break; case "send-test-email": $response = validate_csrf() ?? $mailer->send_test_mail(); break; default: $response["message"] = "Unknown POST action '" . $_POST["action"] . "'."; break; } } else if (isset($_GET["action"])) { $response = match ($_GET["action"]) { "get-user-data" => validate_logged_in() ?? $user_manager->get_user_data($_SESSION["uuid"]), "list-trackings" => validate_logged_in() ?? $tracking_manager->list_trackings($_SESSION["uuid"]), "is-alive" => $mediawiki->people_are_alive(array("John Cusack", "Cameron Diaz", "John Malkovich")), default => new Response("Unknown GET action '" . $_GET["action"] . "'.", false), }; } else { $response = new Response("Unknown method.", false); } header("Content-type:application/json;charset=utf-8"); exit(json_encode(array( "message" => $response->message, "satisfied" => $response->satisfied, "token" => $_SESSION["token"] )));