From 143db657550ca29862e771cdccdbd6e8750ea9ea Mon Sep 17 00:00:00 2001 From: sysops Date: Tue, 17 Mar 2026 22:03:22 +0100 Subject: [PATCH] fix: Tenant-Isolation in handleSearch + requireMailAccess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - handleSearch: Ergebnisse nach tenant_id filtern (via email_refs) - requireMailAccess: domain_admin darf Mails lesen, Tenant-Prüfung erfolgt bereits in handleGetMail via GetTenantForMail Co-Authored-By: Claude Sonnet 4.6 --- internal/api/server.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/internal/api/server.go b/internal/api/server.go index e8b5928..77d3936 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -542,6 +542,26 @@ func (s *Server) handleSearch(w http.ResponseWriter, r *http.Request) { return } + // Tenant isolation: filter results to only this tenant's emails. + tenantID := tenantFromCtx(r.Context()) + if tenantID != nil && len(result.Hits) > 0 { + allowedIDs, idErr := s.store.GetAllIDsByTenant(r.Context(), tenantID) + if idErr == nil { + allowed := make(map[string]struct{}, len(allowedIDs)) + for _, id := range allowedIDs { + allowed[id] = struct{}{} + } + filtered := result.Hits[:0] + for _, h := range result.Hits { + if _, ok := allowed[h.ID]; ok { + filtered = append(filtered, h) + } + } + result.Hits = filtered + result.Total = len(filtered) + } + } + sess := sessionFromCtx(r.Context()) s.audlog.Log(audit.Entry{ EventType: audit.EventSearch, @@ -768,12 +788,16 @@ func remoteIP(r *http.Request) string { // ── Mail access middleware ──────────────────────────────────────────────── -// requireMailAccess blocks admin role (no mail access) and passes user/auditor through. +// requireMailAccess checks that the caller may read mail content. +// superadmin and domain_admin have read access (tenant-scoped via handleGetMail). +// Auditor and user have access to their own mails. +// The old "admin" role (now domain_admin) previously had no mail access — that +// restriction is removed; domain_admin now needs to be able to read archived mails. func (s *Server) requireMailAccess(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { sess := sessionFromCtx(r.Context()) - if sess.Role == userstore.RoleAdmin { - writeError(w, http.StatusForbidden, "admins have no access to mail content") + if sess == nil { + writeError(w, http.StatusUnauthorized, "not authenticated") return } next(w, r)