Files
grafana/pkg/services/accesscontrol/acimpl/accesscontrol_test.go
Ieva 105313f5c2 RBAC: Adding action set resolver for RBAC evaluation (#86801)
* add action set resolver

* rename variables

* some fixes and some tests

* more tests

* more tests, and put action set storing behind a feature toggle

* undo change from cfg to feature mgmt - will cover it in a separate PR due to the amount of test changes

* fix dependency cycle, update some tests

* add one more test

* fix for feature toggle check not being set on test configs

* linting fixes

* check that action set name can be split nicely

* clean up tests by turning GetActionSetNames into a function

* undo accidental change

* test fix

* more test fixes
2024-05-09 10:18:03 +01:00

197 lines
7.3 KiB
Go

package acimpl_test
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
)
func TestAccessControl_Evaluate(t *testing.T) {
type testCase struct {
desc string
user user.SignedInUser
evaluator accesscontrol.Evaluator
resolverPrefix string
expected bool
expectedErr error
scopeResolver accesscontrol.ScopeAttributeResolver
actionSets map[string][]string
}
tests := []testCase{
{
desc: "expect user to have access when correct permission is stored on user",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {accesscontrol.ActionTeamsWrite: {"teams:*"}},
},
},
evaluator: accesscontrol.EvalPermission(accesscontrol.ActionTeamsWrite, "teams:id:1"),
expected: true,
},
{
desc: "expect user to not have access without required permissions",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {accesscontrol.ActionTeamsWrite: {"teams:*"}},
},
},
evaluator: accesscontrol.EvalPermission(accesscontrol.ActionOrgUsersWrite, "users:id:1"),
expected: false,
},
{
desc: "expect user to have access when resolver translate scope",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {accesscontrol.ActionTeamsWrite: {"another:scope"}},
},
},
evaluator: accesscontrol.EvalPermission(accesscontrol.ActionTeamsWrite, "teams:id:1"),
resolverPrefix: "teams:id:",
scopeResolver: accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
return []string{"another:scope"}, nil
}),
expected: true,
},
{
desc: "expect user to have access when resolver translates actions to action sets",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {"folders:edit": {"folders:uid:test_folder"}},
},
},
evaluator: accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, "folders:uid:test_folder"),
actionSets: map[string][]string{
"folders:edit": {dashboards.ActionFoldersWrite, dashboards.ActionFoldersRead, dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsRead},
},
expected: true,
},
{
desc: "expect user to have access when resolver translates scopes, as well as expands actions to action sets",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {"folders:edit": {"folders:uid:test_folder"}},
},
},
evaluator: accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, "dashboards:uid:test_dashboard"),
actionSets: map[string][]string{
"folders:edit": {dashboards.ActionFoldersWrite, dashboards.ActionFoldersRead, dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsRead},
},
resolverPrefix: "dashboards:uid:",
scopeResolver: accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
return []string{"folders:uid:test_folder"}, nil
}),
expected: true,
},
{
desc: "expect user to have access with eval all evaluator when resolver translates scopes, as well as expands actions to action sets",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {"folders:edit": {"folders:uid:test_folder"}},
},
},
evaluator: accesscontrol.EvalAll(
accesscontrol.EvalPermission(dashboards.ActionFoldersRead, "folders:uid:test_folder"),
accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, "dashboards:uid:test_dashboard"),
),
actionSets: map[string][]string{
"folders:edit": {dashboards.ActionFoldersWrite, dashboards.ActionFoldersRead, dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsRead},
},
resolverPrefix: "dashboards:uid:",
scopeResolver: accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
return []string{"folders:uid:test_folder"}, nil
}),
expected: true,
},
{
desc: "expect user to not have access with eval all evaluator with resolvers when not all permissions resolve to permissions that the user has",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {"folders:edit": {"folders:uid:test_folder"}},
},
},
evaluator: accesscontrol.EvalAll(
accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, "datasources:uid:test_ds"),
accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, "dashboards:uid:test_dashboard"),
),
actionSets: map[string][]string{
"folders:edit": {dashboards.ActionFoldersWrite, dashboards.ActionFoldersRead, dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsRead},
},
resolverPrefix: "dashboards:uid:",
scopeResolver: accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
return []string{"folders:uid:test_folder"}, nil
}),
expected: false,
},
{
desc: "expect user to have access with eval any evaluator when resolver translates scopes, as well as expands actions to action sets",
user: user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {"folders:edit": {"folders:uid:test_folder"}},
},
},
evaluator: accesscontrol.EvalAny(
accesscontrol.EvalPermission(dashboards.ActionDashboardsDelete, "dashboards:uid:test_dashboard"),
accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, "dashboards:uid:test_dashboard"),
),
actionSets: map[string][]string{
"folders:edit": {dashboards.ActionFoldersWrite, dashboards.ActionFoldersRead, dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsRead},
},
resolverPrefix: "dashboards:uid:",
scopeResolver: accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
return []string{"folders:uid:test_folder"}, nil
}),
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
cfg := setting.NewCfg()
cfg.IsFeatureToggleEnabled = func(ft string) bool {
return ft == featuremgmt.FlagAccessActionSets
}
ac := acimpl.ProvideAccessControl(cfg)
if tt.scopeResolver != nil {
ac.RegisterScopeAttributeResolver(tt.resolverPrefix, tt.scopeResolver)
}
if tt.actionSets != nil {
actionSetResolver := resourcepermissions.NewActionSetService(ac)
for actionSet, actions := range tt.actionSets {
splitActionSet := strings.Split(actionSet, ":")
actionSetResolver.StoreActionSet(splitActionSet[0], splitActionSet[1], actions)
}
ac.RegisterActionResolver(actionSetResolver)
}
hasAccess, err := ac.Evaluate(context.Background(), &tt.user, tt.evaluator)
assert.Equal(t, tt.expected, hasAccess)
if tt.expectedErr != nil {
assert.Equal(t, tt.expectedErr, err)
} else {
assert.NoError(t, err)
}
})
}
}