fix(PROJ-63): Defense-in-Depth Tenant-Scope-Härtung der Admin-Endpunkte

tenantAccessAllowed()-Check in allen {id}-Handlern von tenant_handlers.go,
tenant_domain_handlers.go und tenant_logo_handlers.go ergänzt — No-op für
globale Admins, zweite Verteidigungslinie für hypothetische tenant-gebundene
Admin-Sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-06-30 14:37:39 +02:00
co-authored by Claude Sonnet 4.6
parent c1338f6721
commit dcb88317ac
6 changed files with 88 additions and 2 deletions
+16
View File
@@ -18,6 +18,11 @@ func (s *Server) handleListTenantDomains(w http.ResponseWriter, r *http.Request)
writeError(w, http.StatusBadRequest, "invalid tenant id")
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
domains, err := s.tenantStore.ListDomains(r.Context(), id)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to list domains")
@@ -37,6 +42,12 @@ func (s *Server) handleAddTenantDomain(w http.ResponseWriter, r *http.Request) {
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
var req struct {
Domain string `json:"domain"`
}
@@ -63,6 +74,11 @@ func (s *Server) handleRemoveTenantDomain(w http.ResponseWriter, r *http.Request
writeError(w, http.StatusBadRequest, "invalid tenant id")
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &tenantID) {
writeError(w, http.StatusForbidden, "access denied")
return
}
didStr := r.PathValue("did")
domainID, err := strconv.ParseInt(didStr, 10, 64)
if err != nil {
+21
View File
@@ -117,6 +117,11 @@ func (s *Server) handleGetTenant(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusBadRequest, "invalid tenant id")
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
tenant, err := s.tenantStore.Get(r.Context(), id)
if err != nil {
writeError(w, http.StatusNotFound, "tenant not found")
@@ -136,6 +141,12 @@ func (s *Server) handleUpdateTenant(w http.ResponseWriter, r *http.Request) {
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
var req struct {
Name string `json:"name"`
Active *bool `json:"active"`
@@ -181,6 +192,11 @@ func (s *Server) handleDeleteTenant(w http.ResponseWriter, r *http.Request) {
}
sess := sessionFromCtx(r.Context())
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sess, &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
if err := s.tenantStore.Delete(r.Context(), id); err != nil {
writeError(w, http.StatusInternalServerError, "failed to delete tenant")
return
@@ -204,6 +220,11 @@ func (s *Server) handleListTenantUsers(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusBadRequest, "invalid tenant id")
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &tenantID) {
writeError(w, http.StatusForbidden, "access denied")
return
}
users, err := s.users.ListByTenant(r.Context(), tenantID)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to list tenant users")
+10
View File
@@ -57,6 +57,11 @@ func (s *Server) handleUploadTenantLogo(w http.ResponseWriter, r *http.Request)
writeError(w, http.StatusBadRequest, "invalid tenant id")
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
s.saveTenantLogo(w, r, id)
}
@@ -70,6 +75,11 @@ func (s *Server) handleDeleteTenantLogo(w http.ResponseWriter, r *http.Request)
writeError(w, http.StatusBadRequest, "invalid tenant id")
return
}
// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
writeError(w, http.StatusForbidden, "access denied")
return
}
if err := s.tenantStore.DeleteLogo(r.Context(), id); err != nil {
writeError(w, http.StatusInternalServerError, "failed to delete logo")
return