From f18f430c0ebc9016b361f2f956a6c7dcc164409f Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 31 Jul 2025 07:02:21 +0200 Subject: [PATCH] added basic user editing --- src/lib/permissions.config.json | 2 +- src/lib/server/auth.ts | 6 +- src/lib/server/database.ts | 89 +++++++++++++------------- src/lib/server/session_store.ts | 8 +++ src/routes/user/+page.server.ts | 69 ++++++++++++++++++++ src/routes/user/+page.svelte | 109 ++++++++++++++++++++++++++++++++ 6 files changed, 238 insertions(+), 45 deletions(-) create mode 100644 src/routes/user/+page.server.ts create mode 100644 src/routes/user/+page.svelte diff --git a/src/lib/permissions.config.json b/src/lib/permissions.config.json index 59d34a7..1deec24 100644 --- a/src/lib/permissions.config.json +++ b/src/lib/permissions.config.json @@ -11,5 +11,5 @@ "meta_permissions": { "MANAGE": ["VIEW", "ADD", "DELETE", "EDIT"] } - }, + } } diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index c66da1a..ae8c08d 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -1,6 +1,6 @@ import Bun from 'bun'; -import { User, get_user_by_name } from "$lib/server/database"; +import { User, get_user_by_name, update_user_password } from "$lib/server/database"; export async function authorize_password(username: string, password: string): Promise { @@ -12,4 +12,8 @@ export async function authorize_password(username: string, password: string): Pr return res ? user : null; } +export async function change_password(user_id: number, new_password: string): Promise { + const hash = await Bun.password.hash(new_password, { algorithm: "bcrypt", cost: 11}); + return update_user_password(user_id, hash) +} diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts index 63625b6..66c3024 100644 --- a/src/lib/server/database.ts +++ b/src/lib/server/database.ts @@ -48,8 +48,8 @@ const USER_DATABASE_SETUP: string[] = [ const USER_DATABASE_ADD_USER: string = "INSERT INTO users (name, gender, address, username, password) VALUES ($name, $gender, $address, $username, $password);"; -const USER_DATABASE_GET_USER: string = - "SELECT * FROM users;"; +const USER_DATABASE_GET_ALL_USER: string = + "SELECT id, username, name FROM users;"; const USER_DATABASE_GET_USER_BY_NAME: string = "SELECT * FROM users WHERE username = $username;" @@ -57,6 +57,9 @@ const USER_DATABASE_GET_USER_BY_NAME: string = const USER_DATABASE_EMPTY: string = "SELECT EXISTS (SELECT 1 FROM users);" +const USER_DATABASE_UPDATE_PASSWORD: string = + "UPDATE users SET password=$password WHERE id=$id;" + /*const USER_DATABASE_ADD_ACCESS_TOKEN: string = "INSERT INTO access_tokens (user_id, token, expiry_date) VALUES ($user_id, $token, $expiry_date);" @@ -83,7 +86,7 @@ const ENTRY_DATABASE_SETUP: string[] = [ record_id INTEGER NOT NULL, date VARCHAR(10), start VARCHAR(5), - end VARCHAR(5), + end VARCHAR(5), comment TEXT, modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, FOREIGN KEY(record_id) REFERENCES records(id) @@ -198,6 +201,19 @@ export class User { this._database = db; } + toUserEntry(): UserEntry { + return { + id: this.id, + gender: this.gender, + name: this.name, + address: this.address, + username: this.username, + password: this.password, + permissions: this.permissions, + created: this.created, + } + } + get_months(): { year: string, month: string }[] { const query = this._database.query(ENTRY_DATABASE_GET_MONTHS); const res = query.all(); @@ -381,51 +397,16 @@ export async function create_user(user: { name: string, gender: string, address: throw e; } } - -function _get_user(): UserEntry | null { +export function get_all_user(): { id: number, username: string, name: string }[] { try { - const statement = user_database.prepare(USER_DATABASE_GET_USER); - const result = statement.get() as UserEntry; - - return result; - - } catch (e) { - if (e instanceof SQLiteError) { - return null; - } - - throw e; - } - -} - -export function get_user(id: number): User | null { - - const user = _get_user(); - if (user == null) { - return null; - } + const query = user_database.prepare(USER_DATABASE_GET_ALL_USER) + const user = query.all() as { id: number, username: string, name: string }[] - const db_name = get_user_db_name(user); - - try { - - fs.mkdir(DATABASES_PATH, { recursive: true }); - - let userdb = new Database(db_name, { create: true, strict: true }); - - if (!is_db_initialized(userdb)) { - setup_db(userdb, ENTRY_DATABASE_SETUP); - } - - return new User(user, userdb); + return user } catch (e) { - console.log(e); - - return null; + throw e } - } export function get_user_by_name(username: string): User | null { @@ -462,3 +443,25 @@ export function do_users_exist(): any { // sqlite trims the first "SELECT " and ";" from the query string return (answer as any)?.[USER_DATABASE_EMPTY.slice(7, -1)]; } + +export function updateUser(data: {id: number, gender?: string, name?: string, address?: string, username?: string }) { + let changed: Array = [] + if (data.gender) changed.push("gender=$gender") + if (data.name) changed.push("name=$name") + if (data.address) changed.push("address=$address") + if (data.username) changed.push("username=$username") + + const update_query = "UPDATE users SET " + changed.join(", ") + " WHERE id=$id;" + + const query = user_database.prepare(update_query) + const result = query.run(data) + + return get_user_by_name(data?.name ?? "") // GET USER BY lastRowId from result +} + +export function update_user_password(user_id: number, password: string) { + const query = user_database.prepare(USER_DATABASE_UPDATE_PASSWORD) + const result = query.run({ password: password, id: user_id }) + + return result.changes > 0 +} diff --git a/src/lib/server/session_store.ts b/src/lib/server/session_store.ts index d27b6a8..cdd5fac 100644 --- a/src/lib/server/session_store.ts +++ b/src/lib/server/session_store.ts @@ -104,6 +104,13 @@ function logout_user_session(token: string): boolean { return true; } +function reload_user_data(user: User) { + const session_info = active_users.get(user.id) + if (!session_info) return + + session_info.user = user +} + async function __clean_session_store() { let cleaned_user_sessions = 0 @@ -135,6 +142,7 @@ export default class SessionStore { static issue_access_token_for_user = issue_access_token_for_user; static get_user_by_access_token = get_user_by_access_token; static logout_user_session = logout_user_session; + static reload_user_data = reload_user_data; } setInterval(__clean_session_store, 15*60*1000); diff --git a/src/routes/user/+page.server.ts b/src/routes/user/+page.server.ts new file mode 100644 index 0000000..b977c47 --- /dev/null +++ b/src/routes/user/+page.server.ts @@ -0,0 +1,69 @@ +import type { PageServerLoad, Actions } from "./$types" + +import { fail } from "@sveltejs/kit" + +import Permissions from "$lib/permissions" +import { toInt } from "$lib/util" + +import SessionStore from "$lib/server/session_store" +import { updateUser } from "$lib/server/database" +import { change_password } from "$lib/server/auth" + +export const load: PageServerLoad = ({ locals, url }) => { + + if (locals.user == null) { + return fail(403, { message: "Unauthorized user" }) + } + + return { + user: locals.user.toUserEntry() + } +} + + +export const actions = { + edit: async ({locals, request}) => { + + if (locals.user == null) { + Logs.route.warn("An unauthorized user tried to edit an user") + return fail(403, { message: "Unauthorized user" }) + } + + const data = await request.formData(); + + const id = toInt((data.get("id") as string|null) ?? "NaN") + const name = data.get("name") as string|null + const gender = data.get("gender") as string|null + const address = data.get("address") as string|null + + const username = data.get("username") as string|null + const password1 = data.get("password1") as string|null + const password2 = data.get("password2") as string|null + + if (isNaN(id) || name == null || gender == null || address == null || username == null) { + return fail(400, { message: "invalid request" }) + } + + if (locals.user.id != id + && (!Permissions.has(locals.user.permissions, Permissions.USERADMIN.EDIT) + || ((password1 != null || password2 != null) && !Permissions.has(locals.user.permissions, Permissions.USERADMIN.EDIT_PASSWORD)))) { + return fail(403, { message: "Unauthorized action" }) + } + + if ((password1 != null || password2 != null)) { + if (password1 != password2) { + return fail(400, { message: "Passwörter müssen übereinstimmen" }) + } + const result = change_password(id, password1!) + if (!result) { + return fail(500, { message: "Database failure"}) + } + } + + const updated_user = updateUser({id, name, gender, address, username}) + SessionStore.reload_user_data(updated_user ?? locals.user) + + return {} + + } +} satisfies Actions diff --git a/src/routes/user/+page.svelte b/src/routes/user/+page.svelte new file mode 100644 index 0000000..e714ab0 --- /dev/null +++ b/src/routes/user/+page.svelte @@ -0,0 +1,109 @@ + + +
+ + +
+ +

Benutzer

+ +

{form?.message}

+ + + + + + + + + + + + + + + + + + + + + + + +
Persönliche Daten
Name
Geschlecht +
Addresse
+ + + + + + + + + + + + + + + + + + + + + +
Benutzer
Benutzername
Passwort ändern
+ + + +
+ +
+ +