prepare('SELECT * FROM frc_user WHERE email = :email'); $query->bindValue(':email', $email); $result = $query->execute(); return $result ? $result->fetchArray(SQLITE3_ASSOC) : false; } /** * Add a user * * @param string $email The e-mail address for the user * @param string $password The user's password * @param SQLite3 $db The data connection to use to add the user */ public static function addUser(string $email, string $password, SQLite3 $db): void { $query = $db->prepare('INSERT INTO frc_user (email, password) VALUES (:email, :password)'); $query->bindValue(':email', $email); $query->bindValue(':password', password_hash($password, self::PW_ALGORITHM)); $query->execute(); } /** * Verify a user's password * * @param array $user The user information retrieved from the database * @param string $password The password provided by the user * @param string|null $returnTo The URL to which the user should be redirected * @param SQLite3 $db The database connection to use to verify the user's credentials */ private static function verifyPassword(array $user, string $password, ?string $returnTo, SQLite3 $db): void { if (password_verify($password, $user['password'])) { if (password_needs_rehash($user['password'], self::PW_ALGORITHM)) { $rehash = $db->prepare('UPDATE frc_user SET password = :hash WHERE id = :id'); $rehash->bindValue(':hash', password_hash($password, self::PW_ALGORITHM)); $rehash->bindValue(':id', $user['id']); $rehash->execute(); } $_SESSION[Key::USER_ID] = $user['id']; $_SESSION[Key::USER_EMAIL] = $user['email']; frc_redirect($returnTo ?? '/'); } } /** * Log on a user with e-mail address and password * * @param string $email The e-mail address for the user (cannot be the single-user mode user) * @param string $password The password provided by the user * @param string|null $returnTo The URL to which the user should be redirected * @param SQLite3 $db The database connection to use to verify the user's credentials */ public static function logOnUser(string $email, string $password, ?string $returnTo, SQLite3 $db): void { if (SECURITY_MODEL == self::SINGLE_USER_WITH_PASSWORD) { $dbEmail = self::SINGLE_USER_EMAIL; } else { if ($email == self::SINGLE_USER_EMAIL) { add_error('Invalid credentials; log on unsuccessful'); return; } $dbEmail = $email; } $user = self::findUserByEmail($dbEmail, $db); if ($user) self::verifyPassword($user, $password, $returnTo, $db); add_error('Invalid credentials; log on unsuccessful'); } /** * Update the password for the given user * * @param string $email The e-mail address of the user whose password should be updated * @param string $password The new password for this user * @param SQLite3 $db The database connection to use in updating the password */ public static function updatePassword(string $email, string $password, SQLite3 $db): void { $query = $db->prepare('UPDATE frc_user SET password = :password WHERE email = :email'); $query->bindValue(':password', password_hash($password, self::PW_ALGORITHM)); $query->bindValue(':email', $email); $query->execute(); } /** * Log on the single user * * @param SQLite3 $db The data connection to use to retrieve the user */ private static function logOnSingleUser(SQLite3 $db): void { $user = self::findUserByEmail(self::SINGLE_USER_EMAIL, $db); if (!$user) { self::addUser(self::SINGLE_USER_EMAIL, self::SINGLE_USER_PASSWORD, $db); $user = self::findUserByEmail(self::SINGLE_USER_EMAIL, $db); } self::verifyPassword($user, self::SINGLE_USER_PASSWORD, $_GET['returnTo'], $db); } /** * Verify that user is logged on * * @param SQLite3 $db The data connection to use if required * @param bool $redirectIfAnonymous Whether to redirect the request if there is no user logged on */ public static function verifyUser(SQLite3 $db, bool $redirectIfAnonymous = true): void { if (key_exists(Key::USER_ID, $_SESSION)) return; if (SECURITY_MODEL == self::SINGLE_USER) self::logOnSingleUser($db); if (SECURITY_MODEL != self::SINGLE_USER_WITH_PASSWORD && SECURITY_MODEL != self::MULTI_USER) { die('Unrecognized security model (' . SECURITY_MODEL . ')'); } if ($redirectIfAnonymous) { header("Location: /user/log-on?returnTo={$_SERVER['REQUEST_URI']}", true, 307); die(); } } }