mirror of https://github.com/grafana/grafana
prometheushacktoberfestmetricsmonitoringalertinggrafanagoinfluxdbmysqlpostgresanalyticsdata-visualizationdashboardbusiness-intelligenceelasticsearch
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
974 lines
31 KiB
974 lines
31 KiB
package acimpl
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
"github.com/grafana/grafana/pkg/plugins"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/permreg"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/licensing"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/tests/testsuite"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
testsuite.Run(m)
|
|
}
|
|
|
|
func setupTestEnv(t testing.TB) *Service {
|
|
t.Helper()
|
|
cfg := setting.NewCfg()
|
|
|
|
ac := &Service{
|
|
cache: localcache.ProvideService(),
|
|
cfg: cfg,
|
|
features: featuremgmt.WithFeatures(),
|
|
log: log.New("accesscontrol"),
|
|
registrations: accesscontrol.RegistrationList{},
|
|
roles: accesscontrol.BuildBasicRoleDefinitions(),
|
|
store: database.ProvideService(db.InitTestDB(t)),
|
|
permRegistry: permreg.ProvidePermissionRegistry(),
|
|
}
|
|
require.NoError(t, ac.RegisterFixedRoles(context.Background()))
|
|
return ac
|
|
}
|
|
|
|
func TestUsageMetrics(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expectedValue int
|
|
}{
|
|
{
|
|
name: "Expecting metric with value 1",
|
|
expectedValue: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
|
|
s := ProvideOSSService(
|
|
cfg,
|
|
database.ProvideService(db.InitTestDB(t)),
|
|
&resourcepermissions.FakeActionSetSvc{},
|
|
localcache.ProvideService(),
|
|
featuremgmt.WithFeatures(),
|
|
tracing.InitializeTracerForTest(),
|
|
nil,
|
|
nil,
|
|
permreg.ProvidePermissionRegistry(),
|
|
nil,
|
|
nil,
|
|
)
|
|
assert.Equal(t, tt.expectedValue, s.GetUsageStats(context.Background())["stats.oss.accesscontrol.enabled.count"])
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_DeclareFixedRoles(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
registrations []accesscontrol.RoleRegistration
|
|
wantErr bool
|
|
err error
|
|
}{
|
|
{
|
|
name: "should work with empty list",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should add registration",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{Name: "fixed:test:test"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should fail registration invalid role name",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{Name: "custom:test:test"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
err: accesscontrol.ErrFixedRolePrefixMissing,
|
|
},
|
|
{
|
|
name: "should fail registration invalid basic role assignment",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{Name: "fixed:test:test"},
|
|
Grants: []string{"WrongAdmin"},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
err: accesscontrol.ErrInvalidBuiltinRole.Build(accesscontrol.ErrInvalidBuiltinRoleData("WrongAdmin")),
|
|
},
|
|
{
|
|
name: "should add multiple registrations at once",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{Name: "fixed:test:test"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
{
|
|
Role: accesscontrol.RoleDTO{Name: "fixed:test2:test2"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ac := setupTestEnv(t)
|
|
|
|
// Reset the registations
|
|
ac.registrations = accesscontrol.RegistrationList{}
|
|
|
|
// Test
|
|
err := ac.DeclareFixedRoles(tt.registrations...)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
assert.ErrorIs(t, err, tt.err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
registrationCnt := 0
|
|
ac.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
|
|
registrationCnt++
|
|
return true
|
|
})
|
|
assert.Equal(t, len(tt.registrations), registrationCnt,
|
|
"expected service registration list to contain all test registrations")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_DeclarePluginRoles(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pluginID string
|
|
registrations []plugins.RoleRegistration
|
|
wantErr bool
|
|
err error
|
|
}{
|
|
{
|
|
name: "should work with empty list",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should add registration",
|
|
pluginID: "test-app",
|
|
registrations: []plugins.RoleRegistration{
|
|
{
|
|
Role: plugins.Role{Name: "Tester"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should add registration with valid permissions",
|
|
pluginID: "test-app",
|
|
registrations: []plugins.RoleRegistration{
|
|
{
|
|
Role: plugins.Role{
|
|
Name: "Tester",
|
|
Permissions: []plugins.Permission{
|
|
{Action: "plugins.app:access", Scope: "plugins:id:test-app"},
|
|
{Action: "test-app:read"},
|
|
{Action: "test-app.resource:read"},
|
|
},
|
|
},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should fail registration invalid permission action",
|
|
pluginID: "test-app",
|
|
registrations: []plugins.RoleRegistration{
|
|
{
|
|
Role: plugins.Role{
|
|
Name: "Tester",
|
|
Permissions: []plugins.Permission{
|
|
{Action: "invalid.test-app.resource:read"},
|
|
},
|
|
},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
err: &accesscontrol.ErrorInvalidRole{},
|
|
},
|
|
{
|
|
name: "should fail registration invalid basic role assignment",
|
|
pluginID: "test-app",
|
|
registrations: []plugins.RoleRegistration{
|
|
{
|
|
Role: plugins.Role{Name: "Tester"},
|
|
Grants: []string{"WrongAdmin"},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
err: accesscontrol.ErrInvalidBuiltinRole.Build(accesscontrol.ErrInvalidBuiltinRoleData("WrongAdmin")),
|
|
},
|
|
{
|
|
name: "should add multiple registrations at once",
|
|
pluginID: "test-app",
|
|
registrations: []plugins.RoleRegistration{
|
|
{
|
|
Role: plugins.Role{Name: "Tester"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
{
|
|
Role: plugins.Role{Name: "Tester2"},
|
|
Grants: []string{"Admin"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ac := setupTestEnv(t)
|
|
ac.features = featuremgmt.WithFeatures(featuremgmt.FlagAccessControlOnCall)
|
|
|
|
// Reset the registations
|
|
ac.registrations = accesscontrol.RegistrationList{}
|
|
|
|
// Test
|
|
err := ac.DeclarePluginRoles(context.Background(), tt.pluginID, tt.pluginID, tt.registrations)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
assert.ErrorIs(t, err, tt.err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
registrationCnt := 0
|
|
ac.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
|
|
registrationCnt++
|
|
return true
|
|
})
|
|
assert.Equal(t, len(tt.registrations), registrationCnt,
|
|
"expected service registration list to contain all test registrations")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_RegisterFixedRoles(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
token licensing.Licensing
|
|
registrations []accesscontrol.RoleRegistration
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "should work with empty list",
|
|
},
|
|
{
|
|
name: "should register and assign role",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{
|
|
Name: "fixed:test:test",
|
|
Permissions: []accesscontrol.Permission{{Action: "test:test"}},
|
|
},
|
|
Grants: []string{"Editor"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should register and assign multiple roles",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{
|
|
Name: "fixed:test:test",
|
|
Permissions: []accesscontrol.Permission{{Action: "test:test"}},
|
|
},
|
|
Grants: []string{"Editor"},
|
|
},
|
|
{
|
|
Role: accesscontrol.RoleDTO{
|
|
Name: "fixed:test2:test2",
|
|
Permissions: []accesscontrol.Permission{
|
|
{Action: "test:test2"},
|
|
{Action: "test:test3", Scope: "test:*"},
|
|
},
|
|
},
|
|
Grants: []string{"Viewer"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "should register and assign fixed and plugins roles",
|
|
registrations: []accesscontrol.RoleRegistration{
|
|
{
|
|
Role: accesscontrol.RoleDTO{
|
|
Name: accesscontrol.PluginRolePrefix + "test-app:tester",
|
|
DisplayName: "Tester",
|
|
Permissions: []accesscontrol.Permission{{Action: "test-app:test"}},
|
|
},
|
|
Grants: []string{"Editor"},
|
|
},
|
|
{
|
|
Role: accesscontrol.RoleDTO{
|
|
Name: "fixed:test2:test2",
|
|
Permissions: []accesscontrol.Permission{
|
|
{Action: "test:test2"},
|
|
{Action: "test:test3", Scope: "test:*"},
|
|
},
|
|
},
|
|
Grants: []string{"Viewer"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ac := setupTestEnv(t)
|
|
|
|
ac.registrations.Append(tt.registrations...)
|
|
|
|
// Test
|
|
err := ac.RegisterFixedRoles(context.Background())
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
// Check
|
|
for _, registration := range tt.registrations {
|
|
// Check builtin roles (parents included) have been granted with the permissions
|
|
for br := range accesscontrol.BuiltInRolesWithParents(registration.Grants) {
|
|
builtinRole, ok := ac.roles[br]
|
|
assert.True(t, ok)
|
|
for _, expectedPermission := range registration.Role.Permissions {
|
|
assert.Contains(t, builtinRole.Permissions, expectedPermission)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUsersPermissions(t *testing.T) {
|
|
searchOption := accesscontrol.SearchOptions{ActionPrefix: "teams"}
|
|
ctx := context.Background()
|
|
listAllPerms := map[string][]string{accesscontrol.ActionUsersPermissionsRead: {"users:*"}}
|
|
listSomePerms := map[string][]string{accesscontrol.ActionUsersPermissionsRead: {"users:id:2"}}
|
|
tests := []struct {
|
|
name string
|
|
siuPermissions map[string][]string
|
|
searchOption accesscontrol.SearchOptions
|
|
ramRoles map[string]*accesscontrol.RoleDTO // BasicRole => RBAC BasicRole
|
|
storedPerms map[int64][]accesscontrol.Permission // UserID => Permissions
|
|
storedRoles map[int64][]string // UserID => Roles
|
|
want map[int64][]accesscontrol.Permission
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "ram only",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: searchOption,
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
}},
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
{
|
|
name: "stored only",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: searchOption,
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
{
|
|
name: "ram and stored",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: searchOption,
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
}},
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
{
|
|
name: "ram only search on scope",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: accesscontrol.SearchOptions{Scope: "teams:id:2"},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
{
|
|
name: "view permission on subset of users only",
|
|
siuPermissions: listSomePerms,
|
|
searchOption: searchOption,
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
{
|
|
name: "check action filter on RAM permissions works correctly",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: searchOption,
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionUsersCreate},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{1: {accesscontrol.RoleGrafanaAdmin}},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
{
|
|
name: "check empty action filter on RAM permissions works correctly",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: accesscontrol.SearchOptions{},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionUsersCreate},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionAnnotationsRead, Scope: "annotations:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{1: {accesscontrol.RoleGrafanaAdmin}},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionUsersCreate},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionAnnotationsRead, Scope: "annotations:*"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// This test is not exactly representative as normally the store would return
|
|
// only the user's basic roles and the user's stored permissions
|
|
name: "check namespacedId filter works correctly",
|
|
siuPermissions: listAllPerms,
|
|
searchOption: accesscontrol.SearchOptions{UserID: 1},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
}},
|
|
string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsWrite, Scope: "teams:*"},
|
|
}},
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}, {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ac := setupTestEnv(t)
|
|
|
|
ac.roles = tt.ramRoles
|
|
ac.store = actest.FakeStore{
|
|
ExpectedUsersPermissions: tt.storedPerms,
|
|
ExpectedUsersRoles: tt.storedRoles,
|
|
}
|
|
|
|
siu := &user.SignedInUser{OrgID: 2, Permissions: map[int64]map[string][]string{2: tt.siuPermissions}}
|
|
got, err := ac.SearchUsersPermissions(ctx, siu, tt.searchOption)
|
|
if tt.wantErr {
|
|
require.NotNil(t, err)
|
|
return
|
|
}
|
|
require.Nil(t, err)
|
|
|
|
require.Len(t, got, len(tt.want), "expected more users permissions")
|
|
for userID, wantPerm := range tt.want {
|
|
gotPerm, ok := got[userID]
|
|
require.True(t, ok, "expected permissions for user", userID)
|
|
|
|
require.ElementsMatch(t, gotPerm, wantPerm)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUserPermissions(t *testing.T) {
|
|
ctx := context.Background()
|
|
tests := []struct {
|
|
name string
|
|
searchOption accesscontrol.SearchOptions
|
|
withActionSets bool
|
|
actionSets map[string][]string
|
|
ramRoles map[string]*accesscontrol.RoleDTO // BasicRole => RBAC BasicRole
|
|
storedPerms map[int64][]accesscontrol.Permission // UserID => Permissions
|
|
storedRoles map[int64][]string // UserID => Roles
|
|
want []accesscontrol.Permission
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "ram only",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
ActionPrefix: "teams",
|
|
UserID: 2,
|
|
},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsCreate},
|
|
}},
|
|
string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
}},
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
{
|
|
name: "stored only",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
ActionPrefix: "teams",
|
|
UserID: 2,
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
},
|
|
},
|
|
{
|
|
name: "ram and stored",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
ActionPrefix: "teams",
|
|
UserID: 2,
|
|
},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
}},
|
|
accesscontrol.RoleGrafanaAdmin: {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
}},
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}},
|
|
2: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
2: {string(identity.RoleAdmin), accesscontrol.RoleGrafanaAdmin},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
},
|
|
},
|
|
{
|
|
name: "check action prefix filter works correctly",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
ActionPrefix: "teams",
|
|
UserID: 1,
|
|
},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionUsersCreate},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionAnnotationsRead, Scope: "annotations:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:*"},
|
|
},
|
|
},
|
|
{
|
|
name: "check action filter works correctly",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
Action: accesscontrol.ActionTeamsRead,
|
|
UserID: 1,
|
|
},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
{Action: accesscontrol.ActionUsersCreate},
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionAnnotationsRead, Scope: "annotations:*"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
},
|
|
},
|
|
{
|
|
name: "check action sets are correctly included if an action is specified",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
Action: "dashboards:read",
|
|
UserID: 1,
|
|
},
|
|
withActionSets: true,
|
|
actionSets: map[string][]string{
|
|
"dashboards:view": {"dashboards:read"},
|
|
"dashboards:edit": {"dashboards:read", "dashboards:write", "dashboards:read-advanced"},
|
|
},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:ram"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored"},
|
|
{Action: "dashboards:edit", Scope: "dashboards:uid:stored2"},
|
|
{Action: "dashboards:view", Scope: "dashboards:uid:stored3"},
|
|
},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:ram"},
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored"},
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored2"},
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored3"},
|
|
},
|
|
},
|
|
{
|
|
name: "check action sets are correctly included if an action prefix is specified",
|
|
searchOption: accesscontrol.SearchOptions{
|
|
ActionPrefix: "dashboards",
|
|
UserID: 1,
|
|
},
|
|
withActionSets: true,
|
|
actionSets: map[string][]string{
|
|
"dashboards:view": {"dashboards:read"},
|
|
"folders:view": {"dashboards:read", "folders:read"},
|
|
"dashboards:edit": {"dashboards:read", "dashboards:write"},
|
|
},
|
|
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:ram"},
|
|
}},
|
|
},
|
|
storedRoles: map[int64][]string{
|
|
1: {string(identity.RoleEditor)},
|
|
},
|
|
storedPerms: map[int64][]accesscontrol.Permission{
|
|
1: {
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored"},
|
|
{Action: "folders:view", Scope: "folders:uid:stored2"},
|
|
{Action: "dashboards:edit", Scope: "dashboards:uid:stored3"},
|
|
},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:ram"},
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored"},
|
|
{Action: "dashboards:read", Scope: "folders:uid:stored2"},
|
|
{Action: "dashboards:read", Scope: "dashboards:uid:stored3"},
|
|
{Action: "dashboards:write", Scope: "dashboards:uid:stored3"},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ac := setupTestEnv(t)
|
|
if tt.withActionSets {
|
|
ac.features = featuremgmt.WithFeatures(featuremgmt.FlagAccessActionSets)
|
|
actionSetSvc := resourcepermissions.NewActionSetService(ac.features)
|
|
for set, actions := range tt.actionSets {
|
|
actionSetName := resourcepermissions.GetActionSetName(strings.Split(set, ":")[0], strings.Split(set, ":")[1])
|
|
actionSetSvc.StoreActionSet(actionSetName, actions)
|
|
}
|
|
ac.actionResolver = actionSetSvc
|
|
}
|
|
|
|
ac.roles = tt.ramRoles
|
|
ac.store = actest.FakeStore{
|
|
ExpectedUsersPermissions: tt.storedPerms,
|
|
ExpectedUsersRoles: tt.storedRoles,
|
|
}
|
|
|
|
got, err := ac.searchUserPermissions(ctx, 1, tt.searchOption)
|
|
if tt.wantErr {
|
|
require.NotNil(t, err)
|
|
return
|
|
}
|
|
require.Nil(t, err)
|
|
|
|
assert.ElementsMatch(t, got, tt.want)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_SaveExternalServiceRole(t *testing.T) {
|
|
type run struct {
|
|
cmd accesscontrol.SaveExternalServiceRoleCommand
|
|
wantErr bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
runs []run
|
|
}{
|
|
{
|
|
name: "can create a role",
|
|
runs: []run{
|
|
{
|
|
cmd: accesscontrol.SaveExternalServiceRoleCommand{
|
|
AssignmentOrgID: 2,
|
|
ServiceAccountID: 2,
|
|
ExternalServiceID: "App 1",
|
|
Permissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:id:1"}},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "can update a role",
|
|
runs: []run{
|
|
{
|
|
cmd: accesscontrol.SaveExternalServiceRoleCommand{
|
|
AssignmentOrgID: 1,
|
|
ServiceAccountID: 2,
|
|
ExternalServiceID: "App 1",
|
|
Permissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:id:1"}},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
cmd: accesscontrol.SaveExternalServiceRoleCommand{
|
|
AssignmentOrgID: 1,
|
|
ServiceAccountID: 2,
|
|
ExternalServiceID: "App 1",
|
|
Permissions: []accesscontrol.Permission{
|
|
{Action: "users:write", Scope: "users:id:1"},
|
|
{Action: "users:write", Scope: "users:id:2"},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test command validity - no service account ID",
|
|
runs: []run{
|
|
{
|
|
cmd: accesscontrol.SaveExternalServiceRoleCommand{
|
|
AssignmentOrgID: 2,
|
|
ExternalServiceID: "App 1",
|
|
Permissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:id:1"}},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
ac := setupTestEnv(t)
|
|
ac.cfg.ManagedServiceAccountsEnabled = true
|
|
ac.features = featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAccounts)
|
|
for _, r := range tt.runs {
|
|
err := ac.SaveExternalServiceRole(ctx, r.cmd)
|
|
if r.wantErr {
|
|
require.Error(t, err)
|
|
continue
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
// Check that the permissions and assignment are stored correctly
|
|
perms, errGetPerms := ac.getUserPermissions(ctx, &user.SignedInUser{OrgID: r.cmd.AssignmentOrgID, UserID: 2}, accesscontrol.Options{})
|
|
require.NoError(t, errGetPerms)
|
|
assert.ElementsMatch(t, r.cmd.Permissions, perms)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_DeleteExternalServiceRole(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
initCmd *accesscontrol.SaveExternalServiceRoleCommand
|
|
externalServiceID string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "handles deleting role that doesn't exist",
|
|
externalServiceID: "App 1",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "handles deleting role that exists",
|
|
initCmd: &accesscontrol.SaveExternalServiceRoleCommand{
|
|
AssignmentOrgID: 1,
|
|
ServiceAccountID: 2,
|
|
ExternalServiceID: "App 1",
|
|
Permissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:id:1"}},
|
|
},
|
|
externalServiceID: "App 1",
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
ac := setupTestEnv(t)
|
|
ac.cfg.ManagedServiceAccountsEnabled = true
|
|
ac.features = featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAccounts)
|
|
|
|
if tt.initCmd != nil {
|
|
err := ac.SaveExternalServiceRole(ctx, *tt.initCmd)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
err := ac.DeleteExternalServiceRole(ctx, tt.externalServiceID)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
if tt.initCmd != nil {
|
|
// Check that the permissions and assignment are removed correctly
|
|
perms, errGetPerms := ac.getUserPermissions(ctx, &user.SignedInUser{OrgID: tt.initCmd.AssignmentOrgID, UserID: 2}, accesscontrol.Options{})
|
|
require.NoError(t, errGetPerms)
|
|
assert.Empty(t, perms)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|