import type { Session, SessionApp } from "$lib/classes/sessions"; import { User, type IUserBroker } from "$lib/classes/users"; import { logger } from "$lib/utils/logger"; import { SSLSnifferApp } from "./sqlite"; import * as argon2 from "argon2"; class UserDB { public user_id: number public username: string public password_hash: string constructor( user_id: number, username: string, password_hash: string ) { this.user_id = user_id this.username = username this.password_hash = password_hash } } export class UserDBBroker implements IUserBroker { private static initialized = false constructor() { if (UserDBBroker.initialized) { // UGLY: make more specific throw Error("UserDB has been already initialized") } logger.debug("Correctly initialized", "UserDBBroker") } public createTable(): void { const stmt = SSLSnifferApp.prepare( ` CREATE TABLE IF NOT EXISTS users ( user_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL , password_hash TEXT NOT NULL ); ` ) stmt.run() stmt.finalize() } public async createUser(username: string, password: string): Promise { this.validateUniqueness(username) const insertUser = SSLSnifferApp.prepare( ` INSERT INTO users (username, password_hash) VALUES (@username, @password); ` ) const passwordHash = await argon2.hash(password) try { insertUser.run({ username: username, password: passwordHash }) } catch (error) { // UGLY: make this more specific console.error("Duplicate after check??") // UGLY: create a logger console.error(`Insert User ${Date.now()}:\n\t${error}\n\n`) throw new Error("You can't have duplicates") } insertUser.finalize() const user = await this.getUser(username, password) if (!user) { // UGLY: make this more specific throw new Error("Something went wrong during the creation of the user") } return user } public async getUser(username: string, password: string): Promise { const userToVerify = this.getUserFromUsername(username) if (!userToVerify) { // UGLY: make this more specific throw new Error("The specified user does not exist on the database") } let match = false try { match = await argon2.verify(userToVerify.password_hash, password) } catch (error) { // UGLY: make this more specific throw new Error("Argon2 had an error") } if (!match) { return null } return new User( userToVerify.user_id, userToVerify.username ) } public async updatePassword(username: string, password: string, newPassword: string): Promise { const userToUpdate = await this.getUser(username, password) if (!userToUpdate) { // UGLY: make this more specific throw new Error("Something went wrong while fetching the user") } const passwordHash = await argon2.hash(newPassword) const stmt = SSLSnifferApp.prepare( ` UPDATE users SET password_hash = @newPassword WHERE username = @username; ` ) stmt.run({ username: userToUpdate.username, newPassword: passwordHash }) stmt.finalize() } public getUserFromSession(session: Session): User { const userDB = this.getUserFromUserID(session.userID) if (!userDB) { // UGLY: be specific throw new Error("Could not find user inside database") } return new User( userDB.user_id, userDB.username ) } private validateUniqueness(username: string) { const user = this.getUserFromUsername(username) if (!user) { return } throw new Error("User is already on the system") } private getUserFromUsername(username: string): UserDB | null { const stmt = SSLSnifferApp.prepare( ` SELECT user_id, username, password_hash FROM users WHERE username = @username; ` ) const user: any | null = stmt.get({ username: username, }) stmt.finalize() if (!user) { return null } return new UserDB( user.user_id, user.username, user.password_hash ) } private getUserFromUserID(userID: number): UserDB | null { const stmt = SSLSnifferApp.prepare( ` SELECT user_id, username, password_hash FROM users WHERE user_id = @user_id; ` ) const user: any | null = stmt.get({ user_id: userID, }) stmt.finalize() if (!user) { return null } return new UserDB( user.user_id, user.username, user.password_hash ) } }