gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "code.gitea.io/gitea/models/organization"
  9. "code.gitea.io/gitea/models/perm"
  10. access_model "code.gitea.io/gitea/models/perm/access"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/log"
  14. api "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/modules/web"
  17. "code.gitea.io/gitea/services/context"
  18. "code.gitea.io/gitea/services/convert"
  19. repo_service "code.gitea.io/gitea/services/repository"
  20. )
  21. // Transfer transfers the ownership of a repository
  22. func Transfer(ctx *context.APIContext) {
  23. // swagger:operation POST /repos/{owner}/{repo}/transfer repository repoTransfer
  24. // ---
  25. // summary: Transfer a repo ownership
  26. // produces:
  27. // - application/json
  28. // parameters:
  29. // - name: owner
  30. // in: path
  31. // description: owner of the repo to transfer
  32. // type: string
  33. // required: true
  34. // - name: repo
  35. // in: path
  36. // description: name of the repo to transfer
  37. // type: string
  38. // required: true
  39. // - name: body
  40. // in: body
  41. // description: "Transfer Options"
  42. // required: true
  43. // schema:
  44. // "$ref": "#/definitions/TransferRepoOption"
  45. // responses:
  46. // "202":
  47. // "$ref": "#/responses/Repository"
  48. // "403":
  49. // "$ref": "#/responses/forbidden"
  50. // "404":
  51. // "$ref": "#/responses/notFound"
  52. // "422":
  53. // "$ref": "#/responses/validationError"
  54. opts := web.GetForm(ctx).(*api.TransferRepoOption)
  55. newOwner, err := user_model.GetUserByName(ctx, opts.NewOwner)
  56. if err != nil {
  57. if user_model.IsErrUserNotExist(err) {
  58. ctx.APIError(http.StatusNotFound, "The new owner does not exist or cannot be found")
  59. return
  60. }
  61. ctx.APIErrorInternal(err)
  62. return
  63. }
  64. if newOwner.Type == user_model.UserTypeOrganization {
  65. if !ctx.Doer.IsAdmin && newOwner.Visibility == api.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx, ctx.Doer.ID) {
  66. // The user shouldn't know about this organization
  67. ctx.APIError(http.StatusNotFound, "The new owner does not exist or cannot be found")
  68. return
  69. }
  70. }
  71. var teams []*organization.Team
  72. if opts.TeamIDs != nil {
  73. if !newOwner.IsOrganization() {
  74. ctx.APIError(http.StatusUnprocessableEntity, "Teams can only be added to organization-owned repositories")
  75. return
  76. }
  77. org := convert.ToOrganization(ctx, organization.OrgFromUser(newOwner))
  78. for _, tID := range *opts.TeamIDs {
  79. team, err := organization.GetTeamByID(ctx, tID)
  80. if err != nil {
  81. ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("team %d not found", tID))
  82. return
  83. }
  84. if team.OrgID != org.ID {
  85. ctx.APIError(http.StatusForbidden, fmt.Errorf("team %d belongs not to org %d", tID, org.ID))
  86. return
  87. }
  88. teams = append(teams, team)
  89. }
  90. }
  91. if ctx.Repo.GitRepo != nil {
  92. _ = ctx.Repo.GitRepo.Close()
  93. ctx.Repo.GitRepo = nil
  94. }
  95. oldFullname := ctx.Repo.Repository.FullName()
  96. if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil {
  97. switch {
  98. case repo_model.IsErrRepoTransferInProgress(err):
  99. ctx.APIError(http.StatusConflict, err)
  100. case repo_model.IsErrRepoAlreadyExist(err):
  101. ctx.APIError(http.StatusUnprocessableEntity, err)
  102. case repo_service.IsRepositoryLimitReached(err):
  103. ctx.APIError(http.StatusForbidden, err)
  104. case errors.Is(err, user_model.ErrBlockedUser):
  105. ctx.APIError(http.StatusForbidden, err)
  106. default:
  107. ctx.APIErrorInternal(err)
  108. }
  109. return
  110. }
  111. if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
  112. log.Trace("Repository transfer initiated: %s -> %s", oldFullname, ctx.Repo.Repository.FullName())
  113. ctx.JSON(http.StatusCreated, convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeAdmin}))
  114. return
  115. }
  116. log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName())
  117. ctx.JSON(http.StatusAccepted, convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeAdmin}))
  118. }
  119. // AcceptTransfer accept a repo transfer
  120. func AcceptTransfer(ctx *context.APIContext) {
  121. // swagger:operation POST /repos/{owner}/{repo}/transfer/accept repository acceptRepoTransfer
  122. // ---
  123. // summary: Accept a repo transfer
  124. // produces:
  125. // - application/json
  126. // parameters:
  127. // - name: owner
  128. // in: path
  129. // description: owner of the repo to transfer
  130. // type: string
  131. // required: true
  132. // - name: repo
  133. // in: path
  134. // description: name of the repo to transfer
  135. // type: string
  136. // required: true
  137. // responses:
  138. // "202":
  139. // "$ref": "#/responses/Repository"
  140. // "403":
  141. // "$ref": "#/responses/forbidden"
  142. // "404":
  143. // "$ref": "#/responses/notFound"
  144. err := repo_service.AcceptTransferOwnership(ctx, ctx.Repo.Repository, ctx.Doer)
  145. if err != nil {
  146. switch {
  147. case repo_model.IsErrNoPendingTransfer(err):
  148. ctx.APIError(http.StatusNotFound, err)
  149. case errors.Is(err, util.ErrPermissionDenied):
  150. ctx.APIError(http.StatusForbidden, err)
  151. case repo_service.IsRepositoryLimitReached(err):
  152. ctx.APIError(http.StatusForbidden, err)
  153. default:
  154. ctx.APIErrorInternal(err)
  155. }
  156. return
  157. }
  158. ctx.JSON(http.StatusAccepted, convert.ToRepo(ctx, ctx.Repo.Repository, ctx.Repo.Permission))
  159. }
  160. // RejectTransfer reject a repo transfer
  161. func RejectTransfer(ctx *context.APIContext) {
  162. // swagger:operation POST /repos/{owner}/{repo}/transfer/reject repository rejectRepoTransfer
  163. // ---
  164. // summary: Reject a repo transfer
  165. // produces:
  166. // - application/json
  167. // parameters:
  168. // - name: owner
  169. // in: path
  170. // description: owner of the repo to transfer
  171. // type: string
  172. // required: true
  173. // - name: repo
  174. // in: path
  175. // description: name of the repo to transfer
  176. // type: string
  177. // required: true
  178. // responses:
  179. // "200":
  180. // "$ref": "#/responses/Repository"
  181. // "403":
  182. // "$ref": "#/responses/forbidden"
  183. // "404":
  184. // "$ref": "#/responses/notFound"
  185. err := repo_service.RejectRepositoryTransfer(ctx, ctx.Repo.Repository, ctx.Doer)
  186. if err != nil {
  187. switch {
  188. case repo_model.IsErrNoPendingTransfer(err):
  189. ctx.APIError(http.StatusNotFound, err)
  190. case errors.Is(err, util.ErrPermissionDenied):
  191. ctx.APIError(http.StatusForbidden, err)
  192. default:
  193. ctx.APIErrorInternal(err)
  194. }
  195. return
  196. }
  197. ctx.JSON(http.StatusOK, convert.ToRepo(ctx, ctx.Repo.Repository, ctx.Repo.Permission))
  198. }