Dashboards: Allow updating a dashboard if the user doesn't have access to the parent folder (#78075)

* change where folder checks are done for dash creation/updates

* add test for folder not being found

* test fixes

* more test fixes

* add nlint directive to where folder IDs are used

* fix bad merge

* fix test
This commit is contained in:
Ieva
2023-11-16 11:11:35 +00:00
committed by GitHub
parent ba717454e1
commit b0448b92e5
9 changed files with 42 additions and 72 deletions
+1 -16
View File
@@ -300,20 +300,6 @@ func getExistingDashboardByIDOrUIDForUpdate(sess *db.Session, dash *dashboards.D
}
}
// nolint:staticcheck
if dash.FolderID > 0 {
var existingFolder dashboards.Dashboard
folderExists, err := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgID, dash.FolderID,
dialect.BooleanStr(true)).Get(&existingFolder)
if err != nil {
return false, fmt.Errorf("SQL query for folder failed: %w", err)
}
if !folderExists {
return false, dashboards.ErrDashboardFolderNotFound
}
}
if !dashWithIdExists && !dashWithUidExists {
return false, nil
}
@@ -335,8 +321,7 @@ func getExistingDashboardByIDOrUIDForUpdate(sess *db.Session, dash *dashboards.D
return isParentFolderChanged, dashboards.ErrDashboardTypeMismatch
}
// nolint:staticcheck
if !dash.IsFolder && dash.FolderID != existing.FolderID {
if !dash.IsFolder && dash.FolderUID != existing.FolderUID {
isParentFolderChanged = true
}
@@ -132,6 +132,23 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
}
}
// Validate folder
if dash.FolderUID != "" {
folder, err := dr.folderStore.GetFolderByUID(ctx, dash.OrgID, dash.FolderUID)
if err != nil {
return nil, err
}
// nolint:staticcheck
dash.FolderID = folder.ID
} else if dash.FolderID != 0 { // nolint:staticcheck
// nolint:staticcheck
folder, err := dr.folderStore.GetFolderByID(ctx, dash.OrgID, dash.FolderID)
if err != nil {
return nil, err
}
dash.FolderUID = folder.UID
}
isParentFolderChanged, err := dr.dashboardStore.ValidateDashboardBeforeSave(ctx, dash, dto.Overwrite)
if err != nil {
return nil, err
@@ -481,7 +481,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
}
err := callSaveWithError(t, cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardFolderNotFound, err)
assert.Equal(t, dashboards.ErrFolderNotFound, err)
})
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
@@ -93,6 +93,16 @@ func TestDashboardService(t *testing.T) {
}
})
t.Run("Should return validation error if a folder that is specified can't be found", func(t *testing.T) {
dto.Dashboard = dashboards.NewDashboard("Dash")
dto.Dashboard.FolderUID = "non-existing-folder"
folderStore := foldertest.FakeFolderStore{}
folderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(nil, dashboards.ErrFolderNotFound).Once()
service.folderStore = &folderStore
_, err := service.SaveDashboard(context.Background(), dto, false)
require.Equal(t, err, dashboards.ErrFolderNotFound)
})
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
fakeStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.Anything, mock.AnythingOfType("bool")).Return(true, nil).Once()
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
+6 -5
View File
@@ -858,13 +858,14 @@ func (s *Service) buildSaveDashboardCommand(ctx context.Context, dto *dashboards
return nil, dashboards.ErrDashboardTitleEmpty
}
// nolint:staticcheck
if dash.IsFolder && dash.FolderID > 0 {
return nil, dashboards.ErrDashboardFolderCannotHaveParent
if strings.EqualFold(dash.Title, dashboards.RootFolderName) {
return nil, dashboards.ErrDashboardFolderNameExists
}
if dash.IsFolder && strings.EqualFold(dash.Title, dashboards.RootFolderName) {
return nil, dashboards.ErrDashboardFolderNameExists
if dash.FolderUID != "" {
if _, err := s.dashboardFolderStore.GetFolderByUID(ctx, dash.OrgID, dash.FolderUID); err != nil {
return nil, err
}
}
if !util.IsValidShortUID(dash.UID) {
@@ -833,6 +833,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
nestedFolderUser := &user.SignedInUser{UserID: 1, OrgID: orgID, Permissions: map[int64]map[string][]string{}}
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("some_parent")}}
@@ -904,6 +905,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, orgID, dashboardFolder.ID).Return(f, nil)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, orgID, dashboardFolder.UID).Return(f, nil)
nestedFolderStore := NewFakeStore()
nestedFolderStore.ExpectedParentFolders = []*folder.Folder{
@@ -914,10 +916,10 @@ func TestNestedFolderService(t *testing.T) {
}
cmd := folder.CreateFolderCommand{
ParentUID: "myFolder1",
ParentUID: dashboardFolder.UID,
OrgID: orgID,
Title: "myFolder",
UID: "myFolder",
Title: "myFolder1",
UID: "myFolder1",
SignedInUser: usr,
}
@@ -1135,6 +1137,7 @@ func TestNestedFolderService(t *testing.T) {
dashboardFolderStore := foldertest.NewFakeFolderStore(t)
dashboardFolderStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil)
dashboardFolderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil)
parents := make([]*folder.Folder, 0, folder.MaxNestedFolderDepth)
for i := 0; i < folder.MaxNestedFolderDepth; i++ {