Access control: Use access control for dashboard and folder (#44702)

* Add actions and scopes

* add resource service for dashboard and folder

* Add dashboard guardian with fgac permission evaluation

* Add CanDelete function to guardian interface

* Add CanDelete property to folder and dashboard dto and set values

* change to correct function name

* Add accesscontrol to folder endpoints

* add access control to dashboard endpoints

* check access for nav links

* Add fixed roles for dashboard and folders

* use correct package

* add hack to override guardian Constructor if accesscontrol is enabled

* Add services

* Add function to handle api backward compatability

* Add permissionServices to HttpServer

* Set permission when new dashboard is created

* Add default permission when creating new dashboard

* Set default permission when creating folder and dashboard

* Add access control filter for dashboard search

* Add to accept list

* Add accesscontrol to dashboardimport

* Disable access control in tests

* Add check to see if user is allow to create a dashboard

* Use SetPermissions

* Use function to set several permissions at once

* remove permissions for folder and dashboard on delete

* update required permission

* set permission for provisioning

* Add CanCreate to dashboard guardian and set correct permisisons for
provisioning

* Dont set admin on folder / dashboard creation

* Add dashboard and folder permission migrations

* Add tests for CanCreate

* Add roles and update descriptions

* Solve uid to id for dashboard and folder permissions

* Add folder and dashboard actions to permission filter

* Handle viewer_can_edit flag

* set folder and dashboard permissions services

* Add dashboard permissions when importing a new dashboard

* Set access control permissions on provisioning

* Pass feature flags and only set permissions if access control is enabled

* only add default permissions for folders and dashboards without folders

* Batch create permissions in migrations


* Remove `dashboards:edit` action

* Remove unused function from interface

* Update pkg/services/guardian/accesscontrol_guardian_test.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
Karl Persson
2022-03-03 15:05:47 +01:00
committed by GitHub
parent 4caf5dbbd9
commit 4982ca3b1d
49 changed files with 2074 additions and 319 deletions

View File

@@ -0,0 +1,227 @@
package guardian
import (
"context"
"strconv"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
var permissionMap = map[string]models.PermissionType{
"View": models.PERMISSION_VIEW,
"Edit": models.PERMISSION_EDIT,
"Admin": models.PERMISSION_ADMIN,
}
var _ DashboardGuardian = new(AccessControlDashboardGuardian)
func NewAccessControlDashboardGuardian(
ctx context.Context, dashboardId int64, user *models.SignedInUser,
store *sqlstore.SQLStore, ac accesscontrol.AccessControl, permissionsServices accesscontrol.PermissionsServices,
) *AccessControlDashboardGuardian {
return &AccessControlDashboardGuardian{
ctx: ctx,
dashboardID: dashboardId,
user: user,
store: store,
ac: ac,
permissionServices: permissionsServices,
}
}
type AccessControlDashboardGuardian struct {
ctx context.Context
dashboardID int64
dashboard *models.Dashboard
user *models.SignedInUser
store *sqlstore.SQLStore
ac accesscontrol.AccessControl
permissionServices accesscontrol.PermissionsServices
}
func (a *AccessControlDashboardGuardian) CanSave() (bool, error) {
if err := a.loadDashboard(); err != nil {
return false, err
}
if a.dashboard.IsFolder {
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersWrite, folderScope(a.dashboardID)))
}
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny(
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsWrite, dashboardScope(a.dashboard.Id)),
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsWrite, folderScope(a.dashboard.FolderId)),
))
}
func (a *AccessControlDashboardGuardian) CanEdit() (bool, error) {
if err := a.loadDashboard(); err != nil {
return false, err
}
if setting.ViewersCanEdit {
return a.CanView()
}
if a.dashboard.IsFolder {
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersWrite, folderScope(a.dashboardID)))
}
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny(
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsWrite, dashboardScope(a.dashboard.Id)),
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsWrite, folderScope(a.dashboard.FolderId)),
))
}
func (a *AccessControlDashboardGuardian) CanView() (bool, error) {
if err := a.loadDashboard(); err != nil {
return false, err
}
if a.dashboard.IsFolder {
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersRead, folderScope(a.dashboardID)))
}
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny(
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsRead, dashboardScope(a.dashboard.Id)),
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsRead, folderScope(a.dashboard.FolderId)),
))
}
func (a *AccessControlDashboardGuardian) CanAdmin() (bool, error) {
if err := a.loadDashboard(); err != nil {
return false, err
}
if a.dashboard.IsFolder {
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAll(
accesscontrol.EvalPermission(accesscontrol.ActionFoldersPermissionsRead, folderScope(a.dashboard.Id)),
accesscontrol.EvalPermission(accesscontrol.ActionFoldersPermissionsWrite, folderScope(a.dashboard.Id)),
))
}
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny(
accesscontrol.EvalAll(
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsPermissionsRead, dashboardScope(a.dashboard.Id)),
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsPermissionsWrite, dashboardScope(a.dashboard.Id)),
),
accesscontrol.EvalAll(
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsPermissionsRead, folderScope(a.dashboard.FolderId)),
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsPermissionsWrite, folderScope(a.dashboard.FolderId)),
),
))
}
func (a *AccessControlDashboardGuardian) CanDelete() (bool, error) {
if err := a.loadDashboard(); err != nil {
return false, err
}
if a.dashboard.IsFolder {
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersDelete, folderScope(a.dashboard.Id)))
}
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny(
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsDelete, dashboardScope(a.dashboard.Id)),
accesscontrol.EvalPermission(accesscontrol.ActionDashboardsDelete, folderScope(a.dashboard.FolderId)),
))
}
func (a *AccessControlDashboardGuardian) CanCreate(folderID int64, isFolder bool) (bool, error) {
if isFolder {
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersCreate))
}
return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionDashboardsCreate, folderScope(folderID)))
}
func (a *AccessControlDashboardGuardian) CheckPermissionBeforeUpdate(permission models.PermissionType, updatePermissions []*models.DashboardAcl) (bool, error) {
// always true for access control
return true, nil
}
// GetAcl translate access control permissions to dashboard acl info
func (a *AccessControlDashboardGuardian) GetAcl() ([]*models.DashboardAclInfoDTO, error) {
if err := a.loadDashboard(); err != nil {
return nil, err
}
svc := a.permissionServices.GetDashboardService()
if a.dashboard.IsFolder {
svc = a.permissionServices.GetFolderService()
}
permissions, err := svc.GetPermissions(a.ctx, a.user, strconv.FormatInt(a.dashboard.Id, 10))
if err != nil {
return nil, err
}
acl := make([]*models.DashboardAclInfoDTO, 0, len(permissions))
for _, p := range permissions {
if !p.IsManaged() {
continue
}
var role *models.RoleType
if p.BuiltInRole != "" {
tmp := models.RoleType(p.BuiltInRole)
role = &tmp
}
acl = append(acl, &models.DashboardAclInfoDTO{
OrgId: a.dashboard.OrgId,
DashboardId: a.dashboard.Id,
FolderId: a.dashboard.FolderId,
Created: p.Created,
Updated: p.Updated,
UserId: p.UserId,
UserLogin: p.UserLogin,
UserEmail: p.UserEmail,
TeamId: p.TeamId,
TeamEmail: p.TeamEmail,
Team: p.Team,
Role: role,
Permission: permissionMap[svc.MapActions(p)],
PermissionName: permissionMap[svc.MapActions(p)].String(),
Uid: a.dashboard.Uid,
Title: a.dashboard.Title,
Slug: a.dashboard.Slug,
IsFolder: a.dashboard.IsFolder,
Url: a.dashboard.GetUrl(),
Inherited: false,
})
}
return acl, nil
}
func (a *AccessControlDashboardGuardian) GetACLWithoutDuplicates() ([]*models.DashboardAclInfoDTO, error) {
return a.GetAcl()
}
func (a *AccessControlDashboardGuardian) GetHiddenACL(cfg *setting.Cfg) ([]*models.DashboardAcl, error) {
// not used with access control
return nil, nil
}
func (a *AccessControlDashboardGuardian) loadDashboard() error {
if a.dashboard == nil {
query := &models.GetDashboardQuery{Id: a.dashboardID, OrgId: a.user.OrgId}
if err := a.store.GetDashboard(a.ctx, query); err != nil {
return err
}
a.dashboard = query.Result
}
return nil
}
func dashboardScope(dashboardID int64) string {
return accesscontrol.Scope("dashboards", "id", strconv.FormatInt(dashboardID, 10))
}
func folderScope(folderID int64) string {
return accesscontrol.Scope("folders", "id", strconv.FormatInt(folderID, 10))
}

View File

@@ -0,0 +1,557 @@
package guardian
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
dashdb "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
type accessControlGuardianTestCase struct {
desc string
dashboardID int64
permissions []*accesscontrol.Permission
viewersCanEdit bool
expected bool
}
func TestAccessControlDashboardGuardian_CanSave(t *testing.T) {
tests := []accessControlGuardianTestCase{
{
desc: "should be able to save with dashboard wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "dashboards:*",
},
},
expected: true,
},
{
desc: "should be able to save with folder wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "folders:*",
},
},
expected: true,
},
{
desc: "should be able to save with dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "dashboards:id:1",
},
},
expected: true,
},
{
desc: "should be able to save with folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "folders:id:0",
},
},
expected: true,
},
{
desc: "should not be able to save with incorrect dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "dashboards:id:10",
},
},
expected: false,
},
{
desc: "should not be able to save with incorrect folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "folders:id:10",
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian := setupAccessControlGuardianTest(t, tt.dashboardID, tt.permissions)
can, err := guardian.CanSave()
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
})
}
}
func TestAccessControlDashboardGuardian_CanEdit(t *testing.T) {
tests := []accessControlGuardianTestCase{
{
desc: "should be able to edit with dashboard wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "dashboards:*",
},
},
expected: true,
},
{
desc: "should be able to edit with folder wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "folders:*",
},
},
expected: true,
},
{
desc: "should be able to edit with dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "dashboards:id:1",
},
},
expected: true,
},
{
desc: "should be able to edit with folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "folders:id:0",
},
},
expected: true,
},
{
desc: "should not be able to edit with incorrect dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "dashboards:id:10",
},
},
expected: false,
},
{
desc: "should not be able to edit with incorrect folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsWrite,
Scope: "folders:id:10",
},
},
expected: false,
},
{
desc: "should be able to edit with read action when viewer_can_edit is true",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "dashboards:id:1",
},
},
viewersCanEdit: true,
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian := setupAccessControlGuardianTest(t, tt.dashboardID, tt.permissions)
if tt.viewersCanEdit {
setting.ViewersCanEdit = true
defer func() { setting.ViewersCanEdit = false }()
}
can, err := guardian.CanEdit()
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
})
}
}
func TestAccessControlDashboardGuardian_CanView(t *testing.T) {
tests := []accessControlGuardianTestCase{
{
desc: "should be able to view with dashboard wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "dashboards:*",
},
},
expected: true,
},
{
desc: "should be able to view with folder wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "folders:*",
},
},
expected: true,
},
{
desc: "should be able to view with dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "dashboards:id:1",
},
},
expected: true,
},
{
desc: "should be able to view with folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "folders:id:0",
},
},
expected: true,
},
{
desc: "should not be able to view with incorrect dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "dashboards:id:10",
},
},
expected: false,
},
{
desc: "should not be able to view with incorrect folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsRead,
Scope: "folders:id:10",
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian := setupAccessControlGuardianTest(t, tt.dashboardID, tt.permissions)
can, err := guardian.CanView()
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
})
}
}
func TestAccessControlDashboardGuardian_CanAdmin(t *testing.T) {
tests := []accessControlGuardianTestCase{
{
desc: "should be able to admin with dashboard wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsPermissionsRead,
Scope: "dashboards:*",
},
{
Action: accesscontrol.ActionDashboardsPermissionsWrite,
Scope: "dashboards:*",
},
},
expected: true,
},
{
desc: "should be able to admin with folder wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsPermissionsRead,
Scope: "folders:*",
},
{
Action: accesscontrol.ActionDashboardsPermissionsWrite,
Scope: "folders:*",
},
},
expected: true,
},
{
desc: "should be able to admin with dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsPermissionsRead,
Scope: "dashboards:id:1",
},
{
Action: accesscontrol.ActionDashboardsPermissionsWrite,
Scope: "dashboards:id:1",
},
},
expected: true,
},
{
desc: "should be able to admin with folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsPermissionsRead,
Scope: "folders:id:0",
},
{
Action: accesscontrol.ActionDashboardsPermissionsWrite,
Scope: "folders:id:0",
},
},
expected: true,
},
{
desc: "should not be able to admin with incorrect dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsPermissionsRead,
Scope: "dashboards:id:10",
},
{
Action: accesscontrol.ActionDashboardsPermissionsWrite,
Scope: "dashboards:id:10",
},
},
expected: false,
},
{
desc: "should not be able to admin with incorrect folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsPermissionsRead,
Scope: "folders:id:10",
},
{
Action: accesscontrol.ActionDashboardsPermissionsWrite,
Scope: "folders:id:10",
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian := setupAccessControlGuardianTest(t, tt.dashboardID, tt.permissions)
can, err := guardian.CanAdmin()
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
})
}
}
func TestAccessControlDashboardGuardian_CanDelete(t *testing.T) {
tests := []accessControlGuardianTestCase{
{
desc: "should be able to delete with dashboard wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsDelete,
Scope: "dashboards:*",
},
},
expected: true,
},
{
desc: "should be able to delete with folder wildcard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsDelete,
Scope: "folders:*",
},
},
expected: true,
},
{
desc: "should be able to delete with dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsDelete,
Scope: "dashboards:id:1",
},
},
expected: true,
},
{
desc: "should be able to delete with folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsDelete,
Scope: "folders:id:0",
},
},
expected: true,
},
{
desc: "should not be able to delete with incorrect dashboard scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsDelete,
Scope: "dashboards:id:10",
},
},
expected: false,
},
{
desc: "should not be able to delete with incorrect folder scope",
dashboardID: 1,
permissions: []*accesscontrol.Permission{
{
Action: accesscontrol.ActionDashboardsDelete,
Scope: "folders:id:10",
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian := setupAccessControlGuardianTest(t, tt.dashboardID, tt.permissions)
can, err := guardian.CanDelete()
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
})
}
}
type accessControlGuardianCanCreateTestCase struct {
desc string
isFolder bool
folderID int64
permissions []*accesscontrol.Permission
expected bool
}
func TestAccessControlDashboardGuardian_CanCreate(t *testing.T) {
tests := []accessControlGuardianCanCreateTestCase{
{
desc: "should be able to create dashboard in folder 0",
isFolder: false,
folderID: 0,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionDashboardsCreate, Scope: "folders:id:0"},
},
expected: true,
},
{
desc: "should be able to create dashboard in any folder",
isFolder: false,
folderID: 100,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionDashboardsCreate, Scope: "folders:*"},
},
expected: true,
},
{
desc: "should not be able to create dashboard without permissions",
isFolder: false,
folderID: 100,
permissions: []*accesscontrol.Permission{},
expected: false,
},
{
desc: "should be able to create folder with correct permissions",
isFolder: true,
folderID: 0,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionFoldersCreate},
},
expected: true,
},
{
desc: "should not be able to create folders without permissions",
isFolder: true,
folderID: 100,
permissions: []*accesscontrol.Permission{},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian := setupAccessControlGuardianTest(t, 0, tt.permissions)
can, err := guardian.CanCreate(tt.folderID, tt.isFolder)
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
})
}
}
func setupAccessControlGuardianTest(t *testing.T, dashID int64, permissions []*accesscontrol.Permission) *AccessControlDashboardGuardian {
t.Helper()
store := sqlstore.InitTestDB(t)
// seed dashboard
_, err := dashdb.ProvideDashboardStore(store).SaveDashboard(models.SaveDashboardCommand{
Dashboard: &simplejson.Json{},
UserId: 1,
OrgId: 1,
FolderId: 0,
})
require.NoError(t, err)
ac := accesscontrolmock.New().WithPermissions(permissions)
services, err := ossaccesscontrol.ProvidePermissionsServices(routing.NewRouteRegister(), store, ac, database.ProvideService(store))
require.NoError(t, err)
return NewAccessControlDashboardGuardian(context.Background(), dashID, &models.SignedInUser{OrgId: 1}, store, ac, services)
}

View File

@@ -21,7 +21,8 @@ type DashboardGuardian interface {
CanEdit() (bool, error)
CanView() (bool, error)
CanAdmin() (bool, error)
HasPermission(permission models.PermissionType) (bool, error)
CanDelete() (bool, error)
CanCreate(folderID int64, isFolder bool) (bool, error)
CheckPermissionBeforeUpdate(permission models.PermissionType, updatePermissions []*models.DashboardAcl) (bool, error)
// GetAcl returns ACL.
@@ -45,6 +46,7 @@ type dashboardGuardianImpl struct {
}
// New factory for creating a new dashboard guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var New = func(ctx context.Context, dashId int64, orgId int64, user *models.SignedInUser) DashboardGuardian {
return &dashboardGuardianImpl{
user: user,
@@ -75,6 +77,16 @@ func (g *dashboardGuardianImpl) CanAdmin() (bool, error) {
return g.HasPermission(models.PERMISSION_ADMIN)
}
func (g *dashboardGuardianImpl) CanDelete() (bool, error) {
// when using dashboard guardian without access control a user can delete a dashboard if they can save it
return g.CanSave()
}
func (g *dashboardGuardianImpl) CanCreate(_ int64, _ bool) (bool, error) {
// when using dashboard guardian without access control a user can create a dashboard if they can save it
return g.CanSave()
}
func (g *dashboardGuardianImpl) HasPermission(permission models.PermissionType) (bool, error) {
if g.user.OrgRole == models.ROLE_ADMIN {
return g.logHasPermissionResult(permission, true, nil)
@@ -325,6 +337,14 @@ func (g *FakeDashboardGuardian) CanAdmin() (bool, error) {
return g.CanAdminValue, nil
}
func (g *FakeDashboardGuardian) CanDelete() (bool, error) {
return g.CanSaveValue, nil
}
func (g *FakeDashboardGuardian) CanCreate(_ int64, _ bool) (bool, error) {
return g.CanSaveValue, nil
}
func (g *FakeDashboardGuardian) HasPermission(permission models.PermissionType) (bool, error) {
return g.HasPermissionValue, nil
}

View File

@@ -0,0 +1,22 @@
package guardian
import (
"context"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
type Provider struct{}
func ProvideService(store *sqlstore.SQLStore, ac accesscontrol.AccessControl, permissionsServices accesscontrol.PermissionsServices, features featuremgmt.FeatureToggles) *Provider {
if features.IsEnabled(featuremgmt.FlagAccesscontrol) {
// TODO: Fix this hack, see https://github.com/grafana/grafana-enterprise/issues/2935
New = func(ctx context.Context, dashId int64, orgId int64, user *models.SignedInUser) DashboardGuardian {
return NewAccessControlDashboardGuardian(ctx, dashId, user, store, ac, permissionsServices)
}
}
return &Provider{}
}