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

locks.go 8.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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/log"
  12. "code.gitea.io/gitea/modules/setting"
  13. api "code.gitea.io/gitea/modules/structs"
  14. )
  15. //checkIsValidRequest check if it a valid request in case of bad request it write the response to ctx.
  16. func checkIsValidRequest(ctx *context.Context, post bool) bool {
  17. if !setting.LFS.StartServer {
  18. writeStatus(ctx, 404)
  19. return false
  20. }
  21. if !MetaMatcher(ctx.Req) {
  22. writeStatus(ctx, 400)
  23. return false
  24. }
  25. if !ctx.IsSigned {
  26. user, _, _, err := parseToken(ctx.Req.Header.Get("Authorization"))
  27. if err != nil {
  28. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  29. writeStatus(ctx, 401)
  30. return false
  31. }
  32. ctx.User = user
  33. }
  34. if post {
  35. mediaParts := strings.Split(ctx.Req.Header.Get("Content-Type"), ";")
  36. if mediaParts[0] != metaMediaType {
  37. writeStatus(ctx, 400)
  38. return false
  39. }
  40. }
  41. return true
  42. }
  43. func handleLockListOut(ctx *context.Context, repo *models.Repository, lock *models.LFSLock, err error) {
  44. if err != nil {
  45. if models.IsErrLFSLockNotExist(err) {
  46. ctx.JSON(200, api.LFSLockList{
  47. Locks: []*api.LFSLock{},
  48. })
  49. return
  50. }
  51. ctx.JSON(500, api.LFSLockError{
  52. Message: "unable to list locks : " + err.Error(),
  53. })
  54. return
  55. }
  56. if repo.ID != lock.RepoID {
  57. ctx.JSON(200, api.LFSLockList{
  58. Locks: []*api.LFSLock{},
  59. })
  60. return
  61. }
  62. ctx.JSON(200, api.LFSLockList{
  63. Locks: []*api.LFSLock{lock.APIFormat()},
  64. })
  65. }
  66. // GetListLockHandler list locks
  67. func GetListLockHandler(ctx *context.Context) {
  68. if !checkIsValidRequest(ctx, false) {
  69. return
  70. }
  71. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  72. rv := unpack(ctx)
  73. repository, err := models.GetRepositoryByOwnerAndName(rv.User, rv.Repo)
  74. if err != nil {
  75. log.Debug("Could not find repository: %s/%s - %s", rv.User, rv.Repo, err)
  76. writeStatus(ctx, 404)
  77. return
  78. }
  79. repository.MustOwner()
  80. authenticated := authenticate(ctx, repository, rv.Authorization, false)
  81. if !authenticated {
  82. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  83. ctx.JSON(401, api.LFSLockError{
  84. Message: "You must have pull access to list locks",
  85. })
  86. return
  87. }
  88. //TODO handle query cursor and limit
  89. id := ctx.Query("id")
  90. if id != "" { //Case where we request a specific id
  91. v, err := strconv.ParseInt(id, 10, 64)
  92. if err != nil {
  93. ctx.JSON(400, api.LFSLockError{
  94. Message: "bad request : " + err.Error(),
  95. })
  96. return
  97. }
  98. lock, err := models.GetLFSLockByID(int64(v))
  99. handleLockListOut(ctx, repository, lock, err)
  100. return
  101. }
  102. path := ctx.Query("path")
  103. if path != "" { //Case where we request a specific id
  104. lock, err := models.GetLFSLock(repository, path)
  105. handleLockListOut(ctx, repository, lock, err)
  106. return
  107. }
  108. //If no query params path or id
  109. lockList, err := models.GetLFSLockByRepoID(repository.ID)
  110. if err != nil {
  111. ctx.JSON(500, api.LFSLockError{
  112. Message: "unable to list locks : " + err.Error(),
  113. })
  114. return
  115. }
  116. lockListAPI := make([]*api.LFSLock, len(lockList))
  117. for i, l := range lockList {
  118. lockListAPI[i] = l.APIFormat()
  119. }
  120. ctx.JSON(200, api.LFSLockList{
  121. Locks: lockListAPI,
  122. })
  123. }
  124. // PostLockHandler create lock
  125. func PostLockHandler(ctx *context.Context) {
  126. if !checkIsValidRequest(ctx, false) {
  127. return
  128. }
  129. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  130. userName := ctx.Params("username")
  131. repoName := strings.TrimSuffix(ctx.Params("reponame"), ".git")
  132. authorization := ctx.Req.Header.Get("Authorization")
  133. repository, err := models.GetRepositoryByOwnerAndName(userName, repoName)
  134. if err != nil {
  135. log.Debug("Could not find repository: %s/%s - %s", userName, repoName, err)
  136. writeStatus(ctx, 404)
  137. return
  138. }
  139. repository.MustOwner()
  140. authenticated := authenticate(ctx, repository, authorization, true)
  141. if !authenticated {
  142. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  143. ctx.JSON(401, api.LFSLockError{
  144. Message: "You must have push access to create locks",
  145. })
  146. return
  147. }
  148. var req api.LFSLockRequest
  149. dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
  150. if err := dec.Decode(&req); err != nil {
  151. writeStatus(ctx, 400)
  152. return
  153. }
  154. lock, err := models.CreateLFSLock(&models.LFSLock{
  155. Repo: repository,
  156. Path: req.Path,
  157. Owner: ctx.User,
  158. })
  159. if err != nil {
  160. if models.IsErrLFSLockAlreadyExist(err) {
  161. ctx.JSON(409, api.LFSLockError{
  162. Lock: lock.APIFormat(),
  163. Message: "already created lock",
  164. })
  165. return
  166. }
  167. if models.IsErrLFSUnauthorizedAction(err) {
  168. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  169. ctx.JSON(401, api.LFSLockError{
  170. Message: "You must have push access to create locks : " + err.Error(),
  171. })
  172. return
  173. }
  174. ctx.JSON(500, api.LFSLockError{
  175. Message: "internal server error : " + err.Error(),
  176. })
  177. return
  178. }
  179. ctx.JSON(201, api.LFSLockResponse{Lock: lock.APIFormat()})
  180. }
  181. // VerifyLockHandler list locks for verification
  182. func VerifyLockHandler(ctx *context.Context) {
  183. if !checkIsValidRequest(ctx, false) {
  184. return
  185. }
  186. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  187. userName := ctx.Params("username")
  188. repoName := strings.TrimSuffix(ctx.Params("reponame"), ".git")
  189. authorization := ctx.Req.Header.Get("Authorization")
  190. repository, err := models.GetRepositoryByOwnerAndName(userName, repoName)
  191. if err != nil {
  192. log.Debug("Could not find repository: %s/%s - %s", userName, repoName, err)
  193. writeStatus(ctx, 404)
  194. return
  195. }
  196. repository.MustOwner()
  197. authenticated := authenticate(ctx, repository, authorization, true)
  198. if !authenticated {
  199. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  200. ctx.JSON(401, api.LFSLockError{
  201. Message: "You must have push access to verify locks",
  202. })
  203. return
  204. }
  205. //TODO handle body json cursor and limit
  206. lockList, err := models.GetLFSLockByRepoID(repository.ID)
  207. if err != nil {
  208. ctx.JSON(500, api.LFSLockError{
  209. Message: "unable to list locks : " + err.Error(),
  210. })
  211. return
  212. }
  213. lockOursListAPI := make([]*api.LFSLock, 0, len(lockList))
  214. lockTheirsListAPI := make([]*api.LFSLock, 0, len(lockList))
  215. for _, l := range lockList {
  216. if l.Owner.ID == ctx.User.ID {
  217. lockOursListAPI = append(lockOursListAPI, l.APIFormat())
  218. } else {
  219. lockTheirsListAPI = append(lockTheirsListAPI, l.APIFormat())
  220. }
  221. }
  222. ctx.JSON(200, api.LFSLockListVerify{
  223. Ours: lockOursListAPI,
  224. Theirs: lockTheirsListAPI,
  225. })
  226. }
  227. // UnLockHandler delete locks
  228. func UnLockHandler(ctx *context.Context) {
  229. if !checkIsValidRequest(ctx, false) {
  230. return
  231. }
  232. ctx.Resp.Header().Set("Content-Type", metaMediaType)
  233. userName := ctx.Params("username")
  234. repoName := strings.TrimSuffix(ctx.Params("reponame"), ".git")
  235. authorization := ctx.Req.Header.Get("Authorization")
  236. repository, err := models.GetRepositoryByOwnerAndName(userName, repoName)
  237. if err != nil {
  238. log.Debug("Could not find repository: %s/%s - %s", userName, repoName, err)
  239. writeStatus(ctx, 404)
  240. return
  241. }
  242. repository.MustOwner()
  243. authenticated := authenticate(ctx, repository, authorization, true)
  244. if !authenticated {
  245. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  246. ctx.JSON(401, api.LFSLockError{
  247. Message: "You must have push access to delete locks",
  248. })
  249. return
  250. }
  251. var req api.LFSLockDeleteRequest
  252. dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
  253. if err := dec.Decode(&req); err != nil {
  254. writeStatus(ctx, 400)
  255. return
  256. }
  257. lock, err := models.DeleteLFSLockByID(ctx.ParamsInt64("lid"), ctx.User, req.Force)
  258. if err != nil {
  259. if models.IsErrLFSUnauthorizedAction(err) {
  260. ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
  261. ctx.JSON(401, api.LFSLockError{
  262. Message: "You must have push access to delete locks : " + err.Error(),
  263. })
  264. return
  265. }
  266. ctx.JSON(500, api.LFSLockError{
  267. Message: "unable to delete lock : " + err.Error(),
  268. })
  269. return
  270. }
  271. ctx.JSON(200, api.LFSLockResponse{Lock: lock.APIFormat()})
  272. }