LibraryPanels: Deletes library panels during folder deletion (#31572)

* Refactor: adds permissions for library panel creation

* Refactor: checks folder permissions for patch requests

* Chore: changes after PR comments

* Refactor: adds permissions to delete

* Refactor: moves get all permission tests out of get all tests

* Chore: move out get all tests to a separate file

* Refactor: adds permissions to get handler

* Refactor: fixes a bug with getting library panels in General folder

* Refactor: adds permissions for connect/disconnect

* Refactor: adds permissions and tests for get connected dashboards

* Tests: adds tests for connected dashboards in General Folder

* LibraryPanels: Deletes library panels during folder deletion

* LibraryPanels: Deletes library panels during folder deletion

* Update pkg/api/folder.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/librarypanels/librarypanels_permissions_test.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Chore: updates after PR comments

* Chore: forgot to change some function signatures

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Hugo Häggmark
2021-03-02 10:34:01 +01:00
committed by GitHub
parent e13a48166b
commit 4bc6a7c407
7 changed files with 127 additions and 2 deletions

View File

@@ -231,6 +231,59 @@ func (lps *LibraryPanelService) disconnectLibraryPanelsForDashboard(c *models.Re
})
}
// deleteLibraryPanelsInFolder deletes all Library Panels for a folder.
func (lps *LibraryPanelService) deleteLibraryPanelsInFolder(c *models.ReqContext, folderUID string) error {
return lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
var folderUIDs []struct {
ID int64 `xorm:"id"`
}
err := session.SQL("SELECT id from dashboard WHERE uid=? AND org_id=? AND is_folder=1", folderUID, c.SignedInUser.OrgId).Find(&folderUIDs)
if err != nil {
return err
}
if len(folderUIDs) != 1 {
return fmt.Errorf("found %d folders, while expecting at most one", len(folderUIDs))
}
folderID := folderUIDs[0].ID
if err := requirePermissionsOnFolder(c.SignedInUser, folderID); err != nil {
return err
}
var dashIDs []struct {
DashboardID int64 `xorm:"dashboard_id"`
}
sql := "SELECT lpd.dashboard_id FROM library_panel AS lp"
sql += " INNER JOIN library_panel_dashboard lpd on lp.id = lpd.librarypanel_id"
sql += " WHERE lp.folder_id=? AND lp.org_id=?"
err = session.SQL(sql, folderID, c.SignedInUser.OrgId).Find(&dashIDs)
if err != nil {
return err
}
if len(dashIDs) > 0 {
return ErrFolderHasConnectedLibraryPanels
}
var panelIDs []struct {
ID int64 `xorm:"id"`
}
err = session.SQL("SELECT id from library_panel WHERE folder_id=? AND org_id=?", folderID, c.SignedInUser.OrgId).Find(&panelIDs)
if err != nil {
return err
}
for _, panelID := range panelIDs {
_, err := session.Exec("DELETE FROM library_panel_dashboard WHERE librarypanel_id=?", panelID.ID)
if err != nil {
return err
}
}
if _, err := session.Exec("DELETE FROM library_panel WHERE folder_id=? AND org_id=?", folderID, c.SignedInUser.OrgId); err != nil {
return err
}
return nil
})
}
func getLibraryPanel(session *sqlstore.DBSession, uid string, orgID int64) (LibraryPanelWithMeta, error) {
libraryPanels := make([]LibraryPanelWithMeta, 0)
sql := sqlStatmentLibrayPanelDTOWithMeta + "WHERE lp.uid=? AND lp.org_id=?"

View File

@@ -219,6 +219,13 @@ func (lps *LibraryPanelService) DisconnectLibraryPanelsForDashboard(c *models.Re
return lps.disconnectLibraryPanelsForDashboard(c, dash.Id, panelCount)
}
func (lps *LibraryPanelService) DeleteLibraryPanelsInFolder(c *models.ReqContext, folderUID string) error {
if !lps.IsEnabled() {
return nil
}
return lps.deleteLibraryPanelsInFolder(c, folderUID)
}
// AddMigration defines database migrations.
// If Panel Library is not enabled does nothing.
func (lps *LibraryPanelService) AddMigration(mg *migrator.Migrator) {

View File

@@ -149,6 +149,25 @@ func TestLibraryPanelPermissions(t *testing.T) {
resp = sc.service.disconnectHandler(sc.reqContext)
require.Equal(t, testCase.status, resp.Status())
})
testScenario(t, fmt.Sprintf("When %s tries to delete all library panels in a folder with %s, it should return correct status", testCase.role, testCase.desc),
func(t *testing.T, sc scenarioContext) {
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
cmd := getCreateCommand(folder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, cmd)
validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role
err := sc.service.DeleteLibraryPanelsInFolder(sc.reqContext, folder.Uid)
switch testCase.status {
case 200:
require.NoError(t, err)
case 403:
require.EqualError(t, err, models.ErrFolderAccessDenied.Error())
default:
t.Fatalf("Unrecognized test case status %d", testCase.status)
}
})
}
var generalFolderCases = []struct {

View File

@@ -630,6 +630,38 @@ func TestDisconnectLibraryPanelsForDashboard(t *testing.T) {
})
}
func TestDeleteLibraryPanelsInFolder(t *testing.T) {
scenarioWithLibraryPanel(t, "When an admin tries to delete a folder that contains connected library panels, it should fail",
func(t *testing.T, sc scenarioContext) {
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
resp := sc.service.connectHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.service.DeleteLibraryPanelsInFolder(sc.reqContext, sc.folder.Uid)
require.EqualError(t, err, ErrFolderHasConnectedLibraryPanels.Error())
})
scenarioWithLibraryPanel(t, "When an admin tries to delete a folder that contains disconnected library panels, it should delete all disconnected library panels too",
func(t *testing.T, sc scenarioContext) {
resp := sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result libraryPanelsResult
err := json.Unmarshal(resp.Body(), &result)
require.NoError(t, err)
require.NotNil(t, result.Result)
require.Equal(t, 1, len(result.Result))
err = sc.service.DeleteLibraryPanelsInFolder(sc.reqContext, sc.folder.Uid)
require.NoError(t, err)
resp = sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err = json.Unmarshal(resp.Body(), &result)
require.NoError(t, err)
require.NotNil(t, result.Result)
require.Equal(t, 0, len(result.Result))
})
}
type libraryPanel struct {
ID int64 `json:"id"`
OrgID int64 `json:"orgId"`

View File

@@ -96,6 +96,8 @@ var (
errLibraryPanelHeaderUIDMissing = errors.New("library panel header is missing required property uid")
// errLibraryPanelHeaderNameMissing is an error for when a library panel header is missing the name property.
errLibraryPanelHeaderNameMissing = errors.New("library panel header is missing required property name")
// ErrFolderHasConnectedLibraryPanels is an error for when an user deletes a folder that contains connected library panels.
ErrFolderHasConnectedLibraryPanels = errors.New("folder contains library panels that are linked to dashboards")
)
// Commands