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.
92 lines
4.9 KiB
Markdown
92 lines
4.9 KiB
Markdown
# 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.
|