Access control: Move call to create default permissions into folder and dashboard service (#46186)

* Move call to create permissions into folder service

* Inject cfg, feature toggles and permissions services into dashboard
service

* Move logic to set default permissions on create dashboard from api to
service

* Move call to set default permissions on import dashboard to dashboard
service

* Set permissions for provisioned dashboard and folders in service
This commit is contained in:
Karl Persson
2022-03-10 12:58:18 +01:00
committed by GitHub
parent a327c4d7d6
commit 52decfaebc
22 changed files with 304 additions and 254 deletions
+1 -1
View File
@@ -378,7 +378,7 @@ func setupHTTPServerWithCfgDb(t *testing.T, useFakeAccessControl, enableAccessCo
RouteRegister: routeRegister,
SQLStore: store,
searchUsersService: searchusers.ProvideUsersService(db, filters.ProvideOSSSearchUserFilter()),
dashboardService: dashboardservice.ProvideDashboardService(dashboardsStore, nil),
dashboardService: dashboardservice.ProvideDashboardService(cfg, dashboardsStore, nil, features, accesscontrolmock.NewPermissionsServicesMock()),
}
// Defining the accesscontrol service has to be done before registering routes
-37
View File
@@ -17,10 +17,8 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
@@ -360,12 +358,6 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, err)
}
if newDashboard {
if err := hs.setDashboardPermissions(c, cmd, dashboard); err != nil {
hs.log.Error("Could not make user admin", "dashboard", dashboard.Title, "user", c.SignedInUser.UserId, "error", err)
}
}
// connect library panels for this dashboard after the dashboard is stored and has an ID
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(ctx, c.SignedInUser, dashboard)
if err != nil {
@@ -383,35 +375,6 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
})
}
func (hs *HTTPServer) setDashboardPermissions(c *models.ReqContext, cmd models.SaveDashboardCommand, dash *models.Dashboard) error {
inFolder := dash.FolderId > 0
if hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
resourceID := strconv.FormatInt(dash.Id, 10)
svc := hs.permissionServices.GetDashboardService()
permissions := []accesscontrol.SetResourcePermissionCommand{
{UserID: c.UserId, Permission: models.PERMISSION_ADMIN.String()},
}
if !inFolder {
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 := svc.SetPermissions(c.Req.Context(), c.OrgId, resourceID, permissions...)
if err != nil {
return err
}
} else if hs.Cfg.EditorsCanAdmin {
if err := hs.dashboardService.MakeUserAdmin(c.Req.Context(), cmd.OrgId, cmd.UserId, dash.Id, !inFolder); err != nil {
return err
}
}
return nil
}
// GetHomeDashboard returns the home dashboard.
func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) response.Response {
prefsQuery := models.GetPreferencesWithDefaultsQuery{User: c.SignedInUser}
+8 -4
View File
@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/models"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards/database"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/manager"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -26,13 +27,16 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
dashboardStore := &database.FakeDashboardStore{}
defer dashboardStore.AssertExpectations(t)
features := featuremgmt.WithFeatures()
mockSQLStore := mockstore.NewSQLStoreMock()
hs := &HTTPServer{
Cfg: settings,
dashboardService: dashboardservice.ProvideDashboardService(dashboardStore, nil),
SQLStore: mockSQLStore,
Features: featuremgmt.WithFeatures(),
Cfg: settings,
SQLStore: mockSQLStore,
Features: features,
dashboardService: dashboardservice.ProvideDashboardService(
settings, dashboardStore, nil, features, accesscontrolmock.NewPermissionsServicesMock(),
),
}
t.Run("Given user has no admin permissions", func(t *testing.T) {
+17 -8
View File
@@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database"
@@ -214,14 +215,18 @@ func TestDashboardAPIEndpoint(t *testing.T) {
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = fakeDash
cfg := setting.NewCfg()
features := featuremgmt.WithFeatures()
dashboardStore := database.ProvideDashboardStore(sqlstore.InitTestDB(t))
hs := &HTTPServer{
Cfg: setting.NewCfg(),
Cfg: cfg,
Live: newTestLive(t),
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
dashboardService: service.ProvideDashboardService(dashboardStore, nil),
SQLStore: mockSQLStore,
dashboardService: service.ProvideDashboardService(
cfg, dashboardStore, nil, features, accesscontrolmock.NewPermissionsServicesMock(),
),
}
hs.SQLStore = mockSQLStore
@@ -934,14 +939,18 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
libraryPanelsService := mockLibraryPanelService{}
libraryElementsService := mockLibraryElementService{}
cfg := setting.NewCfg()
features := featuremgmt.WithFeatures()
hs := &HTTPServer{
Cfg: setting.NewCfg(),
LibraryPanelService: &libraryPanelsService,
LibraryElementService: &libraryElementsService,
ProvisioningService: provisioningService,
dashboardProvisioningService: service.ProvideDashboardService(dashboardStore, nil),
SQLStore: sc.sqlStore,
Cfg: cfg,
LibraryPanelService: &libraryPanelsService,
LibraryElementService: &libraryElementsService,
SQLStore: sc.sqlStore,
ProvisioningService: provisioningService,
dashboardProvisioningService: service.ProvideDashboardService(
cfg, dashboardStore, nil, features, accesscontrolmock.NewPermissionsServicesMock(),
),
}
hs.callGetDashboard(sc)
-28
View File
@@ -11,8 +11,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"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/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/util"
@@ -73,36 +71,10 @@ func (hs *HTTPServer) CreateFolder(c *models.ReqContext) response.Response {
return apierrors.ToFolderErrorResponse(err)
}
if err := hs.setFolderPermission(c, folder.Id); err != nil {
hs.log.Error("Could not make user admin", "folder", folder.Title, "user",
c.SignedInUser.UserId, "error", err)
}
g := guardian.New(c.Req.Context(), folder.Id, c.OrgId, c.SignedInUser)
return response.JSON(200, hs.toFolderDto(c.Req.Context(), g, folder))
}
func (hs *HTTPServer) setFolderPermission(c *models.ReqContext, folderID int64) error {
if hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
resourceID := strconv.FormatInt(folderID, 10)
svc := hs.permissionServices.GetFolderService()
_, err := svc.SetPermissions(c.Req.Context(), c.OrgId, resourceID, []accesscontrol.SetResourcePermissionCommand{
{UserID: c.UserId, Permission: models.PERMISSION_ADMIN.String()},
{BuiltinRole: string(models.ROLE_EDITOR), Permission: models.PERMISSION_EDIT.String()},
{BuiltinRole: string(models.ROLE_VIEWER), Permission: models.PERMISSION_VIEW.String()},
}...)
if err != nil {
return err
}
} else if hs.Cfg.EditorsCanAdmin {
if err := hs.folderService.MakeUserAdmin(c.Req.Context(), c.OrgId, c.UserId, folderID, true); err != nil {
return err
}
}
return nil
}
func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
cmd := models.UpdateFolderCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
+14 -1
View File
@@ -5,6 +5,8 @@ import (
"fmt"
"testing"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -32,7 +34,18 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
dashboardStore := &database.FakeDashboardStore{}
defer dashboardStore.AssertExpectations(t)
hs := &HTTPServer{Cfg: settings, folderService: folderService, dashboardService: service.ProvideDashboardService(dashboardStore, nil), Features: featuremgmt.WithFeatures()}
features := featuremgmt.WithFeatures()
permissionsServices := accesscontrolmock.NewPermissionsServicesMock()
hs := &HTTPServer{
Cfg: settings,
Features: features,
folderService: folderService,
permissionServices: permissionsServices,
dashboardService: service.ProvideDashboardService(
settings, dashboardStore, nil, features, permissionsServices,
),
}
t.Run("Given folder not exists", func(t *testing.T) {
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrFolderNotFound).Twice()