docs(arbeitskacheln): Open-Source-/HiOrg-Vergleich ausgewertet, 14 neue Kacheln ausgearbeitet

Vergleich mit InvenTree, Snipe-IT, Grocy, Shelf.nu, Resgrid, KP Front, FleetMS,
HiOrg-Server ergibt neue Kacheln (ASSET-010 Custody, FILE-008 Hash-Audit-Journal,
FILE-009 Notizen, NOTIF-003, MAINT-007 Tankbuch, ZUST-003 Standort-Scoping,
FOUND-007 Import/Export, FOUND-008 Fristen-Dienst, READY-006 Statistik-Dashboard,
UI2-006..010) sowie Referenz-Ergänzungen bei bestehenden Lücken. Alle Design-
Entscheidungen (Datenmodell, Sicherheitsanforderungen bei ASSET-010) geklärt.

Neues Epic 23 (Flutter-Begleit-App, Sondierungs-Prototyp FLUT-001) inkl. geklärtem
TLS-Blocker (Domain mabea.perlbach-edv.de mit Let's-Encrypt-Zertifikat statt
selbstsigniertem Server-Zertifikat).

Alle Kacheln bleiben offen/ungeplant, nur ausgearbeitet, keine Implementierung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-10 00:49:32 +02:00
co-authored by Claude Sonnet 5
parent 3e39c3b7fd
commit 9c6426190e
21 changed files with 1954 additions and 50 deletions
+144 -1
View File
@@ -1,6 +1,7 @@
# Epic 04 — Assets
Details zu ASSET-001 … ASSET-008 (vollständig).
Details zu ASSET-001 … ASSET-010. ASSET-010 ergänzt am 2026-09-09 aus Open-Source-Vergleich
(Shelf.nu Custody-Feature), noch offen/ungeplant.
---
@@ -146,6 +147,148 @@ Details zu ASSET-001 … ASSET-008 (vollständig).
- **DoD:** mind. ein Set mit 2+ Mitgliedern funktionsfähig. **Lücke gegenüber MABEA:**
Beladungsvorlage ist konzeptionell ähnlich, aber fest ans Objekt gebunden — kein
eigenständiges, wiederverwendbares Set-Konzept.
- **Referenz (Open-Source-Vergleich 2026-09-09):** Shelf.nu (`shelf-nu/shelf.nu`) nennt
dasselbe Konzept „Kits" — Assets zu einer wiederverwendbaren Einheit bündeln
(Laptop+Charger+Dock). Bestätigt den Bedarf, löst aber keine MABEA-spezifische Frage
zusätzlich (Set-Aufbau bleibt wie hier beschrieben).
## ASSET-010 — Custody/Ausgabe an Person (P2, neu, vollständig ausgearbeitet 2026-09-09)
- **Ziel:** Nachvollziehen, welche Person ein bestimmtes Einzelgerät aktuell
persönlich in Verwahrung hat — unabhängig vom Standort-/Objekt-bezogenen
ZUST-001 und von der reinen Kontroll-**Zuständigkeit** in ZUST-002.
- **Abgrenzung zu ZUST-002 (geklärt 2026-09-09):** ZUST-002 sagt „Max soll den
Notfallkoffer #12 kontrollieren" — das Objekt bleibt am Standort, jeder darf es
trotzdem kontrollieren, reine Empfehlung ohne Zugriffssperre. ASSET-010 sagt „Max
hat Funkgerät #7 heute persönlich dabei" — das Gerät wandert physisch mit der
Person, bis sie es zurückgibt; danach kann es an jemand anderen ausgegeben
werden. Unterschiedliche Konzepte (Kontroll-Zuständigkeit vs. physischer Besitz),
echter Bedarf bestätigt — kein Duplikat.
- **Ziel-Entität (geklärt 2026-09-09): `GeraetInstanz`, nicht generisches `Objekt`.**
`GeraetInstanz` hängt an einer `Objektposition` und trägt die eigene
Seriennummer (`backend/app/models/geraet_instanz.py`) — genau das einzelne,
seriennummerpflichtige Gerät (Funkgerät, Atemschutzgerät), das eine Person
persönlich mitführt. Ein ganzes `Objekt` (z.B. ein Fahrzeug oder ein
Materiallager) wird nicht „mitgeführt" — dafür ist Custody nicht gedacht.
- **Beschreibung:** Ausgabe-/Rücknahme-Vorgang je `GeraetInstanz` an eine `Person`
(PERS-001), mit Zeitstempel, Bestätigungsmechanismus (siehe unten) und Verlauf.
- **Benutzerwert:** Bei Verlust/Defekt sofort klar, wer das Gerät zuletzt hatte,
ohne Rückfrage-Runde.
- **Bestätigungsmechanismus bei Ausgabe (überarbeitet 2026-09-09, final 2026-09-09
— Unterschrift OPTIONAL pro Ausgabe, cross-device per QR-Scan):**
- Unterschrift/Protokoll ist **keine Pflicht für jede Ausgabe**, sondern eine
Entscheidung der ausgebenden Stelle im Moment der Ausgabe — Toggle „Mit
Protokoll+Unterschrift" im Ausgabe-Dialog. Ohne Toggle: einfache,
formlose Ausgabe wie ursprünglich geplant (nur Custody-Zeile, kein PDF, kein
QR-Signier-Flow) — schnelle Übergabe für den Alltag. Mit Toggle: voller
Signier-Flow inkl. PDF-Protokoll, gedacht für kritisches/hochwertiges
Material oder wenn die ausgebende Stelle es für nötig hält.
- **Ablauf (nur wenn „Mit Protokoll" gewählt):** Portal (Tablet/Desktop der
ausgebenden Stelle) zeigt nach Auswahl
von Gerät+Empfänger einen QR-Code an, der auf eine einmalig gültige,
kurzlebige Signier-Seite verweist (Token-basiert, kein Login nötig — die
Legitimation ist der physische Besitz des Tablets der ausgebenden Stelle +
der QR-Code selbst). Empfänger scannt den QR-Code mit dem **eigenen** Handy,
öffnet die Signier-Seite, unterschreibt dort per Touch-Canvas auf dem eigenen
Gerät (hygienischer/praktischer als eine geteilte Tablet-Oberfläche), sendet
ab. Das Portal erkennt den Abschluss (Polling auf den Ausgabe-Status) und
zeigt „unterschrieben ✓" an, danach gilt die Ausgabe als abgeschlossen.
- Signier-Token: einmalig verwendbar, kurze Lebensdauer (z.B. 5 Minuten), an
genau einen Ausgabevorgang gebunden — verhindert Wiederverwendung/Weitergabe
des Links.
- Aus Signatur + Geräte-/Personendaten + Zeitstempel wird automatisch ein
**Ausgabeprotokoll** (PDF) generiert und über den DOC-001-Mechanismus am
Custody-Eintrag UND in der Akte des Geräts abgelegt.
- Bei Rücknahme: dieselbe Wahl gilt spiegelbildlich — wurde die Ausgabe mit
Protokoll gemacht, läuft die Rücknahme ebenfalls über QR-Code→eigenes
Handy→Unterschrift→Rückgabeprotokoll. Wurde die Ausgabe formlos gemacht,
ist auch die Rücknahme formlos (kein nachträglicher Zwang zum Protokoll).
- **Sicherheitsanforderungen (Security-Review 2026-09-09, owasp-top10-expert —
verbindlich für die Implementierung, kein Nice-to-have):**
1. **Token:** `secrets.token_urlsafe(32)` (256 Bit), serverseitig nur als
SHA-256-Hash gespeichert (wie ein Passwort-Reset-Token) — Klartext existiert
nur im QR-Code/URL, nie in Logs. Access-Logs dieser Route ohne Query-String
protokollieren.
2. **Route:** Token als Pfad-Parameter (`/signieren/{token}`), nicht Query —
Query-Strings landen häufiger in Logs/Referrer/Browser-History.
`slowapi`-Rate-Limit pro IP UND pro Token (z.B. 10/Minute). Lookup
ausschließlich über Token-Hash, niemals über fortlaufende Custody-ID (IDOR).
3. **Race Condition:** Token-Verwendung als atomares DB-Update, nicht
Read-then-Write — `UPDATE ... WHERE token_hash=$1 AND status='ausstehend'`,
Rowcount prüfen statt separatem Check+Write.
4. **QR-Abfoto-Risiko:** bewusst akzeptiertes Restrisiko, **kein zusätzlicher
PIN-Schritt** (Entscheidung 2026-09-09 — Reibung im Feldeinsatz wiegt
schwerer, Ausgabe findet ohnehin unter Aufsicht der ausgebenden Stelle
statt, die den Vorgang beobachtet). 5-Minuten-Ablauf + Einmalverwendung des
Tokens bleiben als Grundschutz bestehen.
5. **Transport:** HTTPS zwingend (ohnehin nur über nginx-TLS erreichbar).
`VITE_API_BASE_URL` bleibt relativ (`/api/v1`) — Empfänger-Handy nutzt
dieselbe Domain/dasselbe TLS-Zertifikat. Explizit auf echtem Fremdgerät
testen, nicht nur localhost.
6. **Datensparsamkeit (DSGVO):** GET-Endpunkt der Signier-Seite liefert nur
Minimaldaten (Gerätebezeichnung, Empfängername, Zeitfenster) — keine
vollständige Custody-Historie, keine internen IDs. Bei ungültigem/
abgelaufenem/bereits verwendetem Token identische generische Fehlermeldung
(kein Enumerieren des Status). Signatur/PDF nach Abschluss nicht mehr über
denselben Token abrufbar — Download nur im authentifizierten Portal-Bereich.
7. **PDF-Generierung:** falls HTML-basiert (`weasyprint`) — Jinja2-Autoescape
aktiv lassen, kein `|safe` auf Nutzereingaben. Signatur-Bild serverseitig
validieren (Magic-Bytes/Größe/Dimensionen), bevor es ins PDF eingebettet
wird — keine beliebigen Datei-URLs oder SVG mit Skript zulassen.
- **Abhängigkeiten:** ASSET-003, PERS-001, DOC-001 (Ausgabeprotokoll-PDF), FOUND-002
(Signier-Token braucht eigene, kurzlebige Tabelle/Cache)
- **Datenmodell:** `geraet_instanz_custody` (geraet_instanz_id →
`geraet_instanz.id`, person_id, ausgegeben_am, ausgegeben_von_benutzer_id,
mit_protokoll: bool (Toggle-Entscheidung der ausgebenden Stelle),
ausgabeprotokoll_dokument_id nullable FK auf `dokument` (nur bei
`mit_protokoll=true`), zurueckgegeben_am nullable,
zurueckgenommen_von_benutzer_id nullable, rueckgabeprotokoll_dokument_id
nullable) — aktuelle Inhaberschaft = Zeile mit `zurueckgegeben_am IS NULL`.
`signier_token` (token, custody_id, ablauf_am, verwendet: bool) — nur relevant
wenn `mit_protokoll=true`, kurzlebig, kann nach Ablauf/Verwendung gelöscht
werden (kein Langzeit-Speicherbedarf).
- **Backend:** Ausgabe-Endpoint verzweigt nach `mit_protokoll``false`: Custody-
Zeile direkt als abgeschlossen anlegen (formlose Ausgabe, wie ursprünglich
geplant); `true`: Custody-Zeile unsigniert anlegen + Signier-Token erzeugen,
QR-Code-Inhalt zurückgeben, öffentlicher (token-authentifizierter, kein Login)
Signier-Endpoint nimmt Signatur-PNG entgegen, erzeugt PDF, markiert Custody als
abgeschlossen, Status-Polling-Endpoint fürs Portal. In beiden Fällen: verhindert
Doppel-Ausgabe (GeraetInstanz nur an eine Person gleichzeitig, DB-Constraint:
max. eine offene Zeile je `geraet_instanz_id`); bei `mit_protokoll=true`
zusätzlich Token-Ablauf serverseitig erzwungen (abgelaufene Tokens/unsignierte
Ausgaben nach Timeout verwerfen)
- **Frontend:** Ausgabe-Dialog am Gerät — Person auswählen, Toggle „Mit
Protokoll+Unterschrift"; bei aktiviertem Toggle QR-Code anzeigen →
Warten-auf-Signatur-Status, sonst sofortiger Abschluss. Separate mobile
Signier-Seite (eigenständige Route, kein Admin-Layout, funktioniert auf jedem
Handy-Browser ohne Login) nur relevant bei aktiviertem Toggle.
- **Mobile:** Ausgabe-Start per QR-Scan am Gerät selbst (wie gehabt), Signatur
läuft auf dem Empfänger-Handy über die separate Signier-Seite
- **QR-Code:** zwei unterschiedliche QR-Codes im Spiel — (1) Geräte-QR zum
Auswählen des Geräts (bestehender Mechanismus), (2) neuer Signier-Token-QR zum
Öffnen der Signier-Seite auf dem Empfänger-Handy
- **Seriennummer/Inventarnummer:** nutzt bestehende `GeraetInstanz`-Identität
- **Akte:** eigener Custody-Verlauf im Akte-Historienbereich (FILE-005)
- **Rechte:** Ausgabe/Rücknahme: Mitarbeiter+; Korrektur: Materialverantwortlicher+
- **Audit:** jede Ausgabe/Rücknahme geloggt, inkl. Zeitpunkt der Signatur
- **Akzeptanzkriterien:** Gerät kann nicht an zwei Personen gleichzeitig ausgegeben
sein; formlose Ausgabe (`mit_protokoll=false`) ist sofort abgeschlossen, kein
Signier-Flow ausgelöst; Ausgabe mit Protokoll ohne abgeschlossene Signatur gilt
nicht als abgeschlossen (Gerät bleibt bis zur Signatur „in Ausgabe schwebend",
nicht schon fest zugeordnet); abgelaufener/bereits verwendeter Signier-Token
wird abgelehnt; Ausgabeprotokoll-
PDF enthält Geräte-, Personen-, Zeit- und Signaturdaten; Verlauf zeigt lückenlose
Kette.
- **Tests:** Doppel-Ausgabe-Ablehnung, abgelaufener-Token-Ablehnungstest, bereits-
verwendeter-Token-Ablehnungstest, PDF-Generierungstest, Ausgabe→Signatur→
Rücknahme→Signatur→Neuausgabe-Zyklus, Race-Condition-Test (paralleler Request
auf denselben Token, nur einer darf gewinnen), Rate-Limit-Test, IDOR-Test
(Token-Lookup nur über Hash, keine ID-Erratbarkeit), Signatur-Bild-Validierungs-
test (ungültige/manipulierte Datei wird abgelehnt).
- **DoD:** offen — Design vollständig geklärt (2026-09-09), bereit für
Implementierung. **Referenz (Open-Source-Vergleich 2026-09-09):** Shelf.nu
(`shelf-nu/shelf.nu`, AGPL-3.0 — nur Konzeptvorbild, keine Codeübernahme wegen
Copyleft) — „Custody"-Feature, Kernaussage „know who has what at all times".
## ASSET-008 — Consumable