Dashboard: Fix editor specific permissions in /api (#113292)
This commit is contained in:
@@ -676,11 +676,14 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
||||
|
||||
// Validate folder
|
||||
if dash.FolderID != 0 || dash.FolderUID != "" { // nolint:staticcheck
|
||||
folder, err := dr.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||
// use a background requester, because the user may have been granted edit access to that specific dashboard, but
|
||||
// not have access to the parent folder. we should still allow editing in that case.
|
||||
ctxIdentity, backgroundRequester := identity.WithServiceIdentity(ctx, dash.OrgID)
|
||||
folder, err := dr.folderService.Get(ctxIdentity, &folder.GetFolderQuery{
|
||||
OrgID: dash.OrgID,
|
||||
UID: &dash.FolderUID,
|
||||
ID: &dash.FolderID, // nolint:staticcheck
|
||||
SignedInUser: dto.User,
|
||||
SignedInUser: backgroundRequester,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1211,5 +1211,137 @@ func TestIntegrationDashboardServicePermissions(t *testing.T) {
|
||||
assert.Contains(t, foundTitles, "dashboard in parent", "Should return dashboard in parent folder")
|
||||
assert.NotContains(t, foundTitles, "dashboard in child", "Should not return dashboard in child folder")
|
||||
})
|
||||
|
||||
t.Run("user with edit permissions on dashboard but not on parent folder should be able to edit the dashboard", func(t *testing.T) {
|
||||
testFolder := createFolder(t, grafanaListedAddr, "restricted folder")
|
||||
testDash := createDashboard(t, grafanaListedAddr, "dashboard with specific permissions", testFolder.ID, testFolder.UID) // nolint:staticcheck
|
||||
restrictedUserID := tests.CreateUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleNone),
|
||||
Login: "restricteduser",
|
||||
Password: "restricteduser",
|
||||
IsAdmin: false,
|
||||
})
|
||||
setDashboardPermissions := func(t *testing.T, grafanaListedAddr string, dashboardUID string, permissions []map[string]interface{}) {
|
||||
t.Helper()
|
||||
|
||||
permissionPayload := map[string]interface{}{
|
||||
"items": permissions,
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(permissionPayload)
|
||||
require.NoError(t, err)
|
||||
|
||||
u := fmt.Sprintf("http://admin:admin@%s/api/dashboards/uid/%s/permissions", grafanaListedAddr, dashboardUID)
|
||||
req, err := http.NewRequest(http.MethodPost, u, bytes.NewBuffer(payloadBytes))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
editPermissions := []map[string]interface{}{
|
||||
{
|
||||
"permission": 2,
|
||||
"userId": restrictedUserID,
|
||||
},
|
||||
}
|
||||
setDashboardPermissions(t, grafanaListedAddr, testDash.UID, editPermissions)
|
||||
|
||||
// user cannot access the folder
|
||||
u := fmt.Sprintf("http://restricteduser:restricteduser@%s/api/folders/%s", grafanaListedAddr, testFolder.UID)
|
||||
resp, err := http.Get(u) // nolint:gosec
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, resp.StatusCode, "User should not have access to the folder")
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
// but can edit the dashboard
|
||||
dashboardPayload := map[string]interface{}{
|
||||
"dashboard": map[string]interface{}{
|
||||
"uid": testDash.UID,
|
||||
"title": "Updated title by restricted user",
|
||||
},
|
||||
"folderUid": testFolder.UID,
|
||||
"overwrite": true,
|
||||
}
|
||||
resp, err = postDashboard(t, grafanaListedAddr, "restricteduser", "restricteduser", dashboardPayload)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode, "User should be able to edit dashboard even without folder access")
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("user with no permissions on dashboard or parent folder should not be able to edit the dashboard", func(t *testing.T) {
|
||||
testFolder := createFolder(t, grafanaListedAddr, "restricted folder")
|
||||
testDash := createDashboard(t, grafanaListedAddr, "dashboard with specific permissions", testFolder.ID, testFolder.UID) // nolint:staticcheck
|
||||
|
||||
// user cannot access the folder
|
||||
u := fmt.Sprintf("http://restricteduser:restricteduser@%s/api/folders/%s", grafanaListedAddr, testFolder.UID)
|
||||
resp, err := http.Get(u) // nolint:gosec
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, resp.StatusCode, "User should not have access to the folder")
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
// and cannot edit the dashboard
|
||||
dashboardPayload := map[string]interface{}{
|
||||
"dashboard": map[string]interface{}{
|
||||
"uid": testDash.UID,
|
||||
"title": "Updated title by restricted user",
|
||||
},
|
||||
"folderUid": testFolder.UID,
|
||||
"overwrite": true,
|
||||
}
|
||||
resp, err = postDashboard(t, grafanaListedAddr, "restricteduser", "restricteduser", dashboardPayload)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, resp.StatusCode, "User should not be able to edit dashboard without any permissions")
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("user with edit permissions on parent folder should be able to edit dashboard through permission inheritance", func(t *testing.T) {
|
||||
inheritedPermissionsUserID := tests.CreateUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(org.RoleNone),
|
||||
Login: "inheriteduser",
|
||||
Password: "inheriteduser",
|
||||
IsAdmin: false,
|
||||
})
|
||||
testFolder := createFolder(t, grafanaListedAddr, "folder with inherited permissions")
|
||||
editFolderPermissions := []map[string]interface{}{
|
||||
{
|
||||
"permission": 2,
|
||||
"userId": inheritedPermissionsUserID,
|
||||
},
|
||||
}
|
||||
setFolderPermissions(t, grafanaListedAddr, testFolder.UID, editFolderPermissions)
|
||||
|
||||
// create dashboard in the folder without specific dashboard permissions
|
||||
testDash := createDashboard(t, grafanaListedAddr, "dashboard inheriting permissions", testFolder.ID, testFolder.UID) // nolint:staticcheck
|
||||
u := fmt.Sprintf("http://inheriteduser:inheriteduser@%s/api/folders/%s", grafanaListedAddr, testFolder.UID)
|
||||
resp, err := http.Get(u) // nolint:gosec
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode, "User should have access to the folder")
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
// and can edit the dashboard by inheriting permissions from the folder
|
||||
dashboardPayload := map[string]interface{}{
|
||||
"dashboard": map[string]interface{}{
|
||||
"uid": testDash.UID,
|
||||
"title": "Updated title via inherited permissions",
|
||||
},
|
||||
"folderUid": testFolder.UID,
|
||||
"overwrite": true,
|
||||
}
|
||||
resp, err = postDashboard(t, grafanaListedAddr, "inheriteduser", "inheriteduser", dashboardPayload)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode, "User should be able to edit dashboard through inherited folder permissions")
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user