package api import ( "encoding/json" "net/http" "strconv" ) // ── Tenant domain handlers ─────────────────────────────────────────────────── func (s *Server) handleListTenantDomains(w http.ResponseWriter, r *http.Request) { if s.tenantStore == nil { writeError(w, http.StatusServiceUnavailable, "tenant store not available") return } id, err := parseTenantID(r) if err != nil { 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") return } writeJSON(w, http.StatusOK, domains) } func (s *Server) handleAddTenantDomain(w http.ResponseWriter, r *http.Request) { if s.tenantStore == nil { writeError(w, http.StatusServiceUnavailable, "tenant store not available") return } id, err := parseTenantID(r) if err != nil { 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 } var req struct { Domain string `json:"domain"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Domain == "" { writeError(w, http.StatusBadRequest, "domain is required") return } domain, err := s.tenantStore.AddDomain(r.Context(), id, req.Domain) if err != nil { writeError(w, http.StatusInternalServerError, "failed to add domain") return } writeJSON(w, http.StatusCreated, domain) } func (s *Server) handleRemoveTenantDomain(w http.ResponseWriter, r *http.Request) { if s.tenantStore == nil { writeError(w, http.StatusServiceUnavailable, "tenant store not available") return } tenantID, err := parseTenantID(r) if err != nil { 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 { writeError(w, http.StatusBadRequest, "invalid domain id") return } if err := s.tenantStore.RemoveDomain(r.Context(), tenantID, domainID); err != nil { writeError(w, http.StatusInternalServerError, "failed to remove domain") return } w.WriteHeader(http.StatusNoContent) }