code.gitea.io/gitea@v1.21.7/models/git/lfs_lock.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package git
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  
    12  	"code.gitea.io/gitea/models/db"
    13  	"code.gitea.io/gitea/models/perm"
    14  	access_model "code.gitea.io/gitea/models/perm/access"
    15  	repo_model "code.gitea.io/gitea/models/repo"
    16  	"code.gitea.io/gitea/models/unit"
    17  	user_model "code.gitea.io/gitea/models/user"
    18  	"code.gitea.io/gitea/modules/setting"
    19  	"code.gitea.io/gitea/modules/util"
    20  )
    21  
    22  // LFSLock represents a git lfs lock of repository.
    23  type LFSLock struct {
    24  	ID      int64     `xorm:"pk autoincr"`
    25  	RepoID  int64     `xorm:"INDEX NOT NULL"`
    26  	OwnerID int64     `xorm:"INDEX NOT NULL"`
    27  	Path    string    `xorm:"TEXT"`
    28  	Created time.Time `xorm:"created"`
    29  }
    30  
    31  func init() {
    32  	db.RegisterModel(new(LFSLock))
    33  }
    34  
    35  // BeforeInsert is invoked from XORM before inserting an object of this type.
    36  func (l *LFSLock) BeforeInsert() {
    37  	l.Path = util.PathJoinRel(l.Path)
    38  }
    39  
    40  // CreateLFSLock creates a new lock.
    41  func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
    42  	dbCtx, committer, err := db.TxContext(ctx)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	defer committer.Close()
    47  
    48  	if err := CheckLFSAccessForRepo(dbCtx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	lock.Path = util.PathJoinRel(lock.Path)
    53  	lock.RepoID = repo.ID
    54  
    55  	l, err := GetLFSLock(dbCtx, repo, lock.Path)
    56  	if err == nil {
    57  		return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
    58  	}
    59  	if !IsErrLFSLockNotExist(err) {
    60  		return nil, err
    61  	}
    62  
    63  	if err := db.Insert(dbCtx, lock); err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	return lock, committer.Commit()
    68  }
    69  
    70  // GetLFSLock returns release by given path.
    71  func GetLFSLock(ctx context.Context, repo *repo_model.Repository, path string) (*LFSLock, error) {
    72  	path = util.PathJoinRel(path)
    73  	rel := &LFSLock{RepoID: repo.ID}
    74  	has, err := db.GetEngine(ctx).Where("lower(path) = ?", strings.ToLower(path)).Get(rel)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	if !has {
    79  		return nil, ErrLFSLockNotExist{0, repo.ID, path}
    80  	}
    81  	return rel, nil
    82  }
    83  
    84  // GetLFSLockByID returns release by given id.
    85  func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
    86  	lock := new(LFSLock)
    87  	has, err := db.GetEngine(ctx).ID(id).Get(lock)
    88  	if err != nil {
    89  		return nil, err
    90  	} else if !has {
    91  		return nil, ErrLFSLockNotExist{id, 0, ""}
    92  	}
    93  	return lock, nil
    94  }
    95  
    96  // GetLFSLockByRepoID returns a list of locks of repository.
    97  func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ([]*LFSLock, error) {
    98  	e := db.GetEngine(ctx)
    99  	if page >= 0 && pageSize > 0 {
   100  		start := 0
   101  		if page > 0 {
   102  			start = (page - 1) * pageSize
   103  		}
   104  		e.Limit(pageSize, start)
   105  	}
   106  	lfsLocks := make([]*LFSLock, 0, pageSize)
   107  	return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID})
   108  }
   109  
   110  // GetTreePathLock returns LSF lock for the treePath
   111  func GetTreePathLock(ctx context.Context, repoID int64, treePath string) (*LFSLock, error) {
   112  	if !setting.LFS.StartServer {
   113  		return nil, nil
   114  	}
   115  
   116  	locks, err := GetLFSLockByRepoID(ctx, repoID, 0, 0)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	for _, lock := range locks {
   121  		if lock.Path == treePath {
   122  			return lock, nil
   123  		}
   124  	}
   125  	return nil, nil
   126  }
   127  
   128  // CountLFSLockByRepoID returns a count of all LFSLocks associated with a repository.
   129  func CountLFSLockByRepoID(ctx context.Context, repoID int64) (int64, error) {
   130  	return db.GetEngine(ctx).Count(&LFSLock{RepoID: repoID})
   131  }
   132  
   133  // DeleteLFSLockByID deletes a lock by given ID.
   134  func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
   135  	dbCtx, committer, err := db.TxContext(ctx)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	defer committer.Close()
   140  
   141  	lock, err := GetLFSLockByID(dbCtx, id)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	if err := CheckLFSAccessForRepo(dbCtx, u.ID, repo, perm.AccessModeWrite); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	if !force && u.ID != lock.OwnerID {
   151  		return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
   152  	}
   153  
   154  	if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	return lock, committer.Commit()
   159  }
   160  
   161  // CheckLFSAccessForRepo check needed access mode base on action
   162  func CheckLFSAccessForRepo(ctx context.Context, ownerID int64, repo *repo_model.Repository, mode perm.AccessMode) error {
   163  	if ownerID == 0 {
   164  		return ErrLFSUnauthorizedAction{repo.ID, "undefined", mode}
   165  	}
   166  	u, err := user_model.GetUserByID(ctx, ownerID)
   167  	if err != nil {
   168  		return err
   169  	}
   170  	perm, err := access_model.GetUserRepoPermission(ctx, repo, u)
   171  	if err != nil {
   172  		return err
   173  	}
   174  	if !perm.CanAccess(mode, unit.TypeCode) {
   175  		return ErrLFSUnauthorizedAction{repo.ID, u.DisplayName(), mode}
   176  	}
   177  	return nil
   178  }