docs(PROJ-72): Feature-Spec nachtragen + Audit-Log-Härtung für abgelehnte Privilege-Checks

Feature-Spec, INDEX.md und features.ts für PROJ-72 (Superadmin-Peer-Patch-Fix)
ergänzt inkl. QA-Ergebnisse. Zusätzlich: abgelehnte Privilege-Escalation-/
Tenant-Isolation-Versuche (403) werden jetzt als Success:false im Audit-Log
protokolliert (vorher nur erfolgreiche Updates) - Härtungspunkt aus QA-Runde.
This commit is contained in:
sysops
2026-07-27 00:08:43 +02:00
parent c580a7c05d
commit 2e952f111d
4 changed files with 102 additions and 1 deletions
+32
View File
@@ -66,6 +66,14 @@ func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) {
// at or above their own level.
sess := sessionFromCtx(r.Context())
if roleLevel(req.Role) >= roleLevel(sess.Role) {
s.audlog.Log(audit.Entry{
EventType: audit.EventUserMgmt,
Username: sess.Username,
TenantID: sess.TenantID,
IPAddress: s.remoteIP(r),
Detail: fmt.Sprintf("denied: insufficient privileges to assign role %q on create", req.Role),
Success: false,
})
writeError(w, http.StatusForbidden, "insufficient privileges to assign this role")
return
}
@@ -148,6 +156,14 @@ func (s *Server) handleUpdateUser(w http.ResponseWriter, r *http.Request) {
}
if sess.TenantID != nil {
if target.TenantID == nil || *target.TenantID != *sess.TenantID {
s.audlog.Log(audit.Entry{
EventType: audit.EventUserMgmt,
Username: sess.Username,
TenantID: sess.TenantID,
IPAddress: s.remoteIP(r),
Detail: fmt.Sprintf("denied: cross-tenant access to user %d", id),
Success: false,
})
writeError(w, http.StatusForbidden, "access denied")
return
}
@@ -159,10 +175,26 @@ func (s *Server) handleUpdateUser(w http.ResponseWriter, r *http.Request) {
// checks would otherwise block superadmin-vs-superadmin management.
if sess.Role != userstore.RoleSuperAdmin {
if roleLevel(target.Role) >= roleLevel(sess.Role) {
s.audlog.Log(audit.Entry{
EventType: audit.EventUserMgmt,
Username: sess.Username,
TenantID: sess.TenantID,
IPAddress: s.remoteIP(r),
Detail: fmt.Sprintf("denied: insufficient privileges to modify user %d", id),
Success: false,
})
writeError(w, http.StatusForbidden, "insufficient privileges to modify this user")
return
}
if req.Role != nil && roleLevel(*req.Role) >= roleLevel(sess.Role) {
s.audlog.Log(audit.Entry{
EventType: audit.EventUserMgmt,
Username: sess.Username,
TenantID: sess.TenantID,
IPAddress: s.remoteIP(r),
Detail: fmt.Sprintf("denied: insufficient privileges to assign role %q to user %d", *req.Role, id),
Success: false,
})
writeError(w, http.StatusForbidden, "insufficient privileges to assign this role")
return
}