From cf28ec19f597fe91ab67bed7f4a3d2d7f6c92338 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Thu, 10 Sep 2026 14:32:52 +0200 Subject: [PATCH] test: Rollentrennung end-to-end absichern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legt einen Benutzer ohne Verwaltungsrechte an und prüft, dass der direkte Aufruf der Protokollseite auf den Hinweis führt — nicht nur, dass der Menüpunkt fehlt. Sich darauf zu verlassen, dass die Oberfläche Knöpfe ausblendet, wäre kein Nachweis. Co-Authored-By: Claude Opus 5 (1M context) --- e2e/rollen.spec.ts | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 e2e/rollen.spec.ts diff --git a/e2e/rollen.spec.ts b/e2e/rollen.spec.ts new file mode 100644 index 0000000..db89306 --- /dev/null +++ b/e2e/rollen.spec.ts @@ -0,0 +1,74 @@ +import { PrismaPg } from '@prisma/adapter-pg' +import { expect, test, type Page } from '@playwright/test' +import bcrypt from 'bcryptjs' +import { PrismaClient } from '../src/generated/prisma/client.js' + +/** + * Prüft, dass die Rollentrennung serverseitig hält. + * + * Die Oberfläche blendet Knöpfe aus — darauf allein darf man sich nicht + * verlassen. Deshalb ruft dieser Test die Verwaltungsseite direkt auf, statt + * nach einem Menüpunkt zu suchen. + */ + +const EMAIL = 'zzz-e2e-benutzer@example.invalid' +const PASSWORT = 'nur-fuer-den-test-1234' + +const verbindung = process.env.DATABASE_URL ?? '' +const db = new PrismaClient({ adapter: new PrismaPg({ connectionString: verbindung }) }) + +test.describe('Rollentrennung', () => { + test.skip(!verbindung, 'DATABASE_URL ist nicht gesetzt.') + + test.beforeAll(async () => { + await db.benutzer.deleteMany({ where: { email: EMAIL } }) + await db.benutzer.create({ + data: { + email: EMAIL, + name: 'Testbenutzer ohne Rechte', + rolle: 'BENUTZER', + passwortHash: await bcrypt.hash(PASSWORT, 10), + }, + }) + }) + + test.afterAll(async () => { + await db.auditEintrag.deleteMany({ where: { benutzerEmail: EMAIL } }) + await db.benutzer.deleteMany({ where: { email: EMAIL } }) + await db.$disconnect() + }) + + async function anmelden(seite: Page): Promise { + await seite.goto('/anmelden') + await seite.getByLabel('E-Mail-Adresse').fill(EMAIL) + await seite.getByLabel('Passwort').fill(PASSWORT) + await seite.getByRole('button', { name: 'Mit Passwort anmelden' }).click() + await expect(seite.getByRole('heading', { name: /Guten Tag/ })).toBeVisible() + } + + test('zeigt einem Benutzer den Menüpunkt Protokoll gar nicht', async ({ page }) => { + await anmelden(page) + await expect(page.getByRole('link', { name: 'Protokoll' })).toHaveCount(0) + }) + + test('lässt einen Benutzer auch beim direkten Aufruf nicht ins Protokoll', async ({ + page, + }) => { + await anmelden(page) + await page.goto('/verwaltung/protokoll') + + await expect(page).toHaveURL(/\/keine-berechtigung/) + await expect( + page.getByRole('heading', { name: /Dazu fehlt dir die Berechtigung/ }), + ).toBeVisible() + }) + + test('zeigt einem Benutzer die Bereiche, aber ohne Formulare', async ({ page }) => { + await anmelden(page) + await page.goto('/bereiche') + + await expect(page.getByRole('heading', { name: 'Bereiche' })).toBeVisible() + await expect(page.getByRole('button', { name: 'Bearbeiten' })).toHaveCount(0) + await expect(page.getByRole('button', { name: 'Bereich anlegen' })).toHaveCount(0) + }) +})