0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

fix(authn): fix several issues with authn, closes #1632 (#1633)

- apply Access-Control-Allow-Credentials only if authn is enabled
- enable Logout route for basic auth
- fixed Logout godoc
- fix Access-Control-Allow-Methods on Logout route
- added allowOrigin option in config example

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
peusebiu 2023-07-19 19:27:04 +03:00 committed by GitHub
parent 04fccd11fd
commit 86a83ca6e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 201 additions and 162 deletions

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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")
})
}

View file

@ -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
}

View file

@ -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")

View file

@ -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

View file

@ -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))
}

View file

@ -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())
}

View file

@ -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)))

View file

@ -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...)