]> source.dussan.org Git - gitea.git/commitdiff
Test if LFS object is accessible (#16865) (#16904)
author6543 <6543@obermui.de>
Tue, 31 Aug 2021 15:58:39 +0000 (17:58 +0200)
committerGitHub <noreply@github.com>
Tue, 31 Aug 2021 15:58:39 +0000 (17:58 +0200)
* Test if object is accessible.

* Added more logging.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
integrations/api_repo_lfs_test.go
services/lfs/server.go

index 9e1e2b04189441224daa7e39c75d88cc660ebf7c..49350c428aa47afa93f37ff9e0bcea596f363835 100644 (file)
@@ -254,6 +254,10 @@ func TestAPILFSBatch(t *testing.T) {
                        assert.NoError(t, err)
                        assert.True(t, exist)
 
+                       repo2 := createLFSTestRepository(t, "batch2")
+                       content := []byte("dummy0")
+                       storeObjectInRepo(t, repo2.ID, &content)
+
                        meta, err := repo.GetLFSMetaObjectByOid(p.Oid)
                        assert.Nil(t, meta)
                        assert.Equal(t, models.ErrLFSObjectNotExist, err)
@@ -359,13 +363,19 @@ func TestAPILFSUpload(t *testing.T) {
                assert.Nil(t, meta)
                assert.Equal(t, models.ErrLFSObjectNotExist, err)
 
-               req := newRequest(t, p, "")
+               t.Run("InvalidAccess", func(t *testing.T) {
+                       req := newRequest(t, p, "invalid")
+                       session.MakeRequest(t, req, http.StatusUnprocessableEntity)
+               })
 
-               session.MakeRequest(t, req, http.StatusOK)
+               t.Run("ValidAccess", func(t *testing.T) {
+                       req := newRequest(t, p, "dummy5")
 
-               meta, err = repo.GetLFSMetaObjectByOid(p.Oid)
-               assert.NoError(t, err)
-               assert.NotNil(t, meta)
+                       session.MakeRequest(t, req, http.StatusOK)
+                       meta, err = repo.GetLFSMetaObjectByOid(p.Oid)
+                       assert.NoError(t, err)
+                       assert.NotNil(t, meta)
+               })
        })
 
        t.Run("MetaAlreadyExists", func(t *testing.T) {
index 6b79a3a36402452c0e5a988c473de7c3fd567ede..424dc91c8c75ca6955da3f54d4ba8f1f1f3eba72 100644 (file)
@@ -5,7 +5,9 @@
 package lfs
 
 import (
+       "crypto/sha256"
        "encoding/base64"
+       "encoding/hex"
        "errors"
        "fmt"
        "io"
@@ -213,14 +215,22 @@ func BatchHandler(ctx *context.Context) {
                                }
                        }
 
-                       if exists {
-                               if meta == nil {
+                       if exists && meta == nil {
+                               accessible, err := models.LFSObjectAccessible(ctx.User, p.Oid)
+                               if err != nil {
+                                       log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
+                                       writeStatus(ctx, http.StatusInternalServerError)
+                                       return
+                               }
+                               if accessible {
                                        _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
                                        if err != nil {
                                                log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
                                                writeStatus(ctx, http.StatusInternalServerError)
                                                return
                                        }
+                               } else {
+                                       exists = false
                                }
                        }
 
@@ -270,29 +280,50 @@ func UploadHandler(ctx *context.Context) {
                return
        }
 
-       meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
-       if err != nil {
-               log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
-               writeStatus(ctx, http.StatusInternalServerError)
-               return
-       }
-
        contentStore := lfs_module.NewContentStore()
-
        exists, err := contentStore.Exists(p)
        if err != nil {
                log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, err)
                writeStatus(ctx, http.StatusInternalServerError)
                return
        }
-       if meta.Existing || exists {
-               ctx.Resp.WriteHeader(http.StatusOK)
-               return
+
+       uploadOrVerify := func() error {
+               if exists {
+                       accessible, err := models.LFSObjectAccessible(ctx.User, p.Oid)
+                       if err != nil {
+                               log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
+                               return err
+                       }
+                       if !accessible {
+                               // The file exists but the user has no access to it.
+                               // The upload gets verified by hashing and size comparison to prove access to it.
+                               hash := sha256.New()
+                               written, err := io.Copy(hash, ctx.Req.Body)
+                               if err != nil {
+                                       log.Error("Error creating hash. Error: %v", err)
+                                       return err
+                               }
+
+                               if written != p.Size {
+                                       return lfs_module.ErrSizeMismatch
+                               }
+                               if hex.EncodeToString(hash.Sum(nil)) != p.Oid {
+                                       return lfs_module.ErrHashMismatch
+                               }
+                       }
+               } else if err := contentStore.Put(p, ctx.Req.Body); err != nil {
+                       log.Error("Error putting LFS MetaObject [%s] into content store. Error: %v", p.Oid, err)
+                       return err
+               }
+               _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
+               return err
        }
 
        defer ctx.Req.Body.Close()
-       if err := contentStore.Put(meta.Pointer, ctx.Req.Body); err != nil {
+       if err := uploadOrVerify(); err != nil {
                if errors.Is(err, lfs_module.ErrSizeMismatch) || errors.Is(err, lfs_module.ErrHashMismatch) {
+                       log.Error("Upload does not match LFS MetaObject [%s]. Error: %v", p.Oid, err)
                        writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error())
                } else {
                        writeStatus(ctx, http.StatusInternalServerError)