diff --git a/code/go/0chain.net/blobbercore/handler/handler_test.go b/code/go/0chain.net/blobbercore/handler/handler_test.go index f09bd17d8..6b7f3dbd0 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_test.go @@ -1109,8 +1109,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { AddRow(alloc.Terms[0].ID, alloc.Terms[0].AllocationID), ) - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "reference_objects"`)). - WithArgs(aa). + mock.ExpectQuery(regexp.QuoteMeta(`SELECT "id" FROM "reference_objects"`)). + WithArgs(aa, aa). WillReturnError(gorm.ErrRecordNotFound) mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "allocation_connections" WHERE`)). @@ -1490,7 +1490,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { test.end() } - fmt.Printf("\nResponse body: %v", test.args.w.Body.String()) + fmt.Printf("\nResponse body for test %v: %v", test.name, test.args.w.Body.String()) assert.Equal(t, test.wantCode, test.args.w.Result().StatusCode) if test.wantCode != http.StatusOK || test.wantBody != "" { assert.Equal(t, test.wantBody, test.args.w.Body.String()) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index bddb45e4a..d70131495 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -5,6 +5,8 @@ import ( "encoding/hex" "encoding/json" "errors" + "fmt" + "strings" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/stats" @@ -23,6 +25,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/lock" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/gosdk/constants" @@ -889,6 +892,10 @@ func (fsh *StorageHandler) CreateDir(ctx context.Context, r *http.Request) (*blo return nil, common.NewError("duplicate_file", "File at path already exists") } + if err := validateParentPathType(ctx, allocationID, dirPath); err != nil { + return nil, err + } + connectionID := r.FormValue("connection_id") if connectionID == "" { return nil, common.NewError("invalid_parameters", "Invalid connection id passed") @@ -948,7 +955,21 @@ func (fsh *StorageHandler) WriteFile(ctx context.Context, r *http.Request) (*blo allocationID := allocationObj.ID fileOperation := getFileOperation(r) - existingFileRef := getExistingFileRef(fsh, ctx, r, allocationObj, fileOperation) + formData := getFormData(ctx, r, allocationObj, fileOperation) + var existingFileRef *reference.Ref + if formData != nil { + existingFileRef, err = reference.GetRefWithID(ctx, allocationID, formData.Path) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + logging.Logger.Error(err.Error()) + return nil, common.NewError("database_error", "Got error while getting ref from database") + } + + if err := validateParentPathType(ctx, allocationID, formData.Path); err != nil { + return nil, err + } + + } + isCollaborator := existingFileRef != nil && reference.IsACollaborator(ctx, existingFileRef.ID, clientID) publicKey := allocationObj.OwnerPublicKey @@ -1040,15 +1061,55 @@ func getFileOperation(r *http.Request) string { return mode } -func getExistingFileRef(fsh *StorageHandler, ctx context.Context, r *http.Request, allocationObj *allocation.Allocation, fileOperation string) *reference.Ref { +func getFormData(ctx context.Context, r *http.Request, allocationObj *allocation.Allocation, fileOperation string) *allocation.UpdateFileChanger { if fileOperation == constants.FileOperationInsert || fileOperation == constants.FileOperationUpdate { var formData allocation.UpdateFileChanger uploadMetaString := r.FormValue(getFormFieldName(fileOperation)) err := json.Unmarshal([]byte(uploadMetaString), &formData) - if err == nil { - return fsh.checkIfFileAlreadyExists(ctx, allocationObj.ID, formData.Path) + if err != nil { + return nil + } + return &formData + } + return nil +} + +// validateParentPathType validates against any parent path not being directory. +func validateParentPathType(ctx context.Context, allocationID, fPath string) error { + if fPath == "" { + return nil + } + + fPath = filepath.Clean(fPath) + if !filepath.IsAbs(fPath) { + return fmt.Errorf("filepath %v is not absolute path", fPath) + } + refs, err := reference.GetRefsTypeFromPaths(ctx, allocationID, getParentPaths(fPath)) + if err != nil { + logging.Logger.Error(err.Error()) + return common.NewError("database_error", "Got error while getting parent refs") + } + + for _, ref := range refs { + if ref == nil { + continue + } + if ref.Type == reference.FILE { + return common.NewError("invalid_path", fmt.Sprintf("parent path %v is of file type", ref.Path)) } } return nil } + +// getParentPaths For path /a/b/c.txt, will return [/a,/a/b] +func getParentPaths(fPath string) []string { + splittedPaths := strings.Split(fPath, "/") + var paths []string + + for i := 0; i < len(splittedPaths); i++ { + subPath := strings.Join(splittedPaths[0:i], "/") + paths = append(paths, subPath) + } + return paths[2:] +} diff --git a/code/go/0chain.net/blobbercore/reference/ref.go b/code/go/0chain.net/blobbercore/reference/ref.go index 80deec905..83cd742a8 100644 --- a/code/go/0chain.net/blobbercore/reference/ref.go +++ b/code/go/0chain.net/blobbercore/reference/ref.go @@ -235,6 +235,55 @@ func GetReferenceFromLookupHash(ctx context.Context, allocationID, path_hash str return nil, err } +// GetRefType Select type from ref and return it +func GetRefType(ctx context.Context, allocationID, path string) (string, error) { + ref := new(Ref) + db := datastore.GetStore().GetTransaction(ctx) + err := db.Select("type").Where("allocation_id=? AND path=?", allocationID, path).First(ref).Error + if err != nil { + return "", err + } + return ref.Type, nil +} + +// GetRefWithID Return Ref with only ID selected in sql query +func GetRefWithID(ctx context.Context, allocationID, path string) (*Ref, error) { + ref := new(Ref) + db := datastore.GetStore().GetTransaction(ctx) + err := db.Select("id").Where("allocation_id=? AND path=?", allocationID, path).First(ref).Error + if err != nil { + return nil, err + } + return ref, nil +} + +// IsRefExist checks if ref with given path exists and returns error other than gorm.ErrRecordNotFound +func IsRefExist(ctx context.Context, allocationID, path string) (bool, error) { + db := datastore.GetStore().GetTransaction(ctx) + ref := new(Ref) + err := db.Select("path").Where("allocation_id=? AND path=?", allocationID, path).First(ref).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return false, nil + } + return false, err + } + + return true, nil +} + +// GetRefsTypeFromPaths Give list of paths it will return refs of respective path with only Type and Path selected in sql query +func GetRefsTypeFromPaths(ctx context.Context, allocationID string, paths []string) (refs []*Ref, err error) { + db := datastore.GetStore().GetTransaction(ctx) + db = db.Select("path", "type") + for _, p := range paths { + db = db.Or(Ref{AllocationID: allocationID, Path: p}) + } + + err = db.Find(&refs).Error + return +} + func GetSubDirsFromPath(p string) []string { path := p parent, cur := filepath.Split(path)