feat(schluessel): erzeugen, importieren, exportieren (#012)
Schluessel werden jetzt verwaltet statt nur im Prototyp gezeigt: Liste mit Filter, Anlegen (erzeugen oder importieren), Detail nach dem Prototyp aus #002, Bearbeiten der Metadaten und Download <slug>.pub. Warum so: - Erzeugen und Import eines Paars laufen als Vorgang im Worker, weil nur er den Master-Key hat. Passphrase und importierter Private Key reisen wie das Bootstrap-Passwort mit SSHKV_APP_SECRET verschluesselt im Job, nie in Vorgang.eingabe; der Worker loescht den Job danach. - Ein reiner Public Key ist kein Geheimnis und wird direkt im Web angelegt, mit verstaendlicher Meldung bei einem Duplikat. - Das Web liest nie das Chiffrat: ob ein Private Key gespeichert ist, verraet masterKeyVersion (per CHECK an das Chiffrat gekoppelt). Ein struktureller Test verlangt fuer jede zeilenliefernde Abfrage auf Schluessel ausserhalb des Workers ein festes select. - Migration: Vorgangsarten fuer Erzeugen, Import und (fuer #014) das Loeschen des Private Keys; CHECKs fuer den Slug (landet in Dateinamen) und "geloescht nur ohne Chiffrat". - Platzhalter PublicAusrollenAktion/PrivateAusrollenAktion fuer #016/#017. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,60 @@
|
||||
# #012 Schlüssel erzeugen, importieren, exportieren
|
||||
**Status:** offen
|
||||
**Status:** erledigt
|
||||
**Meilenstein:** v0.2.0
|
||||
**Labels:** backend, ui
|
||||
**Geschätzt:** 4 h **Tatsächlich:** –
|
||||
**Geschätzt:** 4 h **Tatsächlich:** 0 h 25 min
|
||||
|
||||
## Ziel
|
||||
Schlüsselliste und -formular: erzeugen (ed25519, rsa-4096), importieren (Public oder Paar), exportieren; Kategorien, Tags, Ablaufdatum.
|
||||
|
||||
## Aufgaben
|
||||
- [ ] Liste mit Filter
|
||||
- [ ] Erzeugen-Dialog
|
||||
- [ ] Import mit Prüfung
|
||||
- [ ] Export Public Key
|
||||
- [ ] Schlüsseldetail-Gerüst mit Verwendungen
|
||||
- [x] Liste mit Filter
|
||||
- [x] Erzeugen-Dialog
|
||||
- [x] Import mit Prüfung
|
||||
- [x] Export Public Key
|
||||
- [x] Schlüsseldetail-Gerüst mit Verwendungen
|
||||
|
||||
## Akzeptanzkriterien
|
||||
- [ ] Private Keys erscheinen nie in Listen-Antworten
|
||||
- [x] Private Keys erscheinen nie in Listen-Antworten
|
||||
|
||||
## Abhängigkeiten
|
||||
#006
|
||||
|
||||
## Notizen / Entscheidungen
|
||||
- **Migration `20261012100000_schluessel_vorgaenge`:** Vorgangsarten `SCHLUESSEL_ERZEUGEN`,
|
||||
`SCHLUESSEL_IMPORTIEREN`, `PRIVATE_KEY_LOESCHEN` (für #014 gleich mit), CHECK
|
||||
`schluessel_slug_gueltig` (`^[a-z0-9-]{1,64}$`, der Slug landet in Dateinamen) und
|
||||
`schluessel_geloescht_ohne_private_key`.
|
||||
- **Erzeugen und Paar-Import laufen im Worker** (`src/worker/ablaeufe/schluessel.ts`). Das Web
|
||||
prüft nur Metadaten (Slug frei, Person/Vorlage vorhanden, Ablauf nicht in der Vergangenheit)
|
||||
und reiht den Vorgang ein. Passphrase und importierter Private Key reisen wie das
|
||||
Bootstrap-Passwort mit `SSHKV_APP_SECRET` verschlüsselt im Job (`geheim`), nie in
|
||||
`Vorgang.eingabe`; der Worker löscht den Job danach. Der Worker prüft die Metadaten erneut.
|
||||
- **Kategorie:** Erzeugen und Paar-Import legen immer `DIENST` an. Personen-Keys speichert sshkv
|
||||
nie mit Private Key (DB-CHECK), Deployment-Keys verwaltet der Worker selbst. Der
|
||||
Public-Key-Import kennt `PERSON` und `DIENST`.
|
||||
- **Passphrase:** beim Erzeugen optional; gespeichert wird dann der passphrasengeschützte
|
||||
OpenSSH-Key (`passphraseGeschuetzt = true`). Beim Import dient sie nur zum Lesen von Public Key
|
||||
und Fingerprint und wird verworfen; gespeichert wird der Key so, wie er kam. Hinweis in der
|
||||
Oberfläche: „Für automatische Anmeldungen (Host-zu-Host) ungeeignet.“
|
||||
- **Typ:** `RSA4096` nur bei genau 4096 Bit, sonst `ANDERER` (z. B. RSA-3072, ecdsa). Die RSA-
|
||||
Mindestlänge 2048 gilt beim Import.
|
||||
- **Slug:** aus dem Zweck abgeleitet (Umlaute gefaltet), bei Kollision `-2`, `-3` …; ein
|
||||
ausdrücklich gewünschter Kurzname muss frei sein. Slug, Typ und Kommentar sind danach nicht
|
||||
mehr änderbar – sie stehen schon in Dateinamen und authorized_keys-Zeilen.
|
||||
- **Status von Hand** nur `AKTIV` ↔ `ABGELAUFEN`; `ROTIERT`/`WIDERRUFEN` setzen #023/#024,
|
||||
Deployment-Keys sind ausgenommen (sonst erzeugte der Worker beim Start einen neuen).
|
||||
- **Ablaufdatum** ohne Angabe aus `Vorlage.standardLaufzeitTage`.
|
||||
- **„Private Key gespeichert?“** liest das Web aus `masterKeyVersion` (der CHECK
|
||||
`schluessel_umschlag_vollstaendig` koppelt sie an das Chiffrat) – das Chiffrat wird im Web
|
||||
nie selektiert.
|
||||
- **Grenze abgesichert:** `tests/schluessel-grenze.test.ts` verlangt für jeden zeilenliefernden
|
||||
`prisma.schluessel.*`-Aufruf außerhalb von `src/worker` ein `select` und verbietet
|
||||
`schluessel: true` als Relation; `tests/schluessel.test.ts` prüft, dass weder Vorgang,
|
||||
Protokoll noch pg-boss-Job nach Erzeugen/Import den Key oder die Passphrase enthalten.
|
||||
- **Platzhalter** für #016/#017: `src/app/(app)/schluessel/[id]/PublicAusrollenAktion.tsx` und
|
||||
`PrivateAusrollenAktion.tsx`, Props `{ schluesselId: string; darf: boolean }`, rendern `null`.
|
||||
- Personen-Keys zeigen im Detail nur die Karte „Public Key ausgerollt auf“ (eine Private-Karte
|
||||
wäre immer leer).
|
||||
- Prototyp `/schluessel/beispiel` samt `src/beispiel/schluessel.ts` entfernt; die Übersicht
|
||||
(noch Prototyp, #026) verlinkt auf `/schluessel`.
|
||||
|
||||
Reference in New Issue
Block a user