Add package visibility restrictions based on attached repository.

This commit is contained in:
smiley
2026-06-17 00:00:00 +00:00
parent f89df74f54
commit 75a160f1d3
2 changed files with 52 additions and 15 deletions
+11 -11
View File
@@ -578,25 +578,25 @@ func ContainerRoutes() *web.Router {
r.Post("", container.AuthenticateNotImplemented)
})
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
r.Group("/{username}", func() {
r.Group("/{username}/{image}", func() {
r.PathGroup("/*", func(g *web.RouterPathGroup) {
g.MatchPath("POST", "/<image:*>/blobs/uploads", reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, container.PostBlobsUploads)
g.MatchPath("GET", "/<image:*>/tags/list", container.VerifyImageName, container.GetTagsList)
g.MatchPath("POST", "/blobs/uploads", reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, container.PostBlobsUploads)
g.MatchPath("GET", "/tags/list", container.VerifyImageName, container.GetTagsList)
patternBlobsUploadsUUID := g.PatternRegexp(`/<image:*>/blobs/uploads/<uuid:[-.=\w]+>`, reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName)
patternBlobsUploadsUUID := g.PatternRegexp(`/blobs/uploads/<uuid:[-.=\w]+>`, reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName)
g.MatchPattern("GET", patternBlobsUploadsUUID, container.GetBlobsUpload)
g.MatchPattern("PATCH", patternBlobsUploadsUUID, container.PatchBlobsUpload)
g.MatchPattern("PUT", patternBlobsUploadsUUID, container.PutBlobsUpload)
g.MatchPattern("DELETE", patternBlobsUploadsUUID, container.DeleteBlobsUpload)
g.MatchPath("HEAD", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.HeadBlob)
g.MatchPath("GET", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.GetBlob)
g.MatchPath("DELETE", `/<image:*>/blobs/<digest>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
g.MatchPath("HEAD", `/blobs/<digest>`, container.VerifyImageName, container.HeadBlob)
g.MatchPath("GET", `/blobs/<digest>`, container.VerifyImageName, container.GetBlob)
g.MatchPath("DELETE", `/blobs/<digest>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
g.MatchPath("HEAD", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.HeadManifest)
g.MatchPath("GET", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.GetManifest)
g.MatchPath("PUT", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.PutManifest)
g.MatchPath("DELETE", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
g.MatchPath("HEAD", `/manifests/<reference>`, container.VerifyImageName, container.HeadManifest)
g.MatchPath("GET", `/manifests/<reference>`, container.VerifyImageName, container.GetManifest)
g.MatchPath("PUT", `/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.PutManifest)
g.MatchPath("DELETE", `/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
})
}, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
+41 -4
View File
@@ -11,6 +11,7 @@ import (
"gitea.dev/models/organization"
packages_model "gitea.dev/models/packages"
"gitea.dev/models/perm"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
@@ -23,6 +24,7 @@ type Package struct {
Owner *user_model.User
AccessMode perm.AccessMode
Descriptor *packages_model.PackageDescriptor
Repository *repo_model.Repository
}
type packageAssignmentCtx struct {
@@ -56,19 +58,35 @@ func PackageAssignmentAPI() func(ctx *APIContext) {
}
func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string)) *Package {
pkgOwner := ctx.ContextUser
accessMode, err := determineAccessMode(ctx.Base, pkgOwner, ctx.Doer)
pkg := getPackage(ctx, errCb)
accessMode, err := determineAccessMode(ctx.Base, ctx.ContextUser, ctx.Doer, pkg.Repository)
if err != nil {
errCb(http.StatusInternalServerError, fmt.Sprintf("determineAccessMode: %v", err))
return nil
}
pkg.AccessMode = accessMode
return pkg
}
func getPackage(ctx *packageAssignmentCtx, errCb func(int, string)) *Package {
pkgOwner := ctx.ContextUser
pkg := &Package{
Owner: pkgOwner,
AccessMode: accessMode,
AccessMode: perm.AccessModeNone,
Repository: nil,
}
packageType := ctx.PathParam("type")
name := ctx.PathParam("name")
image := ctx.PathParam("image")
if image != "" {
name = image
packageType = "container"
}
if packageType == "" || name == "" {
return pkg
}
@@ -90,6 +108,8 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string)) *Pack
errCb(http.StatusInternalServerError, fmt.Sprintf("GetPackageDescriptor: %v", err))
return pkg
}
pkg.Repository = pkg.Descriptor.Repository
} else {
p, err := packages_model.GetPackageByName(ctx, pkg.Owner.ID, packages_model.Type(packageType), name)
if err != nil {
@@ -105,12 +125,20 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string)) *Pack
Package: p,
Owner: pkg.Owner,
}
if p.RepoID > 0 {
pkg.Repository, err = repo_model.GetRepositoryByID(ctx, p.RepoID)
if err != nil {
errCb(http.StatusInternalServerError, fmt.Sprintf("GetRepositoryByID: %v", err))
return pkg
}
}
}
return pkg
}
func determineAccessMode(ctx *Base, pkgOwner, doer *user_model.User) (perm.AccessMode, error) {
func determineAccessMode(ctx *Base, pkgOwner, doer *user_model.User, pkgRepo *repo_model.Repository) (perm.AccessMode, error) {
if setting.Service.RequireSignInViewStrict && (doer == nil || doer.IsGhost()) {
return perm.AccessModeNone, nil
}
@@ -150,6 +178,15 @@ func determineAccessMode(ctx *Base, pkgOwner, doer *user_model.User) (perm.Acces
// 2. If user is unauthorized or no org member, check if org is visible
accessMode = perm.AccessModeRead
}
} else if pkgRepo != nil {
if doer != nil {
// 1. Check if user is package owner
if doer.ID == pkgOwner.ID {
accessMode = perm.AccessModeOwner
} else if !pkgRepo.IsPrivate { // 2. Check if package repository is public
accessMode = perm.AccessModeRead
}
}
} else {
if doer != nil && !doer.IsGhost() {
// 1. Check if user is package owner