Access Control: Add function to set several permissions on a resource in one transaction (#44768)

This commit is contained in:
Karl Persson 2022-02-07 17:04:32 +01:00 committed by GitHub
parent 178193c84b
commit 922b9465ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 368 additions and 107 deletions

View File

@ -42,6 +42,8 @@ type ResourcePermissionsService interface {
SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*ResourcePermission, error)
// SetBuiltInRolePermission sets permission on resource for a built-in role (Admin, Editor, Viewer)
SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole string, resourceID string, permission string) (*ResourcePermission, error)
// SetPermissions sets several permissions on resource for either built-in role, team or user
SetPermissions(ctx context.Context, orgID int64, resourceID string, commands ...SetResourcePermissionCommand) ([]ResourcePermission, error)
}
type User struct {

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions/types"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
@ -80,7 +81,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
user, team := createUserAndTeam(t, sql, tt.orgID)
for _, id := range tt.userPermissions {
_, err := store.SetUserResourcePermission(context.Background(), tt.orgID, accesscontrol.User{ID: user.Id}, accesscontrol.SetResourcePermissionCommand{
_, err := store.SetUserResourcePermission(context.Background(), tt.orgID, accesscontrol.User{ID: user.Id}, types.SetResourcePermissionCommand{
Actions: []string{"dashboards:write"},
Resource: "dashboards",
ResourceID: id,
@ -89,7 +90,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
}
for _, id := range tt.teamPermissions {
_, err := store.SetTeamResourcePermission(context.Background(), tt.orgID, team.Id, accesscontrol.SetResourcePermissionCommand{
_, err := store.SetTeamResourcePermission(context.Background(), tt.orgID, team.Id, types.SetResourcePermissionCommand{
Actions: []string{"dashboards:read"},
Resource: "dashboards",
ResourceID: id,
@ -98,7 +99,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
}
for _, id := range tt.builtinPermissions {
_, err := store.SetBuiltInResourcePermission(context.Background(), tt.orgID, "Admin", accesscontrol.SetResourcePermissionCommand{
_, err := store.SetBuiltInResourcePermission(context.Background(), tt.orgID, "Admin", types.SetResourcePermissionCommand{
Actions: []string{"dashboards:read"},
Resource: "dashboards",
ResourceID: id,

View File

@ -35,7 +35,7 @@ func (p *flatResourcePermission) Managed() bool {
func (s *AccessControlStore) SetUserResourcePermission(
ctx context.Context, orgID int64, user accesscontrol.User,
cmd accesscontrol.SetResourcePermissionCommand,
cmd types.SetResourcePermissionCommand,
hook types.UserResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
if user.ID == 0 {
@ -45,24 +45,34 @@ func (s *AccessControlStore) SetUserResourcePermission(
var err error
var permission *accesscontrol.ResourcePermission
err = s.sql.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
permission, err = s.setResourcePermission(sess, orgID, managedUserRoleName(user.ID), s.userAdder(sess, orgID, user.ID), cmd)
if err == nil && hook != nil {
return hook(sess, orgID, user, cmd.ResourceID, cmd.Permission)
}
permission, err = s.setUserResourcePermission(sess, orgID, user, cmd, hook)
return err
})
return permission, err
}
func (s *AccessControlStore) setUserResourcePermission(
sess *sqlstore.DBSession, orgID int64, user accesscontrol.User,
cmd types.SetResourcePermissionCommand,
hook types.UserResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
permission, err := s.setResourcePermission(sess, orgID, managedUserRoleName(user.ID), s.userAdder(sess, orgID, user.ID), cmd)
if err != nil {
return nil, err
}
if hook != nil {
if err := hook(sess, orgID, user, cmd.ResourceID, cmd.Permission); err != nil {
return nil, err
}
}
return permission, nil
}
func (s *AccessControlStore) SetTeamResourcePermission(
ctx context.Context, orgID, teamID int64,
cmd accesscontrol.SetResourcePermissionCommand,
cmd types.SetResourcePermissionCommand,
hook types.TeamResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
if teamID == 0 {
@ -73,24 +83,35 @@ func (s *AccessControlStore) SetTeamResourcePermission(
var permission *accesscontrol.ResourcePermission
err = s.sql.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
permission, err = s.setResourcePermission(sess, orgID, managedTeamRoleName(teamID), s.teamAdder(sess, orgID, teamID), cmd)
if err == nil && hook != nil {
return hook(sess, orgID, teamID, cmd.ResourceID, cmd.Permission)
}
permission, err = s.setTeamResourcePermission(sess, orgID, teamID, cmd, hook)
return err
})
return permission, err
}
func (s *AccessControlStore) setTeamResourcePermission(
sess *sqlstore.DBSession, orgID, teamID int64,
cmd types.SetResourcePermissionCommand,
hook types.TeamResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
permission, err := s.setResourcePermission(sess, orgID, managedTeamRoleName(teamID), s.teamAdder(sess, orgID, teamID), cmd)
if err != nil {
return nil, err
}
if hook != nil {
if err := hook(sess, orgID, teamID, cmd.ResourceID, cmd.Permission); err != nil {
return nil, err
}
}
return permission, nil
}
func (s *AccessControlStore) SetBuiltInResourcePermission(
ctx context.Context, orgID int64, builtInRole string,
cmd accesscontrol.SetResourcePermissionCommand,
cmd types.SetResourcePermissionCommand,
hook types.BuiltinResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
if !models.RoleType(builtInRole).IsValid() || builtInRole == accesscontrol.RoleGrafanaAdmin {
@ -101,10 +122,7 @@ func (s *AccessControlStore) SetBuiltInResourcePermission(
var permission *accesscontrol.ResourcePermission
err = s.sql.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
permission, err = s.setResourcePermission(sess, orgID, managedBuiltInRoleName(builtInRole), s.builtInRoleAdder(sess, orgID, builtInRole), cmd)
if err == nil && hook != nil {
return hook(sess, orgID, builtInRole, cmd.ResourceID, cmd.Permission)
}
permission, err = s.setBuiltInResourcePermission(sess, orgID, builtInRole, cmd, hook)
return err
})
@ -115,10 +133,61 @@ func (s *AccessControlStore) SetBuiltInResourcePermission(
return permission, nil
}
func (s *AccessControlStore) setBuiltInResourcePermission(
sess *sqlstore.DBSession, orgID int64, builtInRole string,
cmd types.SetResourcePermissionCommand,
hook types.BuiltinResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
permission, err := s.setResourcePermission(sess, orgID, managedBuiltInRoleName(builtInRole), s.builtInRoleAdder(sess, orgID, builtInRole), cmd)
if err != nil {
return nil, err
}
if hook != nil {
if err := hook(sess, orgID, builtInRole, cmd.ResourceID, cmd.Permission); err != nil {
return nil, err
}
}
return permission, nil
}
func (s *AccessControlStore) SetResourcePermissions(
ctx context.Context, orgID int64,
commands []types.SetResourcePermissionsCommand,
hooks types.ResourceHooks,
) ([]accesscontrol.ResourcePermission, error) {
var err error
var permissions []accesscontrol.ResourcePermission
err = s.sql.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
for _, cmd := range commands {
var p *accesscontrol.ResourcePermission
if cmd.User.ID != 0 {
p, err = s.setUserResourcePermission(sess, orgID, cmd.User, cmd.SetResourcePermissionCommand, hooks.User)
} else if cmd.TeamID != 0 {
p, err = s.setTeamResourcePermission(sess, orgID, cmd.TeamID, cmd.SetResourcePermissionCommand, hooks.Team)
} else if models.RoleType(cmd.BuiltinRole).IsValid() || cmd.BuiltinRole == accesscontrol.RoleGrafanaAdmin {
p, err = s.setBuiltInResourcePermission(sess, orgID, cmd.BuiltinRole, cmd.SetResourcePermissionCommand, hooks.BuiltInRole)
}
if err != nil {
return err
}
if p != nil {
permissions = append(permissions, *p)
}
}
return nil
})
return permissions, err
}
type roleAdder func(roleID int64) error
func (s *AccessControlStore) setResourcePermission(
sess *sqlstore.DBSession, orgID int64, roleName string, adder roleAdder, cmd accesscontrol.SetResourcePermissionCommand,
sess *sqlstore.DBSession, orgID int64, roleName string, adder roleAdder, cmd types.SetResourcePermissionCommand,
) (*accesscontrol.ResourcePermission, error) {
role, err := s.getOrCreateManagedRole(sess, orgID, roleName, adder)
if err != nil {
@ -159,22 +228,20 @@ func (s *AccessControlStore) setResourcePermission(
return nil, err
}
var permissions []flatResourcePermission
for action := range missing {
p, err := s.createResourcePermission(sess, role.ID, action, cmd.Resource, cmd.ResourceID)
id, err := s.createResourcePermission(sess, role.ID, action, cmd.Resource, cmd.ResourceID)
if err != nil {
return nil, err
}
permissions = append(permissions, *p)
keep = append(keep, id)
}
keptPermissions, err := s.getResourcePermissions(sess, cmd.ResourceID, keep)
permissions, err := s.getResourcePermissions(sess, cmd.ResourceID, keep)
if err != nil {
return nil, err
}
permission := flatPermissionsToResourcePermission(append(permissions, keptPermissions...))
permission := flatPermissionsToResourcePermission(permissions)
if permission == nil {
return &accesscontrol.ResourcePermission{}, nil
}
@ -182,7 +249,7 @@ func (s *AccessControlStore) setResourcePermission(
return permission, nil
}
func (s *AccessControlStore) GetResourcesPermissions(ctx context.Context, orgID int64, query accesscontrol.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
func (s *AccessControlStore) GetResourcesPermissions(ctx context.Context, orgID int64, query types.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
var result []accesscontrol.ResourcePermission
err := s.sql.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
@ -194,45 +261,19 @@ func (s *AccessControlStore) GetResourcesPermissions(ctx context.Context, orgID
return result, err
}
func (s *AccessControlStore) createResourcePermission(sess *sqlstore.DBSession, roleID int64, action, resource string, resourceID string) (*flatResourcePermission, error) {
func (s *AccessControlStore) createResourcePermission(sess *sqlstore.DBSession, roleID int64, action, resource string, resourceID string) (int64, error) {
permission := managedPermission(action, resource, resourceID)
permission.RoleID = roleID
permission.Created = time.Now()
permission.Updated = time.Now()
if _, err := sess.Insert(&permission); err != nil {
return nil, err
return 0, err
}
rawSql := `
SELECT
p.*,
? AS resource_id,
ur.user_id AS user_id,
u.login AS user_login,
u.email AS user_email,
tr.team_id AS team_id,
t.name AS team,
t.email AS team_email,
r.name as role_name
FROM permission p
LEFT JOIN role r ON p.role_id = r.id
LEFT JOIN team_role tr ON r.id = tr.role_id
LEFT JOIN team t ON tr.team_id = t.id
LEFT JOIN user_role ur ON r.id = ur.role_id
LEFT JOIN ` + s.sql.Dialect.Quote("user") + ` u ON ur.user_id = u.id
WHERE p.id = ?
`
p := &flatResourcePermission{}
if _, err := sess.SQL(rawSql, resourceID, permission.ID).Get(p); err != nil {
return nil, err
}
return p, nil
return permission.ID, nil
}
func (s *AccessControlStore) getResourcesPermissions(sess *sqlstore.DBSession, orgID int64, query accesscontrol.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
func (s *AccessControlStore) getResourcesPermissions(sess *sqlstore.DBSession, orgID int64, query types.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
if len(query.Actions) == 0 {
return nil, nil
}
@ -544,13 +585,15 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, re
tr.team_id AS team_id,
t.name AS team,
t.email AS team_email,
r.name as role_name
r.name as role_name,
br.role AS built_in_role
FROM permission p
INNER JOIN role r ON p.role_id = r.id
LEFT JOIN team_role tr ON r.id = tr.role_id
LEFT JOIN team t ON tr.team_id = t.id
LEFT JOIN user_role ur ON r.id = ur.role_id
LEFT JOIN ` + s.sql.Dialect.Quote("user") + ` u ON ur.user_id = u.id
LEFT JOIN builtin_role br ON r.id = br.role_id
WHERE p.id IN (?` + strings.Repeat(",?", len(ids)-1) + `)
`

View File

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions/types"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
@ -54,7 +55,7 @@ func benchmarkDSPermissions(b *testing.B, dsNum, usersNum int) {
func getDSPermissions(b *testing.B, store *AccessControlStore, dataSources []int64) {
dsId := dataSources[0]
permissions, err := store.GetResourcesPermissions(context.Background(), accesscontrol.GlobalOrgID, accesscontrol.GetResourcesPermissionsQuery{
permissions, err := store.GetResourcesPermissions(context.Background(), accesscontrol.GlobalOrgID, types.GetResourcesPermissionsQuery{
Actions: []string{dsAction},
Resource: dsResource,
ResourceIDs: []string{strconv.Itoa(int(dsId))},
@ -94,7 +95,7 @@ func GenerateDatasourcePermissions(b *testing.B, db *sqlstore.SQLStore, ac *Acce
context.Background(),
accesscontrol.GlobalOrgID,
accesscontrol.User{ID: userIds[i]},
accesscontrol.SetResourcePermissionCommand{
types.SetResourcePermissionCommand{
Actions: []string{dsAction},
Resource: dsResource,
ResourceID: strconv.Itoa(int(dsID)),
@ -111,7 +112,7 @@ func GenerateDatasourcePermissions(b *testing.B, db *sqlstore.SQLStore, ac *Acce
context.Background(),
accesscontrol.GlobalOrgID,
teamIds[i],
accesscontrol.SetResourcePermissionCommand{
types.SetResourcePermissionCommand{
Actions: []string{"datasources:query"},
Resource: "datasources",
ResourceID: strconv.Itoa(int(dsID)),

View File

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions/types"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
@ -21,7 +22,7 @@ type setUserResourcePermissionTest struct {
actions []string
resource string
resourceID string
seeds []accesscontrol.SetResourcePermissionCommand
seeds []types.SetResourcePermissionCommand
}
func TestAccessControlStore_SetUserResourcePermission(t *testing.T) {
@ -40,7 +41,7 @@ func TestAccessControlStore_SetUserResourcePermission(t *testing.T) {
actions: []string{},
resource: "datasources",
resourceID: "1",
seeds: []accesscontrol.SetResourcePermissionCommand{
seeds: []types.SetResourcePermissionCommand{
{
Actions: []string{"datasources:query"},
Resource: "datasources",
@ -55,7 +56,7 @@ func TestAccessControlStore_SetUserResourcePermission(t *testing.T) {
actions: []string{"datasources:query", "datasources:write"},
resource: "datasources",
resourceID: "1",
seeds: []accesscontrol.SetResourcePermissionCommand{
seeds: []types.SetResourcePermissionCommand{
{
Actions: []string{"datasources:write"},
Resource: "datasources",
@ -74,7 +75,7 @@ func TestAccessControlStore_SetUserResourcePermission(t *testing.T) {
require.NoError(t, err)
}
added, err := store.SetUserResourcePermission(context.Background(), test.userID, accesscontrol.User{ID: test.userID}, accesscontrol.SetResourcePermissionCommand{
added, err := store.SetUserResourcePermission(context.Background(), test.userID, accesscontrol.User{ID: test.userID}, types.SetResourcePermissionCommand{
Actions: test.actions,
Resource: test.resource,
ResourceID: test.resourceID,
@ -98,7 +99,7 @@ type setTeamResourcePermissionTest struct {
actions []string
resource string
resourceID string
seeds []accesscontrol.SetResourcePermissionCommand
seeds []types.SetResourcePermissionCommand
}
func TestAccessControlStore_SetTeamResourcePermission(t *testing.T) {
@ -118,7 +119,7 @@ func TestAccessControlStore_SetTeamResourcePermission(t *testing.T) {
actions: []string{"datasources:query", "datasources:write"},
resource: "datasources",
resourceID: "1",
seeds: []accesscontrol.SetResourcePermissionCommand{
seeds: []types.SetResourcePermissionCommand{
{
Actions: []string{"datasources:query"},
Resource: "datasources",
@ -133,7 +134,7 @@ func TestAccessControlStore_SetTeamResourcePermission(t *testing.T) {
actions: []string{},
resource: "datasources",
resourceID: "1",
seeds: []accesscontrol.SetResourcePermissionCommand{
seeds: []types.SetResourcePermissionCommand{
{
Actions: []string{"datasources:query"},
Resource: "datasources",
@ -152,7 +153,7 @@ func TestAccessControlStore_SetTeamResourcePermission(t *testing.T) {
require.NoError(t, err)
}
added, err := store.SetTeamResourcePermission(context.Background(), test.orgID, test.teamID, accesscontrol.SetResourcePermissionCommand{
added, err := store.SetTeamResourcePermission(context.Background(), test.orgID, test.teamID, types.SetResourcePermissionCommand{
Actions: test.actions,
Resource: test.resource,
ResourceID: test.resourceID,
@ -176,7 +177,7 @@ type setBuiltInResourcePermissionTest struct {
actions []string
resource string
resourceID string
seeds []accesscontrol.SetResourcePermissionCommand
seeds []types.SetResourcePermissionCommand
}
func TestAccessControlStore_SetBuiltInResourcePermission(t *testing.T) {
@ -196,7 +197,7 @@ func TestAccessControlStore_SetBuiltInResourcePermission(t *testing.T) {
actions: []string{"datasources:query", "datasources:write"},
resource: "datasources",
resourceID: "1",
seeds: []accesscontrol.SetResourcePermissionCommand{
seeds: []types.SetResourcePermissionCommand{
{
Actions: []string{"datasources:query"},
Resource: "datasources",
@ -211,7 +212,7 @@ func TestAccessControlStore_SetBuiltInResourcePermission(t *testing.T) {
actions: []string{},
resource: "datasources",
resourceID: "1",
seeds: []accesscontrol.SetResourcePermissionCommand{
seeds: []types.SetResourcePermissionCommand{
{
Actions: []string{"datasources:query"},
Resource: "datasources",
@ -230,7 +231,7 @@ func TestAccessControlStore_SetBuiltInResourcePermission(t *testing.T) {
require.NoError(t, err)
}
added, err := store.SetBuiltInResourcePermission(context.Background(), test.orgID, test.builtInRole, accesscontrol.SetResourcePermissionCommand{
added, err := store.SetBuiltInResourcePermission(context.Background(), test.orgID, test.builtInRole, types.SetResourcePermissionCommand{
Actions: test.actions,
Resource: test.resource,
ResourceID: test.resourceID,
@ -247,6 +248,69 @@ func TestAccessControlStore_SetBuiltInResourcePermission(t *testing.T) {
}
}
type setResourcePermissionsTest struct {
desc string
orgID int64
commands []types.SetResourcePermissionsCommand
}
func TestAccessControlStore_SetResourcePermissions(t *testing.T) {
tests := []setResourcePermissionsTest{
{
desc: "should set all permissions provided",
orgID: 1,
commands: []types.SetResourcePermissionsCommand{
{
User: accesscontrol.User{ID: 1},
SetResourcePermissionCommand: types.SetResourcePermissionCommand{
Actions: []string{"datasources:query"},
Resource: "datasources",
ResourceID: "1",
},
},
{
TeamID: 3,
SetResourcePermissionCommand: types.SetResourcePermissionCommand{
Actions: []string{"datasources:query"},
Resource: "datasources",
ResourceID: "1",
},
},
{
BuiltinRole: "Admin",
SetResourcePermissionCommand: types.SetResourcePermissionCommand{
Actions: []string{"datasources:query"},
Resource: "datasources",
ResourceID: "1",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
store, _ := setupTestEnv(t)
permissions, err := store.SetResourcePermissions(context.Background(), tt.orgID, tt.commands, types.ResourceHooks{})
require.NoError(t, err)
require.Len(t, permissions, len(tt.commands))
for i, c := range tt.commands {
if len(c.Actions) == 0 {
assert.Equal(t, accesscontrol.ResourcePermission{}, permissions[i])
} else {
assert.Len(t, permissions[i].Actions, len(c.Actions))
assert.Equal(t, c.TeamID, permissions[i].TeamId)
assert.Equal(t, c.User.ID, permissions[i].UserId)
assert.Equal(t, c.BuiltinRole, permissions[i].BuiltInRole)
assert.Equal(t, accesscontrol.GetResourceScope(c.Resource, c.ResourceID), permissions[i].Scope)
}
}
})
}
}
type getResourcesPermissionsTest struct {
desc string
numUsers int
@ -318,7 +382,7 @@ func TestAccessControlStore_GetResourcesPermissions(t *testing.T) {
seedResourcePermissions(t, store, sql, test.actions, test.resource, id, test.numUsers)
}
permissions, err := store.GetResourcesPermissions(context.Background(), 1, accesscontrol.GetResourcesPermissionsQuery{
permissions, err := store.GetResourcesPermissions(context.Background(), 1, types.GetResourcesPermissionsQuery{
Actions: test.actions,
Resource: test.resource,
ResourceIDs: test.resourceIDs,
@ -352,7 +416,7 @@ func seedResourcePermissions(t *testing.T, store *AccessControlStore, sql *sqlst
})
require.NoError(t, err)
_, err = store.SetUserResourcePermission(context.Background(), 1, accesscontrol.User{ID: u.Id}, accesscontrol.SetResourcePermissionCommand{
_, err = store.SetUserResourcePermission(context.Background(), 1, accesscontrol.User{ID: u.Id}, types.SetResourcePermissionCommand{
Actions: actions,
Resource: resource,
ResourceID: resourceID,

View File

@ -235,17 +235,10 @@ func (p *ResourcePermission) Contains(targetActions []string) bool {
}
type SetResourcePermissionCommand struct {
Actions []string
Resource string
ResourceID string
Permission string
}
type GetResourcesPermissionsQuery struct {
Actions []string
Resource string
ResourceIDs []string
OnlyManaged bool
UserID int64
TeamID int64
BuiltinRole string
Permission string
}
type SQLFilter struct {

View File

@ -18,26 +18,32 @@ type Store interface {
SetUserResourcePermission(
ctx context.Context, orgID int64,
user accesscontrol.User,
cmd accesscontrol.SetResourcePermissionCommand,
cmd types.SetResourcePermissionCommand,
hook types.UserResourceHookFunc,
) (*accesscontrol.ResourcePermission, error)
// SetTeamResourcePermission sets permission for managed team role on a resource
SetTeamResourcePermission(
ctx context.Context, orgID, teamID int64,
cmd accesscontrol.SetResourcePermissionCommand,
cmd types.SetResourcePermissionCommand,
hook types.TeamResourceHookFunc,
) (*accesscontrol.ResourcePermission, error)
// SetBuiltInResourcePermission sets permissions for managed builtin role on a resource
SetBuiltInResourcePermission(
ctx context.Context, orgID int64, builtinRole string,
cmd accesscontrol.SetResourcePermissionCommand,
cmd types.SetResourcePermissionCommand,
hook types.BuiltinResourceHookFunc,
) (*accesscontrol.ResourcePermission, error)
SetResourcePermissions(
ctx context.Context, orgID int64,
commands []types.SetResourcePermissionsCommand,
hooks types.ResourceHooks,
) ([]accesscontrol.ResourcePermission, error)
// GetResourcesPermissions will return all permission for all supplied resource ids
GetResourcesPermissions(ctx context.Context, orgID int64, query accesscontrol.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error)
GetResourcesPermissions(ctx context.Context, orgID int64, query types.GetResourcesPermissionsQuery) ([]accesscontrol.ResourcePermission, error)
}
func New(options Options, router routing.RouteRegister, ac accesscontrol.AccessControl, store Store, sqlStore *sqlstore.SQLStore) (*Service, error) {
@ -93,7 +99,7 @@ type Service struct {
}
func (s *Service) GetPermissions(ctx context.Context, orgID int64, resourceID string) ([]accesscontrol.ResourcePermission, error) {
return s.store.GetResourcesPermissions(ctx, orgID, accesscontrol.GetResourcesPermissionsQuery{
return s.store.GetResourcesPermissions(ctx, orgID, types.GetResourcesPermissionsQuery{
Actions: s.actions,
Resource: s.options.Resource,
ResourceIDs: []string{resourceID},
@ -102,10 +108,6 @@ func (s *Service) GetPermissions(ctx context.Context, orgID int64, resourceID st
}
func (s *Service) SetUserPermission(ctx context.Context, orgID int64, user accesscontrol.User, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
if !s.options.Assignments.Users {
return nil, ErrInvalidAssignment
}
actions, err := s.mapPermission(permission)
if err != nil {
return nil, err
@ -119,7 +121,7 @@ func (s *Service) SetUserPermission(ctx context.Context, orgID int64, user acces
return nil, err
}
return s.store.SetUserResourcePermission(ctx, orgID, user, accesscontrol.SetResourcePermissionCommand{
return s.store.SetUserResourcePermission(ctx, orgID, user, types.SetResourcePermissionCommand{
Actions: actions,
Permission: permission,
ResourceID: resourceID,
@ -128,10 +130,6 @@ func (s *Service) SetUserPermission(ctx context.Context, orgID int64, user acces
}
func (s *Service) SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
if !s.options.Assignments.Teams {
return nil, ErrInvalidAssignment
}
actions, err := s.mapPermission(permission)
if err != nil {
return nil, err
@ -145,7 +143,7 @@ func (s *Service) SetTeamPermission(ctx context.Context, orgID, teamID int64, re
return nil, err
}
return s.store.SetTeamResourcePermission(ctx, orgID, teamID, accesscontrol.SetResourcePermissionCommand{
return s.store.SetTeamResourcePermission(ctx, orgID, teamID, types.SetResourcePermissionCommand{
Actions: actions,
Permission: permission,
ResourceID: resourceID,
@ -154,10 +152,6 @@ func (s *Service) SetTeamPermission(ctx context.Context, orgID, teamID int64, re
}
func (s *Service) SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
if !s.options.Assignments.BuiltInRoles {
return nil, ErrInvalidAssignment
}
actions, err := s.mapPermission(permission)
if err != nil {
return nil, err
@ -171,7 +165,7 @@ func (s *Service) SetBuiltInRolePermission(ctx context.Context, orgID int64, bui
return nil, err
}
return s.store.SetBuiltInResourcePermission(ctx, orgID, builtInRole, accesscontrol.SetResourcePermissionCommand{
return s.store.SetBuiltInResourcePermission(ctx, orgID, builtInRole, types.SetResourcePermissionCommand{
Actions: actions,
Permission: permission,
ResourceID: resourceID,
@ -179,6 +173,55 @@ func (s *Service) SetBuiltInRolePermission(ctx context.Context, orgID int64, bui
}, s.options.OnSetBuiltInRole)
}
func (s *Service) SetPermissions(
ctx context.Context, orgID int64, resourceID string,
commands ...accesscontrol.SetResourcePermissionCommand,
) ([]accesscontrol.ResourcePermission, error) {
if err := s.validateResource(ctx, orgID, resourceID); err != nil {
return nil, err
}
dbCommands := make([]types.SetResourcePermissionsCommand, 0, len(commands))
for _, cmd := range commands {
if cmd.UserID != 0 {
if err := s.validateUser(ctx, orgID, cmd.UserID); err != nil {
return nil, err
}
} else if cmd.TeamID != 0 {
if err := s.validateTeam(ctx, orgID, cmd.TeamID); err != nil {
return nil, err
}
} else {
if err := s.validateBuiltinRole(ctx, cmd.BuiltinRole); err != nil {
return nil, err
}
}
actions, err := s.mapPermission(cmd.Permission)
if err != nil {
return nil, err
}
dbCommands = append(dbCommands, types.SetResourcePermissionsCommand{
User: accesscontrol.User{ID: cmd.UserID},
TeamID: cmd.TeamID,
BuiltinRole: cmd.BuiltinRole,
SetResourcePermissionCommand: types.SetResourcePermissionCommand{
Actions: actions,
Resource: s.options.Resource,
ResourceID: resourceID,
Permission: cmd.Permission,
},
})
}
return s.store.SetResourcePermissions(ctx, orgID, dbCommands, types.ResourceHooks{
User: s.options.OnSetUser,
Team: s.options.OnSetTeam,
BuiltInRole: s.options.OnSetBuiltInRole,
})
}
func (s *Service) mapActions(permission accesscontrol.ResourcePermission) string {
for _, p := range s.permissions {
if permission.Contains(s.options.PermissionsToActions[p]) {
@ -209,6 +252,10 @@ func (s *Service) validateResource(ctx context.Context, orgID int64, resourceID
}
func (s *Service) validateUser(ctx context.Context, orgID, userID int64) error {
if !s.options.Assignments.Users {
return ErrInvalidAssignment
}
if err := s.sqlStore.GetSignedInUser(ctx, &models.GetSignedInUserQuery{OrgId: orgID, UserId: userID}); err != nil {
return err
}
@ -216,6 +263,10 @@ func (s *Service) validateUser(ctx context.Context, orgID, userID int64) error {
}
func (s *Service) validateTeam(ctx context.Context, orgID, teamID int64) error {
if !s.options.Assignments.Teams {
return ErrInvalidAssignment
}
if err := s.sqlStore.GetTeamById(ctx, &models.GetTeamByIdQuery{OrgId: orgID, Id: teamID}); err != nil {
return err
}
@ -223,6 +274,10 @@ func (s *Service) validateTeam(ctx context.Context, orgID, teamID int64) error {
}
func (s *Service) validateBuiltinRole(ctx context.Context, builtinRole string) error {
if !s.options.Assignments.BuiltInRoles {
return ErrInvalidAssignment
}
if err := accesscontrol.ValidateBuiltInRoles([]string{builtinRole}); err != nil {
return err
}

View File

@ -143,6 +143,77 @@ func TestService_SetBuiltInRolePermission(t *testing.T) {
}
}
type setPermissionsTest struct {
desc string
options Options
commands []accesscontrol.SetResourcePermissionCommand
expectErr bool
}
func TestService_SetPermissions(t *testing.T) {
tests := []setPermissionsTest{
{
desc: "should set all permissions",
options: Options{
Resource: "dashboards",
Assignments: Assignments{
Users: true,
Teams: true,
BuiltInRoles: true,
},
PermissionsToActions: map[string][]string{
"View": {"dashboards:read"},
},
},
commands: []accesscontrol.SetResourcePermissionCommand{
{UserID: 1, Permission: "View"},
{TeamID: 1, Permission: "View"},
{BuiltinRole: "Editor", Permission: "View"},
},
},
{
desc: "should return error for invalid permission",
options: Options{
Resource: "dashboards",
Assignments: Assignments{
Users: true,
Teams: true,
BuiltInRoles: true,
},
PermissionsToActions: map[string][]string{
"View": {"dashboards:read"},
},
},
commands: []accesscontrol.SetResourcePermissionCommand{
{UserID: 1, Permission: "View"},
{TeamID: 1, Permission: "View"},
{BuiltinRole: "Editor", Permission: "Not real permission"},
},
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
service, sql := setupTestEnvironment(t, []*accesscontrol.Permission{}, tt.options)
// seed user
_, err := sql.CreateUser(context.Background(), models.CreateUserCommand{Login: "user", OrgId: 1})
require.NoError(t, err)
_, err = sql.CreateTeam("team", "", 1)
require.NoError(t, err)
permissions, err := service.SetPermissions(context.Background(), 1, "1", tt.commands...)
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Len(t, permissions, len(tt.commands))
}
})
}
}
func setupTestEnvironment(t *testing.T, permissions []*accesscontrol.Permission, ops Options) (*Service, *sqlstore.SQLStore) {
t.Helper()

View File

@ -5,6 +5,12 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore"
)
type ResourceHooks struct {
User UserResourceHookFunc
Team TeamResourceHookFunc
BuiltInRole BuiltinResourceHookFunc
}
type UserResourceHookFunc func(session *sqlstore.DBSession, orgID int64, user accesscontrol.User, resourceID, permission string) error
type TeamResourceHookFunc func(session *sqlstore.DBSession, orgID, teamID int64, resourceID, permission string) error
type BuiltinResourceHookFunc func(session *sqlstore.DBSession, orgID int64, builtInRole, resourceID, permission string) error

View File

@ -0,0 +1,25 @@
package types
import "github.com/grafana/grafana/pkg/services/accesscontrol"
type SetResourcePermissionCommand struct {
Actions []string
Resource string
ResourceID string
Permission string
}
type SetResourcePermissionsCommand struct {
User accesscontrol.User
TeamID int64
BuiltinRole string
SetResourcePermissionCommand
}
type GetResourcesPermissionsQuery struct {
Actions []string
Resource string
ResourceIDs []string
OnlyManaged bool
}