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 }