code.gitea.io/gitea@v1.22.3/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  	existing := &PackageBlob{}
    45  
    46  	has, err := e.Where(builder.Eq{
    47  		"size":        pb.Size,
    48  		"hash_md5":    pb.HashMD5,
    49  		"hash_sha1":   pb.HashSHA1,
    50  		"hash_sha256": pb.HashSHA256,
    51  		"hash_sha512": pb.HashSHA512,
    52  	}).Get(existing)
    53  	if err != nil {
    54  		return nil, false, err
    55  	}
    56  	if has {
    57  		return existing, true, nil
    58  	}
    59  	if _, err = e.Insert(pb); err != nil {
    60  		return nil, false, err
    61  	}
    62  	return pb, false, nil
    63  }
    64  
    65  // GetBlobByID gets a blob by id
    66  func GetBlobByID(ctx context.Context, blobID int64) (*PackageBlob, error) {
    67  	pb := &PackageBlob{}
    68  
    69  	has, err := db.GetEngine(ctx).ID(blobID).Get(pb)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	if !has {
    74  		return nil, ErrPackageBlobNotExist
    75  	}
    76  	return pb, nil
    77  }
    78  
    79  // ExistPackageBlobWithSHA returns if a package blob exists with the provided sha
    80  func ExistPackageBlobWithSHA(ctx context.Context, blobSha256 string) (bool, error) {
    81  	return db.GetEngine(ctx).Exist(&PackageBlob{
    82  		HashSHA256: blobSha256,
    83  	})
    84  }
    85  
    86  // FindExpiredUnreferencedBlobs gets all blobs without associated files older than the specific duration
    87  func FindExpiredUnreferencedBlobs(ctx context.Context, olderThan time.Duration) ([]*PackageBlob, error) {
    88  	pbs := make([]*PackageBlob, 0, 10)
    89  	return pbs, db.GetEngine(ctx).
    90  		Table("package_blob").
    91  		Join("LEFT", "package_file", "package_file.blob_id = package_blob.id").
    92  		Where("package_file.id IS NULL AND package_blob.created_unix < ?", time.Now().Add(-olderThan).Unix()).
    93  		Find(&pbs)
    94  }
    95  
    96  // DeleteBlobByID deletes a blob by id
    97  func DeleteBlobByID(ctx context.Context, blobID int64) error {
    98  	_, err := db.GetEngine(ctx).ID(blobID).Delete(&PackageBlob{})
    99  	return err
   100  }
   101  
   102  // GetTotalBlobSize returns the total blobs size in bytes
   103  func GetTotalBlobSize(ctx context.Context) (int64, error) {
   104  	return db.GetEngine(ctx).
   105  		SumInt(&PackageBlob{}, "size")
   106  }
   107  
   108  // GetTotalUnreferencedBlobSize returns the total size of all unreferenced blobs in bytes
   109  func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) {
   110  	return db.GetEngine(ctx).
   111  		Table("package_blob").
   112  		Join("LEFT", "package_file", "package_file.blob_id = package_blob.id").
   113  		Where("package_file.id IS NULL").
   114  		SumInt(&PackageBlob{}, "size")
   115  }
   116  
   117  // IsBlobAccessibleForUser tests if the user has access to the blob
   118  func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model.User) (bool, error) {
   119  	if user.IsAdmin {
   120  		return true, nil
   121  	}
   122  
   123  	maxTeamAuthorize := builder.
   124  		Select("max(team.authorize)").
   125  		From("team").
   126  		InnerJoin("team_user", "team_user.team_id = team.id").
   127  		Where(builder.Eq{"team_user.uid": user.ID}.And(builder.Expr("team_user.org_id = `user`.id")))
   128  
   129  	maxTeamUnitAccessMode := builder.
   130  		Select("max(team_unit.access_mode)").
   131  		From("team").
   132  		InnerJoin("team_user", "team_user.team_id = team.id").
   133  		InnerJoin("team_unit", "team_unit.team_id = team.id").
   134  		Where(builder.Eq{"team_user.uid": user.ID, "team_unit.type": unit.TypePackages}.And(builder.Expr("team_user.org_id = `user`.id")))
   135  
   136  	cond := builder.Eq{"package_blob.id": blobID}.And(
   137  		// owner = user
   138  		builder.Eq{"`user`.id": user.ID}.
   139  			// user can see owner
   140  			Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})).
   141  			// owner is an organization and user has access to it
   142  			Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}.
   143  				And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))),
   144  	)
   145  
   146  	return db.GetEngine(ctx).
   147  		Table("package_blob").
   148  		Join("INNER", "package_file", "package_file.blob_id = package_blob.id").
   149  		Join("INNER", "package_version", "package_version.id = package_file.version_id").
   150  		Join("INNER", "package", "package.id = package_version.package_id").
   151  		Join("INNER", "user", "`user`.id = package.owner_id").
   152  		Where(cond).
   153  		Exist(&PackageBlob{})
   154  }