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:
@@ -3,6 +3,7 @@ import fs from 'fs/promises'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { exec } from 'child_process'
|
import { exec } from 'child_process'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
|
import { getUserFromToken } from '../../utils/auth.js'
|
||||||
|
|
||||||
const execAsync = promisify(exec)
|
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 {
|
try {
|
||||||
// Multer-Middleware für File-Upload
|
// Multer-Middleware für File-Upload
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
|
|||||||
@@ -1,8 +1,26 @@
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { getUserFromToken } from '../../utils/auth.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
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)
|
const { filename, content } = await readBody(event)
|
||||||
|
|
||||||
if (!filename || !content) {
|
if (!filename || !content) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import multer from 'multer'
|
import multer from 'multer'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { getUserFromToken } from '../../utils/auth.js'
|
||||||
|
|
||||||
// Multer-Konfiguration für PDF-Uploads
|
// Multer-Konfiguration für PDF-Uploads
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
@@ -31,12 +32,35 @@ const upload = multer({
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
// Prüfe Authentifizierung
|
let token = getCookie(event, 'auth_token')
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
const authHeader = getHeader(event, 'authorization')
|
const authHeader = getHeader(event, 'authorization')
|
||||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||||
|
token = authHeader.substring(7).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 401,
|
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'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,15 @@ export default defineEventHandler(async (event) => {
|
|||||||
try {
|
try {
|
||||||
const spielplaeneDir = path.join(process.cwd(), 'public', 'spielplaene')
|
const spielplaeneDir = path.join(process.cwd(), 'public', 'spielplaene')
|
||||||
|
|
||||||
console.log('=== SPIELPLÄNE API ===')
|
|
||||||
console.log('Verzeichnis:', spielplaeneDir)
|
|
||||||
|
|
||||||
// Prüfe, ob das Verzeichnis existiert
|
// Prüfe, ob das Verzeichnis existiert
|
||||||
try {
|
try {
|
||||||
await fs.access(spielplaeneDir)
|
await fs.access(spielplaeneDir)
|
||||||
} catch {
|
} catch {
|
||||||
console.log('Verzeichnis nicht gefunden')
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lese alle Dateien im Verzeichnis
|
// Lese alle Dateien im Verzeichnis
|
||||||
const dateien = await fs.readdir(spielplaeneDir)
|
const dateien = await fs.readdir(spielplaeneDir)
|
||||||
console.log('Alle Dateien:', dateien)
|
|
||||||
|
|
||||||
// Filtere nur relevante Dateitypen
|
// Filtere nur relevante Dateitypen
|
||||||
const erlaubteExtensions = ['.pdf', '.xlsx', '.xls', '.doc', '.docx']
|
const erlaubteExtensions = ['.pdf', '.xlsx', '.xls', '.doc', '.docx']
|
||||||
@@ -27,9 +22,6 @@ export default defineEventHandler(async (event) => {
|
|||||||
return erlaubteExtensions.includes(ext)
|
return erlaubteExtensions.includes(ext)
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('Gefilterte Dateien:', gefiltert)
|
|
||||||
console.log('Anzahl:', gefiltert.length)
|
|
||||||
|
|
||||||
return gefiltert
|
return gefiltert
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Lesen der Spielpläne:', error)
|
console.error('Fehler beim Lesen der Spielpläne:', error)
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import { createEvent, mockSuccessReadBody } from './setup'
|
import { createEvent, mockSuccessReadBody } from './setup'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
|
vi.mock('../server/utils/auth.js', () => ({
|
||||||
|
getUserFromToken: vi.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('multer', () => {
|
vi.mock('multer', () => {
|
||||||
const single = vi.fn((field) => (req, _res, cb) => {
|
const single = vi.fn((field) => (req, _res, cb) => {
|
||||||
if (req.__mockMulterError) {
|
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 uploadSpielplanHandler from '../server/api/cms/upload-spielplan-pdf.post.js'
|
||||||
import satzungUploadHandler from '../server/api/cms/satzung-upload.post.js'
|
import satzungUploadHandler from '../server/api/cms/satzung-upload.post.js'
|
||||||
|
|
||||||
|
const { getUserFromToken } = await import('../server/utils/auth.js')
|
||||||
|
|
||||||
describe('CMS File Endpoints', () => {
|
describe('CMS File Endpoints', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.restoreAllMocks()
|
vi.restoreAllMocks()
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
getUserFromToken.mockReset()
|
||||||
|
getUserFromToken.mockResolvedValue({ id: 'admin', role: 'admin' })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('POST /api/cms/save-csv', () => {
|
describe('POST /api/cms/save-csv', () => {
|
||||||
it('validiert Eingaben', async () => {
|
it('validiert Eingaben', async () => {
|
||||||
const event = createEvent()
|
const event = createEvent({ cookies: { auth_token: 'token' } })
|
||||||
mockSuccessReadBody({})
|
mockSuccessReadBody({})
|
||||||
|
|
||||||
await expect(saveCsvHandler(event)).rejects.toMatchObject({ statusCode: 400 })
|
await expect(saveCsvHandler(event)).rejects.toMatchObject({ statusCode: 400 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('speichert erlaubte Datei', async () => {
|
it('speichert erlaubte Datei', async () => {
|
||||||
const event = createEvent()
|
const event = createEvent({ cookies: { auth_token: 'token' } })
|
||||||
mockSuccessReadBody({ filename: 'mannschaften.csv', content: 'data' })
|
mockSuccessReadBody({ filename: 'mannschaften.csv', content: 'data' })
|
||||||
vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
|
vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
|
||||||
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
|
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
|
||||||
@@ -66,14 +74,16 @@ describe('CMS File Endpoints', () => {
|
|||||||
const event = createEvent({ method: 'POST' })
|
const event = createEvent({ method: 'POST' })
|
||||||
event.node.req.__mockFile = { filename: 'file.pdf', originalname: 'orig.pdf', path: 'tmp', mimetype: 'application/pdf' }
|
event.node.req.__mockFile = { filename: 'file.pdf', originalname: 'orig.pdf', path: 'tmp', mimetype: 'application/pdf' }
|
||||||
event.node.req.body = { type: 'gesamt' }
|
event.node.req.body = { type: 'gesamt' }
|
||||||
|
getUserFromToken.mockResolvedValue(null)
|
||||||
|
|
||||||
await expect(uploadSpielplanHandler(event)).rejects.toMatchObject({ statusCode: 401 })
|
await expect(uploadSpielplanHandler(event)).rejects.toMatchObject({ statusCode: 401 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('lädt PDF hoch und gibt Erfolg zurück', async () => {
|
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.__mockFile = { filename: 'spielplan_gesamt.pdf', originalname: 'orig.pdf', path: 'tmp', mimetype: 'application/pdf' }
|
||||||
event.node.req.body = { type: 'gesamt' }
|
event.node.req.body = { type: 'gesamt' }
|
||||||
|
getUserFromToken.mockResolvedValue({ id: 'admin', role: 'admin' })
|
||||||
|
|
||||||
const response = await uploadSpielplanHandler(event)
|
const response = await uploadSpielplanHandler(event)
|
||||||
expect(response.success).toBe(true)
|
expect(response.success).toBe(true)
|
||||||
@@ -83,10 +93,11 @@ describe('CMS File Endpoints', () => {
|
|||||||
|
|
||||||
describe('POST /api/cms/satzung-upload', () => {
|
describe('POST /api/cms/satzung-upload', () => {
|
||||||
it('verarbeitet hochgeladene Satzung', async () => {
|
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' }
|
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, 'readFile').mockResolvedValueOnce(JSON.stringify({ seiten: {}, vorstand: { vorsitzender: { email: '' }, schriftfuehrer: { email: '' } } }))
|
||||||
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
|
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
|
||||||
|
getUserFromToken.mockResolvedValue({ id: 'admin', role: 'admin' })
|
||||||
|
|
||||||
const response = await satzungUploadHandler(event)
|
const response = await satzungUploadHandler(event)
|
||||||
expect(response.success).toBe(true)
|
expect(response.success).toBe(true)
|
||||||
|
|||||||
Reference in New Issue
Block a user