mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
- 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:
parent
04fccd11fd
commit
86a83ca6e3
12 changed files with 201 additions and 162 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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...)
|
||||
|
|
Loading…
Reference in a new issue