mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 10:03:33 -06:00
894 lines
27 KiB
Go
894 lines
27 KiB
Go
package acimpl
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"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/models/roletype"
|
|
"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/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/licensing"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
func setupTestEnv(t testing.TB) *Service {
|
|
t.Helper()
|
|
cfg := setting.NewCfg()
|
|
|
|
ac := &Service{
|
|
cfg: cfg,
|
|
log: log.New("accesscontrol"),
|
|
registrations: accesscontrol.RegistrationList{},
|
|
store: database.ProvideService(db.InitTestDB(t)),
|
|
roles: accesscontrol.BuildBasicRoleDefinitions(),
|
|
features: featuremgmt.WithFeatures(),
|
|
}
|
|
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)),
|
|
localcache.ProvideService(),
|
|
featuremgmt.WithFeatures(),
|
|
)
|
|
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,
|
|
},
|
|
{
|
|
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,
|
|
},
|
|
{
|
|
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(roletype.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(roletype.RoleEditor)},
|
|
2: {string(roletype.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(roletype.RoleEditor)},
|
|
2: {string(roletype.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(roletype.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(roletype.RoleEditor)},
|
|
2: {string(roletype.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: "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(roletype.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:*"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
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
|
|
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(roletype.RoleEditor): {Permissions: []accesscontrol.Permission{
|
|
{Action: accesscontrol.ActionTeamsCreate},
|
|
}},
|
|
string(roletype.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(roletype.RoleEditor)},
|
|
2: {string(roletype.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(roletype.RoleEditor)},
|
|
2: {string(roletype.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(roletype.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(roletype.RoleEditor)},
|
|
2: {string(roletype.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(roletype.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(roletype.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(roletype.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(roletype.RoleEditor)},
|
|
},
|
|
want: []accesscontrol.Permission{
|
|
{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,
|
|
}
|
|
|
|
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 TestPermissionCacheKey(t *testing.T) {
|
|
testcases := []struct {
|
|
name string
|
|
signedInUser *user.SignedInUser
|
|
expected string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "should return correct key for user",
|
|
signedInUser: &user.SignedInUser{
|
|
OrgID: 1,
|
|
UserID: 1,
|
|
},
|
|
expected: "rbac-permissions-1-user-1",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "should return correct key for api key",
|
|
signedInUser: &user.SignedInUser{
|
|
OrgID: 1,
|
|
ApiKeyID: 1,
|
|
IsServiceAccount: false,
|
|
},
|
|
expected: "rbac-permissions-1-apikey-1",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "should return correct key for service account",
|
|
signedInUser: &user.SignedInUser{
|
|
OrgID: 1,
|
|
UserID: 1,
|
|
IsServiceAccount: true,
|
|
},
|
|
expected: "rbac-permissions-1-service-1",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "should return correct key for matching a service account with userId -1",
|
|
signedInUser: &user.SignedInUser{
|
|
OrgID: 1,
|
|
UserID: -1,
|
|
IsServiceAccount: true,
|
|
},
|
|
expected: "rbac-permissions-1-service--1",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
name: "should return error if not matching any",
|
|
signedInUser: &user.SignedInUser{
|
|
OrgID: 1,
|
|
UserID: -1,
|
|
},
|
|
expected: "",
|
|
expectedErr: user.ErrNoUniqueID,
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
str, err := permissionCacheKey(tc.signedInUser)
|
|
require.Equal(t, tc.expectedErr, err)
|
|
assert.Equal(t, tc.expected, str)
|
|
})
|
|
}
|
|
}
|
|
|
|
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{
|
|
OrgID: 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{
|
|
Global: true,
|
|
ServiceAccountID: 2,
|
|
ExternalServiceID: "App 1",
|
|
Permissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:id:1"}},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
cmd: accesscontrol.SaveExternalServiceRoleCommand{
|
|
Global: true,
|
|
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{
|
|
OrgID: 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.features = featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAuth)
|
|
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.OrgID, 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{
|
|
Global: true,
|
|
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.features = featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAuth)
|
|
|
|
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.OrgID, UserID: 2}, accesscontrol.Options{})
|
|
require.NoError(t, errGetPerms)
|
|
assert.Empty(t, perms)
|
|
}
|
|
})
|
|
}
|
|
}
|