2025-02-21 12:28:11 +05:30

204 lines
7.1 KiB
Go

package collections
import (
"fmt"
"github.com/ente-io/museum/ente"
"github.com/ente-io/museum/pkg/controller/access"
"github.com/ente-io/museum/pkg/utils/auth"
"github.com/ente-io/stacktrace"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
// AddFiles adds files to a collection
func (c *CollectionController) AddFiles(ctx *gin.Context, userID int64, files []ente.CollectionFileItem, cID int64) error {
resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: cID,
ActorUserID: userID,
IncludeDeleted: false,
})
if err != nil {
return stacktrace.Propagate(err, "failed to verify collection access")
}
if !resp.Role.CanAdd() {
return stacktrace.Propagate(ente.ErrPermissionDenied, fmt.Sprintf("user %d with role %s can not add files", userID, *resp.Role))
}
collectionOwnerID := resp.Collection.Owner.ID
filesOwnerID := userID
// Verify that the user owns each file
fileIDs := make([]int64, 0)
for _, file := range files {
fileIDs = append(fileIDs, file.ID)
}
err = c.AccessCtrl.VerifyFileOwnership(ctx, &access.VerifyFileOwnershipParams{
ActorUserId: userID,
FileIDs: fileIDs,
})
if err != nil {
return stacktrace.Propagate(err, "Failed to verify fileOwnership")
}
err = c.CollectionRepo.AddFiles(cID, collectionOwnerID, files, filesOwnerID)
if err != nil {
return stacktrace.Propagate(err, "")
}
return nil
}
// RestoreFiles restore files from trash and add to the collection
func (c *CollectionController) RestoreFiles(ctx *gin.Context, userID int64, cID int64, files []ente.CollectionFileItem) error {
_, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: cID,
ActorUserID: userID,
IncludeDeleted: false,
VerifyOwner: true,
})
if err != nil {
return stacktrace.Propagate(err, "failed to verify collection access")
}
// Verify that the user owns each file
for _, file := range files {
// todo #perf find owners of all files
ownerID, err := c.FileRepo.GetOwnerID(file.ID)
if err != nil {
return stacktrace.Propagate(err, "")
}
if ownerID != userID {
log.WithFields(log.Fields{
"file_id": file.ID,
"owner_id": ownerID,
"user_id": userID,
}).Error("invalid ops: can't add file which isn't owned by user")
return stacktrace.Propagate(ente.ErrPermissionDenied, "")
}
}
err = c.CollectionRepo.RestoreFiles(ctx, userID, cID, files)
if err != nil {
return stacktrace.Propagate(err, "")
}
return nil
}
// MoveFiles from one collection to another collection. Both the collections and files should belong to
// single user
func (c *CollectionController) MoveFiles(ctx *gin.Context, req ente.MoveFilesRequest) error {
userID := auth.GetUserID(ctx.Request.Header)
_, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: req.FromCollectionID,
ActorUserID: userID,
IncludeDeleted: false,
VerifyOwner: true,
})
if err != nil {
return stacktrace.Propagate(err, "failed to verify if actor owns fromCollection")
}
_, err = c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: req.ToCollectionID,
ActorUserID: userID,
IncludeDeleted: false,
VerifyOwner: true,
})
if err != nil {
return stacktrace.Propagate(err, "failed to verify if actor owns toCollection")
}
// Verify that the user owns each file
fileIDs := make([]int64, 0)
for _, file := range req.Files {
fileIDs = append(fileIDs, file.ID)
}
err = c.AccessCtrl.VerifyFileOwnership(ctx, &access.VerifyFileOwnershipParams{
ActorUserId: userID,
FileIDs: fileIDs,
})
if err != nil {
stacktrace.Propagate(err, "Failed to verify fileOwnership")
}
err = c.CollectionRepo.MoveFiles(ctx, req.ToCollectionID, req.FromCollectionID, req.Files, userID, userID)
return stacktrace.Propagate(err, "") // return nil if err is nil
}
// RemoveFilesV3 removes files from a collection as long as owner(s) of the file is different from collection owner
func (c *CollectionController) RemoveFilesV3(ctx *gin.Context, req ente.RemoveFilesV3Request) error {
actorUserID := auth.GetUserID(ctx.Request.Header)
resp, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: req.CollectionID,
ActorUserID: actorUserID,
VerifyOwner: false,
})
if err != nil {
return stacktrace.Propagate(err, "failed to verify collection access")
}
err = c.isRemoveAllowed(ctx, actorUserID, resp.Collection.Owner.ID, req.FileIDs)
if err != nil {
return stacktrace.Propagate(err, "file removal check failed")
}
err = c.CollectionRepo.RemoveFilesV3(ctx, req.CollectionID, req.FileIDs)
if err != nil {
return stacktrace.Propagate(err, "failed to remove files")
}
return nil
}
// isRemoveAllowed verifies that given set of files can be removed from the collection or not
func (c *CollectionController) isRemoveAllowed(ctx *gin.Context, actorUserID int64, collectionOwnerID int64, fileIDs []int64) error {
ownerToFilesMap, err := c.FileRepo.GetOwnerToFileIDsMap(ctx, fileIDs)
if err != nil {
return stacktrace.Propagate(err, "failed to get owner to fileIDs map")
}
// verify that none of the file belongs to the collection owner
if _, ok := ownerToFilesMap[collectionOwnerID]; ok {
return ente.NewBadRequestWithMessage("can not remove files owned by album owner")
}
if collectionOwnerID != actorUserID {
// verify that user is only trying to remove files owned by them
if len(ownerToFilesMap) > 1 {
return stacktrace.Propagate(ente.ErrPermissionDenied, "can not remove files owned by others")
}
// verify that user is only trying to remove files owned by them
if _, ok := ownerToFilesMap[actorUserID]; !ok {
return stacktrace.Propagate(ente.ErrPermissionDenied, "can not remove files owned by others")
}
}
return nil
}
func (c *CollectionController) IsCopyAllowed(ctx *gin.Context, actorUserID int64, req ente.CopyFileSyncRequest) error {
// verify that srcCollectionID is accessible by actorUserID
if _, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: req.SrcCollectionID,
ActorUserID: actorUserID,
}); err != nil {
return stacktrace.Propagate(err, "failed to verify srcCollection access")
}
// verify that dstCollectionID is owned by actorUserID
if _, err := c.AccessCtrl.GetCollection(ctx, &access.GetCollectionParams{
CollectionID: req.DstCollection,
ActorUserID: actorUserID,
VerifyOwner: true,
}); err != nil {
return stacktrace.Propagate(err, "failed to ownership of the dstCollection access")
}
// verify that all FileIDs exists in the srcCollection
fileIDs := make([]int64, len(req.CollectionFileItems))
for idx, file := range req.CollectionFileItems {
fileIDs[idx] = file.ID
}
if err := c.CollectionRepo.VerifyAllFileIDsExistsInCollection(ctx, req.SrcCollectionID, fileIDs); err != nil {
return stacktrace.Propagate(err, "failed to verify fileIDs in srcCollection")
}
dsMap, err := c.FileRepo.GetOwnerToFileIDsMap(ctx, fileIDs)
if err != nil {
return err
}
// verify that none of the file belongs to actorUserID
if _, ok := dsMap[actorUserID]; ok {
return ente.NewBadRequestWithMessage("can not copy files owned by actor")
}
return nil
}