]> source.dussan.org Git - gitea.git/commitdiff
Test if container blob is accessible before mounting (#22759) (#25783)
authorGiteabot <teabot@gitea.io>
Sun, 9 Jul 2023 13:00:59 +0000 (09:00 -0400)
committerGitHub <noreply@github.com>
Sun, 9 Jul 2023 13:00:59 +0000 (09:00 -0400)
Backport #22759 by @KN4CK3R

related #16865

This PR adds an accessibility check before mounting container blobs.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
models/packages/package_blob.go
routers/api/packages/container/container.go
tests/integration/api_packages_container_test.go

index a55109af964a65afd37d487fa882b5961fa2eecb..d1f470d5205a843d879bdc9e7d7bc97a57e0fc57 100644 (file)
@@ -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{})
+}
index 883fe73cbdf2d1f9ae9362e4d8d3067a9eda5b49..f098be78e141781727348ff653e50e2420a0406f 100644 (file)
@@ -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
+                       }
                }
        }
 
index 3d9319f37001c9bbc3ee90328dce7690bed39950..41d717c8f5b3748f456daef8d2779588fe28fb9b 100644 (file)
@@ -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)