RBAC: Change annotation filter to use dashboard based annotation scopes (#78635)

change annotation filter to use dash based annotation scopes
This commit is contained in:
Ieva
2023-11-29 10:34:44 +00:00
committed by GitHub
parent ff7dd17c56
commit 791881f910
7 changed files with 130 additions and 66 deletions

View File

@@ -49,32 +49,42 @@ func (authz *AuthService) Authorize(ctx context.Context, orgID int64, user ident
if !has {
return nil, ErrReadForbidden.Errorf("user does not have permission to read annotations")
}
scopeTypes := annotationScopeTypes(scopes)
_, canAccessOrgAnnotations := scopeTypes[annotations.Organization.String()]
_, canAccessDashAnnotations := scopeTypes[annotations.Dashboard.String()]
if authz.features.IsEnabled(ctx, featuremgmt.FlagAnnotationPermissionUpdate) {
canAccessDashAnnotations = true
}
var visibleDashboards map[string]int64
var err error
if _, ok := scopeTypes[annotations.Dashboard.String()]; ok {
visibleDashboards, err = authz.userVisibleDashboards(ctx, user, orgID)
if canAccessDashAnnotations {
visibleDashboards, err = authz.dashboardsWithVisibleAnnotations(ctx, user, orgID)
if err != nil {
return nil, ErrAccessControlInternal.Errorf("failed to fetch dashboards: %w", err)
}
}
return &AccessResources{
Dashboards: visibleDashboards,
ScopeTypes: scopeTypes,
Dashboards: visibleDashboards,
CanAccessDashAnnotations: canAccessDashAnnotations,
CanAccessOrgAnnotations: canAccessOrgAnnotations,
}, nil
}
func (authz *AuthService) userVisibleDashboards(ctx context.Context, user identity.Requester, orgID int64) (map[string]int64, error) {
func (authz *AuthService) dashboardsWithVisibleAnnotations(ctx context.Context, user identity.Requester, orgID int64) (map[string]int64, error) {
recursiveQueriesSupported, err := authz.db.RecursiveQueriesAreSupported()
if err != nil {
return nil, err
}
filterType := searchstore.TypeDashboard
if authz.features.IsEnabled(ctx, featuremgmt.FlagAnnotationPermissionUpdate) {
filterType = searchstore.TypeAnnotation
}
filters := []any{
permissions.NewAccessControlDashboardPermissionFilter(user, dashboardaccess.PERMISSION_VIEW, searchstore.TypeDashboard, authz.features, recursiveQueriesSupported),
permissions.NewAccessControlDashboardPermissionFilter(user, dashboardaccess.PERMISSION_VIEW, filterType, authz.features, recursiveQueriesSupported),
searchstore.OrgFilter{OrgId: orgID},
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/annotations/testutil"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -16,11 +15,6 @@ import (
"github.com/stretchr/testify/require"
)
var (
dashScopeType = annotations.Dashboard.String()
orgScopeType = annotations.Organization.String()
)
func TestIntegrationAuthorize(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
@@ -28,8 +22,6 @@ func TestIntegrationAuthorize(t *testing.T) {
sql := db.InitTestDB(t)
authz := NewAuthService(sql, featuremgmt.WithFeatures())
dash1 := testutil.CreateDashboard(t, sql, featuremgmt.WithFeatures(), dashboards.SaveDashboardCommand{
UserID: 1,
OrgID: 1,
@@ -55,6 +47,7 @@ func TestIntegrationAuthorize(t *testing.T) {
type testCase struct {
name string
permissions map[string][]string
featureToggle string
expectedResources *AccessResources
expectedErr error
}
@@ -67,8 +60,46 @@ func TestIntegrationAuthorize(t *testing.T) {
dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll},
},
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
ScopeTypes: map[any]struct{}{dashScopeType: {}, orgScopeType: {}},
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
CanAccessOrgAnnotations: true,
CanAccessDashAnnotations: true,
},
},
{
name: "should have no dashboards if missing annotation read permission on dashboards and FlagAnnotationPermissionUpdate is enabled",
permissions: map[string][]string{
accesscontrol.ActionAnnotationsRead: {accesscontrol.ScopeAnnotationsAll},
dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll},
},
featureToggle: featuremgmt.FlagAnnotationPermissionUpdate,
expectedResources: &AccessResources{
Dashboards: nil,
CanAccessOrgAnnotations: true,
CanAccessDashAnnotations: true,
},
},
{
name: "should have dashboard and organization scope and all dashboards if FlagAnnotationPermissionUpdate is enabled",
permissions: map[string][]string{
accesscontrol.ActionAnnotationsRead: {accesscontrol.ScopeAnnotationsTypeOrganization, dashboards.ScopeDashboardsAll},
},
featureToggle: featuremgmt.FlagAnnotationPermissionUpdate,
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
CanAccessOrgAnnotations: true,
CanAccessDashAnnotations: true,
},
},
{
name: "should have dashboard and organization scope and all dashboards if FlagAnnotationPermissionUpdate is enabled and folder based scope is used",
permissions: map[string][]string{
accesscontrol.ActionAnnotationsRead: {accesscontrol.ScopeAnnotationsTypeOrganization, dashboards.ScopeFoldersAll},
},
featureToggle: featuremgmt.FlagAnnotationPermissionUpdate,
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
CanAccessOrgAnnotations: true,
CanAccessDashAnnotations: true,
},
},
{
@@ -78,8 +109,8 @@ func TestIntegrationAuthorize(t *testing.T) {
dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll},
},
expectedResources: &AccessResources{
Dashboards: nil,
ScopeTypes: map[any]struct{}{orgScopeType: {}},
Dashboards: nil,
CanAccessOrgAnnotations: true,
},
},
{
@@ -89,8 +120,20 @@ func TestIntegrationAuthorize(t *testing.T) {
dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll},
},
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
ScopeTypes: map[any]struct{}{dashScopeType: {}},
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
CanAccessDashAnnotations: true,
},
},
{
name: "should have only dashboard scope and all dashboards if FlagAnnotationPermissionUpdate is enabled",
permissions: map[string][]string{
accesscontrol.ActionAnnotationsRead: {dashboards.ScopeDashboardsAll},
},
featureToggle: featuremgmt.FlagAnnotationPermissionUpdate,
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID, dash2.UID: dash2.ID},
CanAccessOrgAnnotations: false,
CanAccessDashAnnotations: true,
},
},
{
@@ -100,8 +143,20 @@ func TestIntegrationAuthorize(t *testing.T) {
dashboards.ActionDashboardsRead: {fmt.Sprintf("dashboards:uid:%s", dash1.UID)},
},
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID},
ScopeTypes: map[any]struct{}{dashScopeType: {}},
Dashboards: map[string]int64{dash1.UID: dash1.ID},
CanAccessDashAnnotations: true,
},
},
{
name: "should have only dashboard scope and only dashboard 1 if FlagAnnotationPermissionUpdate is enabled",
permissions: map[string][]string{
accesscontrol.ActionAnnotationsRead: {dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dash1.UID)},
},
featureToggle: featuremgmt.FlagAnnotationPermissionUpdate,
expectedResources: &AccessResources{
Dashboards: map[string]int64{dash1.UID: dash1.ID},
CanAccessOrgAnnotations: false,
CanAccessDashAnnotations: true,
},
},
}
@@ -111,6 +166,8 @@ func TestIntegrationAuthorize(t *testing.T) {
u.Permissions = map[int64]map[string][]string{1: tc.permissions}
testutil.SetupRBACPermission(t, sql, role, u)
authz := NewAuthService(sql, featuremgmt.WithFeatures(tc.featureToggle))
resources, err := authz.Authorize(context.Background(), 1, u)
require.NoError(t, err)
@@ -118,9 +175,8 @@ func TestIntegrationAuthorize(t *testing.T) {
require.Equal(t, tc.expectedResources.Dashboards, resources.Dashboards)
}
if tc.expectedResources.ScopeTypes != nil {
require.Equal(t, tc.expectedResources.ScopeTypes, resources.ScopeTypes)
}
require.Equal(t, tc.expectedResources.CanAccessDashAnnotations, resources.CanAccessDashAnnotations)
require.Equal(t, tc.expectedResources.CanAccessOrgAnnotations, resources.CanAccessOrgAnnotations)
if tc.expectedErr != nil {
require.Equal(t, tc.expectedErr, err)

View File

@@ -4,8 +4,10 @@ package accesscontrol
type AccessResources struct {
// Dashboards is a map of dashboard UIDs to IDs
Dashboards map[string]int64
// ScopeTypes contains the scope types that the user has access to. At most `dashboard` and `organization`
ScopeTypes map[any]struct{}
// CanAccessDashAnnotations true if the user is allowed to access some dashboard annotations
CanAccessDashAnnotations bool
// CanAccessOrgAnnotations true if the user is allowed to access organization annotations
CanAccessOrgAnnotations bool
}
type dashboardProjection struct {