mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Git LFS lock api (#2938)
* Implement routes * move to api/sdk and create model * Implement add + list * List return 200 empty list no 404 * Add verify lfs lock api * Add delete and start implementing auth control * Revert to code.gitea.io/sdk/gitea vendor * Apply needed check for all lfs locks route * Add simple tests * fix lint * Improve tests * Add delete test + fix * Add lfs ascii header * Various fixes from review + remove useless code + add more corner case testing * Remove repo link since only id is needed. Save a little of memory and cpu time. * Improve tests * Use TEXT column format for path + test * fix mispell * Use NewRequestWithJSON for POST tests * Clean path * Improve DB format * Revert uniquess repoid+path * (Re)-setup uniqueness + max path length * Fixed TEXT in place of VARCHAR * Settle back to maximum VARCHAR(3072) * Let place for repoid in key * Let place for repoid in key * Let place for repoid in key * Revert back
This commit is contained in:
		
				
					committed by
					
						
						Lauris BH
					
				
			
			
				
	
			
			
			
						parent
						
							6ad4990a65
						
					
				
				
					commit
					d99f4ab003
				
			
							
								
								
									
										176
									
								
								integrations/api_repo_lfs_locks_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								integrations/api_repo_lfs_locks_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						api "code.gitea.io/sdk/gitea"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPILFSLocksNotStarted(t *testing.T) {
 | 
				
			||||||
 | 
						prepareTestEnv(t)
 | 
				
			||||||
 | 
						setting.LFS.StartServer = false
 | 
				
			||||||
 | 
						user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
 | 
				
			||||||
 | 
						repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := NewRequestf(t, "GET", "/%s/%s/info/lfs/locks", user.Name, repo.Name)
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						req = NewRequestf(t, "POST", "/%s/%s/info/lfs/locks", user.Name, repo.Name)
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						req = NewRequestf(t, "GET", "/%s/%s/info/lfs/locks/verify", user.Name, repo.Name)
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
						req = NewRequestf(t, "GET", "/%s/%s/info/lfs/locks/10/unlock", user.Name, repo.Name)
 | 
				
			||||||
 | 
						MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPILFSLocksNotLogin(t *testing.T) {
 | 
				
			||||||
 | 
						prepareTestEnv(t)
 | 
				
			||||||
 | 
						setting.LFS.StartServer = true
 | 
				
			||||||
 | 
						user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
 | 
				
			||||||
 | 
						repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := NewRequestf(t, "GET", "/%s/%s/info/lfs/locks", user.Name, repo.Name)
 | 
				
			||||||
 | 
						req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
						req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
						resp := MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
						var lfsLockError api.LFSLockError
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &lfsLockError)
 | 
				
			||||||
 | 
						assert.Equal(t, "You must have pull access to list locks : User undefined doesn't have rigth to list for lfs lock [rid: 1]", lfsLockError.Message)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPILFSLocksLogged(t *testing.T) {
 | 
				
			||||||
 | 
						prepareTestEnv(t)
 | 
				
			||||||
 | 
						setting.LFS.StartServer = true
 | 
				
			||||||
 | 
						user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) //in org 3
 | 
				
			||||||
 | 
						user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) //in org 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
						repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // own by org 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							user       *models.User
 | 
				
			||||||
 | 
							repo       *models.Repository
 | 
				
			||||||
 | 
							path       string
 | 
				
			||||||
 | 
							httpResult int
 | 
				
			||||||
 | 
							addTime    []int
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "foo/bar.zip", httpResult: http.StatusCreated, addTime: []int{0}},
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusCreated, addTime: []int{0}},
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusConflict},
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "Foo/BaR.zip", httpResult: http.StatusConflict},
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "Foo/Test/../subFOlder/../Relative/../BaR.zip", httpResult: http.StatusConflict},
 | 
				
			||||||
 | 
							{user: user4, repo: repo1, path: "FoO/BaR.zip", httpResult: http.StatusForbidden},
 | 
				
			||||||
 | 
							{user: user4, repo: repo1, path: "path/test-user4", httpResult: http.StatusForbidden},
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "patH/Test-user4", httpResult: http.StatusCreated, addTime: []int{0}},
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, path: "some/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/path", httpResult: http.StatusCreated, addTime: []int{0}},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{user: user2, repo: repo3, path: "test/foo/bar.zip", httpResult: http.StatusCreated, addTime: []int{1, 2}},
 | 
				
			||||||
 | 
							{user: user4, repo: repo3, path: "test/foo/bar.zip", httpResult: http.StatusConflict},
 | 
				
			||||||
 | 
							{user: user4, repo: repo3, path: "test/foo/bar.bin", httpResult: http.StatusCreated, addTime: []int{1, 2}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resultsTests := []struct {
 | 
				
			||||||
 | 
							user        *models.User
 | 
				
			||||||
 | 
							repo        *models.Repository
 | 
				
			||||||
 | 
							totalCount  int
 | 
				
			||||||
 | 
							oursCount   int
 | 
				
			||||||
 | 
							theirsCount int
 | 
				
			||||||
 | 
							locksOwners []*models.User
 | 
				
			||||||
 | 
							locksTimes  []time.Time
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{user: user2, repo: repo1, totalCount: 4, oursCount: 4, theirsCount: 0, locksOwners: []*models.User{user2, user2, user2, user2}, locksTimes: []time.Time{}},
 | 
				
			||||||
 | 
							{user: user2, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
 | 
				
			||||||
 | 
							{user: user4, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deleteTests := []struct {
 | 
				
			||||||
 | 
							user   *models.User
 | 
				
			||||||
 | 
							repo   *models.Repository
 | 
				
			||||||
 | 
							lockID string
 | 
				
			||||||
 | 
						}{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//create locks
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							session := loginUser(t, test.user.Name)
 | 
				
			||||||
 | 
							req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/info/lfs/locks", test.repo.FullName()), map[string]string{"path": test.path})
 | 
				
			||||||
 | 
							req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							session.MakeRequest(t, req, test.httpResult)
 | 
				
			||||||
 | 
							if len(test.addTime) > 0 {
 | 
				
			||||||
 | 
								for _, id := range test.addTime {
 | 
				
			||||||
 | 
									resultsTests[id].locksTimes = append(resultsTests[id].locksTimes, time.Now())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//check creation
 | 
				
			||||||
 | 
						for _, test := range resultsTests {
 | 
				
			||||||
 | 
							session := loginUser(t, test.user.Name)
 | 
				
			||||||
 | 
							req := NewRequestf(t, "GET", "/%s/info/lfs/locks", test.repo.FullName())
 | 
				
			||||||
 | 
							req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							var lfsLocks api.LFSLockList
 | 
				
			||||||
 | 
							DecodeJSON(t, resp, &lfsLocks)
 | 
				
			||||||
 | 
							assert.Len(t, lfsLocks.Locks, test.totalCount)
 | 
				
			||||||
 | 
							for i, lock := range lfsLocks.Locks {
 | 
				
			||||||
 | 
								assert.EqualValues(t, test.locksOwners[i].DisplayName(), lock.Owner.Name)
 | 
				
			||||||
 | 
								assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 1*time.Second)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/info/lfs/locks/verify", test.repo.FullName()), map[string]string{})
 | 
				
			||||||
 | 
							req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							var lfsLocksVerify api.LFSLockListVerify
 | 
				
			||||||
 | 
							DecodeJSON(t, resp, &lfsLocksVerify)
 | 
				
			||||||
 | 
							assert.Len(t, lfsLocksVerify.Ours, test.oursCount)
 | 
				
			||||||
 | 
							assert.Len(t, lfsLocksVerify.Theirs, test.theirsCount)
 | 
				
			||||||
 | 
							for _, lock := range lfsLocksVerify.Ours {
 | 
				
			||||||
 | 
								assert.EqualValues(t, test.user.DisplayName(), lock.Owner.Name)
 | 
				
			||||||
 | 
								deleteTests = append(deleteTests, struct {
 | 
				
			||||||
 | 
									user   *models.User
 | 
				
			||||||
 | 
									repo   *models.Repository
 | 
				
			||||||
 | 
									lockID string
 | 
				
			||||||
 | 
								}{test.user, test.repo, lock.ID})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, lock := range lfsLocksVerify.Theirs {
 | 
				
			||||||
 | 
								assert.NotEqual(t, test.user.DisplayName(), lock.Owner.Name)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//remove all locks
 | 
				
			||||||
 | 
						for _, test := range deleteTests {
 | 
				
			||||||
 | 
							session := loginUser(t, test.user.Name)
 | 
				
			||||||
 | 
							req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/info/lfs/locks/%s/unlock", test.repo.FullName(), test.lockID), map[string]string{})
 | 
				
			||||||
 | 
							req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							var lfsLockRep api.LFSLockResponse
 | 
				
			||||||
 | 
							DecodeJSON(t, resp, &lfsLockRep)
 | 
				
			||||||
 | 
							assert.Equal(t, test.lockID, lfsLockRep.Lock.ID)
 | 
				
			||||||
 | 
							assert.Equal(t, test.user.DisplayName(), lfsLockRep.Lock.Owner.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check that we don't have any lock
 | 
				
			||||||
 | 
						for _, test := range resultsTests {
 | 
				
			||||||
 | 
							session := loginUser(t, test.user.Name)
 | 
				
			||||||
 | 
							req := NewRequestf(t, "GET", "/%s/info/lfs/locks", test.repo.FullName())
 | 
				
			||||||
 | 
							req.Header.Set("Accept", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
 | 
				
			||||||
 | 
							resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
							var lfsLocks api.LFSLockList
 | 
				
			||||||
 | 
							DecodeJSON(t, resp, &lfsLocks)
 | 
				
			||||||
 | 
							assert.Len(t, lfsLocks.Locks, 0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -27,7 +27,7 @@ HTTP_PORT        = 3001
 | 
				
			|||||||
ROOT_URL         = http://localhost:3001/
 | 
					ROOT_URL         = http://localhost:3001/
 | 
				
			||||||
DISABLE_SSH      = false
 | 
					DISABLE_SSH      = false
 | 
				
			||||||
SSH_PORT         = 22
 | 
					SSH_PORT         = 22
 | 
				
			||||||
LFS_START_SERVER = false
 | 
					LFS_START_SERVER = true
 | 
				
			||||||
OFFLINE_MODE     = false
 | 
					OFFLINE_MODE     = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[mailer]
 | 
					[mailer]
 | 
				
			||||||
@@ -65,4 +65,3 @@ LEVEL = Debug
 | 
				
			|||||||
INSTALL_LOCK   = true
 | 
					INSTALL_LOCK   = true
 | 
				
			||||||
SECRET_KEY     = 9pCviYTWSb
 | 
					SECRET_KEY     = 9pCviYTWSb
 | 
				
			||||||
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
 | 
					INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ HTTP_PORT        = 3002
 | 
				
			|||||||
ROOT_URL         = http://localhost:3002/
 | 
					ROOT_URL         = http://localhost:3002/
 | 
				
			||||||
DISABLE_SSH      = false
 | 
					DISABLE_SSH      = false
 | 
				
			||||||
SSH_PORT         = 22
 | 
					SSH_PORT         = 22
 | 
				
			||||||
LFS_START_SERVER = false
 | 
					LFS_START_SERVER = true
 | 
				
			||||||
OFFLINE_MODE     = false
 | 
					OFFLINE_MODE     = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[mailer]
 | 
					[mailer]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,13 +2,13 @@ APP_NAME = Gitea: Git with a cup of tea
 | 
				
			|||||||
RUN_MODE = prod
 | 
					RUN_MODE = prod
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[database]
 | 
					[database]
 | 
				
			||||||
DB_TYPE  = sqlite3
 | 
					DB_TYPE = sqlite3
 | 
				
			||||||
PATH     = :memory:
 | 
					PATH    = :memory:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[indexer]
 | 
					[indexer]
 | 
				
			||||||
ISSUE_INDEXER_PATH = integrations/indexers-sqlite/issues.bleve
 | 
					ISSUE_INDEXER_PATH   = integrations/indexers-sqlite/issues.bleve
 | 
				
			||||||
REPO_INDEXER_ENABLED = true
 | 
					REPO_INDEXER_ENABLED = true
 | 
				
			||||||
REPO_INDEXER_PATH = integrations/indexers-sqlite/repos.bleve
 | 
					REPO_INDEXER_PATH    = integrations/indexers-sqlite/repos.bleve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[repository]
 | 
					[repository]
 | 
				
			||||||
ROOT = integrations/gitea-integration-sqlite/gitea-repositories
 | 
					ROOT = integrations/gitea-integration-sqlite/gitea-repositories
 | 
				
			||||||
@@ -22,21 +22,22 @@ HTTP_PORT        = 3003
 | 
				
			|||||||
ROOT_URL         = http://localhost:3003/
 | 
					ROOT_URL         = http://localhost:3003/
 | 
				
			||||||
DISABLE_SSH      = false
 | 
					DISABLE_SSH      = false
 | 
				
			||||||
SSH_PORT         = 22
 | 
					SSH_PORT         = 22
 | 
				
			||||||
LFS_START_SERVER = false
 | 
					LFS_START_SERVER = true
 | 
				
			||||||
OFFLINE_MODE     = false
 | 
					OFFLINE_MODE     = false
 | 
				
			||||||
 | 
					LFS_JWT_SECRET   = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[mailer]
 | 
					[mailer]
 | 
				
			||||||
ENABLED = false
 | 
					ENABLED = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[service]
 | 
					[service]
 | 
				
			||||||
REGISTER_EMAIL_CONFIRM     = false
 | 
					REGISTER_EMAIL_CONFIRM            = false
 | 
				
			||||||
ENABLE_NOTIFY_MAIL         = false
 | 
					ENABLE_NOTIFY_MAIL                = false
 | 
				
			||||||
DISABLE_REGISTRATION       = false
 | 
					DISABLE_REGISTRATION              = false
 | 
				
			||||||
ENABLE_CAPTCHA             = false
 | 
					ENABLE_CAPTCHA                    = false
 | 
				
			||||||
REQUIRE_SIGNIN_VIEW        = false
 | 
					REQUIRE_SIGNIN_VIEW               = false
 | 
				
			||||||
DEFAULT_KEEP_EMAIL_PRIVATE = false
 | 
					DEFAULT_KEEP_EMAIL_PRIVATE        = false
 | 
				
			||||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
 | 
					DEFAULT_ALLOW_CREATE_ORGANIZATION = true
 | 
				
			||||||
NO_REPLY_ADDRESS           = noreply.example.org
 | 
					NO_REPLY_ADDRESS                  = noreply.example.org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[picture]
 | 
					[picture]
 | 
				
			||||||
DISABLE_GRAVATAR        = false
 | 
					DISABLE_GRAVATAR        = false
 | 
				
			||||||
@@ -46,7 +47,7 @@ ENABLE_FEDERATED_AVATAR = false
 | 
				
			|||||||
PROVIDER = file
 | 
					PROVIDER = file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[log]
 | 
					[log]
 | 
				
			||||||
MODE = console,file
 | 
					MODE      = console,file
 | 
				
			||||||
ROOT_PATH = sqlite-log
 | 
					ROOT_PATH = sqlite-log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[log.console]
 | 
					[log.console]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -506,6 +506,63 @@ func (err ErrLastOrgOwner) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
 | 
						return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//.____   ____________________
 | 
				
			||||||
 | 
					//|    |  \_   _____/   _____/
 | 
				
			||||||
 | 
					//|    |   |    __) \_____  \
 | 
				
			||||||
 | 
					//|    |___|     \  /        \
 | 
				
			||||||
 | 
					//|_______ \___  / /_______  /
 | 
				
			||||||
 | 
					//        \/   \/          \/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrLFSLockNotExist represents a "LFSLockNotExist" kind of error.
 | 
				
			||||||
 | 
					type ErrLFSLockNotExist struct {
 | 
				
			||||||
 | 
						ID     int64
 | 
				
			||||||
 | 
						RepoID int64
 | 
				
			||||||
 | 
						Path   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrLFSLockNotExist checks if an error is a ErrLFSLockNotExist.
 | 
				
			||||||
 | 
					func IsErrLFSLockNotExist(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrLFSLockNotExist)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrLFSLockNotExist) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("lfs lock does not exist [id: %d, rid: %d, path: %s]", err.ID, err.RepoID, err.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrLFSLockUnauthorizedAction represents a "LFSLockUnauthorizedAction" kind of error.
 | 
				
			||||||
 | 
					type ErrLFSLockUnauthorizedAction struct {
 | 
				
			||||||
 | 
						RepoID   int64
 | 
				
			||||||
 | 
						UserName string
 | 
				
			||||||
 | 
						Action   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrLFSLockUnauthorizedAction checks if an error is a ErrLFSLockUnauthorizedAction.
 | 
				
			||||||
 | 
					func IsErrLFSLockUnauthorizedAction(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrLFSLockUnauthorizedAction)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrLFSLockUnauthorizedAction) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("User %s doesn't have rigth to %s for lfs lock [rid: %d]", err.UserName, err.Action, err.RepoID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.
 | 
				
			||||||
 | 
					type ErrLFSLockAlreadyExist struct {
 | 
				
			||||||
 | 
						RepoID int64
 | 
				
			||||||
 | 
						Path   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrLFSLockAlreadyExist checks if an error is a ErrLFSLockAlreadyExist.
 | 
				
			||||||
 | 
					func IsErrLFSLockAlreadyExist(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrLFSLockAlreadyExist)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrLFSLockAlreadyExist) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("lfs lock already exists [rid: %d, path: %s]", err.RepoID, err.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// __________                           .__  __
 | 
					// __________                           .__  __
 | 
				
			||||||
// \______   \ ____ ______   ____  _____|__|/  |_  ___________ ___.__.
 | 
					// \______   \ ____ ______   ____  _____|__|/  |_  ___________ ___.__.
 | 
				
			||||||
//  |       _// __ \\____ \ /  _ \/  ___/  \   __\/  _ \_  __ <   |  |
 | 
					//  |       _// __ \\____ \ /  _ \/  ___/  \   __\/  _ \_  __ <   |  |
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										146
									
								
								models/lfs_lock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								models/lfs_lock.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						api "code.gitea.io/sdk/gitea"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LFSLock represents a git lfs lock of repository.
 | 
				
			||||||
 | 
					type LFSLock struct {
 | 
				
			||||||
 | 
						ID      int64     `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
						RepoID  int64     `xorm:"INDEX NOT NULL"`
 | 
				
			||||||
 | 
						Owner   *User     `xorm:"-"`
 | 
				
			||||||
 | 
						OwnerID int64     `xorm:"INDEX NOT NULL"`
 | 
				
			||||||
 | 
						Path    string    `xorm:"TEXT"`
 | 
				
			||||||
 | 
						Created time.Time `xorm:"created"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BeforeInsert is invoked from XORM before inserting an object of this type.
 | 
				
			||||||
 | 
					func (l *LFSLock) BeforeInsert() {
 | 
				
			||||||
 | 
						l.OwnerID = l.Owner.ID
 | 
				
			||||||
 | 
						l.Path = cleanPath(l.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AfterLoad is invoked from XORM after setting the values of all fields of this object.
 | 
				
			||||||
 | 
					func (l *LFSLock) AfterLoad() {
 | 
				
			||||||
 | 
						l.Owner, _ = GetUserByID(l.OwnerID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func cleanPath(p string) string {
 | 
				
			||||||
 | 
						return strings.ToLower(path.Clean(p))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// APIFormat convert a Release to lfs.LFSLock
 | 
				
			||||||
 | 
					func (l *LFSLock) APIFormat() *api.LFSLock {
 | 
				
			||||||
 | 
						return &api.LFSLock{
 | 
				
			||||||
 | 
							ID:       strconv.FormatInt(l.ID, 10),
 | 
				
			||||||
 | 
							Path:     l.Path,
 | 
				
			||||||
 | 
							LockedAt: l.Created,
 | 
				
			||||||
 | 
							Owner: &api.LFSLockOwner{
 | 
				
			||||||
 | 
								Name: l.Owner.DisplayName(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateLFSLock creates a new lock.
 | 
				
			||||||
 | 
					func CreateLFSLock(lock *LFSLock) (*LFSLock, error) {
 | 
				
			||||||
 | 
						err := CheckLFSAccessForRepo(lock.Owner, lock.RepoID, "create")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l, err := GetLFSLock(lock.RepoID, lock.Path)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !IsErrLFSLockNotExist(err) {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = x.InsertOne(lock)
 | 
				
			||||||
 | 
						return lock, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetLFSLock returns release by given path.
 | 
				
			||||||
 | 
					func GetLFSLock(repoID int64, path string) (*LFSLock, error) {
 | 
				
			||||||
 | 
						path = cleanPath(path)
 | 
				
			||||||
 | 
						rel := &LFSLock{RepoID: repoID, Path: path}
 | 
				
			||||||
 | 
						has, err := x.Get(rel)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !has {
 | 
				
			||||||
 | 
							return nil, ErrLFSLockNotExist{0, repoID, path}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return rel, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetLFSLockByID returns release by given id.
 | 
				
			||||||
 | 
					func GetLFSLockByID(id int64) (*LFSLock, error) {
 | 
				
			||||||
 | 
						lock := new(LFSLock)
 | 
				
			||||||
 | 
						has, err := x.ID(id).Get(lock)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						} else if !has {
 | 
				
			||||||
 | 
							return nil, ErrLFSLockNotExist{id, 0, ""}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return lock, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetLFSLockByRepoID returns a list of locks of repository.
 | 
				
			||||||
 | 
					func GetLFSLockByRepoID(repoID int64) (locks []*LFSLock, err error) {
 | 
				
			||||||
 | 
						err = x.Where("repo_id = ?", repoID).Find(&locks)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteLFSLockByID deletes a lock by given ID.
 | 
				
			||||||
 | 
					func DeleteLFSLockByID(id int64, u *User, force bool) (*LFSLock, error) {
 | 
				
			||||||
 | 
						lock, err := GetLFSLockByID(id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = CheckLFSAccessForRepo(u, lock.RepoID, "delete")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !force && u.ID != lock.OwnerID {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = x.ID(id).Delete(new(LFSLock))
 | 
				
			||||||
 | 
						return lock, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//CheckLFSAccessForRepo check needed access mode base on action
 | 
				
			||||||
 | 
					func CheckLFSAccessForRepo(u *User, repoID int64, action string) error {
 | 
				
			||||||
 | 
						if u == nil {
 | 
				
			||||||
 | 
							return ErrLFSLockUnauthorizedAction{repoID, "undefined", action}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mode := AccessModeRead
 | 
				
			||||||
 | 
						if action == "create" || action == "delete" || action == "verify" {
 | 
				
			||||||
 | 
							mode = AccessModeWrite
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo, err := GetRepositoryByID(repoID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						has, err := HasAccess(u.ID, repo, mode)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else if !has {
 | 
				
			||||||
 | 
							return ErrLFSLockUnauthorizedAction{repo.ID, u.DisplayName(), action}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -117,6 +117,7 @@ func init() {
 | 
				
			|||||||
		new(TrackedTime),
 | 
							new(TrackedTime),
 | 
				
			||||||
		new(DeletedBranch),
 | 
							new(DeletedBranch),
 | 
				
			||||||
		new(RepoIndexerStatus),
 | 
							new(RepoIndexerStatus),
 | 
				
			||||||
 | 
							new(LFSLock),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gonicNames := []string{"SSL", "UID"}
 | 
						gonicNames := []string{"SSL", "UID"}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										236
									
								
								modules/lfs/locks.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								modules/lfs/locks.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package lfs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						api "code.gitea.io/sdk/gitea"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/macaron.v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkRequest(req macaron.Request) int {
 | 
				
			||||||
 | 
						if !setting.LFS.StartServer {
 | 
				
			||||||
 | 
							return 404
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !MetaMatcher(req) || req.Header.Get("Content-Type") != metaMediaType {
 | 
				
			||||||
 | 
							return 400
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 200
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleLockListOut(ctx *context.Context, lock *models.LFSLock, err error) {
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrLFSLockNotExist(err) {
 | 
				
			||||||
 | 
								ctx.JSON(200, api.LFSLockList{
 | 
				
			||||||
 | 
									Locks: []*api.LFSLock{},
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "unable to list locks : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ctx.Repo.Repository.ID != lock.RepoID {
 | 
				
			||||||
 | 
							ctx.JSON(200, api.LFSLockList{
 | 
				
			||||||
 | 
								Locks: []*api.LFSLock{},
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(200, api.LFSLockList{
 | 
				
			||||||
 | 
							Locks: []*api.LFSLock{lock.APIFormat()},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetListLockHandler list locks
 | 
				
			||||||
 | 
					func GetListLockHandler(ctx *context.Context) {
 | 
				
			||||||
 | 
						status := checkRequest(ctx.Req)
 | 
				
			||||||
 | 
						if status != 200 {
 | 
				
			||||||
 | 
							writeStatus(ctx, status)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Resp.Header().Set("Content-Type", metaMediaType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository.ID, "list")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrLFSLockUnauthorizedAction(err) {
 | 
				
			||||||
 | 
								ctx.JSON(403, api.LFSLockError{
 | 
				
			||||||
 | 
									Message: "You must have pull access to list locks : " + err.Error(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "unable to list lock : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//TODO handle query cursor and limit
 | 
				
			||||||
 | 
						id := ctx.Query("id")
 | 
				
			||||||
 | 
						if id != "" { //Case where we request a specific id
 | 
				
			||||||
 | 
							v, err := strconv.ParseInt(id, 10, 64)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.JSON(400, api.LFSLockError{
 | 
				
			||||||
 | 
									Message: "bad request : " + err.Error(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							lock, err := models.GetLFSLockByID(int64(v))
 | 
				
			||||||
 | 
							handleLockListOut(ctx, lock, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path := ctx.Query("path")
 | 
				
			||||||
 | 
						if path != "" { //Case where we request a specific id
 | 
				
			||||||
 | 
							lock, err := models.GetLFSLock(ctx.Repo.Repository.ID, path)
 | 
				
			||||||
 | 
							handleLockListOut(ctx, lock, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//If no query params path or id
 | 
				
			||||||
 | 
						lockList, err := models.GetLFSLockByRepoID(ctx.Repo.Repository.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "unable to list locks : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						lockListAPI := make([]*api.LFSLock, len(lockList))
 | 
				
			||||||
 | 
						for i, l := range lockList {
 | 
				
			||||||
 | 
							lockListAPI[i] = l.APIFormat()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(200, api.LFSLockList{
 | 
				
			||||||
 | 
							Locks: lockListAPI,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PostLockHandler create lock
 | 
				
			||||||
 | 
					func PostLockHandler(ctx *context.Context) {
 | 
				
			||||||
 | 
						status := checkRequest(ctx.Req)
 | 
				
			||||||
 | 
						if status != 200 {
 | 
				
			||||||
 | 
							writeStatus(ctx, status)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Resp.Header().Set("Content-Type", metaMediaType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req api.LFSLockRequest
 | 
				
			||||||
 | 
						dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
 | 
				
			||||||
 | 
						err := dec.Decode(&req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							writeStatus(ctx, 400)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lock, err := models.CreateLFSLock(&models.LFSLock{
 | 
				
			||||||
 | 
							RepoID: ctx.Repo.Repository.ID,
 | 
				
			||||||
 | 
							Path:   req.Path,
 | 
				
			||||||
 | 
							Owner:  ctx.User,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrLFSLockAlreadyExist(err) {
 | 
				
			||||||
 | 
								ctx.JSON(409, api.LFSLockError{
 | 
				
			||||||
 | 
									Lock:    lock.APIFormat(),
 | 
				
			||||||
 | 
									Message: "already created lock",
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if models.IsErrLFSLockUnauthorizedAction(err) {
 | 
				
			||||||
 | 
								ctx.JSON(403, api.LFSLockError{
 | 
				
			||||||
 | 
									Message: "You must have push access to create locks : " + err.Error(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "internal server error : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(201, api.LFSLockResponse{Lock: lock.APIFormat()})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VerifyLockHandler list locks for verification
 | 
				
			||||||
 | 
					func VerifyLockHandler(ctx *context.Context) {
 | 
				
			||||||
 | 
						status := checkRequest(ctx.Req)
 | 
				
			||||||
 | 
						if status != 200 {
 | 
				
			||||||
 | 
							writeStatus(ctx, status)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Resp.Header().Set("Content-Type", metaMediaType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := models.CheckLFSAccessForRepo(ctx.User, ctx.Repo.Repository.ID, "verify")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrLFSLockUnauthorizedAction(err) {
 | 
				
			||||||
 | 
								ctx.JSON(403, api.LFSLockError{
 | 
				
			||||||
 | 
									Message: "You must have push access to verify locks : " + err.Error(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "unable to verify lock : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//TODO handle body json cursor and limit
 | 
				
			||||||
 | 
						lockList, err := models.GetLFSLockByRepoID(ctx.Repo.Repository.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "unable to list locks : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						lockOursListAPI := make([]*api.LFSLock, 0, len(lockList))
 | 
				
			||||||
 | 
						lockTheirsListAPI := make([]*api.LFSLock, 0, len(lockList))
 | 
				
			||||||
 | 
						for _, l := range lockList {
 | 
				
			||||||
 | 
							if l.Owner.ID == ctx.User.ID {
 | 
				
			||||||
 | 
								lockOursListAPI = append(lockOursListAPI, l.APIFormat())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								lockTheirsListAPI = append(lockTheirsListAPI, l.APIFormat())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(200, api.LFSLockListVerify{
 | 
				
			||||||
 | 
							Ours:   lockOursListAPI,
 | 
				
			||||||
 | 
							Theirs: lockTheirsListAPI,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnLockHandler delete locks
 | 
				
			||||||
 | 
					func UnLockHandler(ctx *context.Context) {
 | 
				
			||||||
 | 
						status := checkRequest(ctx.Req)
 | 
				
			||||||
 | 
						if status != 200 {
 | 
				
			||||||
 | 
							writeStatus(ctx, status)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Resp.Header().Set("Content-Type", metaMediaType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req api.LFSLockDeleteRequest
 | 
				
			||||||
 | 
						dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
 | 
				
			||||||
 | 
						err := dec.Decode(&req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							writeStatus(ctx, 400)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lock, err := models.DeleteLFSLockByID(ctx.ParamsInt64("lid"), ctx.User, req.Force)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrLFSLockUnauthorizedAction(err) {
 | 
				
			||||||
 | 
								ctx.JSON(403, api.LFSLockError{
 | 
				
			||||||
 | 
									Message: "You must have push access to delete locks : " + err.Error(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.JSON(500, api.LFSLockError{
 | 
				
			||||||
 | 
								Message: "unable to delete lock : " + err.Error(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSON(200, api.LFSLockResponse{Lock: lock.APIFormat()})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -685,6 +685,12 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
				m.Any("/objects/:oid", lfs.ObjectOidHandler)
 | 
									m.Any("/objects/:oid", lfs.ObjectOidHandler)
 | 
				
			||||||
				m.Post("/objects", lfs.PostHandler)
 | 
									m.Post("/objects", lfs.PostHandler)
 | 
				
			||||||
				m.Post("/verify", lfs.VerifyHandler)
 | 
									m.Post("/verify", lfs.VerifyHandler)
 | 
				
			||||||
 | 
									m.Group("/locks", func() {
 | 
				
			||||||
 | 
										m.Get("/", lfs.GetListLockHandler)
 | 
				
			||||||
 | 
										m.Post("/", lfs.PostLockHandler)
 | 
				
			||||||
 | 
										m.Post("/verify", lfs.VerifyLockHandler)
 | 
				
			||||||
 | 
										m.Post("/:lid/unlock", lfs.UnLockHandler)
 | 
				
			||||||
 | 
									}, context.RepoAssignment())
 | 
				
			||||||
				m.Any("/*", func(ctx *context.Context) {
 | 
									m.Any("/*", func(ctx *context.Context) {
 | 
				
			||||||
					ctx.Handle(404, "", nil)
 | 
										ctx.Handle(404, "", nil)
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user