From 3618af361bb41b3d4d539023350156269600f457 Mon Sep 17 00:00:00 2001 From: Christian Risi <75698846+CnF-Gris@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:52:44 +0000 Subject: [PATCH] Created barebones for user database --- src/lib/classes/users.ts | 79 ++++++++++++++ src/lib/db-utils/SQL/Users.sql | 17 +++ src/lib/db-utils/Users.ts | 183 +++++++++++++++++++++++++++++++++ src/lib/db-utils/sqlite.ts | 23 +++-- 4 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 src/lib/classes/users.ts create mode 100644 src/lib/db-utils/SQL/Users.sql diff --git a/src/lib/classes/users.ts b/src/lib/classes/users.ts new file mode 100644 index 0000000..5cebc5d --- /dev/null +++ b/src/lib/classes/users.ts @@ -0,0 +1,79 @@ +export interface IUserBroker { + + createTable(): void + createUser(username: string, password: string): Promise + getUser(username: string, password: string): Promise + updatePassword(username: string, password: string, newPassword: string): Promise + getUserFromSession(session: string): User + + + +} + +export class User { + + public userID: number + public username: string + + constructor( + userID: number, + username: string + ) { + this.userID = userID + this.username = username + } + +} + + +export class UserApp { + + private static broker : IUserBroker + private static initialized: boolean = false + + + public static init(broker: IUserBroker) { + + if (UserApp.initialized) { + // UGLY: make this Error more specific + throw Error("UserApp has already been initialized") + } + + UserApp.initialized = true + UserApp.broker = broker + UserApp.broker.createTable() + + } + + public static getUserFromSession(session: string): User { + UserApp.assertInitialized() + return UserApp.broker.getUserFromSession(session) + } + + public static createUser(username: string, password: string) { + UserApp.assertInitialized() + return UserApp.broker.createUser(username, password) + } + + public static getUser(username: string, password: string): User { + UserApp.assertInitialized() + return UserApp.broker.getUser(username, password) + } + + public static updatePassword(username: string, password: string, newPassword: string) { + UserApp.assertInitialized() + return UserApp.broker.updatePassword(username, password, newPassword) + } + + + private static assertInitialized() { + + if (!UserApp.initialized) { + // UGLY: make more specific + throw Error("User app has't been Initialized yet!") + } + } + + + +} \ No newline at end of file diff --git a/src/lib/db-utils/SQL/Users.sql b/src/lib/db-utils/SQL/Users.sql new file mode 100644 index 0000000..a9bd8bf --- /dev/null +++ b/src/lib/db-utils/SQL/Users.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS users ( + user_id INTEGER PRIMARY KEY, + username TEXT UNIQUE NOT NULL , + password_hash TEXT NOT NULL +); + +INSERT INTO users (username, password_hash) +VALUES (@username, @password); + +SELECT user_id, username, password_hash +FROM users +WHERE username = @username; + +UPDATE users +SET password_hash = @newPassword +WHERE username = @username; + diff --git a/src/lib/db-utils/Users.ts b/src/lib/db-utils/Users.ts index e69de29..92cf3d5 100644 --- a/src/lib/db-utils/Users.ts +++ b/src/lib/db-utils/Users.ts @@ -0,0 +1,183 @@ +import { User, type IUserBroker } from "$lib/classes/users"; +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 (UserDB.initialized) { + // UGLY: make more specific + throw Error("UserDB has been already initialized") + } + } + + + public createTable(): void { + const stmt = SSLSnifferApp.prepare( + ` + CREATE TABLE IF NOT EXISTS users ( + user_id INTEGER PRIMARY KEY, + 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.getUserByUsername(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() + } + + // TODO: implement this + public getUserFromSession(session: string): User { + throw new Error("Method not implemented."); + } + + private validateUniqueness(username: string) { + + const user = this.getUserByUsername(username) + + if (!user) { + return + } + + throw new Error("User is already on the system") + } + + private getUserByUsername(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 + ) + + } + +} \ No newline at end of file diff --git a/src/lib/db-utils/sqlite.ts b/src/lib/db-utils/sqlite.ts index cb970b5..d825e6d 100644 --- a/src/lib/db-utils/sqlite.ts +++ b/src/lib/db-utils/sqlite.ts @@ -5,20 +5,24 @@ export class SSLSnifferApp { private static initialized = false - private __db: Database + private static db: Database // UGLY: should be more flexible - constructor() { - this.__db = SSLSnifferApp.initDatabase() + public static init() { + + if (SSLSnifferApp.initialized) { + throw new Error("SSLSniffer has already been initialized") + } + + SSLSnifferApp.db = SSLSnifferApp.initDatabase() SSLSnifferApp.initialized = true } - public get db() { - return this.__db + public static prepare(query: string) { + return SSLSnifferApp.db.prepare(query) } - // This needs to be static as it doesn't need the object private static initDatabase() { const db = new Database( @@ -35,14 +39,15 @@ export class SSLSnifferApp { } - private closeDatabase(graceful?: boolean) { + private static closeDatabase(graceful?: boolean) { + if (!graceful) { - graceful = true + graceful = true } // Change of variable to make it more readable const forceful = !graceful - this.db.close(forceful) + SSLSnifferApp.db.close(forceful) } }