From 2c98d4e31e60c2aead1c3ea9a44e693b47f6e363 Mon Sep 17 00:00:00 2001 From: "Florine W. Dekker" Date: Tue, 23 Aug 2022 12:22:57 +0200 Subject: [PATCH] Reuse single connection throughout entire query --- composer.json | 2 +- composer.lock | Bin 12142 -> 12142 bytes package-lock.json | Bin 225103 -> 225103 bytes package.json | 2 +- src/main/api.php | 12 +++- src/main/index.html | 2 + src/main/php/TrackingManager.php | 82 +++++++++++----------- src/main/php/UserManager.php | 116 ++++++++++++++++++------------- 8 files changed, 118 insertions(+), 98 deletions(-) diff --git a/composer.json b/composer.json index 523f85c..a035f65 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.17", + "version": "0.0.18", "type": "project", "license": "MIT", "homepage": "https://git.fwdekker.com/tools/death-notifier", diff --git a/composer.lock b/composer.lock index 4e8d02704cda8e5c7556f3fdce77c5109908194e..e7c79ac071d512dbf56192ea09b4d718902d34c3 100644 GIT binary patch delta 44 zcmaDC_bzUOFr&O_T4G{KvW1DUNm`PHagvEalBuadno**qk(q^glIdn6MjJfa#Escs=29gN?M|!shM$#nMtB?s->|-iiJf|s^w-QMjJfpushHandler(new StreamHandler($config["logger"]["file"], $config["logger"]["level"])); ErrorHandler::register($logger); +// Connect to database +$db_exists = file_exists($config["database"]["filename"]); +$conn = Database::connect($config["database"]["filename"]); + // Instantiate utility classes $mediawiki = new Mediawiki($logger->withName("Mediawiki")); -$user_manager = new UserManager($logger->withName("UserManager"), $config["database"]["filename"]); -$tracking_manager = new TrackingManager($logger->withName("TrackingManager"), $mediawiki, $config["database"]["filename"]); +$user_manager = new UserManager($logger->withName("UserManager"), $conn); +$tracking_manager = new TrackingManager($logger->withName("TrackingManager"), $mediawiki, $conn); $mailer = new Mailer($logger->withName("Mailer"), $config); // Create db if it does not exist -if (!file_exists($config["database"]["filename"])) { +if (!$db_exists) { $logger->warning("Database does not exist. Creating new database at '" . $config["database"]["filename"] . "'."); $user_manager->install(); @@ -218,6 +223,7 @@ if (isset($_POST["action"])) { if (!$response->satisfied) { session_destroy(); session_start(); + $_SESSION["token"] = generate_csrf_token($logger); } break; case "get-user-data": diff --git a/src/main/index.html b/src/main/index.html index 02c0b5f..8c671a9 100644 --- a/src/main/index.html +++ b/src/main/index.html @@ -48,6 +48,7 @@

+

+
diff --git a/src/main/php/TrackingManager.php b/src/main/php/TrackingManager.php index f3bc74b..a6a661b 100644 --- a/src/main/php/TrackingManager.php +++ b/src/main/php/TrackingManager.php @@ -20,27 +20,27 @@ class TrackingManager * @var Logger The logger to use for logging. */ private Logger $logger; - /** - * @var string The filename of the database to interact with. - */ - private string $db_filename; /** * @var Mediawiki The Mediawiki instance to use for interacting with Wikipedia. */ private Mediawiki $mediawiki; + /** + * @var PDO The database connection to interact with. + */ + private PDO $conn; /** * Constructs a new tracking manager. * * @param Logger $logger the logger to use for logging - * @param string $db_filename the filename of the database to interact with + * @param PDO $conn the database connection to interact with */ - public function __construct(Logger $logger, Mediawiki $mediawiki, string $db_filename) + public function __construct(Logger $logger, Mediawiki $mediawiki, PDO $conn) { $this->logger = $logger; $this->mediawiki = $mediawiki; - $this->db_filename = $db_filename; + $this->conn = $conn; } @@ -51,12 +51,11 @@ class TrackingManager */ public function install(): void { - $conn = Database::connect($this->db_filename); - $conn->exec("CREATE TABLE trackings(user_uuid text not null, - person_name text not null, - status text not null, - deleted int default 0, - PRIMARY KEY (user_uuid, person_name));"); + $this->conn->exec("CREATE TABLE trackings(user_uuid text not null, + person_name text not null, + status text not null, + deleted int not null default 0, + PRIMARY KEY (user_uuid, person_name));"); } /** @@ -94,31 +93,30 @@ class TrackingManager satisfied: false ); - $conn = Database::connect($this->db_filename); - $conn->beginTransaction(); + $this->conn->beginTransaction(); - $stmt = $conn->prepare("SELECT COUNT(*) as count - FROM trackings - WHERE user_uuid=:user_uuid AND person_name=:person_name;"); + $stmt = $this->conn->prepare("SELECT COUNT(*) as count + FROM trackings + WHERE user_uuid=:user_uuid AND person_name=:person_name;"); $stmt->bindValue(":user_uuid", $user_uuid); $stmt->bindValue(":person_name", $normalized_name); $stmt->execute(); if ($stmt->fetch(PDO::FETCH_ASSOC)["count"] > 0) { - $conn->rollBack(); + $this->conn->rollBack(); return new Response( payload: ["target" => "personName", "message" => "You are already tracking this person."], satisfied: false ); } - $stmt = $conn->prepare("INSERT INTO trackings (user_uuid, person_name, status) - VALUES (:user_uuid, :person_name, :status);"); + $stmt = $this->conn->prepare("INSERT INTO trackings (user_uuid, person_name, status) + VALUES (:user_uuid, :person_name, :status);"); $stmt->bindValue(":user_uuid", $user_uuid); $stmt->bindValue(":person_name", $normalized_name); $stmt->bindValue(":status", $status); $stmt->execute(); - $conn->commit(); + $this->conn->commit(); return new Response(payload: null, satisfied: true); } @@ -137,9 +135,8 @@ class TrackingManager satisfied: false ); - $conn = Database::connect($this->db_filename); - $stmt = $conn->prepare("DELETE FROM trackings - WHERE user_uuid=:user_uuid AND person_name=:person_name;"); + $stmt = $this->conn->prepare("DELETE FROM trackings + WHERE user_uuid=:user_uuid AND person_name=:person_name;"); $stmt->bindValue(":user_uuid", $user_uuid); $stmt->bindValue(":person_name", $person_name); $stmt->execute(); @@ -155,10 +152,9 @@ class TrackingManager */ public function list_trackings(string $user_uuid): Response { - $conn = Database::connect($this->db_filename); - $stmt = $conn->prepare("SELECT person_name, status, deleted - FROM trackings - WHERE user_uuid=:user_uuid;"); + $stmt = $this->conn->prepare("SELECT person_name, status, deleted + FROM trackings + WHERE user_uuid=:user_uuid;"); $stmt->bindValue(":user_uuid", $user_uuid); $stmt->execute(); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -173,8 +169,7 @@ class TrackingManager */ public function list_all_unique_person_names(): array { - $conn = Database::connect($this->db_filename); - $stmt = $conn->prepare("SELECT DISTINCT person_name FROM trackings;"); + $stmt = $this->conn->prepare("SELECT DISTINCT person_name FROM trackings;"); $stmt->execute(); return array_column($stmt->fetchAll(PDO::FETCH_ASSOC), "person_name"); } @@ -189,13 +184,13 @@ class TrackingManager { $people_statuses = $this->mediawiki->people_statuses($people_names); - $conn = Database::connect($this->db_filename); - $conn->beginTransaction(); + // Begin transaction + $this->conn->beginTransaction(); // Process redirects - $stmt = $conn->prepare("UPDATE trackings - SET person_name=:new_person_name - WHERE person_name=:old_person_name;"); + $stmt = $this->conn->prepare("UPDATE trackings + SET person_name=:new_person_name + WHERE person_name=:old_person_name;"); $stmt->bindParam(":old_person_name", $from); $stmt->bindParam(":new_person_name", $to); foreach ($people_statuses->redirects as $from => $to) @@ -203,22 +198,23 @@ class TrackingManager $stmt->execute(); // Process deletions - $stmt = $conn->prepare("UPDATE trackings - SET deleted=1 - WHERE person_name=:person_name;"); + $stmt = $this->conn->prepare("UPDATE trackings + SET deleted=1 + WHERE person_name=:person_name;"); $stmt->bindParam(":person_name", $missing_title); foreach ($people_statuses->missing as $missing_title) $stmt->execute(); // Process status changes - $stmt = $conn->prepare("UPDATE trackings - SET status=:status, deleted=0 - WHERE person_name=:person_name;"); + $stmt = $this->conn->prepare("UPDATE trackings + SET status=:status, deleted=0 + WHERE person_name=:person_name;"); $stmt->bindParam(":status", $person_status); $stmt->bindParam(":person_name", $person_name); foreach ($people_statuses->results as $person_name => $person_status) $stmt->execute(); - $conn->commit(); + // Commit transaction + $this->conn->commit(); } } diff --git a/src/main/php/UserManager.php b/src/main/php/UserManager.php index 44f9ca0..796a16c 100644 --- a/src/main/php/UserManager.php +++ b/src/main/php/UserManager.php @@ -31,21 +31,21 @@ class UserManager */ private Logger $logger; /** - * @var string The filename of the database to interact with. + * @var PDO The database connection to interact with. */ - private string $db_filename; + private PDO $conn; /** * Constructs a new user manager. * * @param Logger $logger the logger to use for logging - * @param string $db_filename the filename of the database to interact with + * @param PDO $conn the database connection to interact with */ - public function __construct(Logger $logger, string $db_filename) + public function __construct(Logger $logger, PDO $conn) { $this->logger = $logger; - $this->db_filename = $db_filename; + $this->conn = $conn; } @@ -56,10 +56,13 @@ class UserManager */ public function install(): void { - $conn = Database::connect($this->db_filename); - $conn->exec("CREATE TABLE users(uuid text primary key not null, email text not null, - email_is_verified int not null default 0, email_verification_token text, - password text not null, password_update_time int not null);"); + $this->conn->exec("CREATE TABLE users(uuid text not null, + email text not null, + email_is_verified int not null default 0, + email_verification_token text, + password text not null, + password_update_time int not null, + PRIMARY KEY (uuid));"); } /** @@ -106,17 +109,18 @@ class UserManager satisfied: false ); - // Connect - $conn = Database::connect($this->db_filename); - $conn->beginTransaction(); + // Begin transaction + $this->conn->beginTransaction(); // Check if email address is already in use - $stmt = $conn->prepare("SELECT COUNT(*) as count FROM users WHERE email=:email;"); + $stmt = $this->conn->prepare("SELECT COUNT(*) as count + FROM users + WHERE email=:email;"); $stmt->bindValue(":email", $email); $stmt->execute(); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result["count"] > 0) { - $conn->rollBack(); + $this->conn->rollBack(); return new Response( payload: ["target" => "email", "message" => "Email address already in use."], satisfied: false @@ -124,8 +128,10 @@ class UserManager } // Register user - $stmt = $conn->prepare("INSERT INTO users (uuid, email, email_verification_token, password, password_update_time) - VALUES (:uuid, :email, :email_verification_token, :password, unixepoch());"); + $stmt = $this->conn->prepare("INSERT INTO users (uuid, email, email_verification_token, + password, password_update_time) + VALUES (:uuid, :email, :email_verification_token, + :password, unixepoch());"); $stmt->bindValue(":uuid", $uuid); $stmt->bindValue(":email", $email); $stmt->bindValue(":email_verification_token", $email_verification_token); @@ -133,7 +139,7 @@ class UserManager $stmt->execute(); // Respond - $conn->commit(); + $this->conn->commit(); $mailer->send_registration_email($email, $email_verification_token); return new Response(payload: null, satisfied: true); } @@ -147,10 +153,11 @@ class UserManager */ public function delete(string $uuid): Response { - $conn = Database::connect($this->db_filename); + $stmt = $this->conn->prepare("DELETE FROM users WHERE uuid=:uuid;"); + $stmt->bindValue(":uuid", $uuid); + $stmt->execute(); - // TODO: Delete trackings! - $stmt = $conn->prepare("DELETE FROM users WHERE uuid=:uuid;"); + $stmt = $this->conn->prepare("DELETE FROM trackings WHERE user_uuid=:uuid"); $stmt->bindValue(":uuid", $uuid); $stmt->execute(); @@ -178,10 +185,9 @@ class UserManager satisfied: false ); - $conn = Database::connect($this->db_filename); - $stmt = $conn->prepare("SELECT uuid, email, email_is_verified, password, password_update_time - FROM users - WHERE email=:email;"); + $stmt = $this->conn->prepare("SELECT uuid, email, email_is_verified, password, password_update_time + FROM users + WHERE email=:email;"); $stmt->bindValue(":email", $email); $stmt->execute(); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -206,8 +212,9 @@ class UserManager */ public function get_user_data(string $uuid): Response { - $conn = Database::connect($this->db_filename); - $stmt = $conn->prepare("SELECT uuid, email, email_is_verified, password_update_time FROM users WHERE uuid=:uuid;"); + $stmt = $this->conn->prepare("SELECT uuid, email, email_is_verified, password_update_time + FROM users + WHERE uuid=:uuid;"); $stmt->bindValue(":uuid", $uuid); $stmt->execute(); $user = $stmt->fetch(PDO::FETCH_ASSOC); @@ -249,12 +256,11 @@ class UserManager satisfied: false ); - // Connect - $conn = Database::connect($this->db_filename); - $conn->beginTransaction(); + // Begin transaction + $this->conn->beginTransaction(); // Check if email address is different - $stmt = $conn->prepare("SELECT email FROM users WHERE uuid=:uuid;"); + $stmt = $this->conn->prepare("SELECT email FROM users WHERE uuid=:uuid;"); $stmt->bindValue(":uuid", $uuid); $stmt->execute(); $email_old = $stmt->fetch(PDO::FETCH_ASSOC)["email"]; @@ -265,11 +271,13 @@ class UserManager ); // Check if email address is already in use - $stmt = $conn->prepare("SELECT COUNT(*) as count FROM users WHERE email=:email;"); + $stmt = $this->conn->prepare("SELECT COUNT(*) as count + FROM users + WHERE email=:email;"); $stmt->bindValue(":email", $email); $stmt->execute(); if ($stmt->fetch(PDO::FETCH_ASSOC)["count"] > 0) { - $conn->rollBack(); + $this->conn->rollBack(); return new Response( payload: ["target" => "email", "message" => "Email address already in use."], satisfied: false @@ -277,14 +285,18 @@ class UserManager } // Update email address - $stmt = $conn->prepare("UPDATE users SET email=:email, email_is_verified=0, email_verification_token=:email_verification_token WHERE uuid=:uuid;"); + $stmt = $this->conn->prepare("UPDATE users + SET email=:email, + email_is_verified=0, + email_verification_token=:email_verification_token + WHERE uuid=:uuid;"); $stmt->bindValue(":uuid", $uuid); $stmt->bindValue(":email", $email); $stmt->bindValue(":email_verification_token", $email_verification_token); $stmt->execute(); // Respond - $conn->commit(); + $this->conn->commit(); $mailer->send_email_verification($email_old, $email, $email_verification_token); return new Response(payload: null, satisfied: true); } @@ -304,16 +316,18 @@ class UserManager satisfied: false ); - $conn = Database::connect($this->db_filename); - $conn->beginTransaction(); + $this->conn->beginTransaction(); - $stmt = $conn->prepare("SELECT email_is_verified, email_verification_token FROM users WHERE email=:email;"); + $stmt = $this->conn->prepare("SELECT email_is_verified, email_verification_token + FROM users + WHERE email=:email;"); $stmt->bindValue(":email", $email); $stmt->execute(); $result = $stmt->fetch(PDO::FETCH_ASSOC); - if ($result === false || $result["email_verification_token"] === null || !hash_equals($result["email_verification_token"], $email_verification_token)) { - $conn->rollBack(); + if ($result === false || $result["email_verification_token"] === null + || !hash_equals($result["email_verification_token"], $email_verification_token)) { + $this->conn->rollBack(); return new Response( payload: ["target" => "email", "message" => "Failed to verify email address."], satisfied: false @@ -321,15 +335,17 @@ class UserManager } if ($result["email_is_verified"]) { - $conn->commit(); + $this->conn->commit(); return new Response(payload: null, satisfied: true); } - $stmt = $conn->prepare("UPDATE users SET email_is_verified=1, email_verification_token=null WHERE email=:email;"); + $stmt = $this->conn->prepare("UPDATE users + SET email_is_verified=1, email_verification_token=null + WHERE email=:email;"); $stmt->bindValue(":email", $email); $stmt->execute(); - $conn->commit(); + $this->conn->commit(); return new Response(payload: null, satisfied: true); } @@ -366,17 +382,16 @@ class UserManager satisfied: false ); - // Connect - $conn = Database::connect($this->db_filename); - $conn->beginTransaction(); + // Begin transaction + $this->conn->beginTransaction(); // Validate old password - $stmt = $conn->prepare("SELECT password FROM users WHERE uuid=:uuid;"); + $stmt = $this->conn->prepare("SELECT password FROM users WHERE uuid=:uuid;"); $stmt->bindValue(":uuid", $uuid); $stmt->execute(); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!password_verify($password_old, $user["password"])) { - $conn->rollBack(); + $this->conn->rollBack(); return new Response( payload: ["target" => "passwordOld", "message" => "Incorrect old password."], satisfied: false @@ -384,14 +399,15 @@ class UserManager } // Update password - $stmt = $conn->prepare("UPDATE users SET password=:password, password_update_time=unixepoch() - WHERE uuid=:uuid;"); + $stmt = $this->conn->prepare("UPDATE users + SET password=:password, password_update_time=unixepoch() + WHERE uuid=:uuid;"); $stmt->bindValue(":uuid", $uuid); $stmt->bindValue(":password", password_hash($password_new, PASSWORD_DEFAULT)); $stmt->execute(); // Respond - $conn->commit(); + $this->conn->commit(); return new Response(payload: null, satisfied: true); } }