From eedb711f2310e3a7d36ccfd392027e04dabe88aa Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 22 Dec 2023 21:04:20 +0800 Subject: [PATCH 1/5] fix --- custom/conf/app.example.ini | 8 +- .../config-cheat-sheet.en-us.md | 4 +- .../config-cheat-sheet.zh-cn.md | 2 - modules/public/public.go | 2 +- modules/setting/cors.go | 4 +- modules/web/route.go | 14 ---- routers/api/v1/api.go | 4 +- routers/web/githttp.go | 22 ++--- routers/web/misc/misc.go | 4 - routers/web/web.go | 45 ++++++---- tests/integration/cors_test.go | 84 +++++++++++++++++-- 11 files changed, 121 insertions(+), 72 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index f9111d541c738..0fcf895b5a292 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1158,15 +1158,9 @@ LEVEL = Info ;; enable cors headers (disabled by default) ;ENABLED = false ;; -;; scheme of allowed requests -;SCHEME = http -;; -;; list of requesting domains that are allowed +;; list of requesting origins that are allowed, eg: "https://*.example.com" ;ALLOW_DOMAIN = * ;; -;; allow subdomains of headers listed above to request -;ALLOW_SUBDOMAIN = false -;; ;; list of methods allowed to request ;METHODS = GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS ;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 1ba5dd04cc406..cd1f31fccd09f 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -196,9 +196,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a ## CORS (`cors`) - `ENABLED`: **false**: enable cors headers (disabled by default) -- `SCHEME`: **http**: scheme of allowed requests -- `ALLOW_DOMAIN`: **\***: list of requesting domains that are allowed -- `ALLOW_SUBDOMAIN`: **false**: allow subdomains of headers listed above to request +- `ALLOW_DOMAIN`: **\***: list of requesting origins that are allowed, eg: "https://*.example.com" - `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request - `MAX_AGE`: **10m**: max time to cache response - `ALLOW_CREDENTIALS`: **false**: allow request with credentials diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index 434d69815b19e..f85da439cd6d9 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -195,9 +195,7 @@ menu: ## 跨域 (`cors`) - `ENABLED`: **false**: 启用 CORS 头部(默认禁用) -- `SCHEME`: **http**: 允许请求的协议 - `ALLOW_DOMAIN`: **\***: 允许请求的域名列表 -- `ALLOW_SUBDOMAIN`: **false**: 允许上述列出的头部的子域名发出请求。 - `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: 允许发起的请求方式列表 - `MAX_AGE`: **10m**: 缓存响应的最大时间 - `ALLOW_CREDENTIALS`: **false**: 允许带有凭据的请求 diff --git a/modules/public/public.go b/modules/public/public.go index 5fbfe30a81c7e..e9b9f58ff22f2 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -33,7 +33,7 @@ func FileHandlerFunc() http.HandlerFunc { assetFS := AssetFS() return func(resp http.ResponseWriter, req *http.Request) { if req.Method != "GET" && req.Method != "HEAD" { - resp.WriteHeader(http.StatusNotFound) + resp.WriteHeader(http.StatusBadRequest) return } handleRequest(resp, req, assetFS, req.URL.Path) diff --git a/modules/setting/cors.go b/modules/setting/cors.go index bafbbab64f990..9e539d434ff2b 100644 --- a/modules/setting/cors.go +++ b/modules/setting/cors.go @@ -12,9 +12,7 @@ import ( // CORSConfig defines CORS settings var CORSConfig = struct { Enabled bool - Scheme string - AllowDomain []string - AllowSubdomain bool + AllowDomain []string // this option is from legacy code, it should be called "AllowedOrigins" actually Methods []string MaxAge time.Duration AllowCredentials bool diff --git a/modules/web/route.go b/modules/web/route.go index 86b83dd72362c..397e04f5c22fb 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -136,20 +136,6 @@ func (r *Route) Get(pattern string, h ...any) { r.Methods("GET", pattern, h...) } -func (r *Route) Options(pattern string, h ...any) { - r.Methods("OPTIONS", pattern, h...) -} - -// GetOptions delegate get and options method -func (r *Route) GetOptions(pattern string, h ...any) { - r.Methods("GET,OPTIONS", pattern, h...) -} - -// PostOptions delegate post and options method -func (r *Route) PostOptions(pattern string, h ...any) { - r.Methods("POST,OPTIONS", pattern, h...) -} - // Head delegate head method func (r *Route) Head(pattern string, h ...any) { r.Methods("HEAD", pattern, h...) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 0e437bb92ec0e..a4c3d6f4440ae 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -822,9 +822,7 @@ func Routes() *web.Route { m.Use(securityHeaders()) if setting.CORSConfig.Enabled { m.Use(cors.Handler(cors.Options{ - // Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option - AllowedOrigins: setting.CORSConfig.AllowDomain, - // setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option + AllowedOrigins: setting.CORSConfig.AllowDomain, AllowedMethods: setting.CORSConfig.Methods, AllowCredentials: setting.CORSConfig.AllowCredentials, AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP"}, setting.CORSConfig.Headers...), diff --git a/routers/web/githttp.go b/routers/web/githttp.go index b2fb5b472f772..8d0d1ce03a3d4 100644 --- a/routers/web/githttp.go +++ b/routers/web/githttp.go @@ -28,16 +28,16 @@ func requireSignIn(ctx *context.Context) { func gitHTTPRouters(m *web.Route) { m.Group("", func() { - m.PostOptions("/git-upload-pack", repo.ServiceUploadPack) - m.PostOptions("/git-receive-pack", repo.ServiceReceivePack) - m.GetOptions("/info/refs", repo.GetInfoRefs) - m.GetOptions("/HEAD", repo.GetTextFile("HEAD")) - m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) - m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) - m.GetOptions("/objects/info/packs", repo.GetInfoPacks) - m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile("")) - m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) - m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) - m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) + m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack) + m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack) + m.Methods("GET,OPTIONS", "/info/refs", repo.GetInfoRefs) + m.Methods("GET,OPTIONS", "/HEAD", repo.GetTextFile("HEAD")) + m.Methods("GET,OPTIONS", "/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) + m.Methods("GET,OPTIONS", "/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) + m.Methods("GET,OPTIONS", "/objects/info/packs", repo.GetInfoPacks) + m.Methods("GET,OPTIONS", "/objects/info/{file:[^/]*}", repo.GetTextFile("")) + m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) + m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) + m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb()) } diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go index e35199401074d..54c93763f6a00 100644 --- a/routers/web/misc/misc.go +++ b/routers/web/misc/misc.go @@ -33,10 +33,6 @@ func DummyOK(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) } -func DummyBadRequest(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusBadRequest) -} - func RobotsTxt(w http.ResponseWriter, req *http.Request) { robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt") if ok, _ := util.IsExist(robotsTxt); !ok { diff --git a/routers/web/web.go b/routers/web/web.go index 359b608c71e72..48119243d3a70 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -61,12 +61,11 @@ const ( ) // CorsHandler return a http handler who set CORS options if enabled by config -func CorsHandler() func(next http.Handler) http.Handler { +func optionsCorsHandler() func(next http.Handler) http.Handler { + var corsHandler func(next http.Handler) http.Handler if setting.CORSConfig.Enabled { - return cors.Handler(cors.Options{ - // Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option - AllowedOrigins: setting.CORSConfig.AllowDomain, - // setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option + corsHandler = cors.Handler(cors.Options{ + AllowedOrigins: setting.CORSConfig.AllowDomain, AllowedMethods: setting.CORSConfig.Methods, AllowCredentials: setting.CORSConfig.AllowCredentials, AllowedHeaders: setting.CORSConfig.Headers, @@ -75,7 +74,22 @@ func CorsHandler() func(next http.Handler) http.Handler { } return func(next http.Handler) http.Handler { - return next + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodOptions { + if corsHandler != nil && r.Header.Get("Access-Control-Request-Method") != "" { + corsHandler(next).ServeHTTP(w, r) + } else { + // it should explicitly deny OPTIONS requests if CORS is disabled, to avoid the following GET/POST handler to be called by the OPTIONS request + w.WriteHeader(http.StatusMethodNotAllowed) + } + return + } + if corsHandler != nil { + corsHandler(next).ServeHTTP(w, r) + } else { + next.ServeHTTP(w, r) + } + }) } } @@ -218,7 +232,7 @@ func Routes() *web.Route { routes := web.NewRoute() routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler - routes.Methods("GET, HEAD", "/assets/*", CorsHandler(), public.FileHandlerFunc()) + routes.Methods("GET, HEAD, OPTIONS", "/assets/*", optionsCorsHandler(), public.FileHandlerFunc()) routes.Methods("GET, HEAD", "/avatars/*", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) routes.Methods("GET, HEAD", "/repo-avatars/*", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) routes.Methods("GET, HEAD", "/apple-touch-icon.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png")) @@ -458,8 +472,8 @@ func registerRoutes(m *web.Route) { m.Get("/change-password", func(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/user/settings/account") }) - m.Any("/*", CorsHandler(), public.FileHandlerFunc()) - }, CorsHandler()) + m.Methods("GET, HEAD", "/*", public.FileHandlerFunc()) + }, optionsCorsHandler()) m.Group("/explore", func() { m.Get("", func(ctx *context.Context) { @@ -532,14 +546,11 @@ func registerRoutes(m *web.Route) { // TODO manage redirection m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) }, ignSignInAndCsrf, reqSignIn) - m.Options("/login/oauth/userinfo", CorsHandler(), misc.DummyBadRequest) - m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth) - m.Options("/login/oauth/access_token", CorsHandler(), misc.DummyBadRequest) - m.Post("/login/oauth/access_token", CorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) - m.Options("/login/oauth/keys", CorsHandler(), misc.DummyBadRequest) - m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys) - m.Options("/login/oauth/introspect", CorsHandler(), misc.DummyBadRequest) - m.Post("/login/oauth/introspect", CorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) + + m.Methods("GET, OPTIONS", "/login/oauth/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth) + m.Methods("POST, OPTIONS", "/login/oauth/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) + m.Methods("GET, OPTIONS", "/login/oauth/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys) + m.Methods("POST, OPTIONS", "/login/oauth/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) m.Group("/user/settings", func() { m.Get("", user_setting.Profile) diff --git a/tests/integration/cors_test.go b/tests/integration/cors_test.go index 83d200402c86b..f84cd3e25e116 100644 --- a/tests/integration/cors_test.go +++ b/tests/integration/cors_test.go @@ -7,17 +7,87 @@ import ( "net/http" "testing" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/routers" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) -func TestCORSNotSet(t *testing.T) { +func TestCORS(t *testing.T) { defer tests.PrepareTestEnv(t)() - req := NewRequest(t, "GET", "/api/v1/version") - session := loginUser(t, "user2") - resp := session.MakeRequest(t, req, http.StatusOK) - assert.Equal(t, resp.Code, http.StatusOK) - corsHeader := resp.Header().Get("Access-Control-Allow-Origin") - assert.Empty(t, corsHeader, "Access-Control-Allow-Origin: generated header should match") // header not set + t.Run("CORS enabled", func(t *testing.T) { + defer test.MockVariableValue(&setting.CORSConfig.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + t.Run("API with CORS", func(t *testing.T) { + // GET api with no CORS header + req := NewRequest(t, "GET", "/api/v1/version") + resp := MakeRequest(t, req, http.StatusOK) + assert.Empty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.Contains(t, resp.Header().Values("Vary"), "Origin") + + // OPTIONS api for CORS + req = NewRequest(t, "OPTIONS", "/api/v1/version"). + SetHeader("Origin", "https://example.com"). + SetHeader("Access-Control-Request-Method", "GET") + resp = MakeRequest(t, req, http.StatusOK) + assert.NotEmpty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.Contains(t, resp.Header().Values("Vary"), "Origin") + }) + + t.Run("Web with CORS", func(t *testing.T) { + // GET userinfo with no CORS header + req := NewRequest(t, "GET", "/login/oauth/userinfo") + resp := MakeRequest(t, req, http.StatusUnauthorized) + assert.Empty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.Contains(t, resp.Header().Values("Vary"), "Origin") + + // OPTIONS userinfo for CORS + req = NewRequest(t, "OPTIONS", "/login/oauth/userinfo"). + SetHeader("Origin", "https://example.com"). + SetHeader("Access-Control-Request-Method", "GET") + resp = MakeRequest(t, req, http.StatusOK) + assert.NotEmpty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.Contains(t, resp.Header().Values("Vary"), "Origin") + + // OPTIONS userinfo for non-CORS + req = NewRequest(t, "OPTIONS", "/login/oauth/userinfo") + resp = MakeRequest(t, req, http.StatusMethodNotAllowed) + }) + }) + + t.Run("CORS disabled", func(t *testing.T) { + defer test.MockVariableValue(&setting.CORSConfig.Enabled, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + t.Run("API without CORS", func(t *testing.T) { + req := NewRequest(t, "GET", "/api/v1/version") + resp := MakeRequest(t, req, http.StatusOK) + assert.Empty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.Empty(t, resp.Header().Values("Vary")) + + req = NewRequest(t, "OPTIONS", "/api/v1/version"). + SetHeader("Origin", "https://example.com"). + SetHeader("Access-Control-Request-Method", "GET") + resp = MakeRequest(t, req, http.StatusMethodNotAllowed) + assert.Empty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.Empty(t, resp.Header().Values("Vary")) + }) + + t.Run("Web without CORS", func(t *testing.T) { + req := NewRequest(t, "GET", "/login/oauth/userinfo") + resp := MakeRequest(t, req, http.StatusUnauthorized) + assert.Empty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.NotContains(t, resp.Header().Values("Vary"), "Origin") + + req = NewRequest(t, "OPTIONS", "/login/oauth/userinfo"). + SetHeader("Origin", "https://example.com"). + SetHeader("Access-Control-Request-Method", "GET") + resp = MakeRequest(t, req, http.StatusMethodNotAllowed) + assert.Empty(t, resp.Header().Get("Access-Control-Allow-Origin")) + assert.NotContains(t, resp.Header().Values("Vary"), "Origin") + }) + }) } From e6741b99ace14d598e324e35cd8cfedd0fe9a737 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 22 Dec 2023 22:31:46 +0800 Subject: [PATCH 2/5] make "/attachments/{uuid}" use CORS --- routers/web/web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/web.go b/routers/web/web.go index 48119243d3a70..0a6e7bc291cfe 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -780,7 +780,7 @@ func registerRoutes(m *web.Route) { m.Group("", func() { m.Get("/{username}", user.UsernameSubRoute) - m.Get("/attachments/{uuid}", repo.GetAttachment) + m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment) }, ignSignIn) m.Post("/{username}", reqSignIn, context_service.UserAssignmentWeb(), user.Action) From edf3e0463de66633d52a6f51ff3960edae18158d Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 22 Dec 2023 22:36:39 +0800 Subject: [PATCH 3/5] improve --- modules/public/public.go | 2 +- routers/web/web.go | 3 ++- tests/integration/cors_test.go | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/public/public.go b/modules/public/public.go index e9b9f58ff22f2..abc6b46158027 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -33,7 +33,7 @@ func FileHandlerFunc() http.HandlerFunc { assetFS := AssetFS() return func(resp http.ResponseWriter, req *http.Request) { if req.Method != "GET" && req.Method != "HEAD" { - resp.WriteHeader(http.StatusBadRequest) + resp.WriteHeader(http.StatusMethodNotAllowed) return } handleRequest(resp, req, assetFS, req.URL.Path) diff --git a/routers/web/web.go b/routers/web/web.go index 0a6e7bc291cfe..d6c3ac19edfe6 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -79,11 +79,12 @@ func optionsCorsHandler() func(next http.Handler) http.Handler { if corsHandler != nil && r.Header.Get("Access-Control-Request-Method") != "" { corsHandler(next).ServeHTTP(w, r) } else { - // it should explicitly deny OPTIONS requests if CORS is disabled, to avoid the following GET/POST handler to be called by the OPTIONS request + // it should explicitly deny OPTIONS requests if CORS handler is executed, to avoid the following GET/POST handler to be incorrectly called by the OPTIONS request w.WriteHeader(http.StatusMethodNotAllowed) } return } + // for non-OPTIONS requests, call the CORS handler to add some related headers like "Vary" if corsHandler != nil { corsHandler(next).ServeHTTP(w, r) } else { diff --git a/tests/integration/cors_test.go b/tests/integration/cors_test.go index f84cd3e25e116..25dfbabf41e1b 100644 --- a/tests/integration/cors_test.go +++ b/tests/integration/cors_test.go @@ -55,6 +55,7 @@ func TestCORS(t *testing.T) { // OPTIONS userinfo for non-CORS req = NewRequest(t, "OPTIONS", "/login/oauth/userinfo") resp = MakeRequest(t, req, http.StatusMethodNotAllowed) + assert.NotContains(t, resp.Header().Values("Vary"), "Origin") }) }) From 2f262287988420726e225866e55605543d6302b4 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 23 Dec 2023 01:07:07 +0800 Subject: [PATCH 4/5] fix comments --- modules/web/route.go | 10 ++++++---- routers/web/web.go | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/web/route.go b/modules/web/route.go index 397e04f5c22fb..805fcb4411529 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -101,16 +101,18 @@ func (r *Route) wrapMiddlewareAndHandler(h []any) ([]func(http.Handler) http.Han return middlewares, handlerFunc } -func (r *Route) Methods(method, pattern string, h ...any) { +// Methods adds the same handlers for multiple http "methods" (separated by ","). +// If any method is invalid, the lower level router will panic. +func (r *Route) Methods(methods, pattern string, h ...any) { middlewares, handlerFunc := r.wrapMiddlewareAndHandler(h) fullPattern := r.getPattern(pattern) - if strings.Contains(method, ",") { - methods := strings.Split(method, ",") + if strings.Contains(methods, ",") { + methods := strings.Split(methods, ",") for _, method := range methods { r.R.With(middlewares...).Method(strings.TrimSpace(method), fullPattern, handlerFunc) } } else { - r.R.With(middlewares...).Method(method, fullPattern, handlerFunc) + r.R.With(middlewares...).Method(methods, fullPattern, handlerFunc) } } diff --git a/routers/web/web.go b/routers/web/web.go index d6c3ac19edfe6..dff90eedc2184 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -60,7 +60,7 @@ const ( GzipMinSize = 1400 ) -// CorsHandler return a http handler who set CORS options if enabled by config +// optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests. func optionsCorsHandler() func(next http.Handler) http.Handler { var corsHandler func(next http.Handler) http.Handler if setting.CORSConfig.Enabled { @@ -79,7 +79,7 @@ func optionsCorsHandler() func(next http.Handler) http.Handler { if corsHandler != nil && r.Header.Get("Access-Control-Request-Method") != "" { corsHandler(next).ServeHTTP(w, r) } else { - // it should explicitly deny OPTIONS requests if CORS handler is executed, to avoid the following GET/POST handler to be incorrectly called by the OPTIONS request + // it should explicitly deny OPTIONS requests if CORS handler is not executed, to avoid the next GET/POST handler being incorrectly called by the OPTIONS request w.WriteHeader(http.StatusMethodNotAllowed) } return From 4ce6f03d36437426ccb43da45d45dfe53921cd00 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 24 Dec 2023 23:56:15 +0800 Subject: [PATCH 5/5] Update modules/setting/cors.go --- modules/setting/cors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/setting/cors.go b/modules/setting/cors.go index 9e539d434ff2b..63daaad60ba75 100644 --- a/modules/setting/cors.go +++ b/modules/setting/cors.go @@ -12,7 +12,7 @@ import ( // CORSConfig defines CORS settings var CORSConfig = struct { Enabled bool - AllowDomain []string // this option is from legacy code, it should be called "AllowedOrigins" actually + AllowDomain []string // FIXME: this option is from legacy code, it actually works as "AllowedOrigins". When refactoring in the future, the config option should also be renamed together. Methods []string MaxAge time.Duration AllowCredentials bool