feat: Nutzerverwaltung für Ebene 4 (Mandanten-Verwaltung)

Ein admin kann jetzt weitere Logins im eigenen Mandanten anlegen
(GET /verwaltung/nutzer, GET/POST /verwaltung/nutzer/neu) mit einer
der vier Mandanten-Rollen — betreiber bleibt Ebene 5 vorbehalten und
kann von keinem Mandanten-Admin vergeben werden. Schließt die Lücke,
dass die Fachebene (verantwortlicher/pruefer) bisher nur per
manuellem SQL-Insert erreichbar war, weil die Firma-Registrierung
ausschließlich einen admin-Nutzer erzeugt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
noroot
2026-08-29 12:34:04 +02:00
parent d1e80dd19f
commit aad517ce23
8 changed files with 276 additions and 1 deletions

View File

@@ -0,0 +1,75 @@
package web_test
import (
"context"
"net/http"
"net/url"
"strings"
"testing"
)
func TestMitarbeiterCannotAccessNutzerverwaltung(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
cookie := seedAccountWithRole(t, fs, "Test-Mandant", "mitarbeiter@example.com", "mitarbeiter")
resp := getWithCookie(t, s, cookie, "/verwaltung/nutzer")
if resp.Code != http.StatusNotFound {
t.Fatalf("status = %d, want 404 for role mitarbeiter on /verwaltung/nutzer", resp.Code)
}
}
func TestAdminCanCreateUserWithMandantenRolle(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
adminCookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
resp := postForm(t, s, adminCookie, "/verwaltung/nutzer", url.Values{
"email": {"neu@example.com"}, "password": {"ein-langes-passwort"}, "role": {"verantwortlicher"},
})
if resp.Code != http.StatusSeeOther {
t.Fatalf("status = %d, want 303, body: %s", resp.Code, resp.Body.String())
}
u, err := fs.GetUserByEmail(context.Background(), "neu@example.com")
if err != nil {
t.Fatalf("GetUserByEmail: %v", err)
}
if u.Role != "verantwortlicher" {
t.Errorf("Role = %q, want verantwortlicher", u.Role)
}
list := getWithCookie(t, s, adminCookie, "/verwaltung/nutzer")
if !strings.Contains(list.Body.String(), "neu@example.com") {
t.Errorf("expected the new user in the Nutzerliste, got: %s", list.Body.String())
}
}
func TestAdminCannotCreateBetreiberUser(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
adminCookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
resp := postForm(t, s, adminCookie, "/verwaltung/nutzer", url.Values{
"email": {"versuch@example.com"}, "password": {"ein-langes-passwort"}, "role": {"betreiber"},
})
if resp.Code != http.StatusOK {
t.Fatalf("status = %d, want 200 (Formular mit Fehler) for role betreiber", resp.Code)
}
if _, err := fs.GetUserByEmail(context.Background(), "versuch@example.com"); err == nil {
t.Error("expected no user to be created for an invalid role")
}
}
func TestAdminCreateUserRejectsDuplicateEmail(t *testing.T) {
fs := newFakeStore()
s := newServer(t, fs)
adminCookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
resp := postForm(t, s, adminCookie, "/verwaltung/nutzer", url.Values{
"email": {"admin@example.com"}, "password": {"ein-langes-passwort"}, "role": {"mitarbeiter"},
})
if resp.Code != http.StatusOK {
t.Fatalf("status = %d, want 200 (Formular mit Fehler) for duplicate email", resp.Code)
}
}