Browse Source

dashboards: reject updates of provisioned dashboards

bergquist 7 years ago
parent
commit
627df67992

+ 2 - 1
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)
 	}
 

+ 1 - 0
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},
 			}
 

+ 22 - 20
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 {

+ 22 - 0
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,

+ 32 - 1
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")
 				})

+ 6 - 1
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
 }
 

+ 7 - 0
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)
+			})
 		})
 	})
 }

+ 4 - 1
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)