import { expect, test } from '@playwright/test'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { authConfig } from '../../support/auth-config'; const root = resolve(__dirname, '../../..'); const toml = readFileSync(resolve(root, 'supabase/config.toml'), 'utf8'); const doc = readFileSync(resolve(root, 'docs/auth-konfiguration.md'), 'utf8'); /** Aktive (nicht auskommentierte) Zeilen von `config.toml`. */ const active = toml .split('\n') .map((l) => l.trim()) .filter((l) => l && !l.startsWith('#')); function value(key: string, section?: string): string | undefined { let current = ''; for (const line of active) { const sec = line.match(/^\[(.+)\]$/); if (sec) { current = sec[1]; continue; } if (section !== undefined && current !== section) continue; const kv = line.match(new RegExp(`^${key}\\s*=\\s*(.+)$`)); if (kv) return kv[1].replace(/^"|"$/g, ''); } return undefined; } test.describe('Auth-Konfiguration: auth-config.ts stimmt mit config.toml und Doku ueberein', () => { test('AUTH-CFG-01: Site-URL', () => { expect(value('site_url', 'auth')).toBe(authConfig.siteUrl); }); test('AUTH-CFG-04: E-Mail-Bestaetigung Pflicht', () => { expect(value('enable_confirmations', 'auth.email')).toBe('true'); }); test('AUTH-CFG-05/-06: Passwort-Mindestlaenge und keine Zeichenklassen', () => { expect(Number(value('minimum_password_length', 'auth'))).toBe(authConfig.passwordMinLength); expect(value('password_requirements', 'auth')).toBe(''); }); test('AUTH-CFG-07: Gueltigkeit der Links', () => { expect(Number(value('otp_expiry', 'auth.email'))).toBe(authConfig.otpExpirySeconds); }); test('AUTH-CFG-08: Mindestabstand lokal 1 s', () => { expect(value('max_frequency', 'auth.email')).toBe(`${authConfig.maxFrequencySeconds}s`); }); test('AUTH-CFG-10/-11: Burst der Drosselung', () => { expect(Number(value('sign_in_sign_ups', 'auth.rate_limit'))).toBe(authConfig.otpBurst); }); test('AUTH-CFG-14/-15: JWT-Laufzeit und Refresh-Token-Rotation', () => { expect(Number(value('jwt_expiry', 'auth'))).toBe(authConfig.jwtExpirySeconds); expect(value('enable_refresh_token_rotation', 'auth')).toBe('true'); expect(Number(value('refresh_token_reuse_interval', 'auth'))).toBe(authConfig.refreshTokenReuseIntervalSeconds); }); test('KTO-01 Kriterium 5: AUTH-CFG-16 Sitzungsbegrenzung ist nicht gesetzt (timebox, inactivity_timeout)', () => { expect(active.some((l) => /^timebox\s*=/.test(l))).toBe(false); expect(active.some((l) => /^inactivity_timeout\s*=/.test(l))).toBe(false); expect(active).not.toContain('[auth.sessions]'); }); test('AUTH-CFG-19: alle Benachrichtigungs-Mails aus', () => { const sections = active.filter((l) => l.startsWith('[auth.email.notification.')); expect(sections.length).toBeGreaterThan(0); for (const s of sections) { expect(value('enabled', s.slice(1, -1)), s).toBe('false'); } }); test('AUTH-CFG-24: SMS-Autoconfirm aus (enable_confirmations = true), Voraussetzung fuer den Enumerationsschutz', () => { expect(value('enable_confirmations', 'auth.sms')).toBe('true'); }); test('docs/auth-konfiguration.md fuehrt AUTH-CFG-01 bis -25', () => { for (let i = 1; i <= 25; i++) { const id = `AUTH-CFG-${String(i).padStart(2, '0')}`; expect(doc, id).toContain(`| ${id} |`); } }); });