0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-06 22:40:28 -05:00

fix(authz): get username from authn.go request context (#1383)

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
peusebiu 2023-04-27 18:13:06 +03:00 committed by GitHub
parent 1a092bd019
commit 07bfc8ab95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 38 deletions

View file

@ -155,20 +155,15 @@ func (ac *AccessController) getUserGroups(username string) []string {
return groupNames return groupNames
} }
// getContext builds ac context(allowed to read repos and if user is admin) and returns it. // getContext updates an AccessControlContext for a user/anonymous and returns a context.Context containing it.
func (ac *AccessController) getContext(username string, request *http.Request) context.Context { func (ac *AccessController) getContext(acCtx *localCtx.AccessControlContext, request *http.Request) context.Context {
acCtx, err := localCtx.GetAccessControlContext(request.Context()) readGlobPatterns := ac.getGlobPatterns(acCtx.Username, acCtx.Groups, Read)
if err != nil { dmcGlobPatterns := ac.getGlobPatterns(acCtx.Username, acCtx.Groups, DetectManifestCollision)
return nil
}
readGlobPatterns := ac.getGlobPatterns(username, acCtx.Groups, Read)
dmcGlobPatterns := ac.getGlobPatterns(username, acCtx.Groups, DetectManifestCollision)
acCtx.ReadGlobPatterns = readGlobPatterns acCtx.ReadGlobPatterns = readGlobPatterns
acCtx.DmcGlobPatterns = dmcGlobPatterns acCtx.DmcGlobPatterns = dmcGlobPatterns
if ac.isAdmin(username) { if ac.isAdmin(acCtx.Username) {
acCtx.IsAdmin = true acCtx.IsAdmin = true
} else { } else {
acCtx.IsAdmin = false acCtx.IsAdmin = false
@ -243,16 +238,23 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
acCtrlr := NewAccessController(ctlr.Config) acCtrlr := NewAccessController(ctlr.Config)
var identity string var identity string
var err error var err error
// allow anonymous authz if no authn present and only default policies are present // anonymous context
identity = "" acCtx := &localCtx.AccessControlContext{}
if isAuthnEnabled(ctlr.Config) && request.Header.Get("Authorization") != "" {
identity, _, err = getUsernamePasswordBasicAuth(request)
if err != nil { // get username from context made in authn.go
if isAuthnEnabled(ctlr.Config) {
// get access control context made in authn.go if authn is enabled
acCtx, err = localCtx.GetAccessControlContext(request.Context())
if err != nil { // should never happen
authFail(response, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay) authFail(response, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)
return
} }
identity = acCtx.Username
} }
if request.TLS != nil { if request.TLS != nil {
@ -268,14 +270,19 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
if identity == "" { if identity == "" {
acCtrlr.Log.Info().Msg("couldn't get identity from TLS certificate") acCtrlr.Log.Info().Msg("couldn't get identity from TLS certificate")
authFail(response, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay) authFail(response, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)
return
} }
} }
} }
ctx := acCtrlr.getContext(identity, request) ctx := acCtrlr.getContext(acCtx, request)
// for extensions, we only need to know the username, whether the user is an admin, and what repositories /* Notes:
// they can read. So, run next() - 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) { if isExtensionURI(request.RequestURI) {
next.ServeHTTP(response, request.WithContext(ctx)) //nolint:contextcheck next.ServeHTTP(response, request.WithContext(ctx)) //nolint:contextcheck

View file

@ -18,7 +18,7 @@ func GetContextKey() *Key {
return &authzCtxKey return &authzCtxKey
} }
// AccessControlContext context passed down to http.Handlers. // AccessControlContext - contains user authn/authz information.
type AccessControlContext struct { type AccessControlContext struct {
// read method action // read method action
ReadGlobPatterns map[string]bool ReadGlobPatterns map[string]bool
@ -29,6 +29,13 @@ type AccessControlContext struct {
Groups []string Groups []string
} }
/*
GetAccessControlContext returns an AccessControlContext struct made available on all http requests
(using context.Context values) by authz and authn middlewares.
its methods and attributes can be used in http.Handlers to get user info for that specific request
(username, groups, if it's an admin, if it can access certain resources).
*/
func GetAccessControlContext(ctx context.Context) (*AccessControlContext, error) { func GetAccessControlContext(ctx context.Context) (*AccessControlContext, error) {
authzCtxKey := GetContextKey() authzCtxKey := GetContextKey()
if authCtx := ctx.Value(authzCtxKey); authCtx != nil { if authCtx := ctx.Value(authzCtxKey); authCtx != nil {
@ -43,7 +50,31 @@ func GetAccessControlContext(ctx context.Context) (*AccessControlContext, error)
return nil, nil //nolint: nilnil return nil, nil //nolint: nilnil
} }
// returns either a user has or not rights on 'repository'. // returns whether or not the user/anonymous who made the request has read permission on 'repository'.
func (acCtx *AccessControlContext) CanReadRepo(repository string) bool {
if acCtx.ReadGlobPatterns != nil {
return acCtx.matchesRepo(acCtx.ReadGlobPatterns, repository)
}
return true
}
/*
returns whether or not the user/anonymous who made the request
has detectManifestCollision permission on 'repository'.
*/
func (acCtx *AccessControlContext) CanDetectManifestCollision(repository string) bool {
if acCtx.DmcGlobPatterns != nil {
return acCtx.matchesRepo(acCtx.DmcGlobPatterns, repository)
}
return false
}
/*
returns whether or not 'repository' can be found in the list of patterns
on which the user who made the request has read permission on.
*/
func (acCtx *AccessControlContext) matchesRepo(globPatterns map[string]bool, repository string) bool { func (acCtx *AccessControlContext) matchesRepo(globPatterns map[string]bool, repository string) bool {
var longestMatchedPattern string var longestMatchedPattern string
@ -61,21 +92,3 @@ func (acCtx *AccessControlContext) matchesRepo(globPatterns map[string]bool, rep
return allowed return allowed
} }
// returns either a user has or not read rights on 'repository'.
func (acCtx *AccessControlContext) CanReadRepo(repository string) bool {
if acCtx.ReadGlobPatterns != nil {
return acCtx.matchesRepo(acCtx.ReadGlobPatterns, repository)
}
return true
}
// returns either a user has or not detectManifestCollision rights on 'repository'.
func (acCtx *AccessControlContext) CanDetectManifestCollision(repository string) bool {
if acCtx.DmcGlobPatterns != nil {
return acCtx.matchesRepo(acCtx.DmcGlobPatterns, repository)
}
return false
}