Schema: Zeitschritt fuer TOTP und Sperre fuer die Ersteinrichtung
Zwei Felder fuer zwei Luecken, in einer Migration: benutzer.totpLetzterSchritt haelt das zuletzt akzeptierte TOTP-Fenster fest. Ohne das gilt ein Code bis zu 90 Sekunden lang beliebig oft. Die Tabelle ersteinrichtung hat genau eine Zeile mit fester Id. Das ist kein Protokoll, sondern eine Sperre: Bisher entschied "gibt es schon einen Benutzer?" und danach wurde angelegt - zwei Anfragen gleichzeitig kamen beide durch. Jetzt entscheidet der Primaerschluessel. Enthalten ist eine Datenmigration: Wo schon ein Zugang besteht, wird die Sperrzeile gesetzt. Ohne sie stuende die Einrichtungsseite auf jedem bestehenden System nach dem Update wieder offen. Die DROP-INDEX-Zeilen zu den Trigramm-Indizes hat das Skript aus #36 wieder entfernt; "npm run db:pruefen" bestaetigt, dass beide stehen. Refs #38
This commit is contained in:
@@ -70,6 +70,13 @@ model User {
|
||||
// TOTP ist Pflicht. Das Secret liegt verschluesselt, nicht im Klartext.
|
||||
totpSecretVerschluesselt String?
|
||||
totpAktiviertAm DateTime?
|
||||
/// Der zuletzt akzeptierte TOTP-Zeitschritt (Unixzeit / 30).
|
||||
///
|
||||
/// Ein TOTP-Code gilt 30 Sekunden, mit Toleranz bis zu 90. In dieser Zeit
|
||||
/// liesse sich derselbe Code ein zweites Mal einloesen - abgelesen ueber
|
||||
/// die Schulter oder abgefangen. Deshalb wird der Schritt festgehalten
|
||||
/// und nur noch ein groesserer akzeptiert.
|
||||
totpLetzterSchritt Int?
|
||||
// Einmal-Wiederherstellungscodes, einzeln Argon2id-gehasht.
|
||||
wiederherstellungscodes String[] @default([])
|
||||
|
||||
@@ -128,6 +135,25 @@ model BetreuungsZugang {
|
||||
@@map("betreuungs_zugang")
|
||||
}
|
||||
|
||||
/// Haelt fest, dass die Ersteinrichtung stattgefunden hat.
|
||||
///
|
||||
/// Die Tabelle hat genau eine Zeile mit fester Id. Das ist kein Protokoll,
|
||||
/// sondern eine Sperre: Vorher stand im Code "wenn es noch keinen Benutzer
|
||||
/// gibt, lege einen an" - zwei Anfragen gleichzeitig kamen beide durch die
|
||||
/// Pruefung und legten beide einen Administrator an. Ueber den
|
||||
/// Primaerschluessel entscheidet das jetzt die Datenbank, und zwar fuer
|
||||
/// genau einen Gewinner.
|
||||
model Ersteinrichtung {
|
||||
/// Immer "einmalig". Der feste Wert ist die Sperre.
|
||||
id String @id
|
||||
|
||||
abgeschlossenAm DateTime @default(now())
|
||||
/// Wer den Zugang angelegt hat - nur zum Nachvollziehen.
|
||||
userId String?
|
||||
|
||||
@@map("ersteinrichtung")
|
||||
}
|
||||
|
||||
/// Fehlgeschlagene und erfolgreiche Anmeldeversuche - Grundlage fuer das
|
||||
/// Rate-Limiting am Login. Wird nach 30 Tagen aufgeraeumt.
|
||||
model LoginVersuch {
|
||||
|
||||
Reference in New Issue
Block a user