fix(auth): ChangePassword unterstützt jetzt DB-User — nicht nur Setup-Admin
Vorher hat ChangePassword das Passwort ausschließlich gegen den setup-store-Admin geprüft und dort gespeichert. User die via User-Management angelegt wurden, bekamen immer 401. Fix: DB-Lookup via Users.FindByEmail, Verify + SetPassword im DB-Store. Setup-Store-Admin bleibt synchron wenn die E-Mail übereinstimmt. Fallback auf setup-store bleibt erhalten für Legacy-Installs (pre-DB). Bonus: %m-Formatverb-Fehler im Scheduler-Alert-Text behoben. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ import (
|
|||||||
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.124"
|
var version = "1.1.125"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.124"
|
var version = "1.1.125"
|
||||||
|
|
||||||
const usage = `edgeguard-ctl — EdgeGuard CLI
|
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.124"
|
var version = "1.1.125"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||||
@@ -441,7 +441,7 @@ func runMemoryCheck(ctx context.Context, a *alerts.Service, d *dedupe) {
|
|||||||
" • Unbound-Cache zu groß (rrset-cache-size in /etc/edgeguard/unbound/unbound.conf)\n"+
|
" • Unbound-Cache zu groß (rrset-cache-size in /etc/edgeguard/unbound/unbound.conf)\n"+
|
||||||
" • Squid cache_mem zu groß (64 MB default)\n"+
|
" • Squid cache_mem zu groß (64 MB default)\n"+
|
||||||
" • PostgreSQL shared_buffers (default ~128 MB)\n"+
|
" • PostgreSQL shared_buffers (default ~128 MB)\n"+
|
||||||
" • Prozesse prüfen: ps aux --sort=-%mem | head -10",
|
" • Prozesse prüfen: ps aux --sort=-%%mem | head -10",
|
||||||
usedPct, usedGB, totalGB)
|
usedPct, usedGB, totalGB)
|
||||||
if _, err := a.Fire(ctx, "mem.high", sev, title, desc); err != nil {
|
if _, err := a.Fire(ctx, "mem.high", sev, title, desc); err != nil {
|
||||||
slog.Warn("scheduler: memory-check alert fire failed", "error", err)
|
slog.Warn("scheduler: memory-check alert fire failed", "error", err)
|
||||||
|
|||||||
@@ -194,12 +194,49 @@ type changePasswordRequest struct {
|
|||||||
// braucht das hier das current_password als Confirmation — verhindert
|
// braucht das hier das current_password als Confirmation — verhindert
|
||||||
// dass eine kompromittierte Session den Account übernimmt ohne dass
|
// dass eine kompromittierte Session den Account übernimmt ohne dass
|
||||||
// das alte Passwort bekannt ist.
|
// das alte Passwort bekannt ist.
|
||||||
|
//
|
||||||
|
// Lookup-Reihenfolge: 1) DB users-Tabelle (alle multi-user-Accounts),
|
||||||
|
// 2) setup-store Admin-Fallback (Legacy / pre-DB). Beim Setup-Admin
|
||||||
|
// werden beide Stores synchron gehalten.
|
||||||
func (h *AuthHandler) ChangePassword(c *gin.Context) {
|
func (h *AuthHandler) ChangePassword(c *gin.Context) {
|
||||||
var req changePasswordRequest
|
var req changePasswordRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
response.BadRequest(c, err)
|
response.BadRequest(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tok := CurrentToken(c)
|
||||||
|
if tok == nil {
|
||||||
|
response.Unauthorized(c, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. DB-backed user (alle via User-Management erstellten Accounts).
|
||||||
|
if h.Users != nil {
|
||||||
|
u, hash, dbErr := h.Users.FindByEmail(c.Request.Context(), tok.Actor)
|
||||||
|
if dbErr == nil {
|
||||||
|
if !usersvc.VerifyPassword(hash, req.CurrentPassword) {
|
||||||
|
response.Unauthorized(c, errors.New("invalid_current_password"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := h.Users.SetPassword(c.Request.Context(), u.ID, req.NewPassword); err != nil {
|
||||||
|
response.Internal(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Setup-Store-Admin synchron halten, falls gleiche E-Mail.
|
||||||
|
if st, _ := h.Setup.Load(); st != nil && strings.EqualFold(st.AdminEmail, tok.Actor) {
|
||||||
|
_ = h.Setup.SetAdminPassword(req.NewPassword)
|
||||||
|
}
|
||||||
|
if h.Audit != nil {
|
||||||
|
_ = h.Audit.Log(c.Request.Context(), actorOf(c), "auth.password.change",
|
||||||
|
tok.Actor, gin.H{"actor": actorOf(c)}, h.NodeID)
|
||||||
|
}
|
||||||
|
response.OK(c, gin.H{"ok": true})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Fallback: setup-store Admin (vor DB-Migration oder nicht migriert).
|
||||||
st, err := h.Setup.Load()
|
st, err := h.Setup.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Internal(c, err)
|
response.Internal(c, err)
|
||||||
@@ -209,10 +246,6 @@ func (h *AuthHandler) ChangePassword(c *gin.Context) {
|
|||||||
response.Err(c, http.StatusServiceUnavailable, errors.New("setup_required"))
|
response.Err(c, http.StatusServiceUnavailable, errors.New("setup_required"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Authorisierte Session ist nicht automatisch der Admin (Phase 4
|
|
||||||
// admin_users-Tabelle könnte mehrere Rollen haben). v1: aktuell
|
|
||||||
// nur der eine Admin-User; trotzdem prüfen wir das current_password
|
|
||||||
// gegen die persistierte Hash.
|
|
||||||
if !st.VerifyAdminPassword(req.CurrentPassword) {
|
if !st.VerifyAdminPassword(req.CurrentPassword) {
|
||||||
response.Unauthorized(c, errors.New("invalid_current_password"))
|
response.Unauthorized(c, errors.New("invalid_current_password"))
|
||||||
return
|
return
|
||||||
@@ -225,10 +258,6 @@ func (h *AuthHandler) ChangePassword(c *gin.Context) {
|
|||||||
_ = h.Audit.Log(c.Request.Context(), actorOf(c), "auth.password.change",
|
_ = h.Audit.Log(c.Request.Context(), actorOf(c), "auth.password.change",
|
||||||
st.AdminEmail, gin.H{"actor": actorOf(c)}, h.NodeID)
|
st.AdminEmail, gin.H{"actor": actorOf(c)}, h.NodeID)
|
||||||
}
|
}
|
||||||
// Neue Session ausstellen — alte Cookie zeigt auf ein Token das
|
|
||||||
// noch gültig ist; das ist OK für UX (kein erzwungener Logout),
|
|
||||||
// sicherheitsbewusster: clearSession + force re-login. Wir
|
|
||||||
// halten's hier ruhig.
|
|
||||||
response.OK(c, gin.H{"ok": true})
|
response.OK(c, gin.H{"ok": true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user