Enhance authentication checks in CMS API endpoints; implement user role validation for admin and board access. Refactor Spielpläne API to remove unnecessary logging and improve error handling. Update tests to mock user authentication and ensure proper validation of file uploads.

This commit is contained in:
Torsten Schulz (local)
2025-11-10 13:18:29 +01:00
parent bde1d32b14
commit 3d6646cf31
5 changed files with 81 additions and 18 deletions

View File

@@ -3,6 +3,7 @@ import fs from 'fs/promises'
import path from 'path'
import { exec } from 'child_process'
import { promisify } from 'util'
import { getUserFromToken } from '../../utils/auth.js'
const execAsync = promisify(exec)
@@ -51,6 +52,23 @@ export default defineEventHandler(async (event) => {
})
}
let token = getCookie(event, 'auth_token')
const currentUser = token ? await getUserFromToken(token) : null
if (!currentUser) {
throw createError({
statusCode: 401,
statusMessage: 'Nicht authentifiziert'
})
}
if (currentUser.role !== 'admin' && currentUser.role !== 'vorstand') {
throw createError({
statusCode: 403,
statusMessage: 'Keine Berechtigung'
})
}
try {
// Multer-Middleware für File-Upload
await new Promise((resolve, reject) => {

View File

@@ -1,8 +1,26 @@
import fs from 'fs/promises'
import path from 'path'
import { getUserFromToken } from '../../utils/auth.js'
export default defineEventHandler(async (event) => {
try {
const token = getCookie(event, 'auth_token')
const currentUser = token ? await getUserFromToken(token) : null
if (!currentUser) {
throw createError({
statusCode: 401,
statusMessage: 'Nicht authentifiziert'
})
}
if (currentUser.role !== 'admin' && currentUser.role !== 'vorstand') {
throw createError({
statusCode: 403,
statusMessage: 'Keine Berechtigung'
})
}
const { filename, content } = await readBody(event)
if (!filename || !content) {

View File

@@ -1,6 +1,7 @@
import multer from 'multer'
import fs from 'fs/promises'
import path from 'path'
import { getUserFromToken } from '../../utils/auth.js'
// Multer-Konfiguration für PDF-Uploads
const storage = multer.diskStorage({
@@ -31,12 +32,35 @@ const upload = multer({
export default defineEventHandler(async (event) => {
try {
// Prüfe Authentifizierung
const authHeader = getHeader(event, 'authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
let token = getCookie(event, 'auth_token')
if (!token) {
const authHeader = getHeader(event, 'authorization')
if (authHeader && authHeader.startsWith('Bearer ')) {
token = authHeader.substring(7).trim()
}
}
if (!token) {
throw createError({
statusCode: 401,
statusMessage: 'Nicht autorisiert'
statusMessage: 'Nicht authentifiziert'
})
}
const currentUser = await getUserFromToken(token)
if (!currentUser) {
throw createError({
statusCode: 401,
statusMessage: 'Nicht authentifiziert'
})
}
if (currentUser.role !== 'admin' && currentUser.role !== 'vorstand') {
throw createError({
statusCode: 403,
statusMessage: 'Keine Berechtigung'
})
}

View File

@@ -5,20 +5,15 @@ export default defineEventHandler(async (event) => {
try {
const spielplaeneDir = path.join(process.cwd(), 'public', 'spielplaene')
console.log('=== SPIELPLÄNE API ===')
console.log('Verzeichnis:', spielplaeneDir)
// Prüfe, ob das Verzeichnis existiert
try {
await fs.access(spielplaeneDir)
} catch {
console.log('Verzeichnis nicht gefunden')
return []
}
// Lese alle Dateien im Verzeichnis
const dateien = await fs.readdir(spielplaeneDir)
console.log('Alle Dateien:', dateien)
// Filtere nur relevante Dateitypen
const erlaubteExtensions = ['.pdf', '.xlsx', '.xls', '.doc', '.docx']
@@ -27,9 +22,6 @@ export default defineEventHandler(async (event) => {
return erlaubteExtensions.includes(ext)
})
console.log('Gefilterte Dateien:', gefiltert)
console.log('Anzahl:', gefiltert.length)
return gefiltert
} catch (error) {
console.error('Fehler beim Lesen der Spielpläne:', error)

View File

@@ -2,6 +2,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createEvent, mockSuccessReadBody } from './setup'
import fs from 'fs/promises'
vi.mock('../server/utils/auth.js', () => ({
getUserFromToken: vi.fn()
}))
vi.mock('multer', () => {
const single = vi.fn((field) => (req, _res, cb) => {
if (req.__mockMulterError) {
@@ -35,22 +39,26 @@ import saveCsvHandler from '../server/api/cms/save-csv.post.js'
import uploadSpielplanHandler from '../server/api/cms/upload-spielplan-pdf.post.js'
import satzungUploadHandler from '../server/api/cms/satzung-upload.post.js'
const { getUserFromToken } = await import('../server/utils/auth.js')
describe('CMS File Endpoints', () => {
beforeEach(() => {
vi.restoreAllMocks()
vi.clearAllMocks()
getUserFromToken.mockReset()
getUserFromToken.mockResolvedValue({ id: 'admin', role: 'admin' })
})
describe('POST /api/cms/save-csv', () => {
it('validiert Eingaben', async () => {
const event = createEvent()
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({})
await expect(saveCsvHandler(event)).rejects.toMatchObject({ statusCode: 400 })
})
it('speichert erlaubte Datei', async () => {
const event = createEvent()
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({ filename: 'mannschaften.csv', content: 'data' })
vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
@@ -66,14 +74,16 @@ describe('CMS File Endpoints', () => {
const event = createEvent({ method: 'POST' })
event.node.req.__mockFile = { filename: 'file.pdf', originalname: 'orig.pdf', path: 'tmp', mimetype: 'application/pdf' }
event.node.req.body = { type: 'gesamt' }
getUserFromToken.mockResolvedValue(null)
await expect(uploadSpielplanHandler(event)).rejects.toMatchObject({ statusCode: 401 })
})
it('lädt PDF hoch und gibt Erfolg zurück', async () => {
const event = createEvent({ method: 'POST', headers: { authorization: 'Bearer token' } })
const event = createEvent({ method: 'POST', headers: { authorization: 'Bearer valid-token' } })
event.node.req.__mockFile = { filename: 'spielplan_gesamt.pdf', originalname: 'orig.pdf', path: 'tmp', mimetype: 'application/pdf' }
event.node.req.body = { type: 'gesamt' }
getUserFromToken.mockResolvedValue({ id: 'admin', role: 'admin' })
const response = await uploadSpielplanHandler(event)
expect(response.success).toBe(true)
@@ -83,10 +93,11 @@ describe('CMS File Endpoints', () => {
describe('POST /api/cms/satzung-upload', () => {
it('verarbeitet hochgeladene Satzung', async () => {
const event = createEvent({ method: 'POST' })
const event = createEvent({ method: 'POST', cookies: { auth_token: 'token' } })
event.node.req.__mockFile = { path: 'public/documents/satzung.pdf', filename: 'satzung.pdf', originalname: 'satzung.pdf', mimetype: 'application/pdf' }
vi.spyOn(fs, 'readFile').mockResolvedValueOnce(JSON.stringify({ seiten: {}, vorstand: { vorsitzender: { email: '' }, schriftfuehrer: { email: '' } } }))
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
getUserFromToken.mockResolvedValue({ id: 'admin', role: 'admin' })
const response = await satzungUploadHandler(event)
expect(response.success).toBe(true)