Skip to content

Commit

Permalink
feat(ui): let UI delete manifests if current user has permissions to …
Browse files Browse the repository at this point in the history
…do so

- RepoMeta now contains a new bool field which tells UI if the user has delete permission
on that specific repo
- apply cors on DeleteManifest route

Signed-off-by: Petu Eusebiu <[email protected]>
  • Loading branch information
eusebiu-constantin-petu-dbk committed Dec 13, 2023
1 parent 86b0a22 commit 0578ff3
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 7 deletions.
2 changes: 1 addition & 1 deletion pkg/api/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ func TestAllowMethodsHeader(t *testing.T) {
// /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,OPTIONS")
So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "HEAD,GET,DELETE,OPTIONS")

// /v2/{name}/referrers/{digest}
resp, err = simpleUserClient.Options(baseURL + "/v2/reponame/referrers/" + digest.String())
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ func (rh *RouteHandler) SetupRoutes() {
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()),
getUIHeadersHandler(rh.c.Config, http.MethodHead, http.MethodGet, http.MethodOptions)(
getUIHeadersHandler(rh.c.Config, http.MethodHead, http.MethodGet, http.MethodDelete, 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(http.MethodGet)
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
rh.UpdateManifest).Methods(http.MethodPut)
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/manifests/{reference}", zreg.NameRegexp.String()),
rh.DeleteManifest).Methods(http.MethodDelete)
applyCORSHeaders(rh.DeleteManifest)).Methods(http.MethodDelete)
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
rh.CheckBlob).Methods(http.MethodHead)
prefixedDistSpecRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/{digest}", zreg.NameRegexp.String()),
Expand Down
31 changes: 31 additions & 0 deletions pkg/extensions/search/convert/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/meta/boltdb"
mTypes "zotregistry.io/zot/pkg/meta/types"
reqCtx "zotregistry.io/zot/pkg/requestcontext"
. "zotregistry.io/zot/pkg/test/image-utils"
"zotregistry.io/zot/pkg/test/mocks"
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
Expand Down Expand Up @@ -815,5 +816,35 @@ func TestConvertErrors(t *testing.T) {
)
So(len(imgSums), ShouldEqual, 0)
})

Convey("RepoMeta2ExpandedRepoInfo - bad ctx value", func() {
uacKey := reqCtx.GetContextKey()
ctx := context.WithValue(ctx, uacKey, "bad context")

_, imgSums := convert.RepoMeta2ExpandedRepoInfo(ctx,
mTypes.RepoMeta{},
map[string]mTypes.ImageMeta{
"digest": {},
},
convert.SkipQGLField{}, nil,
log,
)
So(len(imgSums), ShouldEqual, 0)
})

Convey("RepoMeta2ExpandedRepoInfo - nil ctx value", func() {
uacKey := reqCtx.GetContextKey()
ctx := context.WithValue(ctx, uacKey, nil)

_, imgSums := convert.RepoMeta2ExpandedRepoInfo(ctx,
mTypes.RepoMeta{},
map[string]mTypes.ImageMeta{
"digest": {},
},
convert.SkipQGLField{}, nil,
log,
)
So(len(imgSums), ShouldEqual, 0)
})
})
}
5 changes: 5 additions & 0 deletions pkg/extensions/search/convert/metadb.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"zotregistry.io/zot/pkg/extensions/search/pagination"
"zotregistry.io/zot/pkg/log"
mTypes "zotregistry.io/zot/pkg/meta/types"
reqCtx "zotregistry.io/zot/pkg/requestcontext"
)

type SkipQGLField struct {
Expand Down Expand Up @@ -136,6 +137,8 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMeta,
repoName := repoMeta.Name
imageSummaries := make([]*gql_generated.ImageSummary, 0, len(repoMeta.Tags))

userCanDeleteTag, _ := reqCtx.CanDelete(ctx, repoName)

for tag, descriptor := range repoMeta.Tags {
imageMeta := imageMetaMap[descriptor.Digest]

Expand All @@ -147,6 +150,8 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMeta,
continue
}

imageSummary.IsDeletable = &userCanDeleteTag

updateImageSummaryVulnerabilities(ctx, imageSummary, skip, cveInfo)

imageSummaries = append(imageSummaries, imageSummary)
Expand Down
65 changes: 65 additions & 0 deletions pkg/extensions/search/gql_generated/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/extensions/search/gql_generated/models_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/extensions/search/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ type ImageSummary {
Information about objects that reference this image
"""
Referrers: [Referrer]
"""
True if current user has delete permission on this tag.
"""
IsDeletable: Boolean
}
"""
Details about a specific version of an image for a certain operating system and architecture.
Expand Down
12 changes: 8 additions & 4 deletions pkg/requestcontext/user_access_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,14 @@ func RepoIsUserAvailable(ctx context.Context, repoName string) (bool, error) {
return false, err
}

// no authn/authz enabled on server
if uac == nil {
return true, nil
return uac.Can(constants.ReadPermission, repoName), nil
}

func CanDelete(ctx context.Context, repoName string) (bool, error) {
uac, err := UserAcFromContext(ctx)
if err != nil {
return false, err
}

return uac.Can("read", repoName), nil
return uac.Can(constants.DeletePermission, repoName), nil
}

0 comments on commit 0578ff3

Please sign in to comment.