diff --git a/server/ente/filedata/filedata.go b/server/ente/filedata/filedata.go index 5b6ec70edd..69da7b37e1 100644 --- a/server/ente/filedata/filedata.go +++ b/server/ente/filedata/filedata.go @@ -26,6 +26,7 @@ type Entity struct { Type ente.ObjectType `json:"type"` EncryptedData string `json:"encryptedData"` DecryptionHeader string `json:"decryptionHeader"` + UpdatedAt int64 `json:"updatedAt"` } type FDDiffRequest struct { diff --git a/server/ente/filedata/putfiledata.go b/server/ente/filedata/putfiledata.go index a4b4f7bf5b..43df9c68d1 100644 --- a/server/ente/filedata/putfiledata.go +++ b/server/ente/filedata/putfiledata.go @@ -11,6 +11,8 @@ type PutFileDataRequest struct { EncryptedData *string `json:"encryptedData,omitempty"` DecryptionHeader *string `json:"decryptionHeader,omitempty"` Version *int `json:"version,omitempty"` + // Used to ensure that the client has correct state before it tries to update the metadata + LastUpdatedAt *int64 `json:"lastUpdatedAt,omitempty"` } func (r PutFileDataRequest) isEncDataPresent() bool { diff --git a/server/pkg/controller/filedata/controller.go b/server/pkg/controller/filedata/controller.go index fabb928ba8..ec269b690c 100644 --- a/server/pkg/controller/filedata/controller.go +++ b/server/pkg/controller/filedata/controller.go @@ -3,6 +3,7 @@ package filedata import ( "context" "errors" + "fmt" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" @@ -20,6 +21,7 @@ import ( "github.com/gin-contrib/requestid" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" + "net/http" "sync" gTime "time" ) @@ -95,6 +97,9 @@ func (c *Controller) InsertOrUpdateMetadata(ctx *gin.Context, req *fileData.PutF if req.Type != ente.MlData { return stacktrace.Propagate(ente.NewBadRequestWithMessage("unsupported object type "+string(req.Type)), "") } + if versionErr := c._validateLastUpdatedAt(ctx, req.LastUpdatedAt, req.FileID, req.Type); versionErr != nil { + return stacktrace.Propagate(versionErr, "") + } bucketID := c.S3Config.GetBucketID(req.Type) objectKey := fileData.ObjectMetadataKey(req.FileID, fileOwnerID, req.Type, nil) @@ -161,6 +166,7 @@ func (c *Controller) GetFileData(ctx *gin.Context, actorUser int64, req fileData Type: doRows[0].Type, EncryptedData: s3MetaObject.EncryptedData, DecryptionHeader: s3MetaObject.DecryptionHeader, + UpdatedAt: doRows[0].UpdatedAt, }, nil } @@ -205,6 +211,7 @@ func (c *Controller) GetFilesData(ctx *gin.Context, req fileData.GetFilesData) ( Type: obj.dbEntry.Type, EncryptedData: obj.s3MetaObject.EncryptedData, DecryptionHeader: obj.s3MetaObject.DecryptionHeader, + UpdatedAt: obj.dbEntry.UpdatedAt, }) } } @@ -317,6 +324,38 @@ func (c *Controller) _checkMetadataReadOrWritePerm(ctx *gin.Context, userID int6 return nil } +func (c *Controller) _validateLastUpdatedAt(ctx *gin.Context, lastUpdatedAt *int64, fileID int64, oType ente.ObjectType) error { + if lastUpdatedAt == nil { + return nil + } + doRows, err := c.Repo.GetFilesData(ctx, oType, []int64{fileID}) + if err != nil { + return stacktrace.Propagate(err, "failed to get data") + } + var invalidVersionErr = &ente.ApiError{ + HttpStatusCode: http.StatusConflict, + Code: "INVALID_VERSION", + Message: "", + } + if len(doRows) == 0 { + if *lastUpdatedAt == 0 { + return nil + } + invalidVersionErr.Message = "non zero version empty data" + return invalidVersionErr + } + if doRows[0].IsDeleted { + invalidVersionErr.Message = "data deleted" + return invalidVersionErr + } + dbUpdatedAt := doRows[0].UpdatedAt + if dbUpdatedAt != *lastUpdatedAt { + invalidVersionErr.Message = fmt.Sprintf("version mismatch expected %d, found %d", dbUpdatedAt, *lastUpdatedAt) + return invalidVersionErr + } + return nil +} + // _checkPreviewWritePerm is func (c *Controller) _checkPreviewWritePerm(ctx *gin.Context, fileID int64, actorID int64) error { err := c.AccessCtrl.VerifyFileOwnership(ctx, &access.VerifyFileOwnershipParams{ diff --git a/server/pkg/repo/passkey/passkey.go b/server/pkg/repo/passkey/passkey.go index 3910181a1d..066f5e12fb 100644 --- a/server/pkg/repo/passkey/passkey.go +++ b/server/pkg/repo/passkey/passkey.go @@ -13,8 +13,8 @@ import ( "github.com/ente-io/stacktrace" "github.com/go-webauthn/webauthn/protocol" "github.com/google/uuid" - "github.com/spf13/viper" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "github.com/ente-io/museum/ente" "github.com/ente-io/museum/pkg/utils/byteMarshaller"