Files
familienarchiv/frontend/src/routes/admin/users/new/+page.server.ts
Marcel 8fc360a596 fix(admin): guard GET /api/users/{id} with @RequirePermission(ADMIN_USER)
Fixes IDOR: the endpoint was publicly accessible to any authenticated user.
Now requires ADMIN_USER permission, matching all other user management endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:09:40 +02:00

46 lines
1.5 KiB
TypeScript

import { error, fail, redirect } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
import { createApiClient } from '$lib/api.server';
import { getErrorMessage } from '$lib/errors';
export const load: PageServerLoad = async ({ fetch, locals }) => {
const user = locals.user;
const hasAdmin = user?.groups?.some((g: { permissions: string[] }) =>
g.permissions.includes('ADMIN')
);
if (!hasAdmin) throw error(403, getErrorMessage('FORBIDDEN'));
const api = createApiClient(fetch);
const groupsResult = await api.GET('/api/groups');
return { groups: groupsResult.data ?? [] };
};
export const actions: Actions = {
default: async ({ request, fetch }) => {
const data = await request.formData();
const api = createApiClient(fetch);
const birthDateRaw = data.get('birthDate') as string;
const result = await api.POST('/api/users', {
body: {
username: data.get('username') as string,
initialPassword: data.get('password') as string,
email: (data.get('email') as string) || undefined,
groupIds: data.getAll('groupIds') as string[],
firstName: (data.get('firstName') as string) || null,
lastName: (data.get('lastName') as string) || null,
birthDate: birthDateRaw || null,
contact: (data.get('contact') as string) || null
}
});
if (!result.response.ok) {
const code = (result.error as unknown as { code?: string })?.code;
return fail(result.response.status, { error: getErrorMessage(code) });
}
throw redirect(303, '/admin/users');
}
};