mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 02:10:45 -06:00
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
This commit is contained in:
parent
6380a01543
commit
105313f5c2
@ -461,11 +461,12 @@ func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureTog
|
||||
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, sc.db, features, supportbundlestest.NewFakeBundleService(), nil)
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
actionSets := resourcepermissions.NewActionSetService(ac)
|
||||
folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions(
|
||||
cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, resourcepermissions.NewActionSetService())
|
||||
cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets)
|
||||
require.NoError(b, err)
|
||||
dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions(
|
||||
cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, resourcepermissions.NewActionSetService())
|
||||
cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets)
|
||||
require.NoError(b, err)
|
||||
|
||||
dashboardSvc, err := dashboardservice.ProvideDashboardServiceImpl(
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -48,6 +49,11 @@ func (a *AccessControl) Evaluate(ctx context.Context, user identity.Requester, e
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// TODO update this to use featuremgmt.FeatureToggles instead of checking the config
|
||||
if a.cfg != nil && a.cfg.IsFeatureToggleEnabled != nil && a.cfg.IsFeatureToggleEnabled(featuremgmt.FlagAccessActionSets) {
|
||||
evaluator = evaluator.AppendActionSets(ctx, a.resolvers.GetActionSetResolver())
|
||||
}
|
||||
|
||||
a.debug(ctx, user, "Evaluating permissions", evaluator)
|
||||
// Test evaluation without scope resolver first, this will prevent 403 for wildcard scopes when resource does not exist
|
||||
if evaluator.Evaluate(permissions) {
|
||||
@ -70,6 +76,10 @@ func (a *AccessControl) RegisterScopeAttributeResolver(prefix string, resolver a
|
||||
a.resolvers.AddScopeAttributeResolver(prefix, resolver)
|
||||
}
|
||||
|
||||
func (a *AccessControl) RegisterActionResolver(resolver accesscontrol.ActionResolver) {
|
||||
a.resolvers.SetActionResolver(resolver)
|
||||
}
|
||||
|
||||
func (a *AccessControl) debug(ctx context.Context, ident identity.Requester, msg string, eval accesscontrol.Evaluator) {
|
||||
namespace, id := ident.GetNamespacedID()
|
||||
a.log.FromContext(ctx).Debug(msg, "namespace", namespace, "id", id, "orgID", ident.GetOrgID(), "permissions", eval.GoString())
|
||||
|
@ -1,12 +1,17 @@
|
||||
package acimpl
|
||||
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"
|
||||
)
|
||||
@ -19,7 +24,8 @@ func TestAccessControl_Evaluate(t *testing.T) {
|
||||
resolverPrefix string
|
||||
expected bool
|
||||
expectedErr error
|
||||
resolver accesscontrol.ScopeAttributeResolver
|
||||
scopeResolver accesscontrol.ScopeAttributeResolver
|
||||
actionSets map[string][]string
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
@ -55,19 +61,127 @@ func TestAccessControl_Evaluate(t *testing.T) {
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission(accesscontrol.ActionTeamsWrite, "teams:id:1"),
|
||||
resolverPrefix: "teams:id:",
|
||||
resolver: accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
|
||||
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) {
|
||||
ac := ProvideAccessControl(setting.NewCfg())
|
||||
cfg := setting.NewCfg()
|
||||
cfg.IsFeatureToggleEnabled = func(ft string) bool {
|
||||
return ft == featuremgmt.FlagAccessActionSets
|
||||
}
|
||||
ac := acimpl.ProvideAccessControl(cfg)
|
||||
|
||||
if tt.resolver != nil {
|
||||
ac.RegisterScopeAttributeResolver(tt.resolverPrefix, tt.resolver)
|
||||
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)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package database
|
||||
package database_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
||||
rs "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||
@ -20,6 +21,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
||||
@ -91,9 +93,9 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
store, permissionStore, usrSvc, teamSvc, _ := setupTestEnv(t)
|
||||
store, permissionStore, usrSvc, teamSvc, _, sql := setupTestEnv(t)
|
||||
|
||||
user, team := createUserAndTeam(t, store.sql, usrSvc, teamSvc, tt.orgID)
|
||||
user, team := createUserAndTeam(t, sql, usrSvc, teamSvc, tt.orgID)
|
||||
|
||||
for _, id := range tt.userPermissions {
|
||||
_, err := permissionStore.SetUserResourcePermission(context.Background(), tt.orgID, accesscontrol.User{ID: user.ID}, rs.SetResourcePermissionCommand{
|
||||
@ -148,7 +150,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, permissions, tt.expected)
|
||||
|
||||
policies, err := GetAccessPolicies(context.Background(), user.OrgID, store.sql.GetSqlxSession(),
|
||||
policies, err := database.GetAccessPolicies(context.Background(), user.OrgID, sql.GetSqlxSession(),
|
||||
func(ctx context.Context, orgID int64, scope string) ([]string, error) {
|
||||
return strings.Split(scope, ":"), nil
|
||||
})
|
||||
@ -195,7 +197,7 @@ func TestAccessControlStore_GetTeamsPermissions(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
store, permissionStore, _, teamSvc, _ := setupTestEnv(t)
|
||||
store, permissionStore, _, teamSvc, _, _ := setupTestEnv(t)
|
||||
|
||||
teams := make([]team.Team, 0)
|
||||
for i := 0; i < len(tt.teamsPermissions); i++ {
|
||||
@ -240,8 +242,8 @@ func TestAccessControlStore_GetTeamsPermissions(t *testing.T) {
|
||||
|
||||
func TestAccessControlStore_DeleteUserPermissions(t *testing.T) {
|
||||
t.Run("expect permissions in all orgs to be deleted", func(t *testing.T) {
|
||||
store, permissionsStore, usrSvc, teamSvc, _ := setupTestEnv(t)
|
||||
user, _ := createUserAndTeam(t, store.sql, usrSvc, teamSvc, 1)
|
||||
store, permissionsStore, usrSvc, teamSvc, _, sql := setupTestEnv(t)
|
||||
user, _ := createUserAndTeam(t, sql, usrSvc, teamSvc, 1)
|
||||
|
||||
// generate permissions in org 1
|
||||
_, err := permissionsStore.SetUserResourcePermission(context.Background(), 1, accesscontrol.User{ID: user.ID}, rs.SetResourcePermissionCommand{
|
||||
@ -280,8 +282,8 @@ func TestAccessControlStore_DeleteUserPermissions(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("expect permissions in org 1 to be deleted", func(t *testing.T) {
|
||||
store, permissionsStore, usrSvc, teamSvc, _ := setupTestEnv(t)
|
||||
user, _ := createUserAndTeam(t, store.sql, usrSvc, teamSvc, 1)
|
||||
store, permissionsStore, usrSvc, teamSvc, _, sql := setupTestEnv(t)
|
||||
user, _ := createUserAndTeam(t, sql, usrSvc, teamSvc, 1)
|
||||
|
||||
// generate permissions in org 1
|
||||
_, err := permissionsStore.SetUserResourcePermission(context.Background(), 1, accesscontrol.User{ID: user.ID}, rs.SetResourcePermissionCommand{
|
||||
@ -322,8 +324,8 @@ func TestAccessControlStore_DeleteUserPermissions(t *testing.T) {
|
||||
|
||||
func TestAccessControlStore_DeleteTeamPermissions(t *testing.T) {
|
||||
t.Run("expect permissions related to team to be deleted", func(t *testing.T) {
|
||||
store, permissionsStore, usrSvc, teamSvc, _ := setupTestEnv(t)
|
||||
user, team := createUserAndTeam(t, store.sql, usrSvc, teamSvc, 1)
|
||||
store, permissionsStore, usrSvc, teamSvc, _, sql := setupTestEnv(t)
|
||||
user, team := createUserAndTeam(t, sql, usrSvc, teamSvc, 1)
|
||||
|
||||
// grant permission to the team
|
||||
_, err := permissionsStore.SetTeamResourcePermission(context.Background(), 1, team.ID, rs.SetResourcePermissionCommand{
|
||||
@ -356,8 +358,8 @@ func TestAccessControlStore_DeleteTeamPermissions(t *testing.T) {
|
||||
assert.Len(t, permissions, 0)
|
||||
})
|
||||
t.Run("expect permissions not related to team to be kept", func(t *testing.T) {
|
||||
store, permissionsStore, usrSvc, teamSvc, _ := setupTestEnv(t)
|
||||
user, team := createUserAndTeam(t, store.sql, usrSvc, teamSvc, 1)
|
||||
store, permissionsStore, usrSvc, teamSvc, _, sql := setupTestEnv(t)
|
||||
user, team := createUserAndTeam(t, sql, usrSvc, teamSvc, 1)
|
||||
|
||||
// grant permission to the team
|
||||
_, err := permissionsStore.SetTeamResourcePermission(context.Background(), 1, team.ID, rs.SetResourcePermissionCommand{
|
||||
@ -468,14 +470,13 @@ func createUsersAndTeams(t *testing.T, store db.DB, svcs helperServices, orgID i
|
||||
return res
|
||||
}
|
||||
|
||||
func setupTestEnv(t testing.TB) (*AccessControlStore, rs.Store, user.Service, team.Service, org.Service) {
|
||||
func setupTestEnv(t testing.TB) (*database.AccessControlStore, rs.Store, user.Service, team.Service, org.Service, *sqlstore.SQLStore) {
|
||||
sql, cfg := db.InitTestDBWithCfg(t)
|
||||
cfg.AutoAssignOrg = true
|
||||
cfg.AutoAssignOrgRole = "Viewer"
|
||||
cfg.AutoAssignOrgId = 1
|
||||
acstore := ProvideService(sql)
|
||||
asService := rs.NewActionSetService()
|
||||
permissionStore := rs.NewStore(sql, featuremgmt.WithFeatures(), &asService)
|
||||
acstore := database.ProvideService(sql)
|
||||
permissionStore := rs.NewStore(sql, featuremgmt.WithFeatures())
|
||||
teamService, err := teamimpl.ProvideService(sql, cfg, tracing.InitializeTracerForTest())
|
||||
require.NoError(t, err)
|
||||
orgService, err := orgimpl.ProvideService(sql, cfg, quotatest.New(false, nil))
|
||||
@ -490,7 +491,7 @@ func setupTestEnv(t testing.TB) (*AccessControlStore, rs.Store, user.Service, te
|
||||
quotatest.New(false, nil), supportbundlestest.NewFakeBundleService(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return acstore, permissionStore, userService, teamService, orgService
|
||||
return acstore, permissionStore, userService, teamService, orgService, sql
|
||||
}
|
||||
|
||||
func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) {
|
||||
@ -735,8 +736,8 @@ func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
acStore, permissionsStore, userSvc, teamSvc, orgSvc := setupTestEnv(t)
|
||||
dbUsers := createUsersAndTeams(t, acStore.sql, helperServices{userSvc, teamSvc, orgSvc}, 1, tt.users)
|
||||
acStore, permissionsStore, userSvc, teamSvc, orgSvc, sql := setupTestEnv(t)
|
||||
dbUsers := createUsersAndTeams(t, sql, helperServices{userSvc, teamSvc, orgSvc}, 1, tt.users)
|
||||
|
||||
// Switch userID and TeamID by the real stored ones
|
||||
for i := range tt.permCmds {
|
||||
@ -815,8 +816,8 @@ func TestAccessControlStore_GetUsersBasicRoles(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
acStore, _, userSvc, teamSvc, orgSvc := setupTestEnv(t)
|
||||
dbUsers := createUsersAndTeams(t, acStore.sql, helperServices{userSvc, teamSvc, orgSvc}, 1, tt.users)
|
||||
acStore, _, userSvc, teamSvc, orgSvc, sql := setupTestEnv(t)
|
||||
dbUsers := createUsersAndTeams(t, sql, helperServices{userSvc, teamSvc, orgSvc}, 1, tt.users)
|
||||
|
||||
// Test
|
||||
dbRoles, err := acStore.GetUsersBasicRoles(ctx, tt.userFilter, 1)
|
||||
|
@ -16,6 +16,9 @@ type Evaluator interface {
|
||||
Evaluate(permissions map[string][]string) bool
|
||||
// MutateScopes executes a sequence of ScopeModifier functions on all embedded scopes of an evaluator and returns a new Evaluator
|
||||
MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error)
|
||||
// AppendActionSets extends the evaluator with relevant action sets
|
||||
// (e.g. evaluator checking `folders:write` is extended to check for any of `folders:write`, `folders:edit`, `folders:admin`)
|
||||
AppendActionSets(ctx context.Context, mutate ActionSetResolver) Evaluator
|
||||
// String returns a string representation of permission required by the evaluator
|
||||
fmt.Stringer
|
||||
fmt.GoStringer
|
||||
@ -107,6 +110,17 @@ func (p permissionEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttri
|
||||
return EvalPermission(p.Action, scopes...), nil
|
||||
}
|
||||
|
||||
func (p permissionEvaluator) AppendActionSets(ctx context.Context, resolve ActionSetResolver) Evaluator {
|
||||
resolvedActions := resolve(ctx, p.Action)
|
||||
|
||||
evals := make([]Evaluator, 0, len(resolvedActions))
|
||||
for _, action := range resolvedActions {
|
||||
evals = append(evals, EvalPermission(action, p.Scopes...))
|
||||
}
|
||||
|
||||
return EvalAny(evals...)
|
||||
}
|
||||
|
||||
func (p permissionEvaluator) String() string {
|
||||
return p.Action
|
||||
}
|
||||
@ -157,6 +171,16 @@ func (a allEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMut
|
||||
return EvalAll(modified...), nil
|
||||
}
|
||||
|
||||
func (a allEvaluator) AppendActionSets(ctx context.Context, resolve ActionSetResolver) Evaluator {
|
||||
evals := make([]Evaluator, 0, len(a.allOf))
|
||||
for _, e := range a.allOf {
|
||||
resolvedSets := e.AppendActionSets(ctx, resolve)
|
||||
evals = append(evals, resolvedSets)
|
||||
}
|
||||
|
||||
return EvalAll(evals...)
|
||||
}
|
||||
|
||||
func (a allEvaluator) String() string {
|
||||
permissions := make([]string, 0, len(a.allOf))
|
||||
for _, e := range a.allOf {
|
||||
@ -218,6 +242,16 @@ func (a anyEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMut
|
||||
return EvalAny(modified...), nil
|
||||
}
|
||||
|
||||
func (a anyEvaluator) AppendActionSets(ctx context.Context, resolve ActionSetResolver) Evaluator {
|
||||
evals := make([]Evaluator, 0, len(a.anyOf))
|
||||
for _, e := range a.anyOf {
|
||||
resolvedSets := e.AppendActionSets(ctx, resolve)
|
||||
evals = append(evals, resolvedSets)
|
||||
}
|
||||
|
||||
return EvalAny(evals...)
|
||||
}
|
||||
|
||||
func (a anyEvaluator) String() string {
|
||||
permissions := make([]string, 0, len(a.anyOf))
|
||||
for _, e := range a.anyOf {
|
||||
|
@ -288,9 +288,9 @@ var DatasourceQueryActions = []string{
|
||||
datasources.ActionQuery,
|
||||
}
|
||||
|
||||
func ProvideDatasourcePermissionsService(features featuremgmt.FeatureToggles, db db.DB, actionSetService resourcepermissions.ActionSetService) *DatasourcePermissionsService {
|
||||
func ProvideDatasourcePermissionsService(features featuremgmt.FeatureToggles, db db.DB) *DatasourcePermissionsService {
|
||||
return &DatasourcePermissionsService{
|
||||
store: resourcepermissions.NewStore(db, features, &actionSetService),
|
||||
store: resourcepermissions.NewStore(db, features),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,10 @@ type ScopeAttributeResolver interface {
|
||||
Resolve(ctx context.Context, orgID int64, scope string) ([]string, error)
|
||||
}
|
||||
|
||||
type ActionResolver interface {
|
||||
Resolve(action string) []string
|
||||
}
|
||||
|
||||
// ScopeAttributeResolverFunc is an adapter to allow functions to implement ScopeAttributeResolver interface
|
||||
type ScopeAttributeResolverFunc func(ctx context.Context, orgID int64, scope string) ([]string, error)
|
||||
|
||||
@ -24,6 +28,8 @@ func (f ScopeAttributeResolverFunc) Resolve(ctx context.Context, orgID int64, sc
|
||||
|
||||
type ScopeAttributeMutator func(context.Context, string) ([]string, error)
|
||||
|
||||
type ActionSetResolver func(context.Context, string) []string
|
||||
|
||||
const (
|
||||
ttl = 30 * time.Second
|
||||
cleanInterval = 2 * time.Minute
|
||||
@ -41,6 +47,7 @@ type Resolvers struct {
|
||||
log log.Logger
|
||||
cache *localcache.CacheService
|
||||
attributeResolvers map[string]ScopeAttributeResolver
|
||||
actionResolver ActionResolver
|
||||
}
|
||||
|
||||
func (s *Resolvers) AddScopeAttributeResolver(prefix string, resolver ScopeAttributeResolver) {
|
||||
@ -48,6 +55,10 @@ func (s *Resolvers) AddScopeAttributeResolver(prefix string, resolver ScopeAttri
|
||||
s.attributeResolvers[prefix] = resolver
|
||||
}
|
||||
|
||||
func (s *Resolvers) SetActionResolver(resolver ActionResolver) {
|
||||
s.actionResolver = resolver
|
||||
}
|
||||
|
||||
func (s *Resolvers) GetScopeAttributeMutator(orgID int64) ScopeAttributeMutator {
|
||||
return func(ctx context.Context, scope string) ([]string, error) {
|
||||
key := getScopeCacheKey(orgID, scope)
|
||||
@ -77,3 +88,15 @@ func (s *Resolvers) GetScopeAttributeMutator(orgID int64) ScopeAttributeMutator
|
||||
func getScopeCacheKey(orgID int64, scope string) string {
|
||||
return fmt.Sprintf("%s-%v", scope, orgID)
|
||||
}
|
||||
|
||||
func (s *Resolvers) GetActionSetResolver() ActionSetResolver {
|
||||
return func(ctx context.Context, action string) []string {
|
||||
if s.actionResolver == nil {
|
||||
return []string{action}
|
||||
}
|
||||
actionSetActions := s.actionResolver.Resolve(action)
|
||||
actions := append(actionSetActions, action)
|
||||
s.log.Debug("Resolved action", "action", action, "resolved_actions", actions)
|
||||
return actions
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,9 @@ func New(cfg *setting.Cfg,
|
||||
for _, a := range actions {
|
||||
actionSet[a] = struct{}{}
|
||||
}
|
||||
actionSetService.StoreActionSet(options.Resource, permission, actions)
|
||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAccessActionSets) {
|
||||
actionSetService.StoreActionSet(options.Resource, permission, actions)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all permissions based on action length. Will be used when mapping between actions to permissions
|
||||
@ -81,7 +83,7 @@ func New(cfg *setting.Cfg,
|
||||
|
||||
s := &Service{
|
||||
ac: ac,
|
||||
store: NewStore(sqlStore, features, &actionSetService),
|
||||
store: NewStore(sqlStore, features),
|
||||
options: options,
|
||||
license: license,
|
||||
permissions: permissions,
|
||||
|
@ -224,6 +224,102 @@ func TestService_SetPermissions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_RegisterActionSets(t *testing.T) {
|
||||
type registerActionSetsTest struct {
|
||||
desc string
|
||||
actionSetsEnabled bool
|
||||
options Options
|
||||
expectedActionSets []ActionSet
|
||||
}
|
||||
|
||||
tests := []registerActionSetsTest{
|
||||
{
|
||||
desc: "should register folder action sets if action sets are enabled",
|
||||
actionSetsEnabled: true,
|
||||
options: Options{
|
||||
Resource: "folders",
|
||||
PermissionsToActions: map[string][]string{
|
||||
"View": {"folders:read", "dashboards:read"},
|
||||
"Edit": {"folders:read", "dashboards:read", "folders:write", "dashboards:write"},
|
||||
},
|
||||
},
|
||||
expectedActionSets: []ActionSet{
|
||||
{
|
||||
Action: "folders:view",
|
||||
Actions: []string{"folders:read", "dashboards:read"},
|
||||
},
|
||||
{
|
||||
Action: "folders:edit",
|
||||
Actions: []string{"folders:read", "dashboards:read", "folders:write", "dashboards:write"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should register dashboard action set if action sets are enabled",
|
||||
actionSetsEnabled: true,
|
||||
options: Options{
|
||||
Resource: "dashboards",
|
||||
PermissionsToActions: map[string][]string{
|
||||
"View": {"dashboards:read"},
|
||||
},
|
||||
},
|
||||
expectedActionSets: []ActionSet{
|
||||
{
|
||||
Action: "dashboards:view",
|
||||
Actions: []string{"dashboards:read"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should not register dashboard action set if action sets are not enabled",
|
||||
actionSetsEnabled: false,
|
||||
options: Options{
|
||||
Resource: "dashboards",
|
||||
PermissionsToActions: map[string][]string{
|
||||
"View": {"dashboards:read"},
|
||||
},
|
||||
},
|
||||
expectedActionSets: []ActionSet{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.IsFeatureToggleEnabled = func(ft string) bool {
|
||||
if ft == featuremgmt.FlagAccessActionSets {
|
||||
return tt.actionSetsEnabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
ac := acimpl.ProvideAccessControl(cfg)
|
||||
actionSets := NewActionSetService(ac)
|
||||
features := featuremgmt.WithFeatures()
|
||||
if tt.actionSetsEnabled {
|
||||
features = featuremgmt.WithFeatures(featuremgmt.FlagAccessActionSets)
|
||||
}
|
||||
_, err := New(
|
||||
setting.NewCfg(), tt.options, features, routing.NewRouteRegister(), licensingtest.NewFakeLicensing(),
|
||||
ac, &actest.FakeService{}, db.InitTestDB(t), nil, nil, actionSets,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(tt.expectedActionSets) > 0 {
|
||||
for _, expectedActionSet := range tt.expectedActionSets {
|
||||
actionSet := actionSets.GetActionSet(expectedActionSet.Action)
|
||||
assert.ElementsMatch(t, expectedActionSet.Actions, actionSet)
|
||||
}
|
||||
} else {
|
||||
// Check that action sets have not been registered
|
||||
for permission := range tt.options.PermissionsToActions {
|
||||
actionSetName := GetActionSetName(tt.options.Resource, permission)
|
||||
assert.Nil(t, actionSets.GetActionSet(actionSetName))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestEnvironment(t *testing.T, ops Options) (*Service, user.Service, team.Service) {
|
||||
t.Helper()
|
||||
|
||||
@ -245,11 +341,11 @@ func setupTestEnvironment(t *testing.T, ops Options) (*Service, user.Service, te
|
||||
|
||||
license := licensingtest.NewFakeLicensing()
|
||||
license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe()
|
||||
ac := acimpl.ProvideAccessControl(cfg)
|
||||
ac := acimpl.ProvideAccessControl(setting.NewCfg())
|
||||
acService := &actest.FakeService{}
|
||||
service, err := New(
|
||||
cfg, ops, featuremgmt.WithFeatures(), routing.NewRouteRegister(), license,
|
||||
ac, acService, sql, teamSvc, userSvc, NewActionSetService(),
|
||||
ac, acService, sql, teamSvc, userSvc, NewActionSetService(ac),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||
@ -17,14 +18,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func NewStore(sql db.DB, features featuremgmt.FeatureToggles, actionsetService *ActionSetService) *store {
|
||||
return &store{sql, features, *actionsetService}
|
||||
func NewStore(sql db.DB, features featuremgmt.FeatureToggles) *store {
|
||||
store := &store{sql: sql, features: features}
|
||||
return store
|
||||
}
|
||||
|
||||
type store struct {
|
||||
sql db.DB
|
||||
features featuremgmt.FeatureToggles
|
||||
actionSetService ActionSetService
|
||||
sql db.DB
|
||||
features featuremgmt.FeatureToggles
|
||||
}
|
||||
|
||||
type flatResourcePermission struct {
|
||||
@ -665,7 +666,7 @@ func (s *store) createPermissions(sess *db.Session, roleID int64, resource, reso
|
||||
Add ACTION SET of managed permissions to in-memory store
|
||||
*/
|
||||
if s.features.IsEnabled(context.TODO(), featuremgmt.FlagAccessActionSets) && permission != "" {
|
||||
actionSetName := s.actionSetService.GetActionSetName(resource, permission)
|
||||
actionSetName := GetActionSetName(resource, permission)
|
||||
p := managedPermission(actionSetName, resource, resourceID, resourceAttribute)
|
||||
p.RoleID = roleID
|
||||
p.Created = time.Now()
|
||||
@ -735,8 +736,10 @@ Stores actionsets IN MEMORY
|
||||
// An example of an action set is "folders:edit" which represents the set of RBAC actions that are granted by edit access to a folder.
|
||||
|
||||
type ActionSetService interface {
|
||||
accesscontrol.ActionResolver
|
||||
|
||||
GetActionSet(actionName string) []string
|
||||
GetActionSetName(resource, permission string) string
|
||||
//GetActionSetName(resource, permission string) string
|
||||
StoreActionSet(resource, permission string, actions []string)
|
||||
}
|
||||
|
||||
@ -747,21 +750,47 @@ type ActionSet struct {
|
||||
|
||||
// InMemoryActionSets is an in-memory implementation of the ActionSetService.
|
||||
type InMemoryActionSets struct {
|
||||
log log.Logger
|
||||
actionSets map[string][]string
|
||||
log log.Logger
|
||||
actionSetToActions map[string][]string
|
||||
actionToActionSets map[string][]string
|
||||
}
|
||||
|
||||
// NewActionSetService returns a new instance of InMemoryActionSetService.
|
||||
func NewActionSetService() ActionSetService {
|
||||
return &InMemoryActionSets{
|
||||
actionSets: make(map[string][]string),
|
||||
log: log.New("resourcepermissions.actionsets"),
|
||||
func NewActionSetService(a *acimpl.AccessControl) ActionSetService {
|
||||
actionSets := &InMemoryActionSets{
|
||||
log: log.New("resourcepermissions.actionsets"),
|
||||
actionSetToActions: make(map[string][]string),
|
||||
actionToActionSets: make(map[string][]string),
|
||||
}
|
||||
a.RegisterActionResolver(actionSets)
|
||||
return actionSets
|
||||
}
|
||||
|
||||
func (s *InMemoryActionSets) Resolve(action string) []string {
|
||||
actionSets := s.actionToActionSets[action]
|
||||
sets := make([]string, 0, len(actionSets))
|
||||
|
||||
for _, actionSet := range actionSets {
|
||||
setParts := strings.Split(actionSet, ":")
|
||||
if len(setParts) != 2 {
|
||||
s.log.Debug("skipping resolution for action set with invalid name", "action set", actionSet)
|
||||
continue
|
||||
}
|
||||
prefix := setParts[0]
|
||||
// Only use action sets for folders and dashboards for now
|
||||
// We need to verify that action sets for other resources do not share names with actions (eg, `datasources:query`)
|
||||
if prefix != "folders" && prefix != "dashboards" {
|
||||
continue
|
||||
}
|
||||
sets = append(sets, actionSet)
|
||||
}
|
||||
|
||||
return sets
|
||||
}
|
||||
|
||||
// GetActionSet returns the action set for the given action.
|
||||
func (s *InMemoryActionSets) GetActionSet(actionName string) []string {
|
||||
actionSet, ok := s.actionSets[actionName]
|
||||
actionSet, ok := s.actionSetToActions[actionName]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@ -769,17 +798,24 @@ func (s *InMemoryActionSets) GetActionSet(actionName string) []string {
|
||||
}
|
||||
|
||||
func (s *InMemoryActionSets) StoreActionSet(resource, permission string, actions []string) {
|
||||
s.log.Debug("storing action set\n")
|
||||
name := s.GetActionSetName(resource, permission)
|
||||
name := GetActionSetName(resource, permission)
|
||||
actionSet := &ActionSet{
|
||||
Action: name,
|
||||
Actions: actions,
|
||||
}
|
||||
s.actionSets[actionSet.Action] = actions
|
||||
s.actionSetToActions[actionSet.Action] = actions
|
||||
|
||||
for _, action := range actions {
|
||||
if _, ok := s.actionToActionSets[action]; !ok {
|
||||
s.actionToActionSets[action] = []string{}
|
||||
}
|
||||
s.actionToActionSets[action] = append(s.actionToActionSets[action], actionSet.Action)
|
||||
}
|
||||
s.log.Debug("stored action set", "action set name", actionSet.Action)
|
||||
}
|
||||
|
||||
// GetActionSetName function creates an action set from a list of actions and stores it inmemory.
|
||||
func (s *InMemoryActionSets) GetActionSetName(resource, permission string) string {
|
||||
func GetActionSetName(resource, permission string) string {
|
||||
// lower cased
|
||||
resource = strings.ToLower(resource)
|
||||
permission = strings.ToLower(permission)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@ -563,8 +564,7 @@ func seedResourcePermissions(
|
||||
|
||||
func setupTestEnv(t testing.TB) (*store, db.DB, *setting.Cfg) {
|
||||
sql, cfg := db.InitTestDBWithCfg(t)
|
||||
asService := NewActionSetService()
|
||||
return NewStore(sql, featuremgmt.WithFeatures(), &asService), sql, cfg
|
||||
return NewStore(sql, featuremgmt.WithFeatures()), sql, cfg
|
||||
}
|
||||
|
||||
func TestStore_IsInherited(t *testing.T) {
|
||||
@ -759,49 +759,85 @@ func retrievePermissionsHelper(store *store, t *testing.T) []orgPermission {
|
||||
return permissions
|
||||
}
|
||||
|
||||
func TestStore_ResourcePermissionsActionSets(t *testing.T) {
|
||||
func TestStore_StoreActionSet(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
type actionSetTest struct {
|
||||
desc string
|
||||
orgID int64
|
||||
actionSet ActionSet
|
||||
desc string
|
||||
resource string
|
||||
action string
|
||||
actions []string
|
||||
}
|
||||
|
||||
tests := []actionSetTest{
|
||||
{
|
||||
desc: "should be able to store actionset",
|
||||
orgID: 1,
|
||||
actionSet: ActionSet{
|
||||
Actions: []string{"folders:read", "folders:write"},
|
||||
},
|
||||
desc: "should be able to store action set",
|
||||
resource: "folders",
|
||||
action: "edit",
|
||||
actions: []string{"folders:read", "folders:write"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
store, _, _ := setupTestEnv(t)
|
||||
store.features = featuremgmt.WithFeatures([]any{featuremgmt.FlagAccessActionSets})
|
||||
store.features = featuremgmt.WithFeatures(featuremgmt.FlagAccessActionSets)
|
||||
ac := acimpl.ProvideAccessControl(setting.NewCfg())
|
||||
asService := NewActionSetService(ac)
|
||||
asService.StoreActionSet(tt.resource, tt.action, tt.actions)
|
||||
|
||||
_, err := store.SetResourcePermissions(context.Background(), 1, []SetResourcePermissionsCommand{
|
||||
{
|
||||
User: accesscontrol.User{ID: 1},
|
||||
SetResourcePermissionCommand: SetResourcePermissionCommand{
|
||||
Actions: tt.actionSet.Actions,
|
||||
Resource: "folders",
|
||||
Permission: "edit",
|
||||
ResourceID: "1",
|
||||
ResourceAttribute: "uid",
|
||||
},
|
||||
},
|
||||
}, ResourceHooks{})
|
||||
require.NoError(t, err)
|
||||
|
||||
actionname := fmt.Sprintf("%s:%s", "folders", "edit")
|
||||
actionSet := store.actionSetService.GetActionSet(actionname)
|
||||
require.Equal(t, tt.actionSet.Actions, actionSet)
|
||||
actionSetName := GetActionSetName(tt.resource, tt.action)
|
||||
actionSet := asService.GetActionSet(actionSetName)
|
||||
require.Equal(t, tt.actions, actionSet)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_ResolveActionSet(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
actionSetService := NewActionSetService(acimpl.ProvideAccessControl(setting.NewCfg()))
|
||||
actionSetService.StoreActionSet("folders", "edit", []string{"folders:read", "folders:write", "dashboards:read", "dashboards:write"})
|
||||
actionSetService.StoreActionSet("folders", "view", []string{"folders:read", "dashboards:read"})
|
||||
actionSetService.StoreActionSet("dashboards", "view", []string{"dashboards:read"})
|
||||
|
||||
type actionSetTest struct {
|
||||
desc string
|
||||
action string
|
||||
expectedActionSets []string
|
||||
}
|
||||
|
||||
tests := []actionSetTest{
|
||||
{
|
||||
desc: "should return empty list for an action that is not a part of any action sets",
|
||||
action: "datasources:query",
|
||||
expectedActionSets: []string{},
|
||||
},
|
||||
{
|
||||
desc: "should be able to resolve one action set for the resource of the same type",
|
||||
action: "folders:write",
|
||||
expectedActionSets: []string{"folders:edit"},
|
||||
},
|
||||
{
|
||||
desc: "should be able to resolve multiple action sets for the resource of the same type",
|
||||
action: "folders:read",
|
||||
expectedActionSets: []string{"folders:view", "folders:edit"},
|
||||
},
|
||||
{
|
||||
desc: "should be able to resolve multiple action sets for the resource of a different type",
|
||||
action: "dashboards:read",
|
||||
expectedActionSets: []string{"folders:view", "folders:edit", "dashboards:view"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
actionSets := actionSetService.Resolve(tt.action)
|
||||
require.ElementsMatch(t, tt.expectedActionSets, actionSets)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -306,8 +306,7 @@ func TestIntegrationSilenceAuth(t *testing.T) {
|
||||
apiClient := newAlertingApiClient(grafanaListedAddr, randomLogin, randomLogin)
|
||||
|
||||
// Set permissions.
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
for _, cmd := range tt.permissions {
|
||||
_, err := permissionsStore.SetUserResourcePermission(
|
||||
context.Background(),
|
||||
|
@ -107,9 +107,8 @@ func TestBacktesting(t *testing.T) {
|
||||
require.Equalf(t, http.StatusForbidden, status, "Response: %s", body)
|
||||
})
|
||||
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
// access control permissions store
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
_, err := permissionsStore.SetUserResourcePermission(context.Background(),
|
||||
accesscontrol.GlobalOrgID,
|
||||
accesscontrol.User{ID: testUserId},
|
||||
|
@ -674,9 +674,8 @@ func TestIntegrationPrometheusRulesPermissions(t *testing.T) {
|
||||
|
||||
apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
||||
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
// access control permissions store
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
apiClient.CreateFolder(t, "folder1", "folder1")
|
||||
|
@ -52,8 +52,7 @@ func TestIntegrationAlertRulePermissions(t *testing.T) {
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
userID := createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
@ -337,8 +336,7 @@ func TestIntegrationAlertRuleNestedPermissions(t *testing.T) {
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
userID := createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
@ -734,8 +732,7 @@ func TestAlertRulePostExport(t *testing.T) {
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
userID := createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
@ -1415,8 +1412,7 @@ func TestIntegrationRuleUpdate(t *testing.T) {
|
||||
AppModeProduction: true,
|
||||
})
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
userID := createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
|
@ -275,8 +275,7 @@ func TestGrafanaRuleConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
// access control permissions store
|
||||
asService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures(), &asService)
|
||||
permissionsStore := resourcepermissions.NewStore(env.SQLStore, featuremgmt.WithFeatures())
|
||||
_, err := permissionsStore.SetUserResourcePermission(context.Background(),
|
||||
accesscontrol.GlobalOrgID,
|
||||
accesscontrol.User{ID: testUserId},
|
||||
|
@ -65,8 +65,7 @@ func TestGetFolders(t *testing.T) {
|
||||
viewerClient := tests.GetClient(grafanaListedAddr, "viewer", "viewer")
|
||||
|
||||
// access control permissions store
|
||||
actionSetService := resourcepermissions.NewActionSetService()
|
||||
permissionsStore := resourcepermissions.NewStore(store, featuremgmt.WithFeatures(), &actionSetService)
|
||||
permissionsStore := resourcepermissions.NewStore(store, featuremgmt.WithFeatures())
|
||||
|
||||
numberOfFolders := 5
|
||||
indexWithoutPermission := 3
|
||||
|
Loading…
Reference in New Issue
Block a user