package handler import ( "net/http" "codeberg.org/VARASYS/ZDDC/zddc/internal/config" ) // CORSMiddleware applies a per-origin CORS policy keyed off cfg.CORSOrigins. // // On any request whose Origin header exactly matches an entry in the // allowlist, the middleware echoes that origin in Access-Control-Allow-Origin // and sets Access-Control-Allow-Credentials: true (we use credentials so the // reverse-proxy-set email header / cookies cross the boundary). Vary: Origin // is always set when the allowlist is non-empty so caches do not collapse // per-origin responses. // // OPTIONS preflight requests are answered directly with 204 + the appropriate // allow-methods/headers, bypassing later middleware (so preflight does not // require auth). Non-OPTIONS requests pass through to the next handler. // // Requests with no Origin header, or an Origin that does not match the // allowlist, get no CORS response headers — the browser blocks them // naturally. An empty allowlist disables CORS entirely. func CORSMiddleware(cfg config.Config, next http.Handler) http.Handler { if len(cfg.CORSOrigins) == 0 { return next } allow := make(map[string]struct{}, len(cfg.CORSOrigins)) for _, o := range cfg.CORSOrigins { allow[o] = struct{}{} } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") if origin != "" { if _, ok := allow[origin]; ok { h := w.Header() h.Set("Access-Control-Allow-Origin", origin) h.Set("Access-Control-Allow-Credentials", "true") h.Add("Vary", "Origin") if r.Method == http.MethodOptions { if reqHeaders := r.Header.Get("Access-Control-Request-Headers"); reqHeaders != "" { h.Set("Access-Control-Allow-Headers", reqHeaders) } h.Set("Access-Control-Allow-Methods", "GET, HEAD, PUT, DELETE, POST, OPTIONS") // Expose ETag and the file API's source / destination headers // so cross-origin clients can read them after a write. h.Set("Access-Control-Expose-Headers", "ETag, X-ZDDC-Source, X-ZDDC-Destination") h.Set("Access-Control-Max-Age", "600") w.WriteHeader(http.StatusNoContent) return } } } next.ServeHTTP(w, r) }) }