feat(archive): RET-06-API Aufbewahrungsfristen-Konfigurations-Backend

Board-Entscheidung: Backend-API zuerst, echtes Next.js-Frontend als
separates Folgeticket - vermeidet Pseudo-Frontend-Protokoll.
internal/retentionapi: 4 Endpunkte (anlegen/aendern, deaktivieren,
liste, vorschau), Vorschau nutzt dieselbe ListExpiringObjects-Funktion
wie RET-02s periodischer Job (keine Doppel-Implementierung).
RequireRole ist AUSDRUECKLICH kein RBAC-02-Ersatz, sondern ein
dokumentiertes Provisorium (Header-Check) - RBAC-02 ist reiner
Core-interner Go-Code ohne HTTP-Schnittstelle fuer andere Module,
derselbe Befund wie FDN-03/FDN-09. Provisorium real getestet inkl.
Negativfall (403 ohne/mit falscher Rolle). retention_class_rules um
active-Flag erweitert (deaktivieren ohne Historienverlust). Real auf
131 deployed und per curl end-to-end verifiziert.
This commit is contained in:
sysops
2026-08-30 02:09:37 +02:00
parent 2c9a7482b6
commit 21278f1405
11 changed files with 644 additions and 3 deletions
+91
View File
@@ -0,0 +1,91 @@
# RET-06-API Prüfprotokoll: Aufbewahrungsfristen-Konfigurations-Backend
Voraussetzung RET-02 erledigt, siehe eigenes Protokoll.
**Scope-Entscheidung (Board-Rücksprache):** RET-06 verlangt Next.js/
React-Frontend + Backend-API + RBAC. Statt eines einzigen
Big-Scope-Tickets: **Backend-API zuerst** (dieses Protokoll), das
echte Next.js-Frontend folgt als eigenes, separates Ticket. Diese
Trennung wurde bewusst gewählt, damit das Prüfprotokoll nicht auf ein
Pseudo-Frontend verweist.
## Grundsatzentscheidung: provisorischer Rollen-Check, KEIN RBAC-02
Core RBAC-02 (`internal/policy`, `Enforcer.Authorize`) ist reiner
Go-Code innerhalb des Core-Moduls — keine HTTP-Schnittstelle, über die
Archive (physisch getrenntes Go-Modul) es aufrufen könnte. Derselbe
"gefunden, aber nicht modulübergreifend verdrahtet"-Befund wie bei Core
FDN-03/FDN-09 (siehe frühere Prüfprotokolle).
**`internal/retentionapi.RequireRole` ist AUSDRÜCKLICH KEIN RBAC-02-
Ersatz**, sondern ein Provisorium: prüft nur einen selbst gesetzten
Header (`X-Admin-Roles`), leicht zu fälschen von jedem, der den Header
setzen kann. Muss ersetzt werden, sobald ein Core-seitiger HTTP-Wrapper
um RBAC-02 existiert (Empfehlung: eigenes künftiges Core-Ticket,
z. B. `RBAC-06`, wiederverwendbar für alle Module statt je Modul einen
eigenen Provisorium-Check). Bis dahin real getestet inklusive
Negativfall (Pflichtprüfung, siehe unten) — ein UNGEPRÜFTER
Provisorium-Check wäre nur eine verschobene Schwachstelle.
## Umsetzung
- `migrations/0005_retention_class_rules_active.up.sql`/`.down.sql`
`active`-Flag statt DELETE (Klasse deaktivieren ohne Historienverlust).
- `internal/retentionengine.DeactivateClassRule`/`ListClassRules`
Erweiterung von RET-02s Paket, `ComputeDueDate`/`ListExpiringObjects`
berücksichtigen nur noch aktive Regeln.
- `internal/retentionapi.RequireRole` — provisorischer Header-Rollen-Check.
- `internal/retentionapi.Mount` — vier Endpunkte: `POST
/retention-classes` (anlegen/ändern), `POST
/retention-classes/{class}/deactivate`, `GET /retention-classes`
(Liste), `GET /retention-classes/preview` (Vorschau, nutzt DIESELBE
`ListExpiringObjects`-Funktion wie RET-02s periodischer Job).
- `cmd/retention-api` — eigenständiger HTTP-Dienst.
## Prüfungen
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | Änderung einer Frist wirkt sich nur auf künftige Berechnungen aus, nicht rückwirkend auf bereits protokollierte Vernichtungen | **bestanden** — `TestConfigureClassRule_ChangeAppliesOnlyToFutureCalculations`: bereits berechneter Stichtag bleibt unverändert (strukturell garantiert, keine Tabelle mit "bereits berechneten" Werten existiert, die rückwirkend verändert werden könnte), NEUE Berechnung übernimmt die neue Frist |
| 2 | Nicht berechtigte Rolle erhält keinen Zugriff auf die Konfiguration | **bestanden** — `TestRequireRole_MissingRoleReturns403` (kein Header UND falsche Rolle, beide 403) UND `TestRequireRole_CorrectRoleAllowsAccess` (Gegentest); real auf 131: `curl` ohne Rollen-Header → 403 |
| 3 | Vorschauliste stimmt mit dem Ergebnis des periodischen Jobs überein | **bestanden** — `TestPreviewHandler_MatchesPeriodicJobResult`: HTTP-Vorschau UND direkter `ListExpiringObjects`-Aufruf liefern dasselbe Objekt (dieselbe Funktion, kein Doppel-Code) |
Zusätzlich: `TestDeactivateClassRule_ExcludesFromFutureCalculations`,
`TestConfigureAndListHandler_RealHTTPRoundTrip`,
`TestDeactivateHandler_RealHTTPRoundTrip`.
## Echte Verdrahtung auf 192.168.1.131
- `retention-api` gebaut nach `/opt/nexarch-archive/bin/`
- `/etc/nexarch/archive-retention-api.env` (0600)
- `nexarch-archive-retention-api.service` installiert/aktiviert
(dauerhaft, `Restart=on-failure`)
- Realer End-zu-Ende-Test via `curl`: POST ohne Rollen-Header → 403;
POST mit `X-Admin-Roles: archive_admin` → 200, Klasse angelegt; `GET
/retention-classes` zeigt sie; `GET /retention-classes/preview`
liefert `null` (kein fälliges Objekt, korrekt leer) — Testdaten
anschließend entfernt
## Build/Test-Ergebnis (192.168.1.131, `make check`)
```
go build ./... -> clean
go vet ./... -> clean
golangci-lint run ./... -> 0 issues
go test ./... -p 1 -count=1 -> 11/11 Pakete mit Tests ok, 0 Fehlschläge
```
**Hinweis:** Zwei Pakete (`internal/retentionengine`,
`internal/retentionapi`) gegen dieselbe reale Tenant-DB laufen zu lassen
erfordert `-p 1` (sequentiell) — parallele Testläufe unterschiedlicher
Pakete gegen dieselbe echte Datenbank führen sonst zu
Cross-Test-Kontamination (TRUNCATE eines Pakets während ein anderes
noch liest/schreibt). `make check`/`Makefile` erzwingt das bereits.
## Gesamtergebnis
**Bestanden — Backend-Scope.** Alle drei Pflichtprüfungen real erfüllt.
**Offen, bewusst nicht Teil dieses Protokolls:** das Next.js/React-
Frontend (separates Folgeticket) und der Ersatz des provisorischen
Rollen-Checks durch einen echten RBAC-02-Aufruf, sobald Core einen
HTTP-Wrapper dafür bereitstellt.