diff --git a/pkg/api/constants/extensions.go b/pkg/api/constants/extensions.go index 72592f70..acd1c25a 100644 --- a/pkg/api/constants/extensions.go +++ b/pkg/api/constants/extensions.go @@ -5,11 +5,17 @@ const ( ExtCatalogPrefix = "/_catalog" ExtOciDiscoverPrefix = "/_oci/ext/discover" // zot specific extensions. - ExtPrefix = "/_zot/ext" - ExtSearchPrefix = ExtPrefix + "/search" - FullSearchPrefix = RoutePrefix + ExtSearchPrefix - ExtMgmtPrefix = ExtPrefix + "/mgmt" - FullMgmtPrefix = RoutePrefix + ExtMgmtPrefix - ExtUserPreferencesPrefix = ExtPrefix + "/userprefs" + ExtPrefix = "/_zot/ext" + + ExtSearch = "/search" + ExtSearchPrefix = ExtPrefix + ExtSearch + FullSearchPrefix = RoutePrefix + ExtSearchPrefix + + ExtMgmt = "/mgmt" + ExtMgmtPrefix = ExtPrefix + ExtMgmt + FullMgmtPrefix = RoutePrefix + ExtMgmtPrefix + + ExtUserPreferences = "/userprefs" + ExtUserPreferencesPrefix = ExtPrefix + ExtUserPreferences FullUserPreferencesPrefix = RoutePrefix + ExtUserPreferencesPrefix ) diff --git a/pkg/api/controller.go b/pkg/api/controller.go index 85ef893d..909d20fd 100644 --- a/pkg/api/controller.go +++ b/pkg/api/controller.go @@ -67,26 +67,6 @@ func NewController(config *config.Config) *Controller { return &controller } -func (c *Controller) CORSHeaders() mux.MiddlewareFunc { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - // CORS - c.CORSHandler(response, request) - - next.ServeHTTP(response, request) - }) - } -} - -func (c *Controller) CORSHandler(response http.ResponseWriter, request *http.Request) { - // allow origin as specified in config if not accept request from anywhere. - if c.Config.HTTP.AllowOrigin == "" { - response.Header().Set("Access-Control-Allow-Origin", "*") - } else { - response.Header().Set("Access-Control-Allow-Origin", c.Config.HTTP.AllowOrigin) - } -} - func DumpRuntimeParams(log log.Logger) { var rLimit syscall.Rlimit @@ -130,7 +110,6 @@ func (c *Controller) Run(reloadCtx context.Context) error { } engine.Use( - c.CORSHeaders(), SessionLogger(c), handlers.RecoveryHandler(handlers.RecoveryLogger(c.Log), handlers.PrintRecoveryStack(false))) diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index 3b7577e1..faaa4d21 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -495,6 +495,7 @@ func TestAllowMethodsHeader(t *testing.T) { conf := config.New() conf.HTTP.Port = port conf.Storage.RootDirectory = dir + conf.HTTP.AllowOrigin = "someOrigin" simpleUser := "simpleUser" simpleUserPassword := "simpleUserPass" diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 3fd77100..9f4fd104 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -69,14 +69,16 @@ func (rh *RouteHandler) SetupRoutes() { prefixedRouter.Use(AuthzHandler(rh.c)) } + applyCORSHeaders := getCORSHeadersHandler(rh.c.Config.HTTP.AllowOrigin) + // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints { prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", zreg.NameRegexp.String()), - rh.ListTags).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.ListTags)).Methods(zcommon.AllowedMethods("GET")...) prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), - rh.CheckManifest).Methods(zcommon.AllowedMethods("HEAD")...) + applyCORSHeaders(rh.CheckManifest)).Methods(zcommon.AllowedMethods("HEAD")...) prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), - rh.GetManifest).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.GetManifest)).Methods(zcommon.AllowedMethods("GET")...) prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), rh.UpdateManifest).Methods("PUT") prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), @@ -99,13 +101,13 @@ func (rh *RouteHandler) SetupRoutes() { rh.DeleteBlobUpload).Methods("DELETE") // support for OCI artifact references prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/referrers/{digest}", zreg.NameRegexp.String()), - rh.GetReferrers).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.GetReferrers)).Methods(zcommon.AllowedMethods("GET")...) prefixedRouter.HandleFunc(constants.ExtCatalogPrefix, - rh.ListRepositories).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.ListRepositories)).Methods(zcommon.AllowedMethods("GET")...) prefixedRouter.HandleFunc(constants.ExtOciDiscoverPrefix, - rh.ListExtensions).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.ListExtensions)).Methods(zcommon.AllowedMethods("GET")...) prefixedRouter.HandleFunc("/", - rh.CheckVersionSupport).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.CheckVersionSupport)).Methods(zcommon.AllowedMethods("GET")...) } // support for ORAS artifact reference types (alpha 1) - image signature use case @@ -122,17 +124,51 @@ func (rh *RouteHandler) SetupRoutes() { prefixedRouter.HandleFunc("/metrics", rh.GetMetrics).Methods("GET") } else { // extended build - ext.SetupMetricsRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, AuthHandler(rh.c), rh.c.Log) - ext.SetupSearchRoutes(rh.c.Config, prefixedRouter, rh.c.StoreController, rh.c.RepoDB, rh.c.CveInfo, rh.c.Log) - ext.SetupUserPreferencesRoutes(rh.c.Config, prefixedRouter, rh.c.StoreController, rh.c.RepoDB, rh.c.CveInfo, + prefixedExtensionsRouter := prefixedRouter.PathPrefix(constants.ExtPrefix).Subrouter() + prefixedExtensionsRouter.Use(CORSHeadersMiddleware(rh.c.Config.HTTP.AllowOrigin)) + + ext.SetupMgmtRoutes(rh.c.Config, prefixedExtensionsRouter, rh.c.Log) + ext.SetupSearchRoutes(rh.c.Config, prefixedExtensionsRouter, rh.c.StoreController, rh.c.RepoDB, rh.c.CveInfo, rh.c.Log) + ext.SetupUserPreferencesRoutes(rh.c.Config, prefixedExtensionsRouter, rh.c.StoreController, rh.c.RepoDB, + rh.c.CveInfo, rh.c.Log) + ext.SetupUIRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log) - ext.SetupMgmtRoutes(rh.c.Config, prefixedRouter, rh.c.Log) + ext.SetupMetricsRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, AuthHandler(rh.c), rh.c.Log) + gqlPlayground.SetupGQLPlaygroundRoutes(rh.c.Config, prefixedRouter, rh.c.StoreController, rh.c.Log) } } } +func CORSHeadersMiddleware(allowOrigin string) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + addCORSHeaders(allowOrigin, response) + + next.ServeHTTP(response, request) + }) + } +} + +func getCORSHeadersHandler(allowOrigin string) func(http.HandlerFunc) http.HandlerFunc { + return func(next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + addCORSHeaders(allowOrigin, response) + + next.ServeHTTP(response, request) + }) + } +} + +func addCORSHeaders(allowOrigin string, response http.ResponseWriter) { + if allowOrigin == "" { + response.Header().Set("Access-Control-Allow-Origin", "*") + } else { + response.Header().Set("Access-Control-Allow-Origin", allowOrigin) + } +} + // Method handlers // CheckVersionSupport godoc diff --git a/pkg/extensions/extension_mgmt.go b/pkg/extensions/extension_mgmt.go index 06d6624f..692aaee6 100644 --- a/pkg/extensions/extension_mgmt.go +++ b/pkg/extensions/extension_mgmt.go @@ -92,6 +92,6 @@ func SetupMgmtRoutes(config *config.Config, router *mux.Router, log log.Logger) mgmt := mgmt{config: config, log: log} - router.PathPrefix(constants.ExtMgmtPrefix).Methods("GET").Handler(addMgmtSecurityHeaders(mgmt.handler())) + router.PathPrefix(constants.ExtMgmt).Methods("GET").Handler(addMgmtSecurityHeaders(mgmt.handler())) } } diff --git a/pkg/extensions/extension_search.go b/pkg/extensions/extension_search.go index 533bee90..b5c6ec93 100644 --- a/pkg/extensions/extension_search.go +++ b/pkg/extensions/extension_search.go @@ -178,7 +178,7 @@ func SetupSearchRoutes(config *config.Config, router *mux.Router, storeControlle if config.Extensions.Search != nil && *config.Extensions.Search.Enable { resConfig := search.GetResolverConfig(log, storeController, repoDB, cveInfo) - extRouter := router.PathPrefix(constants.ExtSearchPrefix).Subrouter() + extRouter := router.PathPrefix(constants.ExtSearch).Subrouter() extRouter.Use(SearchACHeadersHandler()) extRouter.Methods("GET", "POST", "OPTIONS"). Handler(addSearchSecurityHeaders(gqlHandler.NewDefaultServer(gql_generated.NewExecutableSchema(resConfig)))) diff --git a/pkg/extensions/extension_userprefs.go b/pkg/extensions/extension_userprefs.go index 8f6d876d..58505bb9 100644 --- a/pkg/extensions/extension_userprefs.go +++ b/pkg/extensions/extension_userprefs.go @@ -30,7 +30,7 @@ func SetupUserPreferencesRoutes(config *config.Config, router *mux.Router, store if config.Extensions.Search != nil && *config.Extensions.Search.Enable { log.Info().Msg("setting up user preferences routes") - userprefsRouter := router.PathPrefix(constants.ExtUserPreferencesPrefix).Subrouter() + userprefsRouter := router.PathPrefix(constants.ExtUserPreferences).Subrouter() userprefsRouter.Use(UserPrefsACHeadersHandler()) userprefsRouter.HandleFunc("", HandleUserPrefs(repoDB, log)).Methods(zcommon.AllowedMethods(http.MethodPut)...)