Folders: Able to fetch folders available for user as "shared" folder (#77774)

* Folders: Show folders user has access to at the root level

* Refactor

* Refactor

* Hide parent folders user has no access to

* Skip expensive computation if possible

* Fix tests

* Fix potential nil access

* Fix duplicated folders

* Fix linter error

* Fix querying folders if no managed permissions set

* Update benchmark

* Add special shared with me folder and fetch available non-root folders on demand

* Fix parents query

* Improve db query for folders

* Reset benchmark changes

* Fix permissions for shared with me folder

* Simplify dedup

* Add option to include shared folder permission to user's permissions

* Fix nil UID

* Remove duplicated folders from shared list

* Only left the base part

* Apply suggestions from code review

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>

* Add tests

* Fix linter errors

---------

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
Alexander Zobnin
2023-11-08 15:28:49 +01:00
committed by GitHub
parent 5fc3ab801b
commit a39242890e
6 changed files with 173 additions and 1 deletions
+81
View File
@@ -7,6 +7,8 @@ import (
"strings"
"sync"
"golang.org/x/exp/slices"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/infra/db"
@@ -246,6 +248,85 @@ func (s *Service) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery)
return filtered, nil
}
// GetSharedWithMe returns folders available to user, which cannot be accessed from the root folders
func (s *Service) GetSharedWithMe(ctx context.Context, cmd *folder.GetChildrenQuery) ([]*folder.Folder, error) {
availableNonRootFolders, err := s.getAvailableNonRootFolders(ctx, cmd.OrgID, cmd.SignedInUser)
if err != nil {
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders to which the user has explicit access: %w", err)
}
rootFolders, err := s.GetChildren(ctx, &folder.GetChildrenQuery{UID: "", OrgID: cmd.OrgID, SignedInUser: cmd.SignedInUser})
if err != nil {
return nil, folder.ErrInternal.Errorf("failed to fetch root folders to which the user has access: %w", err)
}
availableNonRootFolders = s.deduplicateAvailableFolders(ctx, availableNonRootFolders, rootFolders)
return availableNonRootFolders, nil
}
func (s *Service) getAvailableNonRootFolders(ctx context.Context, orgID int64, user identity.Requester) ([]*folder.Folder, error) {
permissions := user.GetPermissions()
folderPermissions := permissions["folders:read"]
folderPermissions = append(folderPermissions, permissions["dashboards:read"]...)
nonRootFolders := make([]*folder.Folder, 0)
folderUids := make([]string, 0)
for _, p := range folderPermissions {
if folderUid, found := strings.CutPrefix(p, "folders:uid:"); found {
if !slices.Contains(folderUids, folderUid) {
folderUids = append(folderUids, folderUid)
}
}
}
if len(folderUids) == 0 {
return nonRootFolders, nil
}
dashFolders, err := s.store.GetFolders(ctx, orgID, folderUids)
if err != nil {
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders: %w", err)
}
for _, f := range dashFolders {
if f.ParentUID != "" {
nonRootFolders = append(nonRootFolders, f)
}
}
return nonRootFolders, nil
}
func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*folder.Folder, rootFolders []*folder.Folder) []*folder.Folder {
allFolders := append(folders, rootFolders...)
foldersDedup := make([]*folder.Folder, 0)
for _, f := range folders {
isSubfolder := slices.ContainsFunc(allFolders, func(folder *folder.Folder) bool {
return f.ParentUID == folder.UID
})
if !isSubfolder {
parents, err := s.GetParents(ctx, folder.GetParentsQuery{UID: f.UID, OrgID: f.OrgID})
if err != nil {
s.log.Error("failed to fetch folder parents", "uid", f.UID, "error", err)
continue
}
for _, parent := range parents {
contains := slices.ContainsFunc(allFolders, func(f *folder.Folder) bool {
return f.UID == parent.UID
})
if contains {
isSubfolder = true
break
}
}
}
if !isSubfolder {
foldersDedup = append(foldersDedup, f)
}
}
return foldersDedup
}
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
if !s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
return nil, nil