From 627df67992e58f64e12bdbae1317ca524cbaa1e7 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 27 Mar 2018 15:12:47 +0200 Subject: [PATCH] dashboards: reject updates of provisioned dashboards --- pkg/api/dashboard.go | 3 +- pkg/api/dashboard_test.go | 1 + pkg/models/dashboards.go | 42 ++++++++++--------- pkg/services/dashboards/dashboard_service.go | 22 ++++++++++ .../dashboards/dashboard_service_test.go | 33 ++++++++++++++- .../sqlstore/dashboard_provisioning.go | 7 +++- .../sqlstore/dashboard_provisioning_test.go | 7 ++++ .../dashboard_service_integration_test.go | 5 ++- 8 files changed, 96 insertions(+), 24 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index e5e4fc560e1..24751896f00 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -235,7 +235,8 @@ func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response { err == m.ErrDashboardWithSameUIDExists || err == m.ErrFolderNotFound || err == m.ErrDashboardFolderCannotHaveParent || - err == m.ErrDashboardFolderNameExists { + err == m.ErrDashboardFolderNameExists || + err == m.ErrDashboardCannotSaveProvisionedDashboard { return Error(400, err.Error(), nil) } diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 0d87023ce40..3d0cb93f0db 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -720,6 +720,7 @@ func TestDashboardApiEndpoint(t *testing.T) { {SaveError: m.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403}, {SaveError: m.ErrDashboardInvalidUid, ExpectedStatusCode: 400}, {SaveError: m.ErrDashboardUidToLong, ExpectedStatusCode: 400}, + {SaveError: m.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400}, {SaveError: m.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412}, } diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index e4f0758fc19..1d52246dcc8 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -13,26 +13,28 @@ import ( // Typed errors var ( - ErrDashboardNotFound = errors.New("Dashboard not found") - ErrDashboardFolderNotFound = errors.New("Folder not found") - ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found") - ErrDashboardWithSameUIDExists = errors.New("A dashboard with the same uid already exists") - ErrDashboardWithSameNameInFolderExists = errors.New("A dashboard with the same name in the folder already exists") - ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else") - ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty") - ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder") - ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard") - ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data") - ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists") - ErrDashboardFailedGenerateUniqueUid = errors.New("Failed to generate unique dashboard id") - ErrDashboardTypeMismatch = errors.New("Dashboard cannot be changed to a folder") - ErrDashboardFolderWithSameNameAsDashboard = errors.New("Folder name cannot be the same as one of its dashboards") - ErrDashboardWithSameNameAsFolder = errors.New("Dashboard name cannot be the same as folder") - ErrDashboardFolderNameExists = errors.New("A folder with that name already exists") - ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard") - ErrDashboardInvalidUid = errors.New("uid contains illegal characters") - ErrDashboardUidToLong = errors.New("uid to long. max 40 characters") - RootFolderName = "General" + ErrDashboardNotFound = errors.New("Dashboard not found") + ErrDashboardFolderNotFound = errors.New("Folder not found") + ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found") + ErrDashboardWithSameUIDExists = errors.New("A dashboard with the same uid already exists") + ErrDashboardWithSameNameInFolderExists = errors.New("A dashboard with the same name in the folder already exists") + ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else") + ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty") + ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder") + ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard") + ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data") + ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists") + ErrDashboardFailedGenerateUniqueUid = errors.New("Failed to generate unique dashboard id") + ErrDashboardTypeMismatch = errors.New("Dashboard cannot be changed to a folder") + ErrDashboardFolderWithSameNameAsDashboard = errors.New("Folder name cannot be the same as one of its dashboards") + ErrDashboardWithSameNameAsFolder = errors.New("Dashboard name cannot be the same as folder") + ErrDashboardFolderNameExists = errors.New("A folder with that name already exists") + ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard") + ErrDashboardInvalidUid = errors.New("uid contains illegal characters") + ErrDashboardUidToLong = errors.New("uid to long. max 40 characters") + ErrDashboardCannotSaveProvisionedDashboard = errors.New("Cannot save provisioned dashboards") + ErrDashboardProvisioningDoesNotExist = errors.New("Dashboard provisioning does not exist") + RootFolderName = "General" ) type UpdatePluginDashboardError struct { diff --git a/pkg/services/dashboards/dashboard_service.go b/pkg/services/dashboards/dashboard_service.go index 02a6ffc8330..7c51c412d39 100644 --- a/pkg/services/dashboards/dashboard_service.go +++ b/pkg/services/dashboards/dashboard_service.go @@ -111,6 +111,11 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, return nil, models.ErrDashboardUpdateAccessDenied } + err := dr.validateDashboardIsNotProvisioned(dash.Id) + if err != nil { + return nil, err + } + cmd := &models.SaveDashboardCommand{ Dashboard: dash.Data, Message: dto.Message, @@ -129,6 +134,23 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, return cmd, nil } +func (dr *dashboardServiceImpl) validateDashboardIsNotProvisioned(dashboardId int64) error { + dpQuery := &models.GetProvisionedDashboardByDashboardId{DashboardId: dashboardId} + err := bus.Dispatch(dpQuery) + + // provisioned dashboards cannot be saved. So we can only save + // this dashboard if ErrDashboardProvisioningDoesNotExist is returned + if err != nil && err != models.ErrDashboardProvisioningDoesNotExist { + return err + } + + if err == nil { + return models.ErrDashboardCannotSaveProvisionedDashboard + } + + return nil +} + func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error { alertCmd := models.UpdateDashboardAlertsCommand{ OrgId: dto.OrgId, diff --git a/pkg/services/dashboards/dashboard_service_test.go b/pkg/services/dashboards/dashboard_service_test.go index 965b10655b3..65c9cabcdc5 100644 --- a/pkg/services/dashboards/dashboard_service_test.go +++ b/pkg/services/dashboards/dashboard_service_test.go @@ -14,7 +14,9 @@ import ( func TestDashboardService(t *testing.T) { Convey("Dashboard service tests", t, func() { - service := dashboardServiceImpl{} + bus.ClearBusHandlers() + + service := &dashboardServiceImpl{} origNewDashboardGuardian := guardian.New guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) @@ -54,6 +56,10 @@ func TestDashboardService(t *testing.T) { return nil }) + bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardByDashboardId) error { + return models.ErrDashboardProvisioningDoesNotExist + }) + testCases := []struct { Uid string Error error @@ -77,7 +83,32 @@ func TestDashboardService(t *testing.T) { } }) + Convey("Should return validation error if dashboard is provisioned", func() { + bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardByDashboardId) error { + cmd.Result = &models.DashboardProvisioning{} + return nil + }) + + bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error { + return nil + }) + + bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error { + return nil + }) + + dto.Dashboard = models.NewDashboard("Dash") + dto.Dashboard.SetId(3) + dto.User = &models.SignedInUser{UserId: 1} + _, err := service.buildSaveDashboardCommand(dto, false) + So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard) + }) + Convey("Should return validation error if alert data is invalid", func() { + bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardByDashboardId) error { + return models.ErrDashboardProvisioningDoesNotExist + }) + bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error { return errors.New("error") }) diff --git a/pkg/services/sqlstore/dashboard_provisioning.go b/pkg/services/sqlstore/dashboard_provisioning.go index 99178d38f9c..b28b2fa1a53 100644 --- a/pkg/services/sqlstore/dashboard_provisioning.go +++ b/pkg/services/sqlstore/dashboard_provisioning.go @@ -21,12 +21,17 @@ type DashboardExtras struct { func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardByDashboardId) error { result := &models.DashboardProvisioning{} - _, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result) + exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result) if err != nil { return err } + if !exist { + return models.ErrDashboardProvisioningDoesNotExist + } + cmd.Result = result + return nil } diff --git a/pkg/services/sqlstore/dashboard_provisioning_test.go b/pkg/services/sqlstore/dashboard_provisioning_test.go index 89b3451a3ac..4bafbbff8ca 100644 --- a/pkg/services/sqlstore/dashboard_provisioning_test.go +++ b/pkg/services/sqlstore/dashboard_provisioning_test.go @@ -60,6 +60,13 @@ func TestDashboardProvisioningTest(t *testing.T) { So(query.Result.DashboardId, ShouldEqual, cmd.Result.Id) So(query.Result.Updated, ShouldEqual, now.Unix()) }) + + Convey("Can query for one provisioned dashboard2", func() { + query := &models.GetProvisionedDashboardByDashboardId{DashboardId: 3000} + + err := GetProvisionedDataByDashboardId(query) + So(err, ShouldEqual, models.ErrDashboardProvisioningDoesNotExist) + }) }) }) } diff --git a/pkg/services/sqlstore/dashboard_service_integration_test.go b/pkg/services/sqlstore/dashboard_service_integration_test.go index d005270c33c..75405558412 100644 --- a/pkg/services/sqlstore/dashboard_service_integration_test.go +++ b/pkg/services/sqlstore/dashboard_service_integration_test.go @@ -19,7 +19,6 @@ func TestIntegratedDashboardService(t *testing.T) { var testOrgId int64 = 1 Convey("Given saved folders and dashboards in organization A", func() { - bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error { return nil }) @@ -28,6 +27,10 @@ func TestIntegratedDashboardService(t *testing.T) { return nil }) + bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardByDashboardId) error { + return models.ErrDashboardProvisioningDoesNotExist + }) + savedFolder := saveTestFolder("Saved folder", testOrgId) savedDashInFolder := saveTestDashboard("Saved dash in folder", testOrgId, savedFolder.Id) saveTestDashboard("Other saved dash in folder", testOrgId, savedFolder.Id)