Deklarix soll ein buchbarer Service werden — dafür muss jede Entität im
Datenmodell über das Frontend steuerbar sein, nicht nur einsehbar.
Schließt drei konkrete Lücken:
- Abteilungen (GET /verwaltung/abteilungen, anlegen/löschen) — ohne
diese Seite blieb die Abteilung-Auswahl im Antrag-Fragebogen leer
und unbenutzbar, das war ein Funktionsdefizit, kein Komfortfehler.
- Eigene Werkzeug-Sperrungen (GET /verwaltung/werkzeuge) — ein Mandant
kann einen zentralen Katalogeintrag jetzt für sich sperren/entsperren,
ohne den zentralen Katalog selbst zu verändern.
- Nutzer-Deaktivierung (POST /verwaltung/nutzer/{id}/deaktivieren bzw.
.../aktivieren, neue Spalte app_user.active, Migration 0012). Nutzer
werden nicht gelöscht (Fremdschlüssel auf antrag/entscheidung/
audit_log würden das verhindern und die Historie zerstören) —
deaktivierte Logins können sich nicht mehr anmelden und verlieren
eine laufende Sitzung sofort. Ein Admin kann sich nicht selbst
deaktivieren.
Zusätzlich: store.ListAktiveGenehmigungenForAccount als Grundlage für
die Wiedervorlage (Schritt 7, Web-Layer folgt).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
103 lines
3.9 KiB
Go
103 lines
3.9 KiB
Go
package web_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestAdminKannNutzerDeaktivierenUndReaktivieren(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
adminCookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
|
|
postForm(t, s, adminCookie, "/verwaltung/nutzer", url.Values{
|
|
"email": {"weg@example.com"}, "password": {"ein-langes-passwort"}, "role": {"mitarbeiter"},
|
|
})
|
|
target, err := fs.GetUserByEmail(context.Background(), "weg@example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetUserByEmail: %v", err)
|
|
}
|
|
|
|
resp := postForm(t, s, adminCookie, "/verwaltung/nutzer/"+target.ID+"/deaktivieren", url.Values{})
|
|
if resp.Code != http.StatusSeeOther {
|
|
t.Fatalf("deaktivieren status = %d, body: %s", resp.Code, resp.Body.String())
|
|
}
|
|
deaktiviert, err := fs.GetUser(context.Background(), target.ID)
|
|
if err != nil || deaktiviert.Active {
|
|
t.Fatalf("expected user to be inactive, got %+v (err %v)", deaktiviert, err)
|
|
}
|
|
|
|
loginResp := postForm(t, s, nil, "/login", url.Values{"email": {"weg@example.com"}, "password": {"ein-langes-passwort"}})
|
|
if !strings.Contains(loginResp.Body.String(), "deaktiviert") {
|
|
t.Errorf("expected a deactivation message on login, got: %s", loginResp.Body.String())
|
|
}
|
|
|
|
reaktivierenResp := postForm(t, s, adminCookie, "/verwaltung/nutzer/"+target.ID+"/aktivieren", url.Values{})
|
|
if reaktivierenResp.Code != http.StatusSeeOther {
|
|
t.Fatalf("aktivieren status = %d, body: %s", reaktivierenResp.Code, reaktivierenResp.Body.String())
|
|
}
|
|
reaktiviert, err := fs.GetUser(context.Background(), target.ID)
|
|
if err != nil || !reaktiviert.Active {
|
|
t.Fatalf("expected user to be active again, got %+v (err %v)", reaktiviert, err)
|
|
}
|
|
}
|
|
|
|
func TestAdminKannSichNichtSelbstDeaktivieren(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
adminCookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
|
|
self, err := fs.GetUserByEmail(context.Background(), "admin@example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetUserByEmail: %v", err)
|
|
}
|
|
|
|
resp := postForm(t, s, adminCookie, "/verwaltung/nutzer/"+self.ID+"/deaktivieren", url.Values{})
|
|
if resp.Code != http.StatusBadRequest {
|
|
t.Fatalf("status = %d, want 400 for self-deactivation", resp.Code)
|
|
}
|
|
}
|
|
|
|
func TestDeaktivierterNutzerVerliertLaufendeSitzungSofort(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
adminCookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
|
|
admin, err := fs.GetUserByEmail(context.Background(), "admin@example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetUserByEmail: %v", err)
|
|
}
|
|
mitarbeiterCookie := seedUserInAccount(t, fs, admin.AccountID, "mitarbeiter@example.com", "mitarbeiter")
|
|
|
|
if resp := getWithCookie(t, s, mitarbeiterCookie, "/antraege"); resp.Code != http.StatusOK {
|
|
t.Fatalf("expected active session to work, status = %d", resp.Code)
|
|
}
|
|
|
|
target, err := fs.GetUserByEmail(context.Background(), "mitarbeiter@example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetUserByEmail: %v", err)
|
|
}
|
|
postForm(t, s, adminCookie, "/verwaltung/nutzer/"+target.ID+"/deaktivieren", url.Values{})
|
|
|
|
resp := getWithCookie(t, s, mitarbeiterCookie, "/antraege")
|
|
if resp.Code != http.StatusSeeOther {
|
|
t.Fatalf("expected the deactivated user's session to be rejected, status = %d", resp.Code)
|
|
}
|
|
}
|
|
|
|
func TestAdminCannotDeactivateForeignAccountUser(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
adminA := seedAccountWithRole(t, fs, "Mandant A", "admin-a@example.com", "admin")
|
|
seedAccountWithRole(t, fs, "Mandant B", "admin-b@example.com", "admin")
|
|
targetB, err := fs.GetUserByEmail(context.Background(), "admin-b@example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetUserByEmail: %v", err)
|
|
}
|
|
|
|
resp := postForm(t, s, adminA, "/verwaltung/nutzer/"+targetB.ID+"/deaktivieren", url.Values{})
|
|
if resp.Code != http.StatusNotFound {
|
|
t.Fatalf("status = %d, want 404 for a foreign account's user", resp.Code)
|
|
}
|
|
}
|