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 ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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/timeutil" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
|  |  | ||||||
|  | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ErrPackageBlobNotExist indicates a package blob not exist error | // 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"). | 		Where("package_file.id IS NULL"). | ||||||
| 		SumInt(&PackageBlob{}, "size") | 		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, | 			Digest:     mount, | ||||||
| 		}) | 		}) | ||||||
| 		if blob != nil { | 		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 { | 				if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil { | ||||||
| 					apiError(ctx, http.StatusInternalServerError, err) | 					apiError(ctx, http.StatusInternalServerError, err) | ||||||
| 					return | 					return | ||||||
| @@ -216,6 +223,7 @@ func InitiateUploadBlob(ctx *context.Context) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	digest := ctx.FormTrim("digest") | 	digest := ctx.FormTrim("digest") | ||||||
| 	if digest != "" { | 	if digest != "" { | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ func TestPackageContainer(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) | 	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 { | 	has := func(l packages_model.PackagePropertyList, name string) bool { | ||||||
| 		for _, pp := range l { | 		for _, pp := range l { | ||||||
| @@ -262,7 +263,16 @@ func TestPackageContainer(t *testing.T) { | |||||||
| 			t.Run("UploadBlob/Mount", func(t *testing.T) { | 			t.Run("UploadBlob/Mount", func(t *testing.T) { | ||||||
| 				defer tests.PrintCurrentTest(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) | 				addTokenAuthHeader(req, userToken) | ||||||
| 				MakeRequest(t, req, http.StatusAccepted) | 				MakeRequest(t, req, http.StatusAccepted) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 KN4CK3R
					KN4CK3R