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:
@@ -126,13 +126,16 @@ export type NeuerSchluessel = {
|
||||
|
||||
/**
|
||||
* Erzeugt ein Schluesselpaar und versiegelt den Private Key sofort. Die ID wird hier
|
||||
* vergeben, weil sie in die Zusatzdaten des Umschlags eingeht.
|
||||
* vergeben, weil sie in die Zusatzdaten des Umschlags eingeht. Mit `passphrase` liegt im
|
||||
* Umschlag der bereits passphrasengeschuetzte OpenSSH-Key; die Passphrase selbst wird nie
|
||||
* gespeichert (Freigabe Frage 6, #012).
|
||||
*/
|
||||
export async function schluesselpaarVersiegeltErzeugen(
|
||||
typ: ErzeugbarerSchluesseltyp,
|
||||
kommentar: string,
|
||||
passphrase?: string,
|
||||
): Promise<NeuerSchluessel> {
|
||||
const paar = await schluesselpaarErzeugen(typ, { kommentar });
|
||||
const paar = await schluesselpaarErzeugen(typ, { kommentar, ...(passphrase ? { passphrase } : {}) });
|
||||
const id = randomUUID();
|
||||
return {
|
||||
id,
|
||||
@@ -142,6 +145,35 @@ export async function schluesselpaarVersiegeltErzeugen(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Versiegelt einen importierten Private Key (#012) so, wie er kam – ein
|
||||
* passphrasengeschuetzter Key bleibt es. Die ID wird hier vergeben (Zusatzdaten).
|
||||
*/
|
||||
export async function importiertenKeyVersiegeln(
|
||||
fingerprint: string,
|
||||
privateKeyOpenssh: string,
|
||||
): Promise<{ readonly id: string; readonly versiegelt: VersiegelterKey }> {
|
||||
const id = randomUUID();
|
||||
return { id, versiegelt: await privateKeyVersiegeln(id, fingerprint, privateKeyOpenssh) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Verwirft den gespeicherten Private Key endgueltig (#014): Chiffrat, Data-Key und Version
|
||||
* auf null, Zeitpunkt festhalten. Liefert false, wenn keiner (mehr) gespeichert war.
|
||||
*/
|
||||
export async function privateKeyVerwerfen(schluesselId: string): Promise<boolean> {
|
||||
const ergebnis = await prisma.schluessel.updateMany({
|
||||
where: { id: schluesselId, privatVerschluesselt: { not: null } },
|
||||
data: {
|
||||
privatVerschluesselt: null,
|
||||
dekVerschluesselt: null,
|
||||
masterKeyVersion: null,
|
||||
privatGeloeschtAm: new Date(),
|
||||
},
|
||||
});
|
||||
return ergebnis.count === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stellt beim Start des Workers sicher, dass es einen Deployment-Key der Instanz gibt
|
||||
* (#006). Gibt die ID zurueck. Gleichzeitige Starts scheitern am partiellen Index
|
||||
|
||||
Reference in New Issue
Block a user