Add soft account deletion safeguards

This commit is contained in:
JB
2026-07-24 23:30:53 -04:00
parent 472528f429
commit 05acc022c7
10 changed files with 131 additions and 10 deletions
+22 -2
View File
@@ -1,6 +1,7 @@
import { getPermissions, requirePermission } from '$lib/server/auth/authorization';
import { getSessionUser } from '$lib/server/auth/session';
import { getDatabase } from '$lib/server/database';
import { softDeleteAccount } from '$lib/server/auth/account-deletion';
import { error, fail } from '@sveltejs/kit';
import { randomUUID } from 'node:crypto';
import type { Actions, PageServerLoad } from './$types';
@@ -9,8 +10,10 @@ export const load: PageServerLoad = async ({ cookies, params }) => {
const operator = await requirePermission(await getSessionUser(cookies), 'users.view');
const database = await getDatabase();
const [profile, roles, permissions, assignedRoles, directPermissions, logins, builds, comments] = await Promise.all([
(await database.prepare(`SELECT users.id,users.username,users.role,users.discord_id,users.created_at,user_status.*
FROM users LEFT JOIN user_status ON user_status.user_id=users.id WHERE users.id=?`)).get(params.id),
(await database.prepare(`SELECT users.id,users.username,users.role,users.discord_id,users.created_at,user_status.*,
deleted_accounts.deleted_at,deleted_accounts.deleted_by,deleted_accounts.reason AS deletion_reason
FROM users LEFT JOIN user_status ON user_status.user_id=users.id
LEFT JOIN deleted_accounts ON deleted_accounts.user_id=users.id WHERE users.id=?`)).get(params.id),
(await database.prepare('SELECT * FROM roles ORDER BY name')).all(),
(await database.prepare('SELECT * FROM permissions ORDER BY name')).all(),
(await database.prepare('SELECT role_id FROM user_roles WHERE user_id=?')).all(params.id),
@@ -82,5 +85,22 @@ export const actions: Actions = {
await (await database.prepare('INSERT INTO audit_log(id,actor_id,entity_type,entity_id,action,after_json,created_at) VALUES(?,?,?,?,?,?,?)'))
.run(randomUUID(), operator.id, 'user', params.id, `moderation.${state}`, JSON.stringify({ reason, bannedUntil: bannedUntil?.toISOString() }), now);
return { moderationSaved: true };
},
deleteAccount: async ({ cookies, request, params }) => {
const operator = await requirePermission(await getSessionUser(cookies), 'users.delete');
if (operator.id === params.id) return fail(400, { adminDeleteError: 'Delete your own account from the agent profile page.' });
const reason = String((await request.formData()).get('reason') ?? '').trim();
if (reason.length < 3) return fail(400, { adminDeleteError: 'Provide a deletion reason.' });
const database = await getDatabase();
const target = await (await database.prepare('SELECT role FROM users WHERE id=?')).get(params.id);
if (!target) return fail(404, { adminDeleteError: 'User not found.' });
const targetAdminRole = await (await database.prepare(`SELECT 1 FROM user_roles WHERE user_id=? AND role_id='role-admin'`)).get(params.id);
if ((target.role === 'admin' || targetAdminRole) && !(await getPermissions(operator)).includes('*')) {
return fail(403, { adminDeleteError: 'Only an administrator can delete another administrator.' });
}
if (!(await softDeleteAccount(database, params.id, operator.id, reason))) {
return fail(409, { adminDeleteError: 'This account has already been deleted.' });
}
return { accountDeleted: true };
}
};