// Ebene 4 (Mandanten-Verwaltung) — Nutzer/Rollen für GENAU EINEN // Mandanten, siehe CLAUDE.md, Fünf Ebenen. Aktuell nur Nutzerverwaltung // (Abteilungen, Anmeldeverfahren, Werkzeug-Freigaben, Rechnungsdaten // sind spätere Ausbaustufen). Es gibt noch keinen Einladungslink/CSV- // Import/SSO (siehe CLAUDE.md, Onboarding) — ein Admin setzt hier // direkt ein Initialpasswort, wie schon bei der Firma-Registrierung. package web import ( "net/http" "github.com/netcell-it/deklarix/internal/auth" ) // mandantenRollen sind die Rollen, die ein Admin innerhalb des eigenen // Mandanten vergeben darf — "betreiber" ist Ebene 5 (Plattform) und // kann von keinem Mandanten-Admin vergeben werden. var mandantenRollen = []string{"mitarbeiter", "verantwortlicher", "pruefer", "admin"} func istMandantenRolle(role string) bool { for _, r := range mandantenRollen { if r == role { return true } } return false } type nutzerListItem struct { Email string Role string } type nutzerListeData struct { Title string Nav navData Nutzer []nutzerListItem } // handleNutzerListe zeigt alle Logins des eigenen Mandanten. func (s *Server) handleNutzerListe(w http.ResponseWriter, r *http.Request) { users, err := s.store.ListUsersForAccount(r.Context(), currentUser(r).AccountID) if err != nil { http.Error(w, "Nutzer konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } data := nutzerListeData{Title: "Nutzerverwaltung", Nav: navFor(r)} for _, u := range users { data.Nutzer = append(data.Nutzer, nutzerListItem{Email: u.Email, Role: u.Role}) } if err := s.templates.ExecuteTemplate(w, "nutzer-liste", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } } type nutzerNeuData struct { Title string Nav navData Rollen []string Error string } func (s *Server) handleNutzerNeuForm(w http.ResponseWriter, r *http.Request) { data := nutzerNeuData{Title: "Nutzer anlegen", Nav: navFor(r), Rollen: mandantenRollen} if err := s.templates.ExecuteTemplate(w, "nutzer-neu", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } } // handleNutzerCreate legt einen weiteren Login im eigenen Mandanten an. // Es gibt keine Einladungsmail (siehe CLAUDE.md, Onboarding — noch // nicht gebaut) — der Admin setzt das Initialpasswort direkt und gibt // es dem/der neuen Nutzer:in auf einem anderen Weg weiter. func (s *Server) handleNutzerCreate(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "ungültiges Formular", http.StatusBadRequest) return } email := r.FormValue("email") password := r.FormValue("password") role := r.FormValue("role") if email == "" || password == "" || !istMandantenRolle(role) { data := nutzerNeuData{Title: "Nutzer anlegen", Nav: navFor(r), Rollen: mandantenRollen, Error: "E-Mail, Passwort und eine gültige Rolle sind Pflicht"} if err := s.templates.ExecuteTemplate(w, "nutzer-neu", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } return } passwordHash, err := auth.HashPassword(password) if err != nil { data := nutzerNeuData{Title: "Nutzer anlegen", Nav: navFor(r), Rollen: mandantenRollen, Error: err.Error()} if err := s.templates.ExecuteTemplate(w, "nutzer-neu", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } return } if _, err := s.store.CreateUser(r.Context(), currentUser(r).AccountID, email, passwordHash, role); err != nil { data := nutzerNeuData{Title: "Nutzer anlegen", Nav: navFor(r), Rollen: mandantenRollen, Error: "Nutzer konnte nicht angelegt werden — E-Mail evtl. schon vergeben"} if err := s.templates.ExecuteTemplate(w, "nutzer-neu", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } return } http.Redirect(w, r, "/verwaltung/nutzer", http.StatusSeeOther) }