Files
paliad/internal/handlers/auth.go
m 460736ad1e refactor(t-paliad-092): rename Go module path patholo → paliad
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.
2026-04-30 16:46:31 +02:00

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
}