You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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