# Benutzer-Lifecycle-Policy

Letzte Aktualisierung: 2026-03-06

## Ziel

Globale Regeln für automatische Benutzer-Deaktivierung und spätere Löschung.

- Stufe 1: Benutzer wird auf `inactive` gesetzt.
- Stufe 2: Inaktiver Benutzer wird nach weiterer Frist gelöscht (Hard Delete).

## Settings

Die Regeln werden in `settings` gespeichert:

- `user_inactivity_deactivate_days`
- `user_inactivity_delete_days`

Semantik:

- Wertebereich: `0..3650`
- `0` deaktiviert die jeweilige Regel
- Löschung wird nur angewendet, wenn Deaktivierung aktiv ist (`deactivate > 0`)

Defaults:

- Deaktivierung: `180`
- Löschung: `365` (ab Inaktivsetzung)

## Referenzdaten

- Deaktivierung basiert auf `COALESCE(last_login_at, created)`.
- Löschung basiert auf `active_changed_at` (nur wenn gesetzt).

## Privilegierte Benutzer (ausgenommen)

Automatische Lifecycle-Aktionen ignorieren Benutzer mit mindestens einer aktiven Rolle, die eine der Permissions hat:

- `settings.update`
- `tenants.update`

## Ausführung

### Manueller Run (Admin UI)

- Endpoint: `POST /admin/settings/run-user-lifecycle`
- Schutz: Login + `settings.update` + CSRF
- Verdrahtung: `SchedulerServicesFactory` erstellt `UserLifecycleService` instanzbasiert.

### Cron/CLI

- Bevorzugt über den zentralen Scheduler:
  - Script: `bin/scheduler-run.php` (führt fällige Jobs aus, inkl. `user_lifecycle_run`)
  - Beispiel (minütlich):

```bash
* * * * * docker compose exec php php bin/scheduler-run.php
```

Exit-Codes:

- `0` erfolgreich
- `1` Fehler/Lock-Konflikt

## Concurrency

Zur Vermeidung paralleler Runs wird ein MySQL-Lock verwendet:

- `GET_LOCK('user_lifecycle_maintenance', 0)`
- `RELEASE_LOCK('user_lifecycle_maintenance')`

## Session-Wirkung

Wenn ein bereits eingeloggter Benutzer automatisch deaktiviert wurde, greift die bestehende Session-Refresh-Logik beim nächsten Request und führt zum Logout.

## Audit-Log (Delete/Restore)

Lifecycle-Events werden in `user_lifecycle_audit_log` protokolliert.

- `deactivate`: erfolgreich ausgeführte automatische Deaktivierung
- `delete`: Löschversuch mit Snapshot
- `restore`: Wiederherstellung aus einem Delete-Event

Wichtige Regeln:

- Delete ist fail-closed: ohne erfolgreich gespeicherten Snapshot wird nicht gelöscht.
- Snapshot wird verschlüsselt in `snapshot_enc` gespeichert.
- Snapshot enthält nur whitelisted Profilfelder (keine Secrets/Tokens).
- Audit-Retention: 365 Tage (`POST /admin/user-lifecycle-audit/purge`).

## Wiederherstellung (ohne Assignments)

Restore ist nur für `action=delete` + `status=success` möglich und nur einmal pro Event.

Konfliktregeln (Hard fail):

- UUID existiert bereits
- E-Mail existiert bereits

Restore-Ergebnis:

- User wird neu angelegt mit Snapshot-Stammdaten
- `active=0`
- zufälliges Passwort
- keine Wiederherstellung von Tenant-/Role-/Department-Zuweisungen

## Rechte

- Ansicht Lifecycle-Protokolle: `user_lifecycle_audit.view`
- Wiederherstellen aus Lifecycle-Protokoll: `users.lifecycle_restore`
- Purge: `settings.update`
