diff --git a/pkg/services/dashboards/accesscontrol.go b/pkg/services/dashboards/accesscontrol.go index fa5f044ddca..0244747506e 100644 --- a/pkg/services/dashboards/accesscontrol.go +++ b/pkg/services/dashboards/accesscontrol.go @@ -43,3 +43,23 @@ func NewNameScopeResolver(db Store) (string, ac.AttributeScopeResolveFunc) { } return prefix, resolver } + +// NewUidScopeResolver provides an AttributeScopeResolver that is able to convert a scope prefixed with "folders:uid:" into an id based scope. +func NewUidScopeResolver(db Store) (string, ac.AttributeScopeResolveFunc) { + prefix := ScopeFoldersProvider.GetResourceScopeUID("") + resolver := func(ctx context.Context, orgID int64, scope string) (string, error) { + if !strings.HasPrefix(scope, prefix) { + return "", ac.ErrInvalidScope + } + uid := scope[len(prefix):] + if len(uid) == 0 { + return "", ac.ErrInvalidScope + } + folder, err := db.GetFolderByUID(ctx, orgID, uid) + if err != nil { + return "", err + } + return ScopeFoldersProvider.GetResourceScope(strconv.FormatInt(folder.Id, 10)), nil + } + return prefix, resolver +} diff --git a/pkg/services/dashboards/accesscontrol_test.go b/pkg/services/dashboards/accesscontrol_test.go index 92d911d3017..69c96f57599 100644 --- a/pkg/services/dashboards/accesscontrol_test.go +++ b/pkg/services/dashboards/accesscontrol_test.go @@ -70,3 +70,59 @@ func TestNewNameScopeResolver(t *testing.T) { require.Empty(t, resolvedScope) }) } + +func TestNewUidScopeResolver(t *testing.T) { + t.Run("prefix should be expected", func(t *testing.T) { + prefix, _ := NewUidScopeResolver(&FakeDashboardStore{}) + require.Equal(t, "folders:uid:", prefix) + }) + + t.Run("resolver should convert to id scope", func(t *testing.T) { + dashboardStore := &FakeDashboardStore{} + + _, resolver := NewUidScopeResolver(dashboardStore) + + orgId := rand.Int63() + uid := util.GenerateShortUID() + + db := &models.Folder{Id: rand.Int63()} + dashboardStore.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() + + scope := "folders:uid:" + uid + + resolvedScope, err := resolver(context.Background(), orgId, scope) + require.NoError(t, err) + + require.Equal(t, fmt.Sprintf("folders:id:%v", db.Id), resolvedScope) + + dashboardStore.AssertCalled(t, "GetFolderByUID", mock.Anything, orgId, uid) + }) + t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { + dashboardStore := &FakeDashboardStore{} + _, resolver := NewUidScopeResolver(dashboardStore) + + _, err := resolver(context.Background(), rand.Int63(), "folders:id:123") + require.ErrorIs(t, err, ac.ErrInvalidScope) + }) + t.Run("resolver should fail if resource of input scope is empty", func(t *testing.T) { + dashboardStore := &FakeDashboardStore{} + _, resolver := NewUidScopeResolver(dashboardStore) + + _, err := resolver(context.Background(), rand.Int63(), "folders:uid:") + require.ErrorIs(t, err, ac.ErrInvalidScope) + }) + t.Run("returns 'not found' if folder does not exist", func(t *testing.T) { + dashboardStore := &FakeDashboardStore{} + + _, resolver := NewUidScopeResolver(dashboardStore) + + orgId := rand.Int63() + dashboardStore.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrDashboardNotFound).Once() + + scope := "folders:uid:" + util.GenerateShortUID() + + resolvedScope, err := resolver(context.Background(), orgId, scope) + require.ErrorIs(t, err, models.ErrDashboardNotFound) + require.Empty(t, resolvedScope) + }) +} diff --git a/pkg/services/dashboards/manager/folder_service.go b/pkg/services/dashboards/manager/folder_service.go index 022507164bd..787a0be04c1 100644 --- a/pkg/services/dashboards/manager/folder_service.go +++ b/pkg/services/dashboards/manager/folder_service.go @@ -34,6 +34,7 @@ func ProvideFolderService( ac accesscontrol.AccessControl, ) *FolderServiceImpl { ac.RegisterAttributeScopeResolver(dashboards.NewNameScopeResolver(dashboardStore)) + ac.RegisterAttributeScopeResolver(dashboards.NewUidScopeResolver(dashboardStore)) return &FolderServiceImpl{ cfg: cfg, diff --git a/pkg/services/dashboards/manager/folder_service_test.go b/pkg/services/dashboards/manager/folder_service_test.go index d1c48b358cd..277eab8377d 100644 --- a/pkg/services/dashboards/manager/folder_service_test.go +++ b/pkg/services/dashboards/manager/folder_service_test.go @@ -40,7 +40,7 @@ func TestProvideFolderService(t *testing.T) { store, nil, features, permissionsServices, ac, ) - require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 1) + require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 2) }) }