feat(routes): add server-side WRITE_ALL guard on write-only routes
Block direct URL navigation to /persons/new, /documents/new, /documents/:id/edit for users without WRITE_ALL permission. E2E tests verify admin user retains access to all write routes. Closes #17 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
31
frontend/e2e/permissions.spec.ts
Normal file
31
frontend/e2e/permissions.spec.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test.describe('Write permissions — admin user', () => {
|
||||||
|
test('admin user sees Neues Dokument link on home page', async ({ page }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
await expect(page.getByRole('link', { name: /Neues Dokument/i })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin user sees Neue Person link on persons page', async ({ page }) => {
|
||||||
|
await page.goto('/persons');
|
||||||
|
await expect(page.getByRole('link', { name: /Neue Person/i })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin user can navigate to /persons/new', async ({ page }) => {
|
||||||
|
await page.goto('/persons/new');
|
||||||
|
await expect(page).toHaveURL('/persons/new');
|
||||||
|
await expect(page.getByLabel('Vorname')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin user can navigate to /documents/new', async ({ page }) => {
|
||||||
|
await page.goto('/documents/new');
|
||||||
|
await expect(page).toHaveURL('/documents/new');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin user sees edit button on person detail page', async ({ page }) => {
|
||||||
|
await page.goto('/persons');
|
||||||
|
const firstPerson = page.locator('a[href^="/persons/"]:not([href="/persons/new"])').first();
|
||||||
|
await firstPerson.click();
|
||||||
|
await expect(page.getByRole('button', { name: /Bearbeiten/i })).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,7 +3,10 @@ import { env } from '$env/dynamic/private';
|
|||||||
import { createApiClient } from '$lib/api.server';
|
import { createApiClient } from '$lib/api.server';
|
||||||
import { parseBackendError, getErrorMessage } from '$lib/errors';
|
import { parseBackendError, getErrorMessage } from '$lib/errors';
|
||||||
|
|
||||||
export async function load({ params, fetch }) {
|
export async function load({ params, fetch, locals }: { params: { id: string }; fetch: typeof globalThis.fetch; locals: App.Locals }) {
|
||||||
|
const canWrite = locals.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('WRITE_ALL')) ?? false;
|
||||||
|
if (!canWrite) throw error(403, 'Forbidden');
|
||||||
|
|
||||||
const { id } = params;
|
const { id } = params;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { error, fail, redirect } from '@sveltejs/kit';
|
||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
import { createApiClient } from '$lib/api.server';
|
import { createApiClient } from '$lib/api.server';
|
||||||
import { parseBackendError, getErrorMessage } from '$lib/errors';
|
import { parseBackendError, getErrorMessage } from '$lib/errors';
|
||||||
|
|
||||||
export async function load({ fetch }) {
|
export async function load({ fetch, locals }: { fetch: typeof globalThis.fetch; locals: App.Locals }) {
|
||||||
|
const canWrite = locals.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('WRITE_ALL')) ?? false;
|
||||||
|
if (!canWrite) throw error(403, 'Forbidden');
|
||||||
|
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
const personsResult = await api.GET('/api/persons');
|
const personsResult = await api.GET('/api/persons');
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { error, fail, redirect } from '@sveltejs/kit';
|
||||||
import { createApiClient } from '$lib/api.server';
|
import { createApiClient } from '$lib/api.server';
|
||||||
|
|
||||||
|
export async function load({ locals }: { locals: App.Locals }) {
|
||||||
|
const canWrite = locals.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('WRITE_ALL')) ?? false;
|
||||||
|
if (!canWrite) throw error(403, 'Forbidden');
|
||||||
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
default: async ({ request, fetch }) => {
|
default: async ({ request, fetch }) => {
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
|
|||||||
Reference in New Issue
Block a user