dashboards: reject updates of provisioned dashboards

This commit is contained in:
bergquist 2018-03-27 15:12:47 +02:00
parent d6faa3d06f
commit 627df67992
8 changed files with 96 additions and 24 deletions

View File

@ -235,7 +235,8 @@ func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response {
err == m.ErrDashboardWithSameUIDExists || err == m.ErrDashboardWithSameUIDExists ||
err == m.ErrFolderNotFound || err == m.ErrFolderNotFound ||
err == m.ErrDashboardFolderCannotHaveParent || err == m.ErrDashboardFolderCannotHaveParent ||
err == m.ErrDashboardFolderNameExists { err == m.ErrDashboardFolderNameExists ||
err == m.ErrDashboardCannotSaveProvisionedDashboard {
return Error(400, err.Error(), nil) return Error(400, err.Error(), nil)
} }

View File

@ -720,6 +720,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
{SaveError: m.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403}, {SaveError: m.ErrDashboardUpdateAccessDenied, ExpectedStatusCode: 403},
{SaveError: m.ErrDashboardInvalidUid, ExpectedStatusCode: 400}, {SaveError: m.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{SaveError: m.ErrDashboardUidToLong, ExpectedStatusCode: 400}, {SaveError: m.ErrDashboardUidToLong, ExpectedStatusCode: 400},
{SaveError: m.ErrDashboardCannotSaveProvisionedDashboard, ExpectedStatusCode: 400},
{SaveError: m.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412}, {SaveError: m.UpdatePluginDashboardError{PluginId: "plug"}, ExpectedStatusCode: 412},
} }

View File

@ -13,26 +13,28 @@ import (
// Typed errors // Typed errors
var ( var (
ErrDashboardNotFound = errors.New("Dashboard not found") ErrDashboardNotFound = errors.New("Dashboard not found")
ErrDashboardFolderNotFound = errors.New("Folder not found") ErrDashboardFolderNotFound = errors.New("Folder not found")
ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found") ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found")
ErrDashboardWithSameUIDExists = errors.New("A dashboard with the same uid already exists") 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") 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") ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty") ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty")
ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder") ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder")
ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard") ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard")
ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data") ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data")
ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists") ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists")
ErrDashboardFailedGenerateUniqueUid = errors.New("Failed to generate unique dashboard id") ErrDashboardFailedGenerateUniqueUid = errors.New("Failed to generate unique dashboard id")
ErrDashboardTypeMismatch = errors.New("Dashboard cannot be changed to a folder") ErrDashboardTypeMismatch = errors.New("Dashboard cannot be changed to a folder")
ErrDashboardFolderWithSameNameAsDashboard = errors.New("Folder name cannot be the same as one of its dashboards") 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") ErrDashboardWithSameNameAsFolder = errors.New("Dashboard name cannot be the same as folder")
ErrDashboardFolderNameExists = errors.New("A folder with that name already exists") ErrDashboardFolderNameExists = errors.New("A folder with that name already exists")
ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard") ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard")
ErrDashboardInvalidUid = errors.New("uid contains illegal characters") ErrDashboardInvalidUid = errors.New("uid contains illegal characters")
ErrDashboardUidToLong = errors.New("uid to long. max 40 characters") ErrDashboardUidToLong = errors.New("uid to long. max 40 characters")
RootFolderName = "General" ErrDashboardCannotSaveProvisionedDashboard = errors.New("Cannot save provisioned dashboards")
ErrDashboardProvisioningDoesNotExist = errors.New("Dashboard provisioning does not exist")
RootFolderName = "General"
) )
type UpdatePluginDashboardError struct { type UpdatePluginDashboardError struct {

View File

@ -111,6 +111,11 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
return nil, models.ErrDashboardUpdateAccessDenied return nil, models.ErrDashboardUpdateAccessDenied
} }
err := dr.validateDashboardIsNotProvisioned(dash.Id)
if err != nil {
return nil, err
}
cmd := &models.SaveDashboardCommand{ cmd := &models.SaveDashboardCommand{
Dashboard: dash.Data, Dashboard: dash.Data,
Message: dto.Message, Message: dto.Message,
@ -129,6 +134,23 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
return cmd, nil 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 { func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error {
alertCmd := models.UpdateDashboardAlertsCommand{ alertCmd := models.UpdateDashboardAlertsCommand{
OrgId: dto.OrgId, OrgId: dto.OrgId,

View File

@ -14,7 +14,9 @@ import (
func TestDashboardService(t *testing.T) { func TestDashboardService(t *testing.T) {
Convey("Dashboard service tests", t, func() { Convey("Dashboard service tests", t, func() {
service := dashboardServiceImpl{} bus.ClearBusHandlers()
service := &dashboardServiceImpl{}
origNewDashboardGuardian := guardian.New origNewDashboardGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
@ -54,6 +56,10 @@ func TestDashboardService(t *testing.T) {
return nil return nil
}) })
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardByDashboardId) error {
return models.ErrDashboardProvisioningDoesNotExist
})
testCases := []struct { testCases := []struct {
Uid string Uid string
Error error 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() { 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 { bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
return errors.New("error") return errors.New("error")
}) })

View File

@ -21,12 +21,17 @@ type DashboardExtras struct {
func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardByDashboardId) error { func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardByDashboardId) error {
result := &models.DashboardProvisioning{} 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 { if err != nil {
return err return err
} }
if !exist {
return models.ErrDashboardProvisioningDoesNotExist
}
cmd.Result = result cmd.Result = result
return nil return nil
} }

View File

@ -60,6 +60,13 @@ func TestDashboardProvisioningTest(t *testing.T) {
So(query.Result.DashboardId, ShouldEqual, cmd.Result.Id) So(query.Result.DashboardId, ShouldEqual, cmd.Result.Id)
So(query.Result.Updated, ShouldEqual, now.Unix()) 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)
})
}) })
}) })
} }

View File

@ -19,7 +19,6 @@ func TestIntegratedDashboardService(t *testing.T) {
var testOrgId int64 = 1 var testOrgId int64 = 1
Convey("Given saved folders and dashboards in organization A", func() { Convey("Given saved folders and dashboards in organization A", func() {
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error { bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
return nil return nil
}) })
@ -28,6 +27,10 @@ func TestIntegratedDashboardService(t *testing.T) {
return nil return nil
}) })
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardByDashboardId) error {
return models.ErrDashboardProvisioningDoesNotExist
})
savedFolder := saveTestFolder("Saved folder", testOrgId) savedFolder := saveTestFolder("Saved folder", testOrgId)
savedDashInFolder := saveTestDashboard("Saved dash in folder", testOrgId, savedFolder.Id) savedDashInFolder := saveTestDashboard("Saved dash in folder", testOrgId, savedFolder.Id)
saveTestDashboard("Other saved dash in folder", testOrgId, savedFolder.Id) saveTestDashboard("Other saved dash in folder", testOrgId, savedFolder.Id)