2025-06-30 11:56:09 +00:00
|
|
|
import type { Session, SessionApp } from "$lib/classes/sessions";
|
2025-06-28 21:52:44 +00:00
|
|
|
import { User, type IUserBroker } from "$lib/classes/users";
|
2025-06-30 11:56:09 +00:00
|
|
|
import { logger } from "$lib/utils/logger";
|
2025-06-28 21:52:44 +00:00
|
|
|
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
|
|
|
|
|
}
|
2025-06-30 11:56:09 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-28 21:52:44 +00:00
|
|
|
export class UserDBBroker implements IUserBroker {
|
|
|
|
|
|
|
|
|
|
private static initialized = false
|
|
|
|
|
|
|
|
|
|
constructor() {
|
2025-06-29 11:01:31 +00:00
|
|
|
if (UserDBBroker.initialized) {
|
2025-06-28 21:52:44 +00:00
|
|
|
// UGLY: make more specific
|
|
|
|
|
throw Error("UserDB has been already initialized")
|
|
|
|
|
}
|
2025-06-30 11:56:09 +00:00
|
|
|
|
|
|
|
|
logger.debug("Correctly initialized", "UserDBBroker")
|
2025-06-28 21:52:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public createTable(): void {
|
|
|
|
|
const stmt = SSLSnifferApp.prepare(
|
|
|
|
|
`
|
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
2025-06-29 11:01:31 +00:00
|
|
|
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
2025-06-28 21:52:44 +00:00
|
|
|
username TEXT UNIQUE NOT NULL ,
|
|
|
|
|
password_hash TEXT NOT NULL
|
|
|
|
|
);
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
stmt.run()
|
|
|
|
|
stmt.finalize()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async createUser(username: string, password: string): Promise<User> {
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
public async getUser(username: string, password: string): Promise<User | null> {
|
|
|
|
|
|
|
|
|
|
const userToVerify = this.getUserFromUsername(username)
|
2025-06-28 21:52:44 +00:00
|
|
|
|
|
|
|
|
if (!userToVerify) {
|
|
|
|
|
// UGLY: make this more specific
|
|
|
|
|
throw new Error("The specified user does not exist on the database")
|
|
|
|
|
}
|
2025-06-30 11:56:09 +00:00
|
|
|
|
2025-06-28 21:52:44 +00:00
|
|
|
let match = false
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
match = await argon2.verify(userToVerify.password_hash, password)
|
2025-06-30 11:56:09 +00:00
|
|
|
} catch (error) {
|
2025-06-28 21:52:44 +00:00
|
|
|
// 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<void> {
|
2025-06-30 11:56:09 +00:00
|
|
|
|
2025-06-28 21:52:44 +00:00
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
)
|
|
|
|
|
|
2025-06-28 21:52:44 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
|
|
|
|
|
|
2025-06-28 21:52:44 +00:00
|
|
|
private validateUniqueness(username: string) {
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
const user = this.getUserFromUsername(username)
|
2025-06-28 21:52:44 +00:00
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error("User is already on the system")
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
private getUserFromUsername(username: string): UserDB | null {
|
2025-06-28 21:52:44 +00:00
|
|
|
const stmt = SSLSnifferApp.prepare(
|
|
|
|
|
`
|
|
|
|
|
SELECT user_id, username, password_hash
|
|
|
|
|
FROM users
|
|
|
|
|
WHERE username = @username;
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
const user: any | null = stmt.get({
|
2025-06-28 21:52:44 +00:00
|
|
|
username: username,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
stmt.finalize()
|
|
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new UserDB(
|
|
|
|
|
user.user_id,
|
|
|
|
|
user.username,
|
|
|
|
|
user.password_hash
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 11:56:09 +00:00
|
|
|
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
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-28 21:52:44 +00:00
|
|
|
}
|