syncova-backup/packages/auth/model.go
Jerrit Fritzsche 610719c316
Some checks failed
CI / Backend (Go) (push) Failing after 3m7s
CI / Frontend (React/TypeScript) (push) Successful in 37s
CI / Sicherheitsprüfungen (push) Successful in 44s
Syncova Backups V1
Enterprise-Backup-, Recovery-, Verification-, Security- und
Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme.

Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als
vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit
nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem
tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes
geht in keine Bewertung als "gut" ein.

Umfang (Phasen 0-23):

- Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll,
  Katalogaufbau allein aus den Manifesten — ohne Datenbank
- Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz
  Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck
- Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell)
- Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive
- Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit
- Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center,
  Ransomware-Heuristik (meldet, handelt nie)
- Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing
- Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository
- Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64

Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup
Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs.

Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die
systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine
wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md
und docs/release-candidate.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:10:54 +02:00

201 lines
8.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package auth enthält Identität, Anmeldung, Sitzungen und rollenbasierte
// Zugriffssteuerung der Control Plane.
//
// Grundsätze (PROMPT.md §41–§45):
// - Berechtigungen werden immer serverseitig geprüft.
// - Fehlermeldungen der Anmeldung geben niemals preis, ob ein Konto existiert.
// - Ein zweiter Faktor ist für privilegierte Konten erzwingbar.
package auth
import (
"errors"
"time"
"github.com/google/uuid"
)
// UserStatus beschreibt den Zustand eines Benutzerkontos.
type UserStatus string
const (
// UserStatusActive erlaubt die Anmeldung.
UserStatusActive UserStatus = "active"
// UserStatusDisabled verbietet die Anmeldung dauerhaft.
UserStatusDisabled UserStatus = "disabled"
// UserStatusLocked verbietet die Anmeldung bis zur Entsperrung.
UserStatusLocked UserStatus = "locked"
)
// User ist ein Benutzerkonto.
//
// Das Passwort erscheint hier bewusst nicht: der Hash verlässt die
// Datenzugriffsschicht nur dort, wo er zur Prüfung gebraucht wird.
type User struct {
// ID ist der öffentliche Bezeichner.
ID uuid.UUID `json:"id"`
// Username ist der Anmeldename.
Username string `json:"username"`
// Email ist die optionale Mailadresse.
Email string `json:"email,omitempty"`
// Status ist der Zustand des Kontos.
Status UserStatus `json:"status"`
// MFAEnabled meldet, ob ein bestätigter zweiter Faktor vorliegt.
MFAEnabled bool `json:"mfa_enabled"`
// Roles sind die zugewiesenen Rollennamen.
Roles []string `json:"roles"`
// Permissions sind die aus den Rollen abgeleiteten Berechtigungen.
Permissions []string `json:"permissions"`
// CreatedAt ist der Anlagezeitpunkt in UTC.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt ist der Zeitpunkt der letzten Änderung in UTC.
UpdatedAt time.Time `json:"updated_at"`
// LastLoginAt ist der Zeitpunkt der letzten erfolgreichen Anmeldung.
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
}
// HasPermission meldet, ob der Benutzer eine Berechtigung besitzt.
func (user User) HasPermission(requiredPermission string) bool {
for _, grantedPermission := range user.Permissions {
if grantedPermission == requiredPermission {
return true
}
}
return false
}
// IsPrivileged meldet, ob das Konto weitreichende Rechte besitzt.
//
// Für solche Konten kann ein zweiter Faktor erzwungen werden (PROMPT.md §43).
func (user User) IsPrivileged() bool {
// Schreibrechte an Benutzern, Rollen oder Sicherheitseinstellungen erlauben
// es, sich selbst weitere Rechte zu verschaffen.
for _, privilegedPermission := range []string{"users.write", "roles.write", "security.write"} {
if user.HasPermission(privilegedPermission) {
return true
}
}
return false
}
// Role ist eine Rolle der Zugriffssteuerung.
type Role struct {
// ID ist der Bezeichner der Rolle.
ID uuid.UUID `json:"id"`
// Name ist der eindeutige technische Name.
Name string `json:"name"`
// Description erklärt den Zweck der Rolle.
Description string `json:"description,omitempty"`
// IsSystem kennzeichnet mitgelieferte Rollen, die nicht gelöscht werden dürfen.
IsSystem bool `json:"is_system"`
// Permissions sind die zugeordneten Berechtigungen.
Permissions []string `json:"permissions"`
}
// Permission ist eine Einzelberechtigung.
type Permission struct {
// Name ist der technische Name im Format bereich.aktion.
Name string `json:"name"`
// Description erklärt, was die Berechtigung erlaubt.
Description string `json:"description"`
}
// Session ist eine aktive Anmeldesitzung.
type Session struct {
// ID ist der Bezeichner der Sitzung.
ID uuid.UUID
// UserID ist der angemeldete Benutzer.
UserID uuid.UUID
// AccessExpiresAt ist die Ablaufzeit des Zugriffstokens in UTC.
AccessExpiresAt time.Time
// RefreshExpiresAt ist die Ablaufzeit des Erneuerungstokens in UTC.
RefreshExpiresAt time.Time
}
// TokenPair sind die bei einer Anmeldung ausgegebenen Tokens.
//
// Die Klartextwerte existieren ausschließlich in dieser Struktur und in der
// Antwort an den Aufrufer; gespeichert wird nur ihr Hash.
type TokenPair struct {
// AccessToken ist das kurzlebige Zugriffstoken.
AccessToken string `json:"access_token"`
// RefreshToken ist das langlebigere Erneuerungstoken.
RefreshToken string `json:"refresh_token"`
// AccessExpiresAt ist die Ablaufzeit des Zugriffstokens in UTC.
AccessExpiresAt time.Time `json:"access_expires_at"`
// RefreshExpiresAt ist die Ablaufzeit des Erneuerungstokens in UTC.
RefreshExpiresAt time.Time `json:"refresh_expires_at"`
}
// LoginResult ist das Ergebnis eines Anmeldeversuchs.
//
// Entweder ist die Anmeldung abgeschlossen (Tokens gesetzt) oder es fehlt noch
// der zweite Faktor (MFARequired gesetzt). Beides gleichzeitig gibt es nicht.
type LoginResult struct {
// MFARequired meldet, dass noch ein zweiter Faktor nötig ist.
MFARequired bool `json:"mfa_required"`
// ChallengeID benennt die offene MFA-Herausforderung.
ChallengeID *uuid.UUID `json:"challenge_id,omitempty"`
// Tokens sind die ausgegebenen Tokens, sofern die Anmeldung abgeschlossen ist.
Tokens *TokenPair `json:"tokens,omitempty"`
// User ist der angemeldete Benutzer, sofern die Anmeldung abgeschlossen ist.
User *User `json:"user,omitempty"`
}
// MFAEnrollment sind die Daten zur Einrichtung eines zweiten Faktors.
//
// Secret und Wiederherstellungscodes werden genau einmal ausgeliefert und
// danach nie wieder abrufbar (PROMPT.md §43).
type MFAEnrollment struct {
// Secret ist das TOTP-Secret in Base32.
Secret string `json:"secret"`
// ProvisioningURI ist die otpauth-URI für Authenticator-Apps.
ProvisioningURI string `json:"provisioning_uri"`
// RecoveryCodes sind die einmalig verwendbaren Wiederherstellungscodes.
RecoveryCodes []string `json:"recovery_codes"`
}
// Fehler der Anmeldung.
//
// ErrInvalidCredentials wird bewusst für unbekannte Benutzer und falsche
// Passwörter gleichermaßen verwendet: eine Unterscheidung erlaubte es, gültige
// Anmeldenamen zu ermitteln (Benutzer-Enumeration).
var (
// ErrInvalidCredentials meldet falsche Anmeldedaten.
ErrInvalidCredentials = errors.New("benutzername oder passwort ist falsch")
// ErrAccountLocked meldet ein wegen zu vieler Fehlversuche gesperrtes Konto.
ErrAccountLocked = errors.New("das konto ist vorübergehend gesperrt")
// ErrAccountDisabled meldet ein deaktiviertes Konto.
ErrAccountDisabled = errors.New("das konto ist deaktiviert")
// ErrInvalidMFACode meldet einen falschen Code des zweiten Faktors.
ErrInvalidMFACode = errors.New("der code ist falsch oder bereits verwendet")
// ErrChallengeNotFound meldet eine unbekannte, abgelaufene oder bereits
// eingelöste MFA-Herausforderung.
ErrChallengeNotFound = errors.New("die anmeldung ist abgelaufen, bitte erneut anmelden")
// ErrChallengeAttemptsExceeded meldet zu viele Fehlversuche beim zweiten Faktor.
ErrChallengeAttemptsExceeded = errors.New("zu viele fehlversuche, bitte erneut anmelden")
// ErrSessionInvalid meldet ein unbekanntes, abgelaufenes oder widerrufenes Token.
ErrSessionInvalid = errors.New("die sitzung ist ungültig oder abgelaufen")
// ErrUserNotFound meldet einen nicht vorhandenen Benutzer.
ErrUserNotFound = errors.New("der benutzer existiert nicht")
// ErrUsernameTaken meldet einen bereits vergebenen Anmeldenamen.
ErrUsernameTaken = errors.New("der benutzername ist bereits vergeben")
// ErrEmailTaken meldet eine bereits vergebene Mailadresse.
ErrEmailTaken = errors.New("die mailadresse ist bereits vergeben")
// ErrRoleNotFound meldet eine nicht vorhandene Rolle.
ErrRoleNotFound = errors.New("die rolle existiert nicht")
// ErrSystemRoleImmutable meldet den Versuch, eine mitgelieferte Rolle zu ändern.
ErrSystemRoleImmutable = errors.New("mitgelieferte rollen können nicht geändert oder gelöscht werden")
// ErrRoleInUse meldet den Versuch, eine zugewiesene Rolle zu löschen.
ErrRoleInUse = errors.New("die rolle ist noch Benutzern zugewiesen")
// ErrMFAAlreadyEnabled meldet einen bereits eingerichteten zweiten Faktor.
ErrMFAAlreadyEnabled = errors.New("für dieses konto ist bereits ein zweiter faktor eingerichtet")
// ErrMFANotEnrolled meldet einen fehlenden zweiten Faktor.
ErrMFANotEnrolled = errors.New("für dieses konto ist kein zweiter faktor eingerichtet")
// ErrLastAdministrator verhindert das Entfernen des letzten Administrators.
ErrLastAdministrator = errors.New("der letzte administrator kann nicht entfernt oder entrechtet werden")
// ErrWeakPassword meldet ein zu schwaches Passwort.
ErrWeakPassword = errors.New("das passwort erfüllt die mindestanforderungen nicht")
)