Add LFS object verification step after upload (#2868)

* 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
This commit is contained in:
Lauris BH 2017-11-08 15:04:19 +02:00 committed by GitHub
parent 61f5c22503
commit ba2e0240c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 2 deletions

View file

@ -1,13 +1,14 @@
package lfs package lfs
import ( import (
"code.gitea.io/gitea/models"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"errors" "errors"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"code.gitea.io/gitea/models"
) )
var ( var (
@ -82,6 +83,20 @@ func (s *ContentStore) Exists(meta *models.LFSMetaObject) bool {
return true 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 { func transformKey(key string) string {
if len(key) < 5 { if len(key) < 5 {
return key return key

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"path"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -15,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )
@ -66,7 +68,12 @@ type ObjectError struct {
// ObjectLink builds a URL linking to the object. // ObjectLink builds a URL linking to the object.
func (v *RequestVars) ObjectLink() string { 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. // 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) 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 // Represent takes a RequestVars and Meta and turns it into a Representation suitable
// for json encoding // for json encoding
func Represent(rv *RequestVars, meta *models.LFSMetaObject, download, upload bool) *Representation { 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} 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 return rep
} }

View file

@ -681,6 +681,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler) m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler)
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.Any("/*", func(ctx *context.Context) { m.Any("/*", func(ctx *context.Context) {
ctx.Handle(404, "", nil) ctx.Handle(404, "", nil)
}) })