]> source.dussan.org Git - gitea.git/commitdiff
Add LFS object verification step after upload (#2868)
authorLauris BH <lauris@nix.lv>
Wed, 8 Nov 2017 13:04:19 +0000 (15:04 +0200)
committerGitHub <noreply@github.com>
Wed, 8 Nov 2017 13:04:19 +0000 (15:04 +0200)
* Add LFS object verification step after upload

* Fix file verification condition and small refactor

* Fix URLs

* Remove newline and return status 422 on failed verification

* Better error hadling

modules/lfs/content_store.go
modules/lfs/server.go
routers/routes/routes.go

index 3e1b2f345b8e2fab04a92f391490daa11d223a09..895b51b8ba98d5347d6c72205bbe6d0350117004 100644 (file)
@@ -1,13 +1,14 @@
 package lfs
 
 import (
-       "code.gitea.io/gitea/models"
        "crypto/sha256"
        "encoding/hex"
        "errors"
        "io"
        "os"
        "path/filepath"
+
+       "code.gitea.io/gitea/models"
 )
 
 var (
@@ -82,6 +83,20 @@ func (s *ContentStore) Exists(meta *models.LFSMetaObject) bool {
        return true
 }
 
+// Verify returns true if the object exists in the content store and size is correct.
+func (s *ContentStore) Verify(meta *models.LFSMetaObject) (bool, error) {
+       path := filepath.Join(s.BasePath, transformKey(meta.Oid))
+
+       fi, err := os.Stat(path)
+       if os.IsNotExist(err) || err == nil && fi.Size() != meta.Size {
+               return false, nil
+       } else if err != nil {
+               return false, err
+       }
+
+       return true, nil
+}
+
 func transformKey(key string) string {
        if len(key) < 5 {
                return key
index d618d6185318546c6f59cfbbbdfac3e789acf398..68f2af15192317a9014ce8d12d7e3ace7466492c 100644 (file)
@@ -6,6 +6,7 @@ import (
        "fmt"
        "io"
        "net/http"
+       "path"
        "regexp"
        "strconv"
        "strings"
@@ -15,6 +16,7 @@ import (
        "code.gitea.io/gitea/modules/context"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
+
        "github.com/dgrijalva/jwt-go"
        "gopkg.in/macaron.v1"
 )
@@ -66,7 +68,12 @@ type ObjectError struct {
 
 // ObjectLink builds a URL linking to the object.
 func (v *RequestVars) ObjectLink() string {
-       return fmt.Sprintf("%s%s/%s/info/lfs/objects/%s", setting.AppURL, v.User, v.Repo, v.Oid)
+       return setting.AppURL + path.Join(v.User, v.Repo, "info/lfs/objects", v.Oid)
+}
+
+// VerifyLink builds a URL for verifying the object.
+func (v *RequestVars) VerifyLink() string {
+       return setting.AppURL + path.Join(v.User, v.Repo, "info/lfs/verify")
 }
 
 // link provides a structure used to build a hypermedia representation of an HTTP link.
@@ -320,6 +327,40 @@ func PutHandler(ctx *context.Context) {
        logRequest(ctx.Req, 200)
 }
 
+// VerifyHandler verify oid and its size from the content store
+func VerifyHandler(ctx *context.Context) {
+       if !setting.LFS.StartServer {
+               writeStatus(ctx, 404)
+               return
+       }
+
+       if !ContentMatcher(ctx.Req) {
+               writeStatus(ctx, 400)
+               return
+       }
+
+       rv := unpack(ctx)
+
+       meta, _ := getAuthenticatedRepoAndMeta(ctx, rv, true)
+       if meta == nil {
+               return
+       }
+
+       contentStore := &ContentStore{BasePath: setting.LFS.ContentPath}
+       ok, err := contentStore.Verify(meta)
+       if err != nil {
+               ctx.Resp.WriteHeader(500)
+               fmt.Fprintf(ctx.Resp, `{"message":"%s"}`, err)
+               return
+       }
+       if !ok {
+               writeStatus(ctx, 422)
+               return
+       }
+
+       logRequest(ctx.Req, 200)
+}
+
 // Represent takes a RequestVars and Meta and turns it into a Representation suitable
 // for json encoding
 func Represent(rv *RequestVars, meta *models.LFSMetaObject, download, upload bool) *Representation {
@@ -347,6 +388,11 @@ func Represent(rv *RequestVars, meta *models.LFSMetaObject, download, upload boo
                rep.Actions["upload"] = &link{Href: rv.ObjectLink(), Header: header}
        }
 
+       if upload && !download {
+               // Force client side verify action while gitea lacks proper server side verification
+               rep.Actions["verify"] = &link{Href: rv.VerifyLink(), Header: header}
+       }
+
        return rep
 }
 
index f1c9f184899a0245701f1218aefebfdb8070329b..5a76dddb66590cb4545421aed91d3759e3117d53 100644 (file)
@@ -681,6 +681,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                                m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler)
                                m.Any("/objects/:oid", lfs.ObjectOidHandler)
                                m.Post("/objects", lfs.PostHandler)
+                               m.Post("/verify", lfs.VerifyHandler)
                                m.Any("/*", func(ctx *context.Context) {
                                        ctx.Handle(404, "", nil)
                                })