diff --git a/pkg/api/authn.go b/pkg/api/authn.go index 3a8845df..34ade183 100644 --- a/pkg/api/authn.go +++ b/pkg/api/authn.go @@ -55,7 +55,7 @@ type AuthnMiddleware struct { func AuthHandler(ctlr *Controller) mux.MiddlewareFunc { authnMiddleware := &AuthnMiddleware{} - if isBearerAuthEnabled(ctlr.Config) { + if ctlr.Config.IsBearerAuthEnabled() { return bearerAuthHandler(ctlr) } @@ -276,8 +276,8 @@ func (amw *AuthnMiddleware) TryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun delay := ctlr.Config.HTTP.Auth.FailDelay // setup sessions cookie store used to preserve logged in user in web sessions - if isAuthnEnabled(ctlr.Config) || isOpenIDAuthEnabled(ctlr.Config) { - // To store custom types in our cookies, + if ctlr.Config.IsBasicAuthnEnabled() { + // To store custom types in our cookies // we must first register them using gob.Register gob.Register(map[string]interface{}{}) @@ -375,10 +375,10 @@ func (amw *AuthnMiddleware) TryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun ctlr.RelyingParties = make(map[string]rp.RelyingParty) for provider := range ctlr.Config.HTTP.Auth.OpenID.Providers { - if IsOpenIDSupported(provider) { + if config.IsOpenIDSupported(provider) { rp := NewRelyingPartyOIDC(ctlr.Config, provider) ctlr.RelyingParties[provider] = rp - } else if IsOauth2Supported(provider) { + } else if config.IsOauth2Supported(provider) { rp := NewRelyingPartyGithub(ctlr.Config, provider) ctlr.RelyingParties[provider] = rp } @@ -572,31 +572,31 @@ func NewRelyingPartyGithub(config *config.Config, provider string) rp.RelyingPar return relyingParty } -func getRelyingPartyArgs(config *config.Config, provider string) ( +func getRelyingPartyArgs(cfg *config.Config, provider string) ( string, string, string, string, []string, []rp.Option, ) { - if _, ok := config.HTTP.Auth.OpenID.Providers[provider]; !ok { + if _, ok := cfg.HTTP.Auth.OpenID.Providers[provider]; !ok { panic(zerr.ErrOpenIDProviderDoesNotExist) } scheme := "http" - if config.HTTP.TLS != nil { + if cfg.HTTP.TLS != nil { scheme = "https" } - clientID := config.HTTP.Auth.OpenID.Providers[provider].ClientID - clientSecret := config.HTTP.Auth.OpenID.Providers[provider].ClientSecret + clientID := cfg.HTTP.Auth.OpenID.Providers[provider].ClientID + clientSecret := cfg.HTTP.Auth.OpenID.Providers[provider].ClientSecret - scopes := config.HTTP.Auth.OpenID.Providers[provider].Scopes + scopes := cfg.HTTP.Auth.OpenID.Providers[provider].Scopes // openid scope must be the first one in list - if !common.Contains(scopes, oidc.ScopeOpenID) && IsOpenIDSupported(provider) { + if !common.Contains(scopes, oidc.ScopeOpenID) && config.IsOpenIDSupported(provider) { scopes = append([]string{oidc.ScopeOpenID}, scopes...) } - port := config.HTTP.Port - issuer := config.HTTP.Auth.OpenID.Providers[provider].Issuer - keyPath := config.HTTP.Auth.OpenID.Providers[provider].KeyPath - baseURL := net.JoinHostPort(config.HTTP.Address, port) + port := cfg.HTTP.Port + issuer := cfg.HTTP.Auth.OpenID.Providers[provider].Issuer + keyPath := cfg.HTTP.Auth.OpenID.Providers[provider].KeyPath + baseURL := net.JoinHostPort(cfg.HTTP.Address, port) redirectURI := fmt.Sprintf("%s://%s%s", scheme, baseURL, constants.CallbackBasePath+fmt.Sprintf("/%s", provider)) options := []rp.Option{ @@ -631,40 +631,6 @@ func getReqContextWithAuthorization(username string, groups []string, request *h return ctx } -func isAuthnEnabled(config *config.Config) bool { - if config.HTTP.Auth != nil && - (config.HTTP.Auth.HTPasswd.Path != "" || config.HTTP.Auth.LDAP != nil) { - return true - } - - return false -} - -func isBearerAuthEnabled(config *config.Config) bool { - if config.HTTP.Auth != nil && - config.HTTP.Auth.Bearer != nil && - config.HTTP.Auth.Bearer.Cert != "" && - config.HTTP.Auth.Bearer.Realm != "" && - config.HTTP.Auth.Bearer.Service != "" { - return true - } - - return false -} - -func isOpenIDAuthEnabled(config *config.Config) bool { - if config.HTTP.Auth != nil && - config.HTTP.Auth.OpenID != nil { - for provider := range config.HTTP.Auth.OpenID.Providers { - if isOpenIDAuthProviderEnabled(config, provider) { - return true - } - } - } - - return false -} - func isAPIKeyEnabled(config *config.Config) bool { if config.Extensions != nil && config.Extensions.APIKey != nil && *config.Extensions.APIKey.Enable { @@ -674,35 +640,6 @@ func isAPIKeyEnabled(config *config.Config) bool { return false } -func isOpenIDAuthProviderEnabled(config *config.Config, provider string) bool { - if providerConfig, ok := config.HTTP.Auth.OpenID.Providers[provider]; ok { - if IsOpenIDSupported(provider) { - if providerConfig.ClientID != "" || providerConfig.Issuer != "" || - len(providerConfig.Scopes) > 0 { - return true - } - } else if IsOauth2Supported(provider) { - if providerConfig.ClientID != "" || len(providerConfig.Scopes) > 0 { - return true - } - } - } - - return false -} - -func IsOpenIDSupported(provider string) bool { - supported := []string{"google", "gitlab", "dex"} - - return common.Contains(supported, provider) -} - -func IsOauth2Supported(provider string) bool { - supported := []string{"github"} - - return common.Contains(supported, provider) -} - func authFail(w http.ResponseWriter, r *http.Request, realm string, delay int) { time.Sleep(time.Duration(delay) * time.Second) diff --git a/pkg/api/authz.go b/pkg/api/authz.go index 6b3bcb3d..9bedef24 100644 --- a/pkg/api/authz.go +++ b/pkg/api/authz.go @@ -276,7 +276,7 @@ func BaseAuthzHandler(ctlr *Controller) mux.MiddlewareFunc { acCtx := &localCtx.AccessControlContext{} // get username from context made in authn.go - if isAuthnEnabled(ctlr.Config) { + if ctlr.Config.IsBasicAuthnEnabled() { // get access control context made in authn.go if authn is enabled acCtx, err = localCtx.GetAccessControlContext(request.Context()) if err != nil { // should never happen diff --git a/pkg/api/config/config.go b/pkg/api/config/config.go index 1e038d7e..9d07e6fb 100644 --- a/pkg/api/config/config.go +++ b/pkg/api/config/config.go @@ -16,6 +16,10 @@ var ( ReleaseTag string //nolint: gochecknoglobals BinaryType string //nolint: gochecknoglobals GoVersion string //nolint: gochecknoglobals + + openIDSupportedProviders = [...]string{"google", "gitlab", "dex"} //nolint: gochecknoglobals + oauth2SupportedProviders = [...]string{"github"} //nolint: gochecknoglobals + ) type StorageConfig struct { @@ -227,3 +231,99 @@ func (c *Config) Sanitize() *Config { return sanitizedConfig } + +func (c *Config) IsLdapAuthEnabled() bool { + if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil { + return true + } + + return false +} + +func (c *Config) IsHtpasswdAuthEnabled() bool { + if c.HTTP.Auth != nil && c.HTTP.Auth.HTPasswd.Path != "" { + return true + } + + return false +} + +func (c *Config) IsBearerAuthEnabled() bool { + if c.HTTP.Auth != nil && + c.HTTP.Auth.Bearer != nil && + c.HTTP.Auth.Bearer.Cert != "" && + c.HTTP.Auth.Bearer.Realm != "" && + c.HTTP.Auth.Bearer.Service != "" { + return true + } + + return false +} + +func (c *Config) IsOpenIDAuthEnabled() bool { + if c.HTTP.Auth != nil && + c.HTTP.Auth.OpenID != nil { + for provider := range c.HTTP.Auth.OpenID.Providers { + if isOpenIDAuthProviderEnabled(c, provider) { + return true + } + } + } + + return false +} + +func (c *Config) IsAPIKeyEnabled() bool { + if c.Extensions != nil && c.Extensions.APIKey != nil && + *c.Extensions.APIKey.Enable { + return true + } + + return false +} + +func (c *Config) IsBasicAuthnEnabled() bool { + if c.IsHtpasswdAuthEnabled() || c.IsLdapAuthEnabled() || + c.IsOpenIDAuthEnabled() || c.IsAPIKeyEnabled() { + return true + } + + return false +} + +func isOpenIDAuthProviderEnabled(config *Config, provider string) bool { + if providerConfig, ok := config.HTTP.Auth.OpenID.Providers[provider]; ok { + if IsOpenIDSupported(provider) { + if providerConfig.ClientID != "" || providerConfig.Issuer != "" || + len(providerConfig.Scopes) > 0 { + return true + } + } else if IsOauth2Supported(provider) { + if providerConfig.ClientID != "" || len(providerConfig.Scopes) > 0 { + return true + } + } + } + + return false +} + +func IsOpenIDSupported(provider string) bool { + for _, supportedProvider := range openIDSupportedProviders { + if supportedProvider == provider { + return true + } + } + + return false +} + +func IsOauth2Supported(provider string) bool { + for _, supportedProvider := range oauth2SupportedProviders { + if supportedProvider == provider { + return true + } + } + + return false +} diff --git a/pkg/api/controller.go b/pkg/api/controller.go index 8b0d93ce..0696e078 100644 --- a/pkg/api/controller.go +++ b/pkg/api/controller.go @@ -261,7 +261,7 @@ func (c *Controller) InitImageStore() error { func (c *Controller) InitMetaDB(reloadCtx context.Context) error { // init metaDB if search is enabled or authn enabled (need to store user profiles) or apikey ext is enabled if (c.Config.Extensions != nil && c.Config.Extensions.Search != nil && *c.Config.Extensions.Search.Enable) || - isAuthnEnabled(c.Config) || isOpenIDAuthEnabled(c.Config) || isAPIKeyEnabled(c.Config) { + c.Config.IsBasicAuthnEnabled() { driver, err := meta.New(c.Config.Storage.StorageConfig, c.Log) //nolint:contextcheck if err != nil { return err diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index d765c398..038a241b 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -565,7 +565,7 @@ func TestHtpasswdSingleCred(t *testing.T) { So(resp.StatusCode(), ShouldEqual, http.StatusNoContent) So(len(resp.Header()), ShouldEqual, 5) So(resp.Header()["Access-Control-Allow-Headers"], ShouldResemble, header) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS") // with invalid creds, it should fail resp, _ = resty.R().SetBasicAuth("chuck", "chuck").Get(baseURL + "/v2/") @@ -630,32 +630,32 @@ func TestAllowMethodsHeader(t *testing.T) { // /v2 resp, err := simpleUserClient.Options(baseURL + "/v2/") So(err, ShouldBeNil) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS") // /v2/{name}/tags/list resp, err = simpleUserClient.Options(baseURL + "/v2/reponame/tags/list") So(err, ShouldBeNil) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS") // /v2/{name}/manifests/{reference} resp, err = simpleUserClient.Options(baseURL + "/v2/reponame/manifests/" + digest.String()) So(err, ShouldBeNil) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,OPTIONS") // /v2/{name}/referrers/{digest} resp, err = simpleUserClient.Options(baseURL + "/v2/reponame/referrers/" + digest.String()) So(err, ShouldBeNil) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS") // /v2/_catalog resp, err = simpleUserClient.Options(baseURL + "/v2/_catalog") So(err, ShouldBeNil) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS") // /v2/_oci/ext/discover resp, err = simpleUserClient.Options(baseURL + "/v2/_oci/ext/discover") So(err, ShouldBeNil) - So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,POST,OPTIONS") + So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS") }) } diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 93fb6f5e..e01e0194 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -30,6 +30,7 @@ import ( "github.com/zitadel/oidc/pkg/oidc" zerr "zotregistry.io/zot/errors" + "zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/constants" apiErr "zotregistry.io/zot/pkg/api/errors" zcommon "zotregistry.io/zot/pkg/common" @@ -63,26 +64,31 @@ func (rh *RouteHandler) SetupRoutes() { applyCORSHeaders := getCORSHeadersHandler(rh.c.Config.HTTP.AllowOrigin) - if isOpenIDAuthEnabled(rh.c.Config) { + if rh.c.Config.IsOpenIDAuthEnabled() { // login path for openID rh.c.Router.HandleFunc(constants.LoginPath, rh.AuthURLHandler()) - // logout path for openID - rh.c.Router.HandleFunc(constants.LogoutPath, applyCORSHeaders(rh.Logout)). - Methods(zcommon.AllowedMethods("POST")...) - // callback path for openID for provider, relyingParty := range rh.c.RelyingParties { - if IsOauth2Supported(provider) { + if config.IsOauth2Supported(provider) { rh.c.Router.HandleFunc(constants.CallbackBasePath+fmt.Sprintf("/%s", provider), rp.CodeExchangeHandler(rh.GithubCodeExchangeCallback(), relyingParty)) - } else if IsOpenIDSupported(provider) { + } else if config.IsOpenIDSupported(provider) { rh.c.Router.HandleFunc(constants.CallbackBasePath+fmt.Sprintf("/%s", provider), rp.CodeExchangeHandler(rp.UserinfoCallback(rh.OpenIDCodeExchangeCallback()), relyingParty)) } } } + /* on every route which may be used by UI we set OPTIONS as allowed METHOD + to enable preflight request from UI to backend */ + if rh.c.Config.IsBasicAuthnEnabled() { + // logout path for openID + rh.c.Router.HandleFunc(constants.LogoutPath, + getUIHeadersHandler(rh.c.Config, http.MethodPost, http.MethodOptions)(applyCORSHeaders(rh.Logout))). + Methods(http.MethodPost, http.MethodOptions) + } + prefixedRouter := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter() prefixedRouter.Use(authHandler) @@ -90,10 +96,10 @@ func (rh *RouteHandler) SetupRoutes() { // authz is being enabled if AccessControl is specified // if Authn is not present AccessControl will have only default policies if rh.c.Config.HTTP.AccessControl != nil { - if isAuthnEnabled(rh.c.Config) { + if rh.c.Config.IsBasicAuthnEnabled() { rh.c.Log.Info().Msg("access control is being enabled") } else { - rh.c.Log.Info().Msg("default policy only access control is being enabled") + rh.c.Log.Info().Msg("anonymous policy only access control is being enabled") } prefixedRouter.Use(BaseAuthzHandler(rh.c)) @@ -103,40 +109,46 @@ func (rh *RouteHandler) SetupRoutes() { // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints { prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", zreg.NameRegexp.String()), - applyCORSHeaders(rh.ListTags)).Methods(zcommon.AllowedMethods("GET")...) + getUIHeadersHandler(rh.c.Config, http.MethodGet, http.MethodOptions)( + applyCORSHeaders(rh.ListTags))).Methods(http.MethodGet, http.MethodOptions) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), - applyCORSHeaders(rh.CheckManifest)).Methods(zcommon.AllowedMethods("HEAD")...) + getUIHeadersHandler(rh.c.Config, http.MethodHead, http.MethodGet, http.MethodOptions)( + applyCORSHeaders(rh.CheckManifest))).Methods(http.MethodHead, http.MethodOptions) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), - applyCORSHeaders(rh.GetManifest)).Methods(zcommon.AllowedMethods("GET")...) + applyCORSHeaders(rh.GetManifest)).Methods(http.MethodGet) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), - rh.UpdateManifest).Methods("PUT") + rh.UpdateManifest).Methods(http.MethodPut) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()), - rh.DeleteManifest).Methods("DELETE") + rh.DeleteManifest).Methods(http.MethodDelete) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()), - rh.CheckBlob).Methods("HEAD") + rh.CheckBlob).Methods(http.MethodHead) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()), - rh.GetBlob).Methods("GET") + rh.GetBlob).Methods(http.MethodGet) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()), - rh.DeleteBlob).Methods("DELETE") + rh.DeleteBlob).Methods(http.MethodDelete) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/", zreg.NameRegexp.String()), - rh.CreateBlobUpload).Methods("POST") + rh.CreateBlobUpload).Methods(http.MethodPost) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()), - rh.GetBlobUpload).Methods("GET") + rh.GetBlobUpload).Methods(http.MethodGet) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()), - rh.PatchBlobUpload).Methods("PATCH") + rh.PatchBlobUpload).Methods(http.MethodPatch) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()), - rh.UpdateBlobUpload).Methods("PUT") + rh.UpdateBlobUpload).Methods(http.MethodPut) prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()), - rh.DeleteBlobUpload).Methods("DELETE") + rh.DeleteBlobUpload).Methods(http.MethodDelete) // support for OCI artifact references prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/referrers/{digest}", zreg.NameRegexp.String()), - applyCORSHeaders(rh.GetReferrers)).Methods(zcommon.AllowedMethods("GET")...) + getUIHeadersHandler(rh.c.Config, http.MethodGet, http.MethodOptions)( + applyCORSHeaders(rh.GetReferrers))).Methods(http.MethodGet, http.MethodOptions) prefixedRouter.HandleFunc(constants.ExtCatalogPrefix, - applyCORSHeaders(rh.ListRepositories)).Methods(zcommon.AllowedMethods("GET")...) + getUIHeadersHandler(rh.c.Config, http.MethodGet, http.MethodOptions)( + applyCORSHeaders(rh.ListRepositories))).Methods(http.MethodGet, http.MethodOptions) prefixedRouter.HandleFunc(constants.ExtOciDiscoverPrefix, - applyCORSHeaders(rh.ListExtensions)).Methods(zcommon.AllowedMethods("GET")...) + getUIHeadersHandler(rh.c.Config, http.MethodGet, http.MethodOptions)( + applyCORSHeaders(rh.ListExtensions))).Methods(http.MethodGet, http.MethodOptions) prefixedRouter.HandleFunc("/", - applyCORSHeaders(rh.CheckVersionSupport)).Methods(zcommon.AllowedMethods("GET")...) + getUIHeadersHandler(rh.c.Config, http.MethodGet, http.MethodOptions)( + applyCORSHeaders(rh.CheckVersionSupport))).Methods(http.MethodGet, http.MethodOptions) } // support for ORAS artifact reference types (alpha 1) - image signature use case @@ -200,6 +212,24 @@ func addCORSHeaders(allowOrigin string, response http.ResponseWriter) { } } +func getUIHeadersHandler(config *config.Config, allowedMethods ...string) func(http.HandlerFunc) http.HandlerFunc { + allowedMethodsValue := strings.Join(allowedMethods, ",") + + return func(next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + response.Header().Set("Access-Control-Allow-Methods", allowedMethodsValue) + response.Header().Set("Access-Control-Allow-Headers", + "Authorization,content-type,"+constants.SessionClientHeaderName) + + if config.IsBasicAuthnEnabled() { + response.Header().Set("Access-Control-Allow-Credentials", "true") + } + + next.ServeHTTP(response, request) + }) + } +} + // Method handlers // CheckVersionSupport godoc @@ -210,10 +240,6 @@ func addCORSHeaders(allowOrigin string, response http.ResponseWriter) { // @Produce json // @Success 200 {string} string "ok". func (rh *RouteHandler) CheckVersionSupport(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - if request.Method == http.MethodOptions { return } @@ -253,10 +279,6 @@ type ImageTags struct { // @Failure 404 {string} string "not found" // @Failure 400 {string} string "bad request". func (rh *RouteHandler) ListTags(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - if request.Method == http.MethodOptions { return } @@ -385,10 +407,6 @@ func (rh *RouteHandler) ListTags(response http.ResponseWriter, request *http.Req // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error". func (rh *RouteHandler) CheckManifest(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - if request.Method == http.MethodOptions { return } @@ -458,12 +476,8 @@ type ExtensionList struct { // @Failure 500 {string} string "internal server error" // @Router /v2/{name}/manifests/{reference} [get]. func (rh *RouteHandler) GetManifest(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - - if request.Method == http.MethodOptions { - return + if rh.c.Config.IsBasicAuthnEnabled() { + response.Header().Set("Access-Control-Allow-Credentials", "true") } vars := mux.Vars(request) @@ -559,10 +573,6 @@ func getReferrers(routeHandler *RouteHandler, // @Failure 500 {string} string "internal server error" // @Router /v2/{name}/referrers/{digest} [get]. func (rh *RouteHandler) GetReferrers(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - if request.Method == http.MethodOptions { return } @@ -1613,10 +1623,6 @@ type RepositoryList struct { // @Failure 500 {string} string "internal server error" // @Router /v2/_catalog [get]. func (rh *RouteHandler) ListRepositories(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - if request.Method == http.MethodOptions { return } @@ -1680,10 +1686,6 @@ func (rh *RouteHandler) ListRepositories(response http.ResponseWriter, request * // @Success 200 {object} api.ExtensionList // @Router /v2/_oci/ext/discover [get]. func (rh *RouteHandler) ListExtensions(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - w.Header().Set("Access-Control-Allow-Credentials", "true") - if r.Method == http.MethodOptions { return } @@ -1698,16 +1700,12 @@ func (rh *RouteHandler) ListExtensions(w http.ResponseWriter, r *http.Request) { // Logout godoc // @Summary Logout by removing current session // @Description Logout by removing current session -// @Router /openid/auth/logout [post] +// @Router /auth/logout [post] // @Accept json // @Produce json // @Success 200 {string} string "ok". // @Failure 500 {string} string "internal server error". func (rh *RouteHandler) Logout(response http.ResponseWriter, request *http.Request) { - response.Header().Set("Access-Control-Allow-Methods", "HEAD,GET,POST,OPTIONS") - response.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - response.Header().Set("Access-Control-Allow-Credentials", "true") - if request.Method == http.MethodOptions { return } diff --git a/pkg/cli/root.go b/pkg/cli/root.go index ee118827..3a9d6572 100644 --- a/pkg/cli/root.go +++ b/pkg/cli/root.go @@ -439,11 +439,11 @@ func validateConfiguration(config *config.Config) error { return nil } -func validateOpenIDConfig(config *config.Config) error { - if config.HTTP.Auth != nil && config.HTTP.Auth.OpenID != nil { - for provider, providerConfig := range config.HTTP.Auth.OpenID.Providers { +func validateOpenIDConfig(cfg *config.Config) error { + if cfg.HTTP.Auth != nil && cfg.HTTP.Auth.OpenID != nil { + for provider, providerConfig := range cfg.HTTP.Auth.OpenID.Providers { //nolint: gocritic - if api.IsOpenIDSupported(provider) { + if config.IsOpenIDSupported(provider) { if providerConfig.ClientID == "" || providerConfig.Issuer == "" || len(providerConfig.Scopes) == 0 { log.Error().Err(errors.ErrBadConfig). @@ -451,7 +451,7 @@ func validateOpenIDConfig(config *config.Config) error { return errors.ErrBadConfig } - } else if api.IsOauth2Supported(provider) { + } else if config.IsOauth2Supported(provider) { if providerConfig.ClientID == "" || len(providerConfig.Scopes) == 0 { log.Error().Err(errors.ErrBadConfig). Msg("OAuth2 provider config requires clientid and scopes parameters") diff --git a/pkg/common/http_server.go b/pkg/common/http_server.go index df40b7f3..62143529 100644 --- a/pkg/common/http_server.go +++ b/pkg/common/http_server.go @@ -11,6 +11,7 @@ import ( "github.com/gorilla/sessions" jsoniter "github.com/json-iterator/go" + "zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/constants" apiErr "zotregistry.io/zot/pkg/api/errors" "zotregistry.io/zot/pkg/log" @@ -30,14 +31,17 @@ func AddExtensionSecurityHeaders() mux.MiddlewareFunc { //nolint:varnamelen } } -func ACHeadersHandler(allowedMethods ...string) mux.MiddlewareFunc { - headerValue := strings.Join(allowedMethods, ",") +func ACHeadersHandler(config *config.Config, allowedMethods ...string) mux.MiddlewareFunc { + allowedMethodsValue := strings.Join(allowedMethods, ",") return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - resp.Header().Set("Access-Control-Allow-Methods", headerValue) + resp.Header().Set("Access-Control-Allow-Methods", allowedMethodsValue) resp.Header().Set("Access-Control-Allow-Headers", "Authorization,content-type,"+constants.SessionClientHeaderName) - resp.Header().Set("Access-Control-Allow-Credentials", "true") + + if config.IsBasicAuthnEnabled() { + resp.Header().Set("Access-Control-Allow-Credentials", "true") + } if req.Method == http.MethodOptions { return diff --git a/pkg/extensions/extension_api_key.go b/pkg/extensions/extension_api_key.go index 7e112eb8..abcfb466 100644 --- a/pkg/extensions/extension_api_key.go +++ b/pkg/extensions/extension_api_key.go @@ -34,7 +34,7 @@ func SetupAPIKeyRoutes(config *config.Config, router *mux.Router, metaDB mTypes. allowedMethods := zcommon.AllowedMethods(http.MethodPost, http.MethodDelete) apiKeyRouter := router.PathPrefix(constants.ExtAPIKey).Subrouter() - apiKeyRouter.Use(zcommon.ACHeadersHandler(allowedMethods...)) + apiKeyRouter.Use(zcommon.ACHeadersHandler(config, allowedMethods...)) apiKeyRouter.Use(zcommon.AddExtensionSecurityHeaders()) apiKeyRouter.Methods(allowedMethods...).Handler(HandleAPIKeyRequest(metaDB, cookieStore, log)) } diff --git a/pkg/extensions/extension_mgmt.go b/pkg/extensions/extension_mgmt.go index f675bebd..325ed074 100644 --- a/pkg/extensions/extension_mgmt.go +++ b/pkg/extensions/extension_mgmt.go @@ -139,7 +139,7 @@ func SetupMgmtRoutes(config *config.Config, router *mux.Router, log log.Logger) allowedMethods := zcommon.AllowedMethods(http.MethodGet, http.MethodPost) mgmtRouter := router.PathPrefix(constants.ExtMgmt).Subrouter() - mgmtRouter.Use(zcommon.ACHeadersHandler(allowedMethods...)) + mgmtRouter.Use(zcommon.ACHeadersHandler(config, allowedMethods...)) mgmtRouter.Use(zcommon.AddExtensionSecurityHeaders()) mgmtRouter.Methods(allowedMethods...).Handler(mgmt.handler()) } diff --git a/pkg/extensions/extension_search.go b/pkg/extensions/extension_search.go index 5c2113da..359d648c 100644 --- a/pkg/extensions/extension_search.go +++ b/pkg/extensions/extension_search.go @@ -167,7 +167,7 @@ func SetupSearchRoutes(config *config.Config, router *mux.Router, storeControlle allowedMethods := zcommon.AllowedMethods(http.MethodGet, http.MethodPost) extRouter := router.PathPrefix(constants.ExtSearch).Subrouter() - extRouter.Use(zcommon.ACHeadersHandler(allowedMethods...)) + extRouter.Use(zcommon.ACHeadersHandler(config, allowedMethods...)) extRouter.Use(zcommon.AddExtensionSecurityHeaders()) extRouter.Methods(allowedMethods...). Handler(gqlHandler.NewDefaultServer(gql_generated.NewExecutableSchema(resConfig))) diff --git a/pkg/extensions/extension_userprefs.go b/pkg/extensions/extension_userprefs.go index 4ca2f05c..6ae67e47 100644 --- a/pkg/extensions/extension_userprefs.go +++ b/pkg/extensions/extension_userprefs.go @@ -36,7 +36,7 @@ func SetupUserPreferencesRoutes(config *config.Config, router *mux.Router, store allowedMethods := zcommon.AllowedMethods(http.MethodPut) userprefsRouter := router.PathPrefix(constants.ExtUserPreferences).Subrouter() - userprefsRouter.Use(zcommon.ACHeadersHandler(allowedMethods...)) + userprefsRouter.Use(zcommon.ACHeadersHandler(config, allowedMethods...)) userprefsRouter.Use(zcommon.AddExtensionSecurityHeaders()) userprefsRouter.HandleFunc("", HandleUserPrefs(metaDB, log)).Methods(allowedMethods...)