From 395d5aed30439f75065c67ce57be470cca133adf Mon Sep 17 00:00:00 2001 From: b5 Date: Wed, 17 Mar 2021 16:33:09 -0400 Subject: [PATCH] fix(api): handle OPTIONS requests on refRoute handlers --- api/api.go | 11 +++++++++++ api/middleware.go | 46 ++++++++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/api/api.go b/api/api.go index 37d918ac1..9e48c641b 100644 --- a/api/api.go +++ b/api/api.go @@ -183,6 +183,16 @@ func handleRefRoute(m *mux.Router, p refRouteParams, f http.HandlerFunc) { for _, route := range routes { if len(p.Methods) > 0 { + hasOptions := false + for _, o := range p.Methods { + if o == http.MethodOptions { + hasOptions = true + break + } + } + if !hasOptions { + p.Methods = append(p.Methods, http.MethodOptions) + } // TODO(b5): this is a band-aid that lets us punt on teaching lib about how to // switch on HTTP verbs. I think we should use tricks like this that leverage // the gorilla/mux package until we get a better sense of how our API uses @@ -202,6 +212,7 @@ func NewServerRoutes(s Server) *mux.Router { if m == nil { m = mux.NewRouter() } + m.Use(corsMiddleware(cfg.API.AllowedOrigins)) m.Use(muxVarsToQueryParamMiddleware) m.Use(refStringMiddleware) m.Use(token.OAuthTokenMiddleware) diff --git a/api/middleware.go b/api/middleware.go index feff73aa5..fc40963f6 100644 --- a/api/middleware.go +++ b/api/middleware.go @@ -26,12 +26,6 @@ func (s Server) mwFunc(handler http.HandlerFunc, shouldLog bool) http.HandlerFun log.Infof("%s %s %s", r.Method, r.URL.Path, time.Now()) } - s.addCORSHeaders(w, r) - if r.Method == http.MethodOptions { - util.EmptyOkHandler(w, r) - return - } - if ok := s.readOnlyCheck(r); ok { handler(w, r) } else { @@ -40,24 +34,36 @@ func (s Server) mwFunc(handler http.HandlerFunc, shouldLog bool) http.HandlerFun } } -func (s *Server) readOnlyCheck(r *http.Request) bool { - return !s.GetConfig().API.ReadOnly || r.Method == "GET" || r.Method == "OPTIONS" -} +// corsMiddleware adds Cross-Origin Resource Sharing headers for any request +// who's origin matches one of allowedOrigins +func corsMiddleware(allowedOrigins []string) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + origin := r.Header.Get("Origin") + for _, o := range allowedOrigins { + if origin == o { + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization") + w.Header().Set("Access-Control-Allow-Credentials", "true") + } + } -// addCORSHeaders adds CORS header info for whitelisted servers -func (s *Server) addCORSHeaders(w http.ResponseWriter, r *http.Request) { - origin := r.Header.Get("Origin") - for _, o := range s.GetConfig().API.AllowedOrigins { - if origin == o { - w.Header().Set("Access-Control-Allow-Origin", origin) - w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization") - w.Header().Set("Access-Control-Allow-Credentials", "true") - return - } + // intercept OPTIONS requests with an early return + if r.Method == http.MethodOptions { + util.EmptyOkHandler(w, r) + return + } + + next.ServeHTTP(w, r) + }) } } +func (s *Server) readOnlyCheck(r *http.Request) bool { + return !s.GetConfig().API.ReadOnly || r.Method == "GET" || r.Method == "OPTIONS" +} + // muxVarsToQueryParamMiddleware moves all mux variables to query parameter // values, failing with an error if a name collision with user-provided query // params occurs