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

@@ -10,6 +10,8 @@ import (
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/accesscontrol"
acmiddleware "github.com/grafana/grafana/pkg/services/accesscontrol/middleware"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/web"
)
@@ -19,21 +21,28 @@ type ImportDashboardAPI struct {
quotaService QuotaService
schemaLoaderService SchemaLoaderService
pluginStore plugins.Store
ac accesscontrol.AccessControl
}
func New(dashboardImportService dashboardimport.Service, quotaService QuotaService,
schemaLoaderService SchemaLoaderService, pluginStore plugins.Store) *ImportDashboardAPI {
schemaLoaderService SchemaLoaderService, pluginStore plugins.Store, ac accesscontrol.AccessControl) *ImportDashboardAPI {
return &ImportDashboardAPI{
dashboardImportService: dashboardImportService,
quotaService: quotaService,
schemaLoaderService: schemaLoaderService,
pluginStore: pluginStore,
ac: ac,
}
}
func (api *ImportDashboardAPI) RegisterAPIEndpoints(routeRegister routing.RouteRegister) {
authorize := acmiddleware.Middleware(api.ac)
routeRegister.Group("/api/dashboards", func(route routing.RouteRegister) {
route.Post("/import", routing.Wrap(api.ImportDashboard))
route.Post(
"/import",
authorize(middleware.ReqSignedIn, accesscontrol.EvalPermission(accesscontrol.ActionDashboardsCreate)),
routing.Wrap(api.ImportDashboard),
)
}, middleware.ReqSignedIn)
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/web/webtest"
"github.com/stretchr/testify/require"
@@ -33,7 +34,7 @@ func TestImportDashboardAPI(t *testing.T) {
},
}
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), schemaLoaderService, nil)
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), schemaLoaderService, nil, acmock.New().WithDisabled())
routeRegister := routing.NewRouteRegister()
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
s := webtest.NewServer(t, routeRegister)
@@ -124,7 +125,7 @@ func TestImportDashboardAPI(t *testing.T) {
},
}
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), schemaLoaderService, nil)
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), schemaLoaderService, nil, acmock.New().WithDisabled())
routeRegister := routing.NewRouteRegister()
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
s := webtest.NewServer(t, routeRegister)
@@ -152,7 +153,7 @@ func TestImportDashboardAPI(t *testing.T) {
t.Run("Quota reached", func(t *testing.T) {
service := &serviceMock{}
schemaLoaderService := &schemaLoaderServiceMock{}
importDashboardAPI := New(service, quotaServiceFunc(quotaReached), schemaLoaderService, nil)
importDashboardAPI := New(service, quotaServiceFunc(quotaReached), schemaLoaderService, nil, acmock.New().WithDisabled())
routeRegister := routing.NewRouteRegister()
importDashboardAPI.RegisterAPIEndpoints(routeRegister)

View File

@@ -2,14 +2,17 @@ package service
import (
"context"
"strconv"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboardimport/api"
"github.com/grafana/grafana/pkg/services/dashboardimport/utils"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/librarypanels"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/schemaloader"
@@ -18,23 +21,29 @@ import (
func ProvideService(routeRegister routing.RouteRegister,
quotaService *quota.QuotaService, schemaLoaderService *schemaloader.SchemaLoaderService,
pluginDashboardManager plugins.PluginDashboardManager, pluginStore plugins.Store,
libraryPanelService librarypanels.Service, dashboardService dashboards.DashboardService) *ImportDashboardService {
libraryPanelService librarypanels.Service, dashboardService dashboards.DashboardService,
ac accesscontrol.AccessControl, permissionsServices accesscontrol.PermissionsServices, features featuremgmt.FeatureToggles,
) *ImportDashboardService {
s := &ImportDashboardService{
pluginDashboardManager: pluginDashboardManager,
dashboardService: dashboardService,
libraryPanelService: libraryPanelService,
features: features,
pluginDashboardManager: pluginDashboardManager,
dashboardService: dashboardService,
libraryPanelService: libraryPanelService,
dashboardPermissionsService: permissionsServices.GetDashboardService(),
}
dashboardImportAPI := api.New(s, quotaService, schemaLoaderService, pluginStore)
dashboardImportAPI := api.New(s, quotaService, schemaLoaderService, pluginStore, ac)
dashboardImportAPI.RegisterAPIEndpoints(routeRegister)
return s
}
type ImportDashboardService struct {
pluginDashboardManager plugins.PluginDashboardManager
dashboardService dashboards.DashboardService
libraryPanelService librarypanels.Service
features featuremgmt.FeatureToggles
pluginDashboardManager plugins.PluginDashboardManager
dashboardService dashboards.DashboardService
libraryPanelService librarypanels.Service
dashboardPermissionsService accesscontrol.PermissionsService
}
func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*dashboardimport.ImportDashboardResponse, error) {
@@ -85,6 +94,12 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
return nil, err
}
if s.features.IsEnabled(featuremgmt.FlagAccesscontrol) {
if err := s.setDashboardPermissions(ctx, req.User, savedDash); err != nil {
return nil, err
}
}
return &dashboardimport.ImportDashboardResponse{
UID: savedDash.Uid,
PluginId: req.PluginId,
@@ -100,3 +115,24 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
Slug: savedDash.Slug,
}, nil
}
func (s *ImportDashboardService) setDashboardPermissions(ctx context.Context, user *models.SignedInUser, dashboard *models.Dashboard) error {
resourceID := strconv.FormatInt(dashboard.Id, 10)
permissions := []accesscontrol.SetResourcePermissionCommand{
{UserID: user.UserId, Permission: models.PERMISSION_ADMIN.String()},
}
if dashboard.FolderId == 0 {
permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
{BuiltinRole: string(models.ROLE_EDITOR), Permission: models.PERMISSION_EDIT.String()},
{BuiltinRole: string(models.ROLE_VIEWER), Permission: models.PERMISSION_VIEW.String()},
}...)
}
_, err := s.dashboardPermissionsService.SetPermissions(ctx, user.OrgId, resourceID, permissions...)
if err != nil {
return err
}
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/librarypanels"
"github.com/stretchr/testify/require"
)
@@ -55,6 +56,7 @@ func TestImportDashboardService(t *testing.T) {
pluginDashboardManager: pluginDashboardManager,
dashboardService: dashboardService,
libraryPanelService: libraryPanelService,
features: featuremgmt.WithFeatures(),
}
req := &dashboardimport.ImportDashboardRequest{
@@ -104,6 +106,7 @@ func TestImportDashboardService(t *testing.T) {
}
libraryPanelService := &libraryPanelServiceMock{}
s := &ImportDashboardService{
features: featuremgmt.WithFeatures(),
dashboardService: dashboardService,
libraryPanelService: libraryPanelService,
}