mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 04:48:26 +08:00
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:
@@ -12,7 +12,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
glob "github.com/bmatcuk/doublestar/v4"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
@@ -734,40 +733,21 @@ func BuildImageInfo(repo string, tag string, manifestDigest godigest.Digest,
|
||||
return imageInfo
|
||||
}
|
||||
|
||||
// returns either a user has or not rights on 'repository'.
|
||||
func 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
|
||||
}
|
||||
|
||||
// get passed context from authzHandler and filter out repos based on permissions.
|
||||
func userAvailableRepos(ctx context.Context, repoList []string) ([]string, error) {
|
||||
var availableRepos []string
|
||||
|
||||
authzCtxKey := localCtx.GetContextKey()
|
||||
if authCtx := ctx.Value(authzCtxKey); authCtx != nil {
|
||||
acCtx, ok := authCtx.(localCtx.AccessControlContext)
|
||||
if !ok {
|
||||
err := errors.ErrBadType
|
||||
// authz request context (set in authz middleware)
|
||||
acCtx, err := localCtx.GetAccessControlContext(ctx)
|
||||
if err != nil {
|
||||
err := errors.ErrBadType
|
||||
|
||||
return []string{}, err
|
||||
}
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
if acCtx != nil {
|
||||
for _, r := range repoList {
|
||||
if acCtx.IsAdmin || matchesRepo(acCtx.GlobPatterns, r) {
|
||||
if acCtx.IsAdmin || acCtx.CanReadRepo(r) {
|
||||
availableRepos = append(availableRepos, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,8 +337,8 @@ func TestExtractImageDetails(t *testing.T) {
|
||||
authzCtxKey := localCtx.GetContextKey()
|
||||
ctx = context.WithValue(ctx, authzCtxKey,
|
||||
localCtx.AccessControlContext{
|
||||
GlobPatterns: map[string]bool{"*": true, "**": true},
|
||||
Username: "jane_doe",
|
||||
ReadGlobPatterns: map[string]bool{"*": true, "**": true},
|
||||
Username: "jane_doe",
|
||||
})
|
||||
configBlobContent, _ := json.MarshalIndent(&config, "", "\t")
|
||||
configDigest := godigest.FromBytes(configBlobContent)
|
||||
@@ -429,8 +429,8 @@ func TestExtractImageDetails(t *testing.T) {
|
||||
Convey("extractImageDetails without proper authz", func() {
|
||||
ctx = context.WithValue(ctx, authzCtxKey,
|
||||
localCtx.AccessControlContext{
|
||||
GlobPatterns: map[string]bool{},
|
||||
Username: "jane_doe",
|
||||
ReadGlobPatterns: map[string]bool{},
|
||||
Username: "jane_doe",
|
||||
})
|
||||
mockOlum := mocks.OciLayoutUtilsMock{
|
||||
GetImageConfigInfoFn: func(repo string, digest godigest.Digest) (
|
||||
|
||||
@@ -1408,7 +1408,7 @@ func TestBasicAuth(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
err = dctlr.StoreController.DefaultStore.DeleteImageManifest(testImage, testImageTag)
|
||||
err = dctlr.StoreController.DefaultStore.DeleteImageManifest(testImage, testImageTag, false)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
resp, err = destClient.R().Get(destBaseURL + "/v2/" + testImage + "/manifests/" + "1.1.1")
|
||||
|
||||
Reference in New Issue
Block a user