mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Access Control: Add function to set several permissions on a resource in one transaction (#44768)
This commit is contained in:
parent
178193c84b
commit
922b9465ec
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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) + `)
|
||||
`
|
||||
|
||||
|
@ -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)),
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user