Fundament: Baseline-Migration mit RLS-Funktion

- 67 Certvia-Migrationen zu prisma/migrations/0001_baseline gesquasht
  (prisma migrate diff --from-empty --to-schema)
- RLS-Block: Rolle craftvia_app (NOLOGIN NOBYPASSRLS, idempotent), GRANTs +
  ALTER DEFAULT PRIVILEGES, wiederverwendbare Funktion enable_tenant_rls(tbl)
  (ENABLE + Policy tenant_isolation USING/WITH CHECK + FORCE + GRANT),
  angewendet auf alle Tenant-Tabellen (inkl. nullable tenant_id wie bisher)
- docs/craftvia/MIGRATIONS.md: Pflichten für neue Tenant-Tabellen
  (enable_tenant_rls + beide TENANT_MODELS-Listen + PII-Felder)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:40:34 +02:00
co-authored by Claude Opus 5
parent 9ec7fa5356
commit 6f3f5a5947
68 changed files with 573 additions and 3927 deletions
+71
View File
@@ -0,0 +1,71 @@
# Craftvia — Migrationen & Row Level Security
## Ausgangslage
Die Certvia-Historie (67 Migrationen) wurde beim Rückbau zu **einer** Baseline gesquasht:
- `prisma/migrations/0001_baseline/migration.sql`
- DDL aus `npx prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script`
- angehängter RLS-Block (Rolle `craftvia_app`, GRANTs, Funktion `enable_tenant_rls`)
Bestehende Datenbanken aus der Certvia-Zeit sind **nicht** migrierbar — lokal neu aufsetzen:
```bash
docker compose exec postgres psql -U craftvia -d postgres -c 'DROP DATABASE IF EXISTS craftvia WITH (FORCE);' -c 'CREATE DATABASE craftvia;'
npx prisma migrate deploy
npx prisma db seed
```
## Mandantentrennung — drei Stellen, die immer zusammen gepflegt werden
Jede neue Tabelle mit `tenant_id` (z. B. `customers`, `work_orders`) braucht:
1. **Migration**: nach dem `CREATE TABLE` im selben Migrationsfile
```sql
SELECT enable_tenant_rls('customers');
```
Die Funktion ist idempotent und setzt `ENABLE ROW LEVEL SECURITY`, die Policy
`tenant_isolation` (`USING` **und** `WITH CHECK` auf
`tenant_id = current_setting('app.tenant_id', true)`), `FORCE ROW LEVEL SECURITY`
und die GRANTs für `craftvia_app`.
2. **`TENANT_MODELS` in `src/server/db.ts`** — aktiviert den Tenant-Guard
(`dbForTenant` filtert/injiziert `tenantId`, prüft Ownership bei Mutationen).
3. **`TENANT_MODELS` in `src/server/backup/topology.ts`** — Backup/Restore/DSGVO-Export.
Die Liste ist bewusst dupliziert; `scripts/test-backup-isolation.ts` bzw.
`scripts/test-rls-enforcement.ts` prüfen Synchronität und RLS-Abdeckung gegen die DB.
Zusätzlich: Felder, die eine `User.id` referenzieren (Zuweisung, Freigabe, Ersteller),
in `src/server/dsgvo/pii-fields.ts` eintragen.
Globale Tabellen (Kataloge ohne `tenant_id`, z. B. künftige Materialstammdaten des
Plattformbetreibers) bekommen **keine** Policy und stehen in keiner TENANT_MODELS-Liste.
## Nullable `tenant_id`
`audit_logs`, `mail_logs` und `auth_tokens` tragen eine nullable `tenant_id`
(Plattform-Ereignisse). Die Policy ist identisch; Zeilen mit `NULL` sind für
`craftvia_app` unsichtbar und werden ausschließlich über den Owner-Client geschrieben.
## Neue Migration anlegen
```bash
# Schema ändern, dann:
npx prisma migrate dev --name <kurzname> --create-only
# migration.sql prüfen und ggf. `SELECT enable_tenant_rls('<tabelle>');` anhängen
npx prisma migrate dev
```
## RLS scharf testen (lokal)
```bash
docker compose exec postgres psql -U craftvia -d craftvia \
-c "ALTER ROLE craftvia_app WITH LOGIN PASSWORD 'craftvia_app_local';"
npx tsx scripts/test-rls-enforcement.ts
```
Für den Betrieb mit scharfer RLS: `RLS_ENFORCED=true` und
`RLS_DATABASE_URL=postgresql://craftvia_app:<pw>@<host>:5432/craftvia?schema=public`.