// @vitest-environment node import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { db } from '@/lib/db' import { visibleAttachmentWhere } from '@/lib/access/attachmentFilter' import { loadDeliverable } from '@/lib/files/service' import type { EffectivePermissions, Permission } from '@/lib/auth/permissions' /** * Ein Anhang hat keine eigene Freigabestufe, er erbt die seines Besitzers. * Diese Tests prüfen beide Seiten davon: dass die Stufe wirklich durchschlägt, * und dass eine Datei, die die Prüfkette nicht bestanden hat, gar nicht erst * ausgeliefert wird - auch nicht an einen Administrator. */ const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL const suite = url ? describe : describe.skip const marke = `anhang-${Date.now()}` function perms( userId: string, clearanceLevel: number, options: { isAdmin?: boolean; permissions?: Permission[] } = {}, ): EffectivePermissions { return { userId, clearanceLevel, permissions: new Set(options.permissions ?? []), isAdmin: options.isAdmin ?? false, hasMembership: true, } } suite('Sichtbarkeit von Anhängen', () => { const nutzer = { stufe1: `${marke}-stufe1`, stufe4: `${marke}-stufe4` } const anhang = { offen: '', geheim: '', imEintrag: '', inQuarantaene: '' } beforeAll(async () => { const vorlage = await db.documentTemplate.create({ data: { key: `${marke}-vorlage`, version: 1, name: 'Prüfvorlage', fields: [] }, }) for (const id of Object.values(nutzer)) { await db.user.create({ data: { id, username: id, displayName: id } }) } const gemeinsam = { templateId: vorlage.id, templateKey: vorlage.key, createdById: nutzer.stufe4, } const offen = await db.case.create({ data: { ...gemeinsam, caseNumber: `${marke}-o`.slice(0, 40), title: 'Offen', classification: 1, status: 'OPEN', }, }) const geheim = await db.case.create({ data: { ...gemeinsam, caseNumber: `${marke}-g`.slice(0, 40), title: 'Geheim', classification: 4, status: 'OPEN', }, }) const eintrag = await db.caseEntry.create({ data: { id: `${marke}-eintrag`, caseId: offen.id, type: 'NOTE', body: 'Nur ab Stufe 4', authorId: nutzer.stufe4, minClearance: 4, }, }) const anlegen = async ( name: string, besitzer: { caseId?: string; entryId?: string }, scanStatus: 'CLEAN' | 'PENDING' = 'CLEAN', ) => ( await db.attachment.create({ data: { originalName: `${name}.png`, mimeType: 'image/png', sizeBytes: 100, sha256: `${marke}-${name}`, storageKey: `store/aa/bb/${marke}-${name}`, uploadedById: nutzer.stufe4, scanStatus, ...besitzer, }, select: { id: true }, }) ).id anhang.offen = await anlegen('offen', { caseId: offen.id }) anhang.geheim = await anlegen('geheim', { caseId: geheim.id }) anhang.imEintrag = await anlegen('eintrag', { entryId: eintrag.id }) anhang.inQuarantaene = await anlegen('quarantaene', { caseId: offen.id }, 'PENDING') }) afterAll(async () => { await db.$disconnect() }) async function sichtbar(p: EffectivePermissions): Promise { const rows = await db.attachment.findMany({ where: { AND: [visibleAttachmentWhere(p), { id: { in: Object.values(anhang) } }] }, select: { id: true }, }) return rows.map((r) => r.id).sort() } it('verbirgt den Anhang einer zu hoch eingestuften Akte, auch in der Zählung', async () => { const p = perms(nutzer.stufe1, 1) const treffer = await sichtbar(p) expect(treffer).toContain(anhang.offen) expect(treffer).not.toContain(anhang.geheim) const anzahl = await db.attachment.count({ where: { AND: [visibleAttachmentWhere(p), { id: { in: Object.values(anhang) } }] }, }) expect(anzahl).toBe(treffer.length) }) it('verbirgt den Anhang eines Eintrags über der Stufe des Lesers', async () => { // Die Akte darf er sehen, den Eintrag nicht - und damit auch dessen Datei // nicht. Sonst verriete die Anhangsliste den Inhalt des Eintrags. expect(await sichtbar(perms(nutzer.stufe1, 1))).not.toContain(anhang.imEintrag) expect(await sichtbar(perms(nutzer.stufe4, 4))).toContain(anhang.imEintrag) }) it('liefert nichts aus, was die Prüfkette nicht bestanden hat', async () => { // Auch nicht dem Administrator: die Datei liegt in der Quarantäne, dort // führt kein Auslieferungspfad hin. expect(await loadDeliverable(perms(nutzer.stufe4, 5, { isAdmin: true }), anhang.inQuarantaene)) .toBeNull() expect(await loadDeliverable(perms(nutzer.stufe4, 4), anhang.offen)).not.toBeNull() }) it('liefert einem zu niedrig eingestuften Leser nichts, sondern nichts Findbares', async () => { // `null` und nicht "verboten": der Endpunkt macht daraus 404, die Existenz // der Datei bleibt verborgen. expect(await loadDeliverable(perms(nutzer.stufe1, 1), anhang.geheim)).toBeNull() }) })