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