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.

249 lines
6.3 KiB

  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package lfs
  5. import (
  6. "encoding/json"
  7. "strconv"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/context"
  11. "code.gitea.io/gitea/modules/setting"
  12. api "code.gitea.io/sdk/gitea"
  13. )
  14. //checkIsValidRequest check if it a valid request in case of bad request it write the response to ctx.
  15. func checkIsValidRequest(ctx *context.Context, post bool) bool {
  16. if !setting.LFS.StartServer {
  17. writeStatus(ctx, 404)
  18. return false
  19. }
  20. if !MetaMatcher(ctx.Req) {
  21. writeStatus(ctx, 400)
  22. return false
  23. }
  24. if !ctx.IsSigned {
  25. user, _, _, err := parseToken(ctx.Req.Header.Get("Authorization"))
  26. if err != nil {
  27. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  28. writeStatus(ctx, 401)
  29. return false
  30. }
  31. ctx.User = user
  32. }
  33. if post {
  34. mediaParts := strings.Split(ctx.Req.Header.Get("Content-Type"), ";")
  35. if mediaParts[0] != metaMediaType {
  36. writeStatus(ctx, 400)
  37. return false
  38. }
  39. }
  40. return true
  41. }
  42. func handleLockListOut(ctx *context.Context, lock *models.LFSLock, err error) {
  43. if err != nil {
  44. if models.IsErrLFSLockNotExist(err) {
  45. ctx.JSON(200, api.LFSLockList{
  46. Locks: []*api.LFSLock{},
  47. })
  48. return
  49. }
  50. ctx.JSON(500, api.LFSLockError{
  51. Message: "unable to list locks : " + err.Error(),
  52. })
  53. return
  54. }
  55. if ctx.Repo.Repository.ID != lock.RepoID {
  56. ctx.JSON(200, api.LFSLockList{
  57. Locks: []*api.LFSLock{},
  58. })
  59. return
  60. }
  61. ctx.JSON(200, api.LFSLockList{
  62. Locks: []*api.LFSLock{lock.APIFormat()},
  63. })
  64. }
  65. // GetListLockHandler list locks
  66. func GetListLockHandler(ctx *context.Context) {
  67. if !checkIsValidRequest(ctx, false) {
  68. return
  69. }
  70. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  71. err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository, models.AccessModeRead)
  72. if err != nil {
  73. if models.IsErrLFSUnauthorizedAction(err) {
  74. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  75. ctx.JSON(401, api.LFSLockError{
  76. Message: "You must have pull access to list locks : " + err.Error(),
  77. })
  78. return
  79. }
  80. ctx.JSON(500, api.LFSLockError{
  81. Message: "unable to list lock : " + err.Error(),
  82. })
  83. return
  84. }
  85. //TODO handle query cursor and limit
  86. id := ctx.Query("id")
  87. if id != "" { //Case where we request a specific id
  88. v, err := strconv.ParseInt(id, 10, 64)
  89. if err != nil {
  90. ctx.JSON(400, api.LFSLockError{
  91. Message: "bad request : " + err.Error(),
  92. })
  93. return
  94. }
  95. lock, err := models.GetLFSLockByID(int64(v))
  96. handleLockListOut(ctx, lock, err)
  97. return
  98. }
  99. path := ctx.Query("path")
  100. if path != "" { //Case where we request a specific id
  101. lock, err := models.GetLFSLock(ctx.Repo.Repository, path)
  102. handleLockListOut(ctx, lock, err)
  103. return
  104. }
  105. //If no query params path or id
  106. lockList, err := models.GetLFSLockByRepoID(ctx.Repo.Repository.ID)
  107. if err != nil {
  108. ctx.JSON(500, api.LFSLockError{
  109. Message: "unable to list locks : " + err.Error(),
  110. })
  111. return
  112. }
  113. lockListAPI := make([]*api.LFSLock, len(lockList))
  114. for i, l := range lockList {
  115. lockListAPI[i] = l.APIFormat()
  116. }
  117. ctx.JSON(200, api.LFSLockList{
  118. Locks: lockListAPI,
  119. })
  120. }
  121. // PostLockHandler create lock
  122. func PostLockHandler(ctx *context.Context) {
  123. if !checkIsValidRequest(ctx, false) {
  124. return
  125. }
  126. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  127. var req api.LFSLockRequest
  128. dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
  129. err := dec.Decode(&req)
  130. if err != nil {
  131. writeStatus(ctx, 400)
  132. return
  133. }
  134. lock, err := models.CreateLFSLock(&models.LFSLock{
  135. Repo: ctx.Repo.Repository,
  136. Path: req.Path,
  137. Owner: ctx.User,
  138. })
  139. if err != nil {
  140. if models.IsErrLFSLockAlreadyExist(err) {
  141. ctx.JSON(409, api.LFSLockError{
  142. Lock: lock.APIFormat(),
  143. Message: "already created lock",
  144. })
  145. return
  146. }
  147. if models.IsErrLFSUnauthorizedAction(err) {
  148. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  149. ctx.JSON(401, api.LFSLockError{
  150. Message: "You must have push access to create locks : " + err.Error(),
  151. })
  152. return
  153. }
  154. ctx.JSON(500, api.LFSLockError{
  155. Message: "internal server error : " + err.Error(),
  156. })
  157. return
  158. }
  159. ctx.JSON(201, api.LFSLockResponse{Lock: lock.APIFormat()})
  160. }
  161. // VerifyLockHandler list locks for verification
  162. func VerifyLockHandler(ctx *context.Context) {
  163. if !checkIsValidRequest(ctx, false) {
  164. return
  165. }
  166. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  167. err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository, models.AccessModeWrite)
  168. if err != nil {
  169. if models.IsErrLFSUnauthorizedAction(err) {
  170. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  171. ctx.JSON(401, api.LFSLockError{
  172. Message: "You must have push access to verify locks : " + err.Error(),
  173. })
  174. return
  175. }
  176. ctx.JSON(500, api.LFSLockError{
  177. Message: "unable to verify lock : " + err.Error(),
  178. })
  179. return
  180. }
  181. //TODO handle body json cursor and limit
  182. lockList, err := models.GetLFSLockByRepoID(ctx.Repo.Repository.ID)
  183. if err != nil {
  184. ctx.JSON(500, api.LFSLockError{
  185. Message: "unable to list locks : " + err.Error(),
  186. })
  187. return
  188. }
  189. lockOursListAPI := make([]*api.LFSLock, 0, len(lockList))
  190. lockTheirsListAPI := make([]*api.LFSLock, 0, len(lockList))
  191. for _, l := range lockList {
  192. if l.Owner.ID == ctx.User.ID {
  193. lockOursListAPI = append(lockOursListAPI, l.APIFormat())
  194. } else {
  195. lockTheirsListAPI = append(lockTheirsListAPI, l.APIFormat())
  196. }
  197. }
  198. ctx.JSON(200, api.LFSLockListVerify{
  199. Ours: lockOursListAPI,
  200. Theirs: lockTheirsListAPI,
  201. })
  202. }
  203. // UnLockHandler delete locks
  204. func UnLockHandler(ctx *context.Context) {
  205. if !checkIsValidRequest(ctx, false) {
  206. return
  207. }
  208. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  209. var req api.LFSLockDeleteRequest
  210. dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
  211. err := dec.Decode(&req)
  212. if err != nil {
  213. writeStatus(ctx, 400)
  214. return
  215. }
  216. lock, err := models.DeleteLFSLockByID(ctx.ParamsInt64("lid"), ctx.User, req.Force)
  217. if err != nil {
  218. if models.IsErrLFSUnauthorizedAction(err) {
  219. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  220. ctx.JSON(401, api.LFSLockError{
  221. Message: "You must have push access to delete locks : " + err.Error(),
  222. })
  223. return
  224. }
  225. ctx.JSON(500, api.LFSLockError{
  226. Message: "unable to delete lock : " + err.Error(),
  227. })
  228. return
  229. }
  230. ctx.JSON(200, api.LFSLockResponse{Lock: lock.APIFormat()})
  231. }