From c6401aeb4de8be52e1476d7a1c9a26f75ce09b67 Mon Sep 17 00:00:00 2001 From: Harlekin Date: Tue, 1 Sep 2026 18:50:07 +0200 Subject: [PATCH] Rollenstruktur und englische Fachbegriffe festgelegt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Discord-Server hat drei Sorten Rollen, die Dokumente kannten nur eine: Zugehörigkeit (AfiS, Personalabteilung) als Türsteher, Rangrollen für die Rechte, Zugangsrollen für die Freigabestufe. Das bestehende RoleMapping trägt das fast, es fehlt nur der Türsteher. Die beiden Felder dafür kommen mit Phase 1, die Struktur ist in docs/13-entscheidungen.md festgehalten. Die Freigabestufen heißen jetzt wie die Zugangsrollen: Unclassified, Restricted Access, Confidential Access, Sensitive Access, Critical Operations, Executive Oversight. Fachbegriffe der Domäne bleiben englisch, auch in einer deutschen Oberfläche. Die Bezeichnungen sind damit Daten in ClearanceLevelConfig, nicht mehr Oberflächentext in lib/i18n/de.ts: sie sind im Adminbereich änderbar, und was der Leiter ändern kann, gehört nicht in eine Übersetzungsdatei. StampMark bekommt den Namen hereingereicht. Vier neue Tests prüfen den Zustand, den der Seed herstellt, gegen die Datenbank statt gegen eine Konstante im Code. --- packages/db/prisma/seed.ts | 22 ++++---- .../db/tests/auslieferungszustand.test.ts | 52 +++++++++++++++++++ web/components/StampMark.tsx | 9 ++-- web/lib/i18n/de.ts | 11 ++-- web/tests/i18n.test.ts | 9 ++-- 5 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 packages/db/tests/auslieferungszustand.test.ts diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts index 50836a9..ea1f476 100644 --- a/packages/db/prisma/seed.ts +++ b/packages/db/prisma/seed.ts @@ -11,17 +11,21 @@ import { createPrismaClient } from '../src/index.js' const db = createPrismaClient() /** - * Auslieferungszustand nach docs/01-produkt.md. Ab Stufe 3 ist die Existenz - * einer Akte für niedrigere Stufen verborgen, darunter nur der Inhalt gesperrt. - * Der Leiter kann das im Adminbereich ändern. + * Auslieferungszustand. Ab Stufe 3 ist die Existenz einer Akte für niedrigere + * Stufen verborgen, darunter ist nur der Inhalt gesperrt. Der Leiter kann das + * im Adminbereich ändern. + * + * Die Namen entsprechen den Zugangsrollen auf dem Discord und bleiben deshalb + * englisch, siehe docs/13-entscheidungen.md. Stufe 0 hat keine eigene + * Zugangsrolle: sie gilt für jedes AfiS-Mitglied ohne eine solche Rolle. */ const CLEARANCE_LEVELS = [ - { level: 0, name: 'Offen', hiddenBelow: false, colorToken: 'stamp-0' }, - { level: 1, name: 'Intern', hiddenBelow: false, colorToken: 'stamp-1' }, - { level: 2, name: 'Vertraulich', hiddenBelow: false, colorToken: 'stamp-2' }, - { level: 3, name: 'Geheim', hiddenBelow: true, colorToken: 'stamp-3' }, - { level: 4, name: 'Streng geheim', hiddenBelow: true, colorToken: 'stamp-4' }, - { level: 5, name: 'Leitungssache', hiddenBelow: true, colorToken: 'stamp-5' }, + { level: 0, name: 'Unclassified', hiddenBelow: false, colorToken: 'stamp-0' }, + { level: 1, name: 'Restricted Access', hiddenBelow: false, colorToken: 'stamp-1' }, + { level: 2, name: 'Confidential Access', hiddenBelow: false, colorToken: 'stamp-2' }, + { level: 3, name: 'Sensitive Access', hiddenBelow: true, colorToken: 'stamp-3' }, + { level: 4, name: 'Critical Operations', hiddenBelow: true, colorToken: 'stamp-4' }, + { level: 5, name: 'Executive Oversight', hiddenBelow: true, colorToken: 'stamp-5' }, ] async function main(): Promise { diff --git a/packages/db/tests/auslieferungszustand.test.ts b/packages/db/tests/auslieferungszustand.test.ts new file mode 100644 index 0000000..9738c57 --- /dev/null +++ b/packages/db/tests/auslieferungszustand.test.ts @@ -0,0 +1,52 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { createPrismaClient, type PrismaClient } from '../src/index.js' + +/** + * Prüft den Zustand, den `pnpm db:seed` herstellt. Das ist kein Testinhalt, + * sondern Konfiguration, ohne die die Anwendung nicht sinnvoll läuft. + */ +const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL +const suite = url ? describe : describe.skip + +let db: PrismaClient + +suite('Auslieferungszustand', () => { + beforeAll(() => { + db = createPrismaClient(url) + }) + + afterAll(async () => { + await db.$disconnect() + }) + + it('kennt sechs Freigabestufen, benannt nach den Zugangsrollen', async () => { + const levels = await db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }) + expect(levels.map((l) => l.level)).toEqual([0, 1, 2, 3, 4, 5]) + expect(levels.map((l) => l.name)).toEqual([ + 'Unclassified', + 'Restricted Access', + 'Confidential Access', + 'Sensitive Access', + 'Critical Operations', + 'Executive Oversight', + ]) + }) + + it('verbirgt die Existenz ab Stufe 3, darunter nicht', async () => { + const levels = await db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }) + expect(levels.map((l) => l.hiddenBelow)).toEqual([false, false, false, true, true, true]) + }) + + it('liefert die Änderungsrichtlinie mit Karenzzeit und ohne Markierung aus', async () => { + const setting = await db.appSetting.findUnique({ where: { id: 1 } }) + expect(setting?.entryEditPolicy).toBe('GRACE_WINDOW') + expect(setting?.entryEditGraceMinutes).toBe(15) + expect(setting?.entryEditMarkGrace).toBe(false) + }) + + it('ordnet im Auslieferungszustand keiner Rolle Rechte zu', async () => { + // entry.edit_own, entry.edit_any und entry.retract schaltet der Leiter + // bewusst frei. Siehe docs/03-auth-und-rechte.md. + expect(await db.roleMapping.count()).toBe(0) + }) +}) diff --git a/web/components/StampMark.tsx b/web/components/StampMark.tsx index 3fc76f9..8ec4161 100644 --- a/web/components/StampMark.tsx +++ b/web/components/StampMark.tsx @@ -1,5 +1,3 @@ -import { de } from '@/lib/i18n/de' - const STAMP_COLOR: Record = { 0: 'var(--color-stamp-0)', 1: 'var(--color-stamp-1)', @@ -17,8 +15,11 @@ export function stampColor(level: number): string { * Stempelmarke oben rechts am Objekt. Der Stempelcharakter kommt aus dem * schmalen Schnitt, der Rahmenlinie und der Farbe, nicht aus Grossbuchstaben. * Die Stufe steht zusaetzlich als Text, nicht nur als Farbe. + * + * `name` kommt aus ClearanceLevelConfig und ist im Adminbereich aenderbar, + * deshalb wird er hereingereicht statt aus lib/i18n/de.ts gelesen. */ -export function StampMark({ level }: { level: number }) { +export function StampMark({ level, name }: { level: number; name: string }) { return ( {level} - {de.clearance.names[level] ?? de.common.unknown} + {name} ) } diff --git a/web/lib/i18n/de.ts b/web/lib/i18n/de.ts index 3804fed..d0c864e 100644 --- a/web/lib/i18n/de.ts +++ b/web/lib/i18n/de.ts @@ -47,14 +47,9 @@ export const de = { label: 'Freigabestufe', classification: 'Klassifizierung', level: (n: number) => `Stufe ${n}`, - names: { - 0: 'Offen', - 1: 'Intern', - 2: 'Vertraulich', - 3: 'Geheim', - 4: 'Streng geheim', - 5: 'Leitungssache', - } as Record, + // Die Bezeichnungen der Stufen stehen nicht hier, sondern in + // ClearanceLevelConfig: sie sind im Adminbereich änderbar und folgen den + // Zugangsrollen des Discord-Servers. Siehe docs/13-entscheidungen.md. locked: 'Inhalt gesperrt', lockedHint: (n: number) => `Sichtbar ab Stufe ${n}`, redacted: 'Geschwärzt', diff --git a/web/tests/i18n.test.ts b/web/tests/i18n.test.ts index 0283390..439ea87 100644 --- a/web/tests/i18n.test.ts +++ b/web/tests/i18n.test.ts @@ -4,11 +4,10 @@ import { join } from 'node:path' import { de } from '@/lib/i18n/de' describe('Sprachkonvention', () => { - it('kennt alle sechs Freigabestufen', () => { - for (let level = 0; level <= 5; level++) { - expect(de.clearance.names[level]).toBeTypeOf('string') - } - expect(de.clearance.names[3]).toBe('Geheim') + it('haelt die Stufenbezeichnungen aus den Texten heraus', () => { + // Sie stehen in ClearanceLevelConfig, sind im Adminbereich aenderbar und + // folgen den Zugangsrollen des Discord-Servers. + expect(de.clearance).not.toHaveProperty('names') }) it('verzichtet auf Ausrufezeichen und Entschuldigungen', () => {