aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/packages/package_blob.go46
-rw-r--r--routers/api/packages/container/container.go22
-rw-r--r--tests/integration/api_packages_container_test.go12
3 files changed, 72 insertions, 8 deletions
diff --git a/models/packages/package_blob.go b/models/packages/package_blob.go
index a55109af96..d1f470d520 100644
--- a/models/packages/package_blob.go
+++ b/models/packages/package_blob.go
@@ -5,11 +5,18 @@ package packages
import (
"context"
+ "strconv"
"time"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
+
+ "xorm.io/builder"
)
// ErrPackageBlobNotExist indicates a package blob not exist error
@@ -98,3 +105,42 @@ func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) {
Where("package_file.id IS NULL").
SumInt(&PackageBlob{}, "size")
}
+
+// IsBlobAccessibleForUser tests if the user has access to the blob
+func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model.User) (bool, error) {
+ if user.IsAdmin {
+ return true, nil
+ }
+
+ maxTeamAuthorize := builder.
+ Select("max(team.authorize)").
+ From("team").
+ InnerJoin("team_user", "team_user.team_id = team.id").
+ Where(builder.Eq{"team_user.uid": user.ID}.And(builder.Expr("team_user.org_id = `user`.id")))
+
+ maxTeamUnitAccessMode := builder.
+ Select("max(team_unit.access_mode)").
+ From("team").
+ InnerJoin("team_user", "team_user.team_id = team.id").
+ InnerJoin("team_unit", "team_unit.team_id = team.id").
+ Where(builder.Eq{"team_user.uid": user.ID, "team_unit.type": unit.TypePackages}.And(builder.Expr("team_user.org_id = `user`.id")))
+
+ cond := builder.Eq{"package_blob.id": blobID}.And(
+ // owner = user
+ builder.Eq{"`user`.id": user.ID}.
+ // user can see owner
+ Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})).
+ // owner is an organization and user has access to it
+ Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}.
+ And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))),
+ )
+
+ return db.GetEngine(ctx).
+ Table("package_blob").
+ Join("INNER", "package_file", "package_file.blob_id = package_blob.id").
+ Join("INNER", "package_version", "package_version.id = package_file.version_id").
+ Join("INNER", "package", "package.id = package_version.package_id").
+ Join("INNER", "user", "`user`.id = package.owner_id").
+ Where(cond).
+ Exist(&PackageBlob{})
+}
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 126be43cdd..8f79805cc8 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -203,17 +203,25 @@ func InitiateUploadBlob(ctx *context.Context) {
Digest: mount,
})
if blob != nil {
- if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil {
+ accessible, err := packages_model.IsBlobAccessibleForUser(ctx, blob.Blob.ID, ctx.Doer)
+ if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
- setResponseHeaders(ctx.Resp, &containerHeaders{
- Location: fmt.Sprintf("/v2/%s/%s/blobs/%s", ctx.Package.Owner.LowerName, image, mount),
- ContentDigest: mount,
- Status: http.StatusCreated,
- })
- return
+ if accessible {
+ if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ setResponseHeaders(ctx.Resp, &containerHeaders{
+ Location: fmt.Sprintf("/v2/%s/%s/blobs/%s", ctx.Package.Owner.LowerName, image, mount),
+ ContentDigest: mount,
+ Status: http.StatusCreated,
+ })
+ return
+ }
}
}
diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go
index fe9208bb05..01002a4413 100644
--- a/tests/integration/api_packages_container_test.go
+++ b/tests/integration/api_packages_container_test.go
@@ -34,6 +34,7 @@ func TestPackageContainer(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
+ privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31})
has := func(l packages_model.PackagePropertyList, name string) bool {
for _, pp := range l {
@@ -262,7 +263,16 @@ func TestPackageContainer(t *testing.T) {
t.Run("UploadBlob/Mount", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads?mount=%s", url, unknownDigest))
+ privateBlobDigest := "sha256:6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d"
+ req := NewRequestWithBody(t, "POST", fmt.Sprintf("%sv2/%s/%s/blobs/uploads?digest=%s", setting.AppURL, privateUser.Name, image, privateBlobDigest), strings.NewReader("gitea"))
+ req = AddBasicAuthHeader(req, privateUser.Name)
+ MakeRequest(t, req, http.StatusCreated)
+
+ req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads?mount=%s", url, unknownDigest))
+ addTokenAuthHeader(req, userToken)
+ MakeRequest(t, req, http.StatusAccepted)
+
+ req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads?mount=%s", url, privateBlobDigest))
addTokenAuthHeader(req, userToken)
MakeRequest(t, req, http.StatusAccepted)