F-6 from t-paliad-074 architecture audit. The Gitea repo was renamed m/patholo → mAi/paliad → m/paliad, but go.mod still declared `mgit.msbls.de/m/patholo` and every internal import echoed the pre-rebrand name. Sweep: - go.mod: module path → mgit.msbls.de/m/paliad - All *.go files: imports rewritten via sed - README.md, docs/design-kanzlai-integration.md: mAi/paliad → m/paliad - Frontend issue-reference comments (mAi/paliad#N → m/paliad#N) in i18n.ts, theme.ts, sidebar.ts, app.ts, Sidebar.tsx, PWAHead.tsx, global.css Verified: go build/vet/test ./... clean, bun run build clean, no remaining mgit.msbls.de/m/patholo or mAi/paliad references outside docs that intentionally describe the rename history.
149 lines
4.6 KiB
Go
149 lines
4.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"mgit.msbls.de/m/paliad/internal/auth"
|
|
"mgit.msbls.de/m/paliad/internal/branding"
|
|
)
|
|
|
|
func handleLoginPage(w http.ResponseWriter, r *http.Request) {
|
|
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil && cookie.Value != "" {
|
|
if _, err := authClient.VerifyToken(cookie.Value); err == nil {
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
|
return
|
|
}
|
|
}
|
|
http.ServeFile(w, r, "dist/login.html")
|
|
}
|
|
|
|
func handleAPILogin(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
|
|
return
|
|
}
|
|
|
|
req.Email = strings.TrimSpace(req.Email)
|
|
if req.Email == "" || req.Password == "" {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Bitte E-Mail und Passwort eingeben."})
|
|
return
|
|
}
|
|
|
|
if !isAllowedEmailDomain(req.Email) {
|
|
writeJSON(w, http.StatusForbidden, map[string]string{"error": "Zugang nur für autorisierte " + branding.Name + "-E-Mail-Adressen."})
|
|
return
|
|
}
|
|
|
|
tokens, err := authClient.SignIn(req.Email, req.Password)
|
|
if err != nil {
|
|
log.Printf("sign in failed for %s: %v", req.Email, err)
|
|
errMsg := "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut."
|
|
if strings.Contains(err.Error(), "Invalid login credentials") {
|
|
errMsg = "Ungültige E-Mail-Adresse oder Passwort."
|
|
}
|
|
writeJSON(w, http.StatusUnauthorized, map[string]string{"error": errMsg})
|
|
return
|
|
}
|
|
|
|
auth.SetAuthCookies(w, r, tokens)
|
|
writeJSON(w, http.StatusOK, map[string]string{"ok": "true"})
|
|
}
|
|
|
|
func handleAPIRegister(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
|
|
return
|
|
}
|
|
|
|
req.Email = strings.TrimSpace(req.Email)
|
|
if req.Email == "" || req.Password == "" {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Bitte alle Felder ausfüllen."})
|
|
return
|
|
}
|
|
|
|
if len(req.Password) < 8 {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Passwort muss mindestens 8 Zeichen lang sein."})
|
|
return
|
|
}
|
|
|
|
if !isAllowedEmailDomain(req.Email) {
|
|
writeJSON(w, http.StatusForbidden, map[string]string{"error": "Registrierung nur für autorisierte " + branding.Name + "-E-Mail-Adressen."})
|
|
return
|
|
}
|
|
|
|
tokens, err := authClient.SignUp(req.Email, req.Password)
|
|
if err != nil {
|
|
log.Printf("sign up failed for %s: %v", req.Email, err)
|
|
errMsg := "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut."
|
|
if strings.Contains(err.Error(), "already registered") || strings.Contains(err.Error(), "already been registered") {
|
|
errMsg = "Ein Account mit dieser E-Mail existiert bereits."
|
|
}
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": errMsg})
|
|
return
|
|
}
|
|
|
|
if tokens != nil {
|
|
auth.SetAuthCookies(w, r, tokens)
|
|
writeJSON(w, http.StatusOK, map[string]string{"redirect": "/"})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]string{"message": "Account erstellt. Bitte melden Sie sich an."})
|
|
}
|
|
|
|
func handleLogout(w http.ResponseWriter, r *http.Request) {
|
|
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil {
|
|
authClient.SignOut(cookie.Value)
|
|
}
|
|
auth.ClearAuthCookies(w)
|
|
http.Redirect(w, r, "/login", http.StatusFound)
|
|
}
|
|
|
|
// isAllowedEmailDomain gates sign-in/register to the firm's email domains.
|
|
// Whitelist is configurable via ALLOWED_EMAIL_DOMAINS (comma-separated),
|
|
// defaulting to hoganlovells.com,hlc.com,hlc.de so legacy and post-merger
|
|
// addresses keep working until IT finishes the domain consolidation.
|
|
// Note: this whitelist intentionally references real DNS domains, not
|
|
// branding.Name — the firm's email domains and the firm's display name are
|
|
// separate concerns and rotate on different cadences.
|
|
func isAllowedEmailDomain(email string) bool {
|
|
parts := strings.SplitN(email, "@", 2)
|
|
if len(parts) != 2 {
|
|
return false
|
|
}
|
|
domain := strings.ToLower(parts[1])
|
|
for _, allowed := range AllowedEmailDomains() {
|
|
if domain == allowed {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func AllowedEmailDomains() []string {
|
|
raw := os.Getenv("ALLOWED_EMAIL_DOMAINS")
|
|
if strings.TrimSpace(raw) == "" {
|
|
return []string{"hoganlovells.com", "hlc.com", "hlc.de"}
|
|
}
|
|
var out []string
|
|
for _, d := range strings.Split(raw, ",") {
|
|
d = strings.TrimSpace(strings.ToLower(d))
|
|
if d != "" {
|
|
out = append(out, d)
|
|
}
|
|
}
|
|
return out
|
|
}
|