code.gitea.io/gitea@v1.21.7/models/packages/package_blob.go (about)

     1  // Copyright 2021 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package packages
     5  
     6  import (
     7  	"context"
     8  	"strconv"
     9  	"time"
    10  
    11  	"code.gitea.io/gitea/models/db"
    12  	"code.gitea.io/gitea/models/perm"
    13  	"code.gitea.io/gitea/models/unit"
    14  	user_model "code.gitea.io/gitea/models/user"
    15  	"code.gitea.io/gitea/modules/structs"
    16  	"code.gitea.io/gitea/modules/timeutil"
    17  	"code.gitea.io/gitea/modules/util"
    18  
    19  	"xorm.io/builder"
    20  )
    21  
    22  // ErrPackageBlobNotExist indicates a package blob not exist error
    23  var ErrPackageBlobNotExist = util.NewNotExistErrorf("package blob does not exist")
    24  
    25  func init() {
    26  	db.RegisterModel(new(PackageBlob))
    27  }
    28  
    29  // PackageBlob represents a package blob
    30  type PackageBlob struct {
    31  	ID          int64              `xorm:"pk autoincr"`
    32  	Size        int64              `xorm:"NOT NULL DEFAULT 0"`
    33  	HashMD5     string             `xorm:"hash_md5 char(32) UNIQUE(md5) INDEX NOT NULL"`
    34  	HashSHA1    string             `xorm:"hash_sha1 char(40) UNIQUE(sha1) INDEX NOT NULL"`
    35  	HashSHA256  string             `xorm:"hash_sha256 char(64) UNIQUE(sha256) INDEX NOT NULL"`
    36  	HashSHA512  string             `xorm:"hash_sha512 char(128) UNIQUE(sha512) INDEX NOT NULL"`
    37  	CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
    38  }
    39  
    40  // GetOrInsertBlob inserts a blob. If the blob exists already the existing blob is returned
    41  func GetOrInsertBlob(ctx context.Context, pb *PackageBlob) (*PackageBlob, bool, error) {
    42  	e := db.GetEngine(ctx)
    43  
    44  	has, err := e.Get(pb)
    45  	if err != nil {
    46  		return nil, false, err
    47  	}
    48  	if has {
    49  		return pb, true, nil
    50  	}
    51  	if _, err = e.Insert(pb); err != nil {
    52  		return nil, false, err
    53  	}
    54  	return pb, false, nil
    55  }
    56  
    57  // GetBlobByID gets a blob by id
    58  func GetBlobByID(ctx context.Context, blobID int64) (*PackageBlob, error) {
    59  	pb := &PackageBlob{}
    60  
    61  	has, err := db.GetEngine(ctx).ID(blobID).Get(pb)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	if !has {
    66  		return nil, ErrPackageBlobNotExist
    67  	}
    68  	return pb, nil
    69  }
    70  
    71  // ExistPackageBlobWithSHA returns if a package blob exists with the provided sha
    72  func ExistPackageBlobWithSHA(ctx context.Context, blobSha256 string) (bool, error) {
    73  	return db.GetEngine(ctx).Exist(&PackageBlob{
    74  		HashSHA256: blobSha256,
    75  	})
    76  }
    77  
    78  // FindExpiredUnreferencedBlobs gets all blobs without associated files older than the specific duration
    79  func FindExpiredUnreferencedBlobs(ctx context.Context, olderThan time.Duration) ([]*PackageBlob, error) {
    80  	pbs := make([]*PackageBlob, 0, 10)
    81  	return pbs, db.GetEngine(ctx).
    82  		Table("package_blob").
    83  		Join("LEFT", "package_file", "package_file.blob_id = package_blob.id").
    84  		Where("package_file.id IS NULL AND package_blob.created_unix < ?", time.Now().Add(-olderThan).Unix()).
    85  		Find(&pbs)
    86  }
    87  
    88  // DeleteBlobByID deletes a blob by id
    89  func DeleteBlobByID(ctx context.Context, blobID int64) error {
    90  	_, err := db.GetEngine(ctx).ID(blobID).Delete(&PackageBlob{})
    91  	return err
    92  }
    93  
    94  // GetTotalBlobSize returns the total blobs size in bytes
    95  func GetTotalBlobSize(ctx context.Context) (int64, error) {
    96  	return db.GetEngine(ctx).
    97  		SumInt(&PackageBlob{}, "size")
    98  }
    99  
   100  // GetTotalUnreferencedBlobSize returns the total size of all unreferenced blobs in bytes
   101  func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) {
   102  	return db.GetEngine(ctx).
   103  		Table("package_blob").
   104  		Join("LEFT", "package_file", "package_file.blob_id = package_blob.id").
   105  		Where("package_file.id IS NULL").
   106  		SumInt(&PackageBlob{}, "size")
   107  }
   108  
   109  // IsBlobAccessibleForUser tests if the user has access to the blob
   110  func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model.User) (bool, error) {
   111  	if user.IsAdmin {
   112  		return true, nil
   113  	}
   114  
   115  	maxTeamAuthorize := builder.
   116  		Select("max(team.authorize)").
   117  		From("team").
   118  		InnerJoin("team_user", "team_user.team_id = team.id").
   119  		Where(builder.Eq{"team_user.uid": user.ID}.And(builder.Expr("team_user.org_id = `user`.id")))
   120  
   121  	maxTeamUnitAccessMode := builder.
   122  		Select("max(team_unit.access_mode)").
   123  		From("team").
   124  		InnerJoin("team_user", "team_user.team_id = team.id").
   125  		InnerJoin("team_unit", "team_unit.team_id = team.id").
   126  		Where(builder.Eq{"team_user.uid": user.ID, "team_unit.type": unit.TypePackages}.And(builder.Expr("team_user.org_id = `user`.id")))
   127  
   128  	cond := builder.Eq{"package_blob.id": blobID}.And(
   129  		// owner = user
   130  		builder.Eq{"`user`.id": user.ID}.
   131  			// user can see owner
   132  			Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})).
   133  			// owner is an organization and user has access to it
   134  			Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}.
   135  				And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))),
   136  	)
   137  
   138  	return db.GetEngine(ctx).
   139  		Table("package_blob").
   140  		Join("INNER", "package_file", "package_file.blob_id = package_blob.id").
   141  		Join("INNER", "package_version", "package_version.id = package_file.version_id").
   142  		Join("INNER", "package", "package.id = package_version.package_id").
   143  		Join("INNER", "user", "`user`.id = package.owner_id").
   144  		Where(cond).
   145  		Exist(&PackageBlob{})
   146  }