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>
109 lines
4.0 KiB
Go
109 lines
4.0 KiB
Go
// 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)
|
|
}
|