Steuerkram/tests/e2e/specs/settings-smtp-authorization.spec.js

80 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-check
// Fix #193: SettingsController::update()/mailTest() verlangten kein
// Auth::requireAdmin() – jeder eingeloggte Benutzer konnte mail_smtp_host
// setzen und per "SMTP testen" sofort einen Verbindungsversuch des Servers
// zu einem beliebigen Ziel auslösen (Ausgangspunkt für DNS-Rebinding/SSRF).
// Dieser Test prüft die Durchsetzung auf HTTP-Ebene, die die PHP-Suite nicht
// erreicht (Router-Guard, Auth::requireAdmin()).
const { test, expect } = require('@playwright/test')
const { uniqueTag } = require('../helpers/app')
test.describe('Autorisierung: SMTP-Einstellungen nur für Admins (Fix #193)', () => {
test('Nicht-Admin wird von POST /settings und /settings/mail-test ausgesperrt, Admin nicht', async ({ page, browser }) => {
const username = uniqueTag('noadmin').toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 64)
const password = 'TempPass123!'
await page.goto('/useradmin')
await page.fill('#field-username', username)
await page.fill('#field-password', password)
await page.click('#submit-btn')
await page.waitForURL('/useradmin')
const row = page.locator('tr', { hasText: username })
await expect(row).toBeVisible()
try {
const context = await browser.newContext({ storageState: { cookies: [], origins: [] } })
const userPage = await context.newPage()
await userPage.goto('/login')
await userPage.fill('#username', username)
await userPage.fill('#password', password)
await userPage.click('form[action="/login"] button[type="submit"]')
await userPage.waitForURL(url => !url.pathname.startsWith('/login'))
// GET /settings bleibt für alle eingeloggten Benutzer erreichbar
// (Firmendaten sind kein Admin-Feature) – nur die SMTP-mutierenden
// Endpunkte werden geschützt.
await userPage.goto('/settings')
const csrfToken = await userPage.evaluate(() => document.querySelector('meta[name="csrf-token"]')?.content || '')
expect(csrfToken).not.toBe('')
const mailTestResponse = await userPage.request.post('/settings/mail-test', {
headers: { 'X-CSRF-Token': csrfToken },
form: {},
})
expect(mailTestResponse.status()).toBe(403)
await expect(mailTestResponse.text()).resolves.toContain('Kein Zugriff')
const updateResponse = await userPage.request.post('/settings', {
headers: { 'X-CSRF-Token': csrfToken },
form: { firma: 'Sollte nicht gespeichert werden', strasse: 'X', plz: '12345', ort: 'X', mail_smtp_host: '127.0.0.1' },
})
expect(updateResponse.status()).toBe(403)
await expect(updateResponse.text()).resolves.toContain('Kein Zugriff')
await context.close()
} finally {
const delForm = row.locator('form[action$="/delete"]')
if (await delForm.count() > 0) {
await delForm.locator('button[type="submit"]').click()
await page.waitForLoadState('networkidle')
}
}
})
test('Admin darf /settings/mail-test weiterhin aufrufen (kein 403)', async ({ page }) => {
await page.goto('/settings')
const csrfToken = await page.evaluate(() => document.querySelector('meta[name="csrf-token"]')?.content || '')
expect(csrfToken).not.toBe('')
const response = await page.request.post('/settings/mail-test', {
headers: { 'X-CSRF-Token': csrfToken },
form: {},
})
// Egal ob die SMTP-Verbindung selbst gelingt oder scheitert (abhängig von
// den hinterlegten Test-Einstellungen) – der Admin-Zugriff selbst darf
// nicht mit 403 verweigert werden.
expect(response.status()).not.toBe(403)
})
})