From 65095855c13894516156b208a66214ed36638187 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Thu, 29 May 2025 09:42:22 +0800 Subject: [PATCH] fix(directlink): direct link should not be accessible if the parent file is in trash bin (#2415) --- pkg/filemanager/fs/dbfs/dbfs.go | 4 ++- pkg/filemanager/fs/dbfs/manage.go | 33 ++++++++++++++++++++++ pkg/filemanager/fs/dbfs/navigator.go | 19 +++++++++++++ pkg/filemanager/fs/dbfs/share_navigator.go | 19 ------------- pkg/filemanager/fs/fs.go | 2 ++ pkg/filemanager/manager/entity.go | 24 +++------------- pkg/filemanager/manager/manager.go | 2 +- 7 files changed, 62 insertions(+), 41 deletions(-) diff --git a/pkg/filemanager/fs/dbfs/dbfs.go b/pkg/filemanager/fs/dbfs/dbfs.go index 590d518..96f0d62 100644 --- a/pkg/filemanager/fs/dbfs/dbfs.go +++ b/pkg/filemanager/fs/dbfs/dbfs.go @@ -45,7 +45,7 @@ type ( func NewDatabaseFS(u *ent.User, fileClient inventory.FileClient, shareClient inventory.ShareClient, l logging.Logger, ls lock.LockSystem, settingClient setting.Provider, storagePolicyClient inventory.StoragePolicyClient, hasher hashid.Encoder, userClient inventory.UserClient, - cache, stateKv cache.Driver) fs.FileSystem { + cache, stateKv cache.Driver, directLinkClient inventory.DirectLinkClient) fs.FileSystem { return &DBFS{ user: u, navigators: make(map[string]Navigator), @@ -59,6 +59,7 @@ func NewDatabaseFS(u *ent.User, fileClient inventory.FileClient, shareClient inv userClient: userClient, cache: cache, stateKv: stateKv, + directLinkClient: directLinkClient, } } @@ -69,6 +70,7 @@ type DBFS struct { userClient inventory.UserClient storagePolicyClient inventory.StoragePolicyClient shareClient inventory.ShareClient + directLinkClient inventory.DirectLinkClient l logging.Logger ls lock.LockSystem settingClient setting.Provider diff --git a/pkg/filemanager/fs/dbfs/manage.go b/pkg/filemanager/fs/dbfs/manage.go index b32ad13..3492115 100644 --- a/pkg/filemanager/fs/dbfs/manage.go +++ b/pkg/filemanager/fs/dbfs/manage.go @@ -9,6 +9,7 @@ import ( "time" "github.com/cloudreve/Cloudreve/v4/ent" + "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/inventory" "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" @@ -608,6 +609,38 @@ func (f *DBFS) MoveOrCopy(ctx context.Context, path []*fs.URI, dst *fs.URI, isCo return ae.Aggregate() } +func (f *DBFS) GetFileFromDirectLink(ctx context.Context, dl *ent.DirectLink) (fs.File, error) { + fileModel, err := dl.Edges.FileOrErr() + if err != nil { + return nil, err + } + + owner, err := fileModel.Edges.OwnerOrErr() + if err != nil { + return nil, err + } + + // File owner must be active + if owner.Status != user.StatusActive { + return nil, fs.ErrDirectLinkInvalid.WithError(fmt.Errorf("file owner is not active")) + } + + file := newFile(nil, fileModel) + + // Traverse to the root file + baseNavigator := newBaseNavigator(f.fileClient, defaultFilter, f.user, f.hasher, f.settingClient.DBFS(ctx)) + root, err := baseNavigator.findRoot(ctx, file) + if err != nil { + return nil, fmt.Errorf("failed to find root file: %w", err) + } + + if root.Name() != inventory.RootFolderName { + return nil, serializer.NewError(serializer.CodeNotFound, "direct link not found", err) + } + + return file, nil +} + func (f *DBFS) deleteEntity(ctx context.Context, target *File, entityId int) (inventory.StorageDiff, error) { if target.PrimaryEntityID() == entityId { return nil, fs.ErrNotSupportedAction.WithError(fmt.Errorf("cannot delete current version")) diff --git a/pkg/filemanager/fs/dbfs/navigator.go b/pkg/filemanager/fs/dbfs/navigator.go index b3dcf50..57f9fba 100644 --- a/pkg/filemanager/fs/dbfs/navigator.go +++ b/pkg/filemanager/fs/dbfs/navigator.go @@ -197,6 +197,25 @@ func (b *baseNavigator) walkNext(ctx context.Context, root *File, next string, i return newFile(root, child), nil } +// findRoot finds the root folder of the given child. +func (b *baseNavigator) findRoot(ctx context.Context, child *File) (*File, error) { + root := child + for { + newRoot, err := b.walkUp(ctx, root) + if err != nil { + if !ent.IsNotFound(err) { + return nil, err + } + + break + } + + root = newRoot + } + + return root, nil +} + func (b *baseNavigator) walkUp(ctx context.Context, child *File) (*File, error) { parent, err := b.fileClient.GetParentFile(ctx, child.Model, false) if err != nil { diff --git a/pkg/filemanager/fs/dbfs/share_navigator.go b/pkg/filemanager/fs/dbfs/share_navigator.go index 6c3f664..470b48f 100644 --- a/pkg/filemanager/fs/dbfs/share_navigator.go +++ b/pkg/filemanager/fs/dbfs/share_navigator.go @@ -300,25 +300,6 @@ func (n *shareNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, return nil } -// findRoot finds the root folder of the given child. -func (n *shareNavigator) findRoot(ctx context.Context, child *File) (*File, error) { - root := child - for { - newRoot, err := n.baseNavigator.walkUp(ctx, root) - if err != nil { - if !ent.IsNotFound(err) { - return nil, err - } - - break - } - - root = newRoot - } - - return root, nil -} - func (n *shareNavigator) Walk(ctx context.Context, levelFiles []*File, limit, depth int, f WalkFunc) error { return n.baseNavigator.walk(ctx, levelFiles, limit, depth, f) } diff --git a/pkg/filemanager/fs/fs.go b/pkg/filemanager/fs/fs.go index 7d9583b..25c9532 100644 --- a/pkg/filemanager/fs/fs.go +++ b/pkg/filemanager/fs/fs.go @@ -93,6 +93,8 @@ type ( // - `delete` is false: set version as current version; // - `delete` is true: delete version. VersionControl(ctx context.Context, path *URI, versionId int, delete bool) error + // GetFileFromDirectLink gets a file from a direct link. + GetFileFromDirectLink(ctx context.Context, dl *ent.DirectLink) (File, error) } UploadManager interface { diff --git a/pkg/filemanager/manager/entity.go b/pkg/filemanager/manager/entity.go index 3dd587d..7198688 100644 --- a/pkg/filemanager/manager/entity.go +++ b/pkg/filemanager/manager/entity.go @@ -8,7 +8,6 @@ import ( "time" "github.com/cloudreve/Cloudreve/v4/ent" - "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/driver" @@ -147,34 +146,19 @@ func (m *manager) GetUrlForRedirectedDirectLink(ctx context.Context, dl *ent.Dir opt.Apply(o) } - file, err := dl.Edges.FileOrErr() + file, err := m.fs.GetFileFromDirectLink(ctx, dl) if err != nil { return "", nil, err } - owner, err := file.Edges.OwnerOrErr() - if err != nil { - return "", nil, err - } - - entities, err := file.Edges.EntitiesOrErr() - if err != nil { - return "", nil, err - } - - // File owner must be active - if owner.Status != user.StatusActive { - return "", nil, fs.ErrDirectLinkInvalid.WithError(fmt.Errorf("file owner is not active")) - } - // Find primary entity - target, found := lo.Find(entities, func(entity *ent.Entity) bool { - return entity.ID == file.PrimaryEntity + target, found := lo.Find(file.Entities(), func(entity fs.Entity) bool { + return entity.ID() == file.PrimaryEntityID() }) if !found { return "", nil, fs.ErrDirectLinkInvalid.WithError(fmt.Errorf("primary entity not found")) } - primaryEntity := fs.NewEntity(target) + primaryEntity := target // Generate url var ( diff --git a/pkg/filemanager/manager/manager.go b/pkg/filemanager/manager/manager.go index 19065f3..d8dc2fe 100644 --- a/pkg/filemanager/manager/manager.go +++ b/pkg/filemanager/manager/manager.go @@ -137,7 +137,7 @@ func NewFileManager(dep dependency.Dep, u *ent.User) FileManager { user: u, settings: dep.SettingProvider(), fs: dbfs.NewDatabaseFS(u, dep.FileClient(), dep.ShareClient(), dep.Logger(), dep.LockSystem(), - dep.SettingProvider(), dep.StoragePolicyClient(), dep.HashIDEncoder(), dep.UserClient(), dep.KV(), dep.NavigatorStateKV()), + dep.SettingProvider(), dep.StoragePolicyClient(), dep.HashIDEncoder(), dep.UserClient(), dep.KV(), dep.NavigatorStateKV(), dep.DirectLinkClient()), kv: dep.KV(), config: config, auth: dep.GeneralAuth(),