gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "code.gitea.io/gitea/models/db"
  9. "code.gitea.io/gitea/models/organization"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/timeutil"
  13. "code.gitea.io/gitea/modules/util"
  14. "xorm.io/builder"
  15. )
  16. // ErrNoPendingRepoTransfer is an error type for repositories without a pending
  17. // transfer request
  18. type ErrNoPendingRepoTransfer struct {
  19. RepoID int64
  20. }
  21. func (err ErrNoPendingRepoTransfer) Error() string {
  22. return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID)
  23. }
  24. // IsErrNoPendingTransfer is an error type when a repository has no pending
  25. // transfers
  26. func IsErrNoPendingTransfer(err error) bool {
  27. _, ok := err.(ErrNoPendingRepoTransfer)
  28. return ok
  29. }
  30. func (err ErrNoPendingRepoTransfer) Unwrap() error {
  31. return util.ErrNotExist
  32. }
  33. // ErrRepoTransferInProgress represents the state of a repository that has an
  34. // ongoing transfer
  35. type ErrRepoTransferInProgress struct {
  36. Uname string
  37. Name string
  38. }
  39. // IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress.
  40. func IsErrRepoTransferInProgress(err error) bool {
  41. _, ok := err.(ErrRepoTransferInProgress)
  42. return ok
  43. }
  44. func (err ErrRepoTransferInProgress) Error() string {
  45. return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
  46. }
  47. func (err ErrRepoTransferInProgress) Unwrap() error {
  48. return util.ErrAlreadyExist
  49. }
  50. // RepoTransfer is used to manage repository transfers
  51. type RepoTransfer struct { //nolint:revive // export stutter
  52. ID int64 `xorm:"pk autoincr"`
  53. DoerID int64
  54. Doer *user_model.User `xorm:"-"`
  55. RecipientID int64
  56. Recipient *user_model.User `xorm:"-"`
  57. RepoID int64
  58. Repo *Repository `xorm:"-"`
  59. TeamIDs []int64
  60. Teams []*organization.Team `xorm:"-"`
  61. CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"`
  62. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"`
  63. }
  64. func init() {
  65. db.RegisterModel(new(RepoTransfer))
  66. }
  67. func (r *RepoTransfer) LoadRecipient(ctx context.Context) error {
  68. if r.Recipient == nil {
  69. u, err := user_model.GetUserByID(ctx, r.RecipientID)
  70. if err != nil {
  71. return err
  72. }
  73. r.Recipient = u
  74. }
  75. return nil
  76. }
  77. func (r *RepoTransfer) LoadRepo(ctx context.Context) error {
  78. if r.Repo == nil {
  79. repo, err := GetRepositoryByID(ctx, r.RepoID)
  80. if err != nil {
  81. return err
  82. }
  83. r.Repo = repo
  84. }
  85. return nil
  86. }
  87. // LoadAttributes fetches the transfer recipient from the database
  88. func (r *RepoTransfer) LoadAttributes(ctx context.Context) error {
  89. if err := r.LoadRecipient(ctx); err != nil {
  90. return err
  91. }
  92. if r.Recipient.IsOrganization() && r.Teams == nil {
  93. teamsMap, err := organization.GetTeamsByIDs(ctx, r.TeamIDs)
  94. if err != nil {
  95. return err
  96. }
  97. for _, team := range teamsMap {
  98. r.Teams = append(r.Teams, team)
  99. }
  100. }
  101. if err := r.LoadRepo(ctx); err != nil {
  102. return err
  103. }
  104. if r.Doer == nil {
  105. u, err := user_model.GetUserByID(ctx, r.DoerID)
  106. if err != nil {
  107. return err
  108. }
  109. r.Doer = u
  110. }
  111. return nil
  112. }
  113. // CanUserAcceptOrRejectTransfer checks if the user has the rights to accept/decline a repo transfer.
  114. // For user, it checks if it's himself
  115. // For organizations, it checks if the user is able to create repos
  116. func (r *RepoTransfer) CanUserAcceptOrRejectTransfer(ctx context.Context, u *user_model.User) bool {
  117. if err := r.LoadAttributes(ctx); err != nil {
  118. log.Error("LoadAttributes: %v", err)
  119. return false
  120. }
  121. if !r.Recipient.IsOrganization() {
  122. return r.RecipientID == u.ID
  123. }
  124. allowed, err := organization.CanCreateOrgRepo(ctx, r.RecipientID, u.ID)
  125. if err != nil {
  126. log.Error("CanCreateOrgRepo: %v", err)
  127. return false
  128. }
  129. return allowed
  130. }
  131. type PendingRepositoryTransferOptions struct {
  132. RepoID int64
  133. SenderID int64
  134. RecipientID int64
  135. }
  136. func (opts *PendingRepositoryTransferOptions) ToConds() builder.Cond {
  137. cond := builder.NewCond()
  138. if opts.RepoID != 0 {
  139. cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
  140. }
  141. if opts.SenderID != 0 {
  142. cond = cond.And(builder.Eq{"doer_id": opts.SenderID})
  143. }
  144. if opts.RecipientID != 0 {
  145. cond = cond.And(builder.Eq{"recipient_id": opts.RecipientID})
  146. }
  147. return cond
  148. }
  149. func GetPendingRepositoryTransfers(ctx context.Context, opts *PendingRepositoryTransferOptions) ([]*RepoTransfer, error) {
  150. transfers := make([]*RepoTransfer, 0, 10)
  151. return transfers, db.GetEngine(ctx).
  152. Where(opts.ToConds()).
  153. Find(&transfers)
  154. }
  155. func IsRepositoryTransferExist(ctx context.Context, repoID int64) (bool, error) {
  156. return db.GetEngine(ctx).Where("repo_id = ?", repoID).Exist(new(RepoTransfer))
  157. }
  158. // GetPendingRepositoryTransfer fetches the most recent and ongoing transfer
  159. // process for the repository
  160. func GetPendingRepositoryTransfer(ctx context.Context, repo *Repository) (*RepoTransfer, error) {
  161. transfers, err := GetPendingRepositoryTransfers(ctx, &PendingRepositoryTransferOptions{RepoID: repo.ID})
  162. if err != nil {
  163. return nil, err
  164. }
  165. if len(transfers) != 1 {
  166. return nil, ErrNoPendingRepoTransfer{RepoID: repo.ID}
  167. }
  168. return transfers[0], nil
  169. }
  170. func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error {
  171. _, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(&RepoTransfer{})
  172. return err
  173. }
  174. // TestRepositoryReadyForTransfer make sure repo is ready to transfer
  175. func TestRepositoryReadyForTransfer(status RepositoryStatus) error {
  176. switch status {
  177. case RepositoryBeingMigrated:
  178. return errors.New("repo is not ready, currently migrating")
  179. case RepositoryPendingTransfer:
  180. return ErrRepoTransferInProgress{}
  181. }
  182. return nil
  183. }
  184. // CreatePendingRepositoryTransfer transfer a repo from one owner to a new one.
  185. // it marks the repository transfer as "pending"
  186. func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repoID int64, teams []*organization.Team) error {
  187. return db.WithTx(ctx, func(ctx context.Context) error {
  188. repo, err := GetRepositoryByID(ctx, repoID)
  189. if err != nil {
  190. return err
  191. }
  192. if _, err := user_model.GetUserByID(ctx, newOwner.ID); err != nil {
  193. return err
  194. }
  195. // Make sure repo is ready to transfer
  196. if err := TestRepositoryReadyForTransfer(repo.Status); err != nil {
  197. return err
  198. }
  199. exist, err := IsRepositoryTransferExist(ctx, repo.ID)
  200. if err != nil {
  201. return err
  202. }
  203. if exist {
  204. return ErrRepoTransferInProgress{
  205. Uname: repo.Owner.LowerName,
  206. Name: repo.Name,
  207. }
  208. }
  209. repo.Status = RepositoryPendingTransfer
  210. if err := UpdateRepositoryColsNoAutoTime(ctx, repo, "status"); err != nil {
  211. return err
  212. }
  213. // Check if new owner has repository with same name.
  214. if has, err := IsRepositoryModelExist(ctx, newOwner, repo.Name); err != nil {
  215. return fmt.Errorf("IsRepositoryExist: %w", err)
  216. } else if has {
  217. return ErrRepoAlreadyExist{
  218. Uname: newOwner.LowerName,
  219. Name: repo.Name,
  220. }
  221. }
  222. transfer := &RepoTransfer{
  223. RepoID: repo.ID,
  224. RecipientID: newOwner.ID,
  225. CreatedUnix: timeutil.TimeStampNow(),
  226. UpdatedUnix: timeutil.TimeStampNow(),
  227. DoerID: doer.ID,
  228. TeamIDs: make([]int64, 0, len(teams)),
  229. }
  230. for k := range teams {
  231. transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID)
  232. }
  233. return db.Insert(ctx, transfer)
  234. })
  235. }