Add pure SSH LFS support (#31516)

Fixes #17554
/claim #17554

Docs PR https://gitea.com/gitea/docs/pulls/49

To test, run pushes like: `GIT_TRACE=1` git push. The trace output
should mention "pure SSH connection".
This commit is contained in:
ConcurrentCrab
2024-09-27 19:57:37 +05:30
committed by GitHub
parent fdb1df9eca
commit 8a9fd7f771
13 changed files with 945 additions and 53 deletions

View File

@ -12,7 +12,9 @@ import (
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/lfs"
"gitea.com/go-chi/binding"
chi_middleware "github.com/go-chi/chi/v5/middleware"
@ -46,6 +48,14 @@ func bind[T any](_ T) any {
}
}
// SwapAuthToken swaps Authorization header with X-Auth header
func swapAuthToken(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
req.Header.Set("Authorization", req.Header.Get("X-Auth"))
next.ServeHTTP(w, req)
})
}
// Routes registers all internal APIs routes to web application.
// These APIs will be invoked by internal commands for example `gitea serv` and etc.
func Routes() *web.Router {
@ -80,5 +90,25 @@ func Routes() *web.Router {
r.Post("/restore_repo", RestoreRepo)
r.Post("/actions/generate_actions_runner_token", GenerateActionsRunnerToken)
r.Group("/repo/{username}/{reponame}", func() {
r.Group("/info/lfs", func() {
r.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
r.Put("/objects/{oid}/{size}", lfs.UploadHandler)
r.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
r.Get("/objects/{oid}", lfs.DownloadHandler)
r.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
r.Group("/locks", func() {
r.Get("/", lfs.GetListLockHandler)
r.Post("/", lfs.PostLockHandler)
r.Post("/verify", lfs.VerifyLockHandler)
r.Post("/{lid}/unlock", lfs.UnLockHandler)
}, lfs.CheckAcceptMediaType)
r.Any("/*", func(ctx *context.Context) {
ctx.NotFound("", nil)
})
}, swapAuthToken)
}, common.Sessioner(), context.Contexter())
// end "/repo/{username}/{reponame}": git (LFS) API mirror
return r
}

View File

@ -136,16 +136,15 @@ func ServCommand(ctx *context.PrivateContext) {
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
repoExist = false
for _, verb := range ctx.FormStrings("verb") {
if verb == "git-upload-pack" {
// User is fetching/cloning a non-existent repository
log.Warn("Failed authentication attempt (cannot find repository: %s/%s) from %s", results.OwnerName, results.RepoName, ctx.RemoteAddr())
ctx.JSON(http.StatusNotFound, private.Response{
UserMsg: fmt.Sprintf("Cannot find repository: %s/%s", results.OwnerName, results.RepoName),
})
return
}
if mode == perm.AccessModeRead {
// User is fetching/cloning a non-existent repository
log.Warn("Failed authentication attempt (cannot find repository: %s/%s) from %s", results.OwnerName, results.RepoName, ctx.RemoteAddr())
ctx.JSON(http.StatusNotFound, private.Response{
UserMsg: fmt.Sprintf("Cannot find repository: %s/%s", results.OwnerName, results.RepoName),
})
return
}
// else fallthrough (push-to-create may kick in below)
} else {
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
ctx.JSON(http.StatusInternalServerError, private.Response{