# CONFIGURATION.md — omsorgCore Übersicht aller Konfigurationswerte des Backends: was ist einstellbar, was ist der Default, wie überschreibt man ihn. Ziel: **kein Wert, den ein Betreiber sinnvoll ändern könnte, steckt fest im Code.** ## Wie Konfiguration geladen wird ASP.NET Core liest Konfiguration in dieser Reihenfolge (später gewinnt): 1. `appsettings.json` — Defaults, die für jede Umgebung gelten. 2. `appsettings.{ASPNETCORE_ENVIRONMENT}.json` (z. B. `appsettings.Development.json`) — Overrides pro Umgebung. **Enthält keine echten Secrets**, nur `CHANGE_ME_...`-Platzhalter. 3. Umgebungsvariablen — verschachtelte Keys wie `Jwt:Secret` werden zu `Jwt__Secret` (doppelter Unterstrich statt Doppelpunkt). 4. In Development zusätzlich: `dotnet user-secrets` (lokal, nicht eingecheckt) — `dotnet user-secrets init` im `OmsorgCore.Api`-Projekt einmalig ausführen, dann z. B. `dotnet user-secrets set "ConnectionStrings:OmsorgCore" "..."`. **Secrets nie einchecken.** `Jwt:Secret`, `ConnectionStrings:OmsorgCore` und (sobald produktiv genutzt) `Seed:AdminPassword` haben in den eingecheckten `appsettings*.json`-Dateien nur Platzhalter — echte Werte kommen aus Umgebungsvariablen oder User-Secrets. ## Auth / JWT | Key | Default | Env-Var | Beschreibung | |---|---|---|---| | `Jwt:Secret` | *(kein Default, Platzhalter in Dev)* | `Jwt__Secret` | Signaturschlüssel für Access-Tokens. Muss mind. 32 Zeichen, produktiv nur per Env-Var/Secret-Store. | | `Jwt:Issuer` | `"OmsorgCore"` | `Jwt__Issuer` | JWT-`iss`-Claim, wird beim Validieren geprüft. | | `Jwt:Audience` | `"OmsorgClients"` | `Jwt__Audience` | JWT-`aud`-Claim, wird beim Validieren geprüft. | | `Jwt:ExpiryMinutes` | `60` | `Jwt__ExpiryMinutes` | Gültigkeitsdauer eines Access-Tokens. | | `RefreshToken:ExpiryDays` | `60` | `RefreshToken__ExpiryDays` | Gültigkeitsdauer eines Refresh-Tokens (sliding — jede Nutzung/Rotation verlängert effektiv die Session). | | `Auth:MaxLoginFailures` | `5` | `Auth__MaxLoginFailures` | Fehlversuche pro IP innerhalb von `Auth:LoginLockoutMinutes`, ab denen `POST /api/auth/login` mit `429` sperrt (zentral für alle Clients, siehe `AuthService.LoginAsync`/`LoginAttempt`). | | `Auth:LoginLockoutMinutes` | `10` | `Auth__LoginLockoutMinutes` | Zeitfenster, in dem Fehlversuche gezählt werden, bevor die Sperre wieder abläuft. | ## Passwort-Reset / E-Mail | Key | Default | Env-Var | Beschreibung | |---|---|---|---| | `Email:Provider` | `"Console"` | `Email__Provider` | Mail-Versandweg. `Console` schreibt Mails nur ins Log (sicherer Default). `Smtp` versendet echt über die Werte unten (implementiert über MailKit). | | `Email:PinExpiryMinutes` | `5` | `Email__PinExpiryMinutes` | Gültigkeitsdauer des 6-stelligen PINs beim "Passwort vergessen"-Flow. | | `Email:MaxAttempts` | `3` | `Email__MaxAttempts` | Maximale Anzahl Fehlversuche bei der PIN-Eingabe, bevor der Code invalidiert wird. | | `Email:ResetTokenExpiryMinutes` | `10` | `Email__ResetTokenExpiryMinutes` | Gültigkeitsdauer des Reset-Tokens nach erfolgreicher PIN-Verifikation. | | `Email:RequestCooldownSeconds` | `60` | `Email__RequestCooldownSeconds` | Mindestabstand zwischen zwei Reset-Anfragen desselben Users (Spam-Schutz). | **Ein SMTP-Server, mehrere Accounts:** die Verbindungsdaten (`Email:Smtp:*`) sind einmal gemeinsam konfiguriert, Login + Absenderidentität liegen pro benanntem Account unter `Email:Accounts::*` — so lässt sich z. B. ein Account exklusiv für Passwort-Reset-Mails führen, getrennt von einem künftigen allgemeinen Absender, ohne einen zweiten Server zu brauchen. | Key | Default | Env-Var | Beschreibung | |---|---|---|---| | `Email:Smtp:Host` | *(leer)* | `Email__Smtp__Host` | SMTP-Server-Adresse. Nur wirksam, wenn `Email:Provider` = `"Smtp"`. | | `Email:Smtp:Port` | `587` | `Email__Smtp__Port` | SMTP-Port. | | `Email:Smtp:EnableSsl` | `true` | `Email__Smtp__EnableSsl` | Ob StartTLS/SSL beim Verbindungsaufbau verwendet wird. | | `Email:Accounts::Username` | *(leer, Platzhalter in Dev)* | `Email__Accounts____Username` | SMTP-Login-Username dieses Accounts. Secret — nie echten Wert einchecken. | | `Email:Accounts::Password` | *(leer, Platzhalter in Dev)* | `Email__Accounts____Password` | SMTP-Login-Passwort dieses Accounts. Secret — produktiv nur per Env-Var/user-secrets, **nie** in `appsettings*.json` einchecken. | | `Email:Accounts::SenderAddress` | *(leer)* | `Email__Accounts____SenderAddress` | Absenderadresse dieses Accounts. | | `Email:Accounts::SenderDisplayName` | `"OMSORG"` | `Email__Accounts____SenderDisplayName` | Absendername dieses Accounts. | | `Email:PasswordResetAccount` | `"PasswordReset"` | `Email__PasswordResetAccount` | Welcher Account-Name aus `Email:Accounts` für Passwort-Reset-Mails verwendet wird. Standardmäßig ist `"PasswordReset"` bereits als Account in `appsettings.json` angelegt (leer, muss per Env-Var/user-secrets befüllt werden). | | `Email:PasswordResetTemplate:Subject` | `"Ihr OMSORG Passwort-Reset-Code"` | `Email__PasswordResetTemplate__Subject` | Betreff der Passwort-Reset-Mail. | | `Email:PasswordResetTemplate:BodyTemplate` | siehe `appsettings.json` | `Email__PasswordResetTemplate__BodyTemplate` | Text der Passwort-Reset-Mail. Platzhalter im Format `$NAME$`: `$RESET_PIN$` (der 6-stellige Code) und `$RESET_PIN_EXPIRY_MINUTES$` (aus `Email:PinExpiryMinutes`). Ersetzung über `OmsorgCore.Email.EmailTemplateRenderer`. | | `Email:UserInviteAccount` | `"UserInvite"` | `Email__UserInviteAccount` | Welcher Account-Name aus `Email:Accounts` für Account-Einladungsmails (neuer User-Account für einen Mitarbeiter) verwendet wird. | | `Email:UserInviteTemplate:Subject` | `"Ihr OMSORG-Zugang"` | `Email__UserInviteTemplate__Subject` | Betreff der Einladungsmail. | | `Email:UserInviteTemplate:BodyTemplate` | siehe `appsettings.json` | `Email__UserInviteTemplate__BodyTemplate` | Text der Einladungsmail. Platzhalter: `$INVITE_PIN$`, `$INVITE_PIN_EXPIRY_DAYS$`. Die Gültigkeitsdauer wird beim Anlegen des Accounts pro Einladung gewählt (`POST /api/users`, `PinValidityDays`, Default 7), nicht global konfiguriert — der Einladungs-PIN läuft über dieselbe `PasswordResetCode`-Tabelle wie der Passwort-vergessen-Flow, nur mit individuell längerer Gültigkeit statt der 5 Minuten aus `Email:PinExpiryMinutes`. | **Kein API-Endpoint zum Setzen dieser Werte:** SMTP-Zugangsdaten und Mail-Texte werden bewusst nie über eine vom Client erreichbare Route entgegengenommen oder ausgeliefert — nur über die üblichen serverseitigen Konfigurationswege oben. Ein `POST /api/admin/email/test-send` (siehe `AdminEmailController`) löst lediglich den Versand einer festen Testmail über die bereits konfigurierte Verbindung aus, liefert aber nie Host/Username/Passwort an den Client zurück. ## Seed (nur Development) | Key | Default | Env-Var | Beschreibung | |---|---|---|---| | `Seed:AdminUsername` | `"admin"` | `Seed__AdminUsername` | Username des automatisch angelegten Standard-Admin-Users. | | `Seed:AdminPassword` | `"abersicher"` | `Seed__AdminPassword` | Passwort des Standard-Admin-Users. | Der Seed läuft nur, wenn `ASPNETCORE_ENVIRONMENT=Development` **und** noch kein `User` in der DB existiert (idempotent, siehe `src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs`). In Produktion läuft er nicht — dort braucht es einen echten User-Anlage-Flow (noch nicht gebaut, siehe `omsorgCore/CLAUDE.md`, "Offene Punkte"). ## Datenbank | Key | Default | Env-Var | Beschreibung | |---|---|---|---| | `ConnectionStrings:OmsorgCore` | *(kein Default, Platzhalter in Dev)* | `ConnectionStrings__OmsorgCore` | Postgres-Connection-String (`Host=...;Port=5432;Database=omsorg_core;Username=...;Password=...`). | ## Logging / Hosting | Key | Default | Env-Var | Beschreibung | |---|---|---|---| | `Logging:LogLevel:Default` | `"Information"` | `Logging__LogLevel__Default` | Standard-Log-Level. | | `Logging:LogLevel:Microsoft.AspNetCore` | `"Warning"` | `Logging__LogLevel__Microsoft.AspNetCore` | Log-Level für das ASP.NET-Core-Framework selbst. | | `AllowedHosts` | `"*"` | `AllowedHosts` | Host-Header-Filter von ASP.NET Core. | ## Bewusst nicht konfigurierbar Diese Werte sind absichtlich fest im Code und **keine** Konfigurationslücke: - **`ModuleType`, `PermissionAction`, `PermissionEffect`** (`src/OmsorgCore.Domain/Enums/`) — die Rechtematrix-Bausteine. Compile-Time-Domänenkonzepte: ein neues Modul oder eine neue Aktion braucht ohnehin neuen Controller-Code, ein Config-Wert würde hier nur Scheinflexibilität vortäuschen. - **JWT-Validierungs-Invarianten** in `Program.cs` (`ValidateIssuer/Audience/Lifetime/IssuerSigningKey = true`) — Sicherheits-Grundannahmen, kein Betriebsparameter. - **Passwort-Hashing-Algorithmus** (`PasswordHasher`, ASP.NET-Identity-Standard-PBKDF2-Iterationszahl) — bewusst der Framework-Default, kein eigenes Krypto-Rad.