mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Test if container blob is accessible before mounting (#22759)
related #16865 This PR adds an accessibility check before mounting container blobs. --------- Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		| @@ -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{}) | ||||
| } | ||||
|   | ||||
| @@ -203,6 +203,13 @@ func InitiateUploadBlob(ctx *context.Context) { | ||||
| 			Digest:     mount, | ||||
| 		}) | ||||
| 		if blob != nil { | ||||
| 			accessible, err := packages_model.IsBlobAccessibleForUser(ctx, blob.Blob.ID, ctx.Doer) | ||||
| 			if err != nil { | ||||
| 				apiError(ctx, http.StatusInternalServerError, err) | ||||
| 				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 | ||||
| @@ -216,6 +223,7 @@ func InitiateUploadBlob(ctx *context.Context) { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	digest := ctx.FormTrim("digest") | ||||
| 	if digest != "" { | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 KN4CK3R
					KN4CK3R