80 lines
3.4 KiB
JavaScript
80 lines
3.4 KiB
JavaScript
// @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)
|
||
})
|
||
})
|