mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
refactor: split AuthZ mdw in 2 different parts, each for a specific purpose (#1542)
- AuthzHandler has now been split in BaseAuthzHandler and DistSpecAuthzHandler The former populates context with user specific data needed in most handlers, while the latter executes access logic specific to distribution-spec handlers. Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro>
This commit is contained in:
+38
-21
@@ -2,15 +2,12 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
glob "github.com/bmatcuk/doublestar/v4"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"zotregistry.io/zot/pkg/api/config"
|
||||
"zotregistry.io/zot/pkg/api/constants"
|
||||
"zotregistry.io/zot/pkg/common"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
localCtx "zotregistry.io/zot/pkg/requestcontext"
|
||||
@@ -220,12 +217,13 @@ func (ac *AccessController) isPermitted(userGroups []string, username, action st
|
||||
return result
|
||||
}
|
||||
|
||||
func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||
func BaseAuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
resource := vars["name"]
|
||||
reference, ok := vars["reference"]
|
||||
/* NOTE:
|
||||
since we only do READ actions in extensions, this middleware is enough for them because
|
||||
it populates the context with user relevant data to be processed by each individual extension
|
||||
*/
|
||||
|
||||
if request.Method == http.MethodOptions {
|
||||
next.ServeHTTP(response, request)
|
||||
@@ -286,17 +284,41 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||
|
||||
ctx := acCtrlr.getContext(acCtx, request)
|
||||
|
||||
/* Notes:
|
||||
- since we only do READ actions in extensions, we can bypass authz for them
|
||||
only need to know the username, whether the user is an admin, or what repos he can read.
|
||||
let each extension to apply authorization on them using localCtx.AccessControlContext{}
|
||||
*/
|
||||
if isExtensionURI(request.RequestURI) {
|
||||
next.ServeHTTP(response, request.WithContext(ctx)) //nolint:contextcheck
|
||||
next.ServeHTTP(response, request.WithContext(ctx)) //nolint:contextcheck
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func DistSpecAuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
if request.Method == http.MethodOptions {
|
||||
next.ServeHTTP(response, request)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(request)
|
||||
resource := vars["name"]
|
||||
reference, ok := vars["reference"]
|
||||
|
||||
acCtrlr := NewAccessController(ctlr.Config)
|
||||
|
||||
var identity string
|
||||
|
||||
var err error
|
||||
|
||||
// get acCtx built in authn and previous authz middlewares
|
||||
acCtx, err := localCtx.GetAccessControlContext(request.Context())
|
||||
if err != nil { // should never happen
|
||||
authFail(response, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// get username from context made in authn.go
|
||||
identity = acCtx.Username
|
||||
|
||||
var action string
|
||||
if request.Method == http.MethodGet || request.Method == http.MethodHead {
|
||||
action = Read
|
||||
@@ -320,17 +342,12 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||
action = Delete
|
||||
}
|
||||
|
||||
can := acCtrlr.can(ctx, identity, action, resource) //nolint:contextcheck
|
||||
can := acCtrlr.can(request.Context(), identity, action, resource) //nolint:contextcheck
|
||||
if !can {
|
||||
common.AuthzFail(response, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)
|
||||
} else {
|
||||
next.ServeHTTP(response, request.WithContext(ctx)) //nolint:contextcheck
|
||||
next.ServeHTTP(response, request) //nolint:contextcheck
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func isExtensionURI(requestURI string) bool {
|
||||
return strings.Contains(requestURI, constants.ExtPrefix) ||
|
||||
requestURI == fmt.Sprintf("%s%s", constants.RoutePrefix, constants.ExtCatalogPrefix)
|
||||
}
|
||||
|
||||
+18
-15
@@ -57,6 +57,8 @@ func NewRouteHandler(c *Controller) *RouteHandler {
|
||||
func (rh *RouteHandler) SetupRoutes() {
|
||||
prefixedRouter := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter()
|
||||
prefixedRouter.Use(AuthHandler(rh.c))
|
||||
|
||||
prefixedDistSpecRouter := prefixedRouter.NewRoute().Subrouter()
|
||||
// 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 && !isBearerAuthEnabled(rh.c.Config) {
|
||||
@@ -66,41 +68,42 @@ func (rh *RouteHandler) SetupRoutes() {
|
||||
rh.c.Log.Info().Msg("default policy only access control is being enabled")
|
||||
}
|
||||
|
||||
prefixedRouter.Use(AuthzHandler(rh.c))
|
||||
prefixedRouter.Use(BaseAuthzHandler(rh.c))
|
||||
prefixedDistSpecRouter.Use(DistSpecAuthzHandler(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()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", zreg.NameRegexp.String()),
|
||||
applyCORSHeaders(rh.ListTags)).Methods(zcommon.AllowedMethods("GET")...)
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
|
||||
applyCORSHeaders(rh.CheckManifest)).Methods(zcommon.AllowedMethods("HEAD")...)
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
|
||||
applyCORSHeaders(rh.GetManifest)).Methods(zcommon.AllowedMethods("GET")...)
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.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()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
|
||||
rh.DeleteManifest).Methods("DELETE")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
|
||||
rh.CheckBlob).Methods("HEAD")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
|
||||
rh.GetBlob).Methods("GET")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
|
||||
rh.DeleteBlob).Methods("DELETE")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/", zreg.NameRegexp.String()),
|
||||
rh.CreateBlobUpload).Methods("POST")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
rh.GetBlobUpload).Methods("GET")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
rh.PatchBlobUpload).Methods("PATCH")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
rh.UpdateBlobUpload).Methods("PUT")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", zreg.NameRegexp.String()),
|
||||
rh.DeleteBlobUpload).Methods("DELETE")
|
||||
// support for OCI artifact references
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/referrers/{digest}", zreg.NameRegexp.String()),
|
||||
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/referrers/{digest}", zreg.NameRegexp.String()),
|
||||
applyCORSHeaders(rh.GetReferrers)).Methods(zcommon.AllowedMethods("GET")...)
|
||||
prefixedRouter.HandleFunc(constants.ExtCatalogPrefix,
|
||||
applyCORSHeaders(rh.ListRepositories)).Methods(zcommon.AllowedMethods("GET")...)
|
||||
|
||||
Reference in New Issue
Block a user