2017-11-28 23:58:37 +03:00
// 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 (
2021-04-05 18:30:52 +03:00
"net/http"
2017-11-28 23:58:37 +03:00
"strconv"
2017-11-29 02:35:23 +03:00
"strings"
2017-11-28 23:58:37 +03:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
2020-12-03 17:05:48 +03:00
"code.gitea.io/gitea/modules/convert"
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2021-04-09 01:25:57 +03:00
lfs_module "code.gitea.io/gitea/modules/lfs"
2019-05-28 13:32:41 +03:00
"code.gitea.io/gitea/modules/log"
2017-11-28 23:58:37 +03:00
"code.gitea.io/gitea/modules/setting"
2019-05-11 13:21:34 +03:00
api "code.gitea.io/gitea/modules/structs"
2017-11-28 23:58:37 +03:00
)
2019-05-28 13:32:41 +03:00
func handleLockListOut ( ctx * context . Context , repo * models . Repository , lock * models . LFSLock , err error ) {
2017-11-28 23:58:37 +03:00
if err != nil {
if models . IsErrLFSLockNotExist ( err ) {
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , api . LFSLockList {
2017-11-28 23:58:37 +03:00
Locks : [ ] * api . LFSLock { } ,
} )
return
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusInternalServerError , api . LFSLockError {
2020-03-09 22:56:18 +03:00
Message : "unable to list locks : Internal Server Error" ,
2017-11-28 23:58:37 +03:00
} )
return
}
2019-05-28 13:32:41 +03:00
if repo . ID != lock . RepoID {
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , api . LFSLockList {
2017-11-28 23:58:37 +03:00
Locks : [ ] * api . LFSLock { } ,
} )
return
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , api . LFSLockList {
2020-12-03 17:05:48 +03:00
Locks : [ ] * api . LFSLock { convert . ToLFSLock ( lock ) } ,
2017-11-28 23:58:37 +03:00
} )
}
// GetListLockHandler list locks
func GetListLockHandler ( ctx * context . Context ) {
2021-06-06 02:59:27 +03:00
rv := getRequestContext ( ctx )
2019-05-28 13:32:41 +03:00
repository , err := models . GetRepositoryByOwnerAndName ( rv . User , rv . Repo )
2017-11-28 23:58:37 +03:00
if err != nil {
2019-05-28 13:32:41 +03:00
log . Debug ( "Could not find repository: %s/%s - %s" , rv . User , rv . Repo , err )
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
ctx . JSON ( 401 , api . LFSLockError {
Message : "You must have pull access to list locks" ,
} )
2019-05-28 13:32:41 +03:00
return
}
repository . MustOwner ( )
2021-05-15 18:32:09 +03:00
authenticated := authenticate ( ctx , repository , rv . Authorization , true , false )
2019-05-28 13:32:41 +03:00
if ! authenticated {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusUnauthorized , api . LFSLockError {
2019-05-28 13:32:41 +03:00
Message : "You must have pull access to list locks" ,
2017-11-28 23:58:37 +03:00
} )
return
}
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , lfs_module . MediaType )
2020-03-09 22:56:18 +03:00
2021-07-29 04:42:15 +03:00
cursor := ctx . FormInt ( "cursor" )
2020-03-09 22:56:18 +03:00
if cursor < 0 {
cursor = 0
}
2021-07-29 04:42:15 +03:00
limit := ctx . FormInt ( "limit" )
2020-03-09 22:56:18 +03:00
if limit > setting . LFS . LocksPagingNum && setting . LFS . LocksPagingNum > 0 {
limit = setting . LFS . LocksPagingNum
} else if limit < 0 {
limit = 0
}
2021-08-11 03:31:13 +03:00
id := ctx . FormString ( "id" )
2017-11-28 23:58:37 +03:00
if id != "" { //Case where we request a specific id
v , err := strconv . ParseInt ( id , 10 , 64 )
if err != nil {
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusBadRequest , api . LFSLockError {
2017-11-28 23:58:37 +03:00
Message : "bad request : " + err . Error ( ) ,
} )
return
}
2019-07-23 21:50:39 +03:00
lock , err := models . GetLFSLockByID ( v )
2020-03-09 22:56:18 +03:00
if err != nil && ! models . IsErrLFSLockNotExist ( err ) {
log . Error ( "Unable to get lock with ID[%s]: Error: %v" , v , err )
}
2019-05-28 13:32:41 +03:00
handleLockListOut ( ctx , repository , lock , err )
2017-11-28 23:58:37 +03:00
return
}
2021-08-11 03:31:13 +03:00
path := ctx . FormString ( "path" )
2017-11-28 23:58:37 +03:00
if path != "" { //Case where we request a specific id
2019-05-28 13:32:41 +03:00
lock , err := models . GetLFSLock ( repository , path )
2020-03-09 22:56:18 +03:00
if err != nil && ! models . IsErrLFSLockNotExist ( err ) {
log . Error ( "Unable to get lock for repository %-v with path %s: Error: %v" , repository , path , err )
}
2019-05-28 13:32:41 +03:00
handleLockListOut ( ctx , repository , lock , err )
2017-11-28 23:58:37 +03:00
return
}
//If no query params path or id
2020-03-09 22:56:18 +03:00
lockList , err := models . GetLFSLockByRepoID ( repository . ID , cursor , limit )
2017-11-28 23:58:37 +03:00
if err != nil {
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to list locks for repository ID[%d]: Error: %v" , repository . ID , err )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusInternalServerError , api . LFSLockError {
2020-03-09 22:56:18 +03:00
Message : "unable to list locks : Internal Server Error" ,
2017-11-28 23:58:37 +03:00
} )
return
}
lockListAPI := make ( [ ] * api . LFSLock , len ( lockList ) )
2020-03-09 22:56:18 +03:00
next := ""
2017-11-28 23:58:37 +03:00
for i , l := range lockList {
2020-12-03 17:05:48 +03:00
lockListAPI [ i ] = convert . ToLFSLock ( l )
2017-11-28 23:58:37 +03:00
}
2020-03-09 22:56:18 +03:00
if limit > 0 && len ( lockList ) == limit {
next = strconv . Itoa ( cursor + 1 )
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , api . LFSLockList {
2017-11-28 23:58:37 +03:00
Locks : lockListAPI ,
2020-03-09 22:56:18 +03:00
Next : next ,
2017-11-28 23:58:37 +03:00
} )
}
// PostLockHandler create lock
func PostLockHandler ( ctx * context . Context ) {
2019-05-28 13:32:41 +03:00
userName := ctx . Params ( "username" )
repoName := strings . TrimSuffix ( ctx . Params ( "reponame" ) , ".git" )
authorization := ctx . Req . Header . Get ( "Authorization" )
repository , err := models . GetRepositoryByOwnerAndName ( userName , repoName )
if err != nil {
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to get repository: %s/%s Error: %v" , userName , repoName , err )
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
ctx . JSON ( 401 , api . LFSLockError {
Message : "You must have push access to create locks" ,
} )
2019-05-28 13:32:41 +03:00
return
}
repository . MustOwner ( )
2021-05-15 18:32:09 +03:00
authenticated := authenticate ( ctx , repository , authorization , true , true )
2019-05-28 13:32:41 +03:00
if ! authenticated {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusUnauthorized , api . LFSLockError {
2019-05-28 13:32:41 +03:00
Message : "You must have push access to create locks" ,
} )
return
}
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , lfs_module . MediaType )
2017-11-28 23:58:37 +03:00
var req api . LFSLockRequest
2021-01-26 18:36:53 +03:00
bodyReader := ctx . Req . Body
2019-10-10 20:42:28 +03:00
defer bodyReader . Close ( )
2021-07-24 19:03:58 +03:00
2019-10-10 20:42:28 +03:00
dec := json . NewDecoder ( bodyReader )
2019-05-28 13:32:41 +03:00
if err := dec . Decode ( & req ) ; err != nil {
2020-03-09 22:56:18 +03:00
log . Warn ( "Failed to decode lock request as json. Error: %v" , err )
2017-11-28 23:58:37 +03:00
writeStatus ( ctx , 400 )
return
}
lock , err := models . CreateLFSLock ( & models . LFSLock {
2019-05-28 13:32:41 +03:00
Repo : repository ,
2018-01-27 19:48:15 +03:00
Path : req . Path ,
Owner : ctx . User ,
2017-11-28 23:58:37 +03:00
} )
if err != nil {
if models . IsErrLFSLockAlreadyExist ( err ) {
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusConflict , api . LFSLockError {
2020-12-03 17:05:48 +03:00
Lock : convert . ToLFSLock ( lock ) ,
2017-11-28 23:58:37 +03:00
Message : "already created lock" ,
} )
return
}
2018-01-27 19:48:15 +03:00
if models . IsErrLFSUnauthorizedAction ( err ) {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusUnauthorized , api . LFSLockError {
2017-11-28 23:58:37 +03:00
Message : "You must have push access to create locks : " + err . Error ( ) ,
} )
return
}
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to CreateLFSLock in repository %-v at %s for user %-v: Error: %v" , repository , req . Path , ctx . User , err )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusInternalServerError , api . LFSLockError {
2020-03-09 22:56:18 +03:00
Message : "internal server error : Internal Server Error" ,
2017-11-28 23:58:37 +03:00
} )
return
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusCreated , api . LFSLockResponse { Lock : convert . ToLFSLock ( lock ) } )
2017-11-28 23:58:37 +03:00
}
// VerifyLockHandler list locks for verification
func VerifyLockHandler ( ctx * context . Context ) {
2019-05-28 13:32:41 +03:00
userName := ctx . Params ( "username" )
repoName := strings . TrimSuffix ( ctx . Params ( "reponame" ) , ".git" )
authorization := ctx . Req . Header . Get ( "Authorization" )
repository , err := models . GetRepositoryByOwnerAndName ( userName , repoName )
2017-11-28 23:58:37 +03:00
if err != nil {
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to get repository: %s/%s Error: %v" , userName , repoName , err )
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
ctx . JSON ( 401 , api . LFSLockError {
Message : "You must have push access to verify locks" ,
} )
2019-05-28 13:32:41 +03:00
return
}
repository . MustOwner ( )
2021-05-15 18:32:09 +03:00
authenticated := authenticate ( ctx , repository , authorization , true , true )
2019-05-28 13:32:41 +03:00
if ! authenticated {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusUnauthorized , api . LFSLockError {
2019-05-28 13:32:41 +03:00
Message : "You must have push access to verify locks" ,
2017-11-28 23:58:37 +03:00
} )
return
}
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , lfs_module . MediaType )
2021-07-29 04:42:15 +03:00
cursor := ctx . FormInt ( "cursor" )
2020-03-09 22:56:18 +03:00
if cursor < 0 {
cursor = 0
}
2021-07-29 04:42:15 +03:00
limit := ctx . FormInt ( "limit" )
2020-03-09 22:56:18 +03:00
if limit > setting . LFS . LocksPagingNum && setting . LFS . LocksPagingNum > 0 {
limit = setting . LFS . LocksPagingNum
} else if limit < 0 {
limit = 0
}
lockList , err := models . GetLFSLockByRepoID ( repository . ID , cursor , limit )
2017-11-28 23:58:37 +03:00
if err != nil {
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to list locks for repository ID[%d]: Error: %v" , repository . ID , err )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusInternalServerError , api . LFSLockError {
2020-03-09 22:56:18 +03:00
Message : "unable to list locks : Internal Server Error" ,
2017-11-28 23:58:37 +03:00
} )
return
}
2020-03-09 22:56:18 +03:00
next := ""
if limit > 0 && len ( lockList ) == limit {
next = strconv . Itoa ( cursor + 1 )
}
2017-11-28 23:58:37 +03:00
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 {
2020-12-03 17:05:48 +03:00
lockOursListAPI = append ( lockOursListAPI , convert . ToLFSLock ( l ) )
2017-11-28 23:58:37 +03:00
} else {
2020-12-03 17:05:48 +03:00
lockTheirsListAPI = append ( lockTheirsListAPI , convert . ToLFSLock ( l ) )
2017-11-28 23:58:37 +03:00
}
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , api . LFSLockListVerify {
2017-11-28 23:58:37 +03:00
Ours : lockOursListAPI ,
Theirs : lockTheirsListAPI ,
2020-03-09 22:56:18 +03:00
Next : next ,
2017-11-28 23:58:37 +03:00
} )
}
// UnLockHandler delete locks
func UnLockHandler ( ctx * context . Context ) {
2019-05-28 13:32:41 +03:00
userName := ctx . Params ( "username" )
repoName := strings . TrimSuffix ( ctx . Params ( "reponame" ) , ".git" )
authorization := ctx . Req . Header . Get ( "Authorization" )
repository , err := models . GetRepositoryByOwnerAndName ( userName , repoName )
if err != nil {
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to get repository: %s/%s Error: %v" , userName , repoName , err )
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
ctx . JSON ( 401 , api . LFSLockError {
Message : "You must have push access to delete locks" ,
} )
2019-05-28 13:32:41 +03:00
return
}
repository . MustOwner ( )
2021-05-15 18:32:09 +03:00
authenticated := authenticate ( ctx , repository , authorization , true , true )
2019-05-28 13:32:41 +03:00
if ! authenticated {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusUnauthorized , api . LFSLockError {
2019-05-28 13:32:41 +03:00
Message : "You must have push access to delete locks" ,
} )
return
}
2021-05-15 18:32:09 +03:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , lfs_module . MediaType )
2017-11-28 23:58:37 +03:00
var req api . LFSLockDeleteRequest
2021-01-26 18:36:53 +03:00
bodyReader := ctx . Req . Body
2019-10-10 20:42:28 +03:00
defer bodyReader . Close ( )
2021-07-24 19:03:58 +03:00
2019-10-10 20:42:28 +03:00
dec := json . NewDecoder ( bodyReader )
2019-05-28 13:32:41 +03:00
if err := dec . Decode ( & req ) ; err != nil {
2020-03-09 22:56:18 +03:00
log . Warn ( "Failed to decode lock request as json. Error: %v" , err )
2017-11-28 23:58:37 +03:00
writeStatus ( ctx , 400 )
return
}
lock , err := models . DeleteLFSLockByID ( ctx . ParamsInt64 ( "lid" ) , ctx . User , req . Force )
if err != nil {
2018-01-27 19:48:15 +03:00
if models . IsErrLFSUnauthorizedAction ( err ) {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=gitea-lfs" )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusUnauthorized , api . LFSLockError {
2017-11-28 23:58:37 +03:00
Message : "You must have push access to delete locks : " + err . Error ( ) ,
} )
return
}
2020-03-09 22:56:18 +03:00
log . Error ( "Unable to DeleteLFSLockByID[%d] by user %-v with force %t: Error: %v" , ctx . ParamsInt64 ( "lid" ) , ctx . User , req . Force , err )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusInternalServerError , api . LFSLockError {
2020-03-09 22:56:18 +03:00
Message : "unable to delete lock : Internal Server Error" ,
2017-11-28 23:58:37 +03:00
} )
return
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , api . LFSLockResponse { Lock : convert . ToLFSLock ( lock ) } )
2017-11-28 23:58:37 +03:00
}