fix(storage): deleting manifests with identical digests (#951)

Suppose we push two identical manifests (sharing same digest) but with
different tags, then deleting by digest should throw an error otherwise
we end up deleting all image tags (with gc) or dangling references
(without gc)

This behaviour is controlled via Authorization, added a new policy
action named detectManifestsCollision which enables this behaviour

Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>

Co-authored-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
peusebiu
2022-11-18 19:35:28 +02:00
committed by GitHub
parent 4e13619dc8
commit 168d21da1e
22 changed files with 507 additions and 141 deletions
+56 -3
View File
@@ -1,5 +1,12 @@
package requestcontext
import (
"context"
glob "github.com/bmatcuk/doublestar/v4" //nolint:gci
"zotregistry.io/zot/errors"
)
type Key int
// request-local context key.
@@ -12,7 +19,53 @@ func GetContextKey() *Key {
// AccessControlContext context passed down to http.Handlers.
type AccessControlContext struct {
GlobPatterns map[string]bool
IsAdmin bool
Username string
// read method action
ReadGlobPatterns map[string]bool
// detectManifestCollision behaviour action
DmcGlobPatterns map[string]bool
IsAdmin bool
Username string
}
func GetAccessControlContext(ctx context.Context) (*AccessControlContext, error) {
authzCtxKey := GetContextKey()
if authCtx := ctx.Value(authzCtxKey); authCtx != nil {
acCtx, ok := authCtx.(AccessControlContext)
if !ok {
return nil, errors.ErrBadType
}
return &acCtx, nil
}
return nil, nil //nolint: nilnil
}
// returns either a user has or not rights on 'repository'.
func (acCtx *AccessControlContext) matchesRepo(globPatterns map[string]bool, repository string) bool {
var longestMatchedPattern string
// because of the longest path matching rule, we need to check all patterns from config
for pattern := range globPatterns {
matched, err := glob.Match(pattern, repository)
if err == nil {
if matched && len(pattern) > len(longestMatchedPattern) {
longestMatchedPattern = pattern
}
}
}
allowed := globPatterns[longestMatchedPattern]
return allowed
}
// returns either a user has or not read rights on 'repository'.
func (acCtx *AccessControlContext) CanReadRepo(repository string) bool {
return acCtx.matchesRepo(acCtx.ReadGlobPatterns, repository)
}
// returns either a user has or not detectManifestCollision rights on 'repository'.
func (acCtx *AccessControlContext) CanDetectManifestCollision(repository string) bool {
return acCtx.matchesRepo(acCtx.DmcGlobPatterns, repository)
}