0 && !empty($_SESSION["email"]) && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["logged_in_default"]; } /** * Checks if the logged-in user is verified. * * @global array $routerConfig Global configuration array containing permission thresholds. * @return bool Returns true if the user is logged in and verified; otherwise, false. */ function isVerified(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["verified"]; } /** * Checks if the logged-in user is trustworthy. * * @global array $routerConfig Global configuration array containing permission thresholds. * @return bool Returns true if the user is logged in and considered trustworthy; otherwise, false. */ function isTrustWorthy(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["trustworthy"]; } /** * Checks if the logged-in user is a moderator. * * @global array $routerConfig Global configuration array containing permission thresholds. * @return bool Returns true if the user is logged in and a moderator; otherwise, false. */ function isModerator(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["moderator"]; } /** * Checks if the logged-in user is a user admin. * * @global array $routerConfig Global configuration array containing permission thresholds. * @return bool Returns true if the user is logged in and a user admin; otherwise, false. */ function isUserAdmin(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["user_admin"]; } /** * Checks if the logged-in user is an admin. * * @global array $routerConfig Global configuration array containing permission thresholds. * @return bool Returns true if the user is logged in and an admin; otherwise, false. */ function isAdmin(): bool { global $routerConfig; return isLoggedIn() && $_SESSION["privilege_level"] >= $routerConfig["permissions"]["admin"]; } /** * Generates a secure token for account activation or other purposes using cryptographic methods. * * @return string|null Returns a hexadecimal token or null in case of an error. */ function generateActivationToken(): ?string { try { return bin2hex(random_bytes(16)); } catch (RandomException) { return null; } } /** * Checks if an email address is available for registration. * * @param string $email The email address to check. * @return bool Returns true if the email is not already registered; otherwise, false. *@global mysqli $mysqli Global mysqli object for database access. */ function isEmailAvailable(string $email): bool { global $mysqli; $stmt = $mysqli->prepare("SELECT COUNT(*) FROM Users WHERE Email = ?"); $stmt->bind_param("s", $email); $stmt->execute(); $count = -1; $stmt->bind_result($count); $stmt->fetch(); $stmt->close(); return $count === 0; } /** * Sets default session data typically used for a logged-out user(includes users that have just visited the page). * * @global array $routerConfig Global configuration array used for setting initial privilege levels. * @return void */ function setDefaultSessionData(): void { global $routerConfig; $_SESSION["ID"] = 0; $_SESSION["first_name"] = ""; $_SESSION["last_name"] = ""; $_SESSION["nickname"] = ""; $_SESSION["email"] = ""; $_SESSION["privilege_level"] = $routerConfig["permissions"]["logged_out"]; } /** * Verifies if the provided password matches the stored hash for the user. * * @param int $userID The user ID whose password is to be verified. * @param string $password The password to verify. * @return bool Returns true if the password matches the stored hash; otherwise, false. *@global mysqli $mysqli Global mysqli object for database access. */ function verifyPassword(int $userID, string $password): bool { global $mysqli; $stmt = $mysqli->prepare("SELECT PasswordHash FROM Users WHERE ID = ?"); $stmt->bind_param("i", $userID); $stmt->execute(); $password_hash = ""; $stmt->bind_result($password_hash); $stmt->fetch(); $stmt->close(); return !empty($password_hash) && !empty($password) && password_verify($password, $password_hash); } /** * Updates session data from the database for the logged-in user. * * @global mysqli $mysqli Global mysqli object for database access. * @return void */ function UpdateSession(): void { global $mysqli; $stmt = $mysqli->prepare("SELECT FirstName, LastName, Nickname, Email, PrivilegeLevel, LastLoginAt, LoginCount, ClassID, FavoriteColor FROM Users WHERE ID = ? AND isActivated = 1"); $stmt->bind_param("i", $_SESSION["ID"]); $stmt->execute(); $first_name = ""; $last_name = ""; $nickname = ""; $email = ""; $privilege_level = 0; $class_id = 0; $favorite_color = 0; $lastLoginAt = null; $loginCount = 0; $stmt->bind_result($first_name, $last_name, $nickname, $email, $privilege_level, $lastLoginAt, $loginCount, $class_id, $favorite_color); $stmt->fetch(); $stmt->close(); $_SESSION["first_name"] = $first_name; $_SESSION["last_name"] = $last_name; $_SESSION["nickname"] = $nickname; $_SESSION["email"] = $email; $_SESSION["privilege_level"] = $privilege_level; $_SESSION["lastLoginAt"] = $lastLoginAt; $_SESSION["loginCount"] = $loginCount; $_SESSION["class_id"] = $class_id; $_SESSION["favorite_color"] = $favorite_color; } /** * Attempts to log in a user with the given credentials. * * @param string $email The user's email address. * @param string $password The user's password. * @global mysqli $mysqli Global database connection object. * @return array An array containing the status of the login attempt ('Success' or 'Fail'). */ function doLogin(string $email, string $password): array { global $mysqli; $found = false; if (!empty($email) && !empty($password)) { $stmt = $mysqli->prepare("SELECT ID, PasswordHash FROM Users WHERE Email = ? AND isActivated = 1"); $stmt->bind_param("s", $email); $stmt->execute(); $uid = 0; $password_hash = ""; $stmt->bind_result($uid, $password_hash); $stmt->fetch(); $stmt->close(); if (password_verify($password, $password_hash)) { $found = true; $_SESSION["ID"] = $uid; UpdateSession(); // Update LastLoginAt and LoginCount $updateLoginStmt = $mysqli->prepare("UPDATE Users SET LastLoginAt = NOW(), LoginCount = LoginCount + 1 WHERE ID = ?"); $updateLoginStmt->bind_param("i", $uid); $updateLoginStmt->execute(); $updateLoginStmt->close(); } } return $found ? ["Status" => "Success"] : ["Status" => "Fail"]; } /** * Logs out the current user by resetting session data. * Fails when the user wasn't logged in * * @return array An array with the logout status ('Success' if logged out, 'Fail' otherwise). */ function doLogout(): array { if(isLoggedIn()){ setDefaultSessionData(); return ["Status" => "Success"]; } else { return ["Status" => "Fail"]; } } /** * Registers a new user with provided personal details and activation token. * * @param string $firstname The user's first name. * @param string $lastname The user's last name. * @param string $email The user's email. * @param string $password The user's password. * @param string $activation_token The activation token to verify the registration. * @global mysqli $mysqli Global database connection object. * @global array $routerConfig Global configuration settings. * @return array An array with the registration status ('Success' or 'Fail'). */ function doRegister(string $firstname, string $lastname, string $email, string $password, string $activation_token): array { global $mysqli, $routerConfig; $status = ["Status" => "Fail"]; if (!empty($activation_token) && !empty($email) && !empty($password) && !empty($firstname) && !empty($lastname) && isEmailAvailable($email)) { $passwordHash = password_hash($password, PASSWORD_DEFAULT); $stmt = $mysqli->prepare("UPDATE Users SET FirstName=?, LastName=?, Email=?, PasswordHash=?, PrivilegeLevel=?, isActivated=1, ActivationToken='', RegisteredAt=NOW() WHERE ActivationToken = ?"); $privilege_level = $routerConfig["permissions"]["logged_in_default"]; /** @noinspection SpellCheckingInspection */ $stmt->bind_param("ssssis", $firstname, $lastname, $email, $passwordHash, $privilege_level, $activation_token); $stmt->execute(); if ($stmt->affected_rows > 0) { $status["Status"] = "Success"; } $stmt->close(); } return $status; } /** * Changes the user's password after verifying the old password. * * @param string $oldPassword The current password for verification. * @param string $newPassword The new password to be set. * @return array An array indicating whether the password change was successful ('Success' or 'Fail'). *@global mysqli $mysqli Global database connection object. */ function changePassword(string $oldPassword, string $newPassword): array { global $mysqli; $status = ["Status" => "Fail"]; $userID = $_SESSION["ID"]; if(!empty($oldPassword) && !empty($newPassword) && isLoggedIn() && verifyPassword($userID, $oldPassword)){ $passwordHash = password_hash($newPassword, PASSWORD_DEFAULT); $stmt = $mysqli->prepare("UPDATE Users SET PasswordHash = ? WHERE ID = ?"); $stmt->bind_param("si", $passwordHash, $userID); $stmt->execute(); if ($stmt->affected_rows > 0) { $status["Status"] = "Success"; } $stmt->close(); } return $status; } /** * Updates user profile information in the database. * * @param string $firstName The new first name. * @param string $lastName The new last name. * @param string $nickname The new nickname. * @return array Status of the profile update ('Success' or 'Fail'). *@global mysqli $mysqli Global database connection object. */ function updateUserProfile(string $firstName, string $lastName, string $nickname): array { global $mysqli; $status = ["Status" => "Fail"]; if (isLoggedIn() && !empty($firstName) && !empty($lastName) && !empty($nickname)) { $stmt = $mysqli->prepare("UPDATE Users SET FirstName = ?, LastName = ?, Nickname = ? WHERE ID = ?"); /** @noinspection SpellCheckingInspection */ $stmt->bind_param("ssssi", $firstName, $lastName, $nickname, $_SESSION["ID"]); $stmt->execute(); if ($stmt->affected_rows > 0) { $status["Status"] = "Success"; } else { $status["Status"] = "$firstName $lastName $nickname"; } $stmt->close(); } return $status; } /** * Updates the email address of the logged-in user after validation. * * @param string $email The new email address to update. * @return array Status of the email update ('Success' or 'Fail'). *@global mysqli $mysqli Global database connection object. */ function updateUserEmail(string $email): array { global $mysqli; $status = ["Status" => "Fail"]; /** @noinspection SpellCheckingInspection */ $validmail = false; if (isLoggedIn() && !empty($email)) { $userID = $_SESSION["ID"]; $stmt_email_check = $mysqli->prepare("SELECT Email FROM Users WHERE ID = ?"); $stmt_email_check->bind_param("i", $userID); $old_email = ""; $stmt_email_check->bind_result($old_email); $stmt_email_check->execute(); $stmt_email_check->fetch(); $stmt_email_check->close(); if ($email != $old_email) { if (isEmailAvailable($email)) { /** @noinspection SpellCheckingInspection */ $validmail = true; } } else { /** @noinspection SpellCheckingInspection */ $validmail = true; } if ($validmail) { $stmt = $mysqli->prepare("UPDATE Users SET Email = ? WHERE ID = ?"); $stmt->bind_param("si", $email, $userID); $stmt->execute(); if ($stmt->affected_rows > 0) { $status["Status"] = "Success"; } $stmt->close(); } } return $status; } /** * Retrieves and updates the current session with user information from the database. * * @return array Contains user information and status if the user is logged in. */ function getUserInfo(): array { $output = ["Status" => "Fail"]; if(isLoggedIn()) { global $mysqli; $userID = $_SESSION["ID"]; $stmt = $mysqli->prepare("SELECT FirstName, LastName, Nickname, Email FROM Users WHERE ID = ?"); $stmt->bind_param("i", $userID); $stmt->execute(); $firstName = ""; $lastName = ""; $nickname = ""; $email = ""; $stmt->bind_result($firstName, $lastName, $nickname, $email); $stmt->fetch(); $stmt->close(); UpdateSession(); $output["Status"] = "Success"; $output["UserInfo"] = [ "ID" => $userID, "FirstName" => $firstName, "LastName" => $lastName, "Nickname" => $nickname, "Email" => $email ]; } return $output; } /** * Generates a specified number of activation codes for user registration and adds them to the database. * * @param int $count Number of activation codes to generate. * @return array An array containing the generated codes and status ('Success' or 'Fail'). *@global mysqli $mysqli Global database connection object. */ function addActivationCodes(int $count): array { global $mysqli; $activationCodes = []; $output = ["Status" => "Fail"]; // Default Status is "Fail" if ($count > 0 && isUserAdmin() && isLoggedIn()) { $stmt = $mysqli->prepare("INSERT INTO Users (ActivationToken, CreatedAt, CreatedBy) VALUES (?, NOW(), ?)"); for ($i = 0; $i < $count; $i++) { $activationCode = generateActivationToken(); $stmt->bind_param("si", $activationCode, $_SESSION["ID"]); $stmt->execute(); if ($stmt->affected_rows > 0) { $activationCodes[] = [ "Code" => $activationCode, "CreatedAt" => date("Y-m-d H:i:s"), "CreatedBy" => $_SESSION["ID"] ]; $output["Status"] = "Success"; $output["ActivationCodes"] = $activationCodes; } } $stmt->close(); } return $output; } /** * Lists all registered users, available only to user admins. * * @global mysqli $mysqli Global database connection object. * @return array An array containing user data and status. */ function listUsers(): array { global $mysqli; $output = ["Status" => "Fail"]; // Default Status is "Fail" if (isUserAdmin()) { $users = []; $result = $mysqli->query("SELECT ID, FirstName, LastName, Nickname, Email, PrivilegeLevel, CreatedAt, RegisteredAt, LastLoginAt, LoginCount, CreatedBy FROM Users WHERE isActivated = 1"); // Check if the query executed Successfully if ($result) { while ($row = $result->fetch_assoc()) { $users[] = $row; } $output["Status"] = "Success"; $output["Users"] = $users; } } return $output; } /** * Lists activation codes available for assigning to new users, available only for user admins. * * @global mysqli $mysqli Global database connection object. * @return array An array containing activation codes and status. */ function listActivationCodes(): array { global $mysqli; $output = ["Status" => "Fail"]; // Default Status is "Fail" if (isUserAdmin()) { $activationCodes = []; // Use placeholders in the query $query = "SELECT ActivationToken, CreatedAt, CreatedBy FROM Users WHERE isActivated = 0"; $stmt = $mysqli->prepare($query); if ($stmt) { // Bind the result variables $activationToken = ""; $createdAt = ""; $createdBy = ""; $stmt->bind_result($activationToken, $createdAt, $createdBy); // Execute the prepared statement $stmt->execute(); // Fetch the results into the bound variables while ($stmt->fetch()) { $activationCodes[] = [ 'ActivationToken' => $activationToken, 'CreatedAt' => $createdAt, 'CreatedBy' => $createdBy ]; } // Check if any results were fetched if (!empty($activationCodes)) { $output["Status"] = "Success"; $output["ActivationCodes"] = $activationCodes; } // Close the statement $stmt->close(); } } return $output; } /** * Deletes a user by their ID, available only to user admins. * * @param int $userID The ID of the user to delete. * @return array Status of the delete operation ('Success' or 'Fail'). *@global mysqli $mysqli Global database connection object. */ function deleteUser(int $userID): array { global $mysqli; $status = ["Status" => "Fail"]; if (!empty($userID) && isUserAdmin()) { $stmt = $mysqli->prepare("DELETE FROM Users WHERE ID = ?"); $stmt->bind_param("i", $userID); $stmt->execute(); if ($stmt->affected_rows > 0) { $status["Status"] = "Success"; } $stmt->close(); } return $status; } /** * Deletes an activation code, available only to user admins. * * @param string $activationCode The activation code to delete. * @return array Status of the delete operation ('Success' or 'Fail'). *@global mysqli $mysqli Global database connection object. */ function deleteActivationCode(string $activationCode): array { global $mysqli; $status = ["Status" => "Fail"]; if (!empty($activationCode) && isUserAdmin()) { $stmt = $mysqli->prepare("DELETE FROM Users WHERE ActivationToken = ?"); $stmt->bind_param("s", $activationCode); $stmt->execute(); if ($stmt->affected_rows > 0) { $status["Status"] = "Success"; } $stmt->close(); } return $status; }