Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
153 lines
5.1 KiB
TypeScript
153 lines
5.1 KiB
TypeScript
// @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<Permission>(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<string[]> {
|
|
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()
|
|
})
|
|
})
|