Restore dashboards: Add RBAC (#90270)
* Restore dashboards: Add RBAC * Add check to navtree * Prevent non-admins from searching deleted dbs * Add check to the route * Cleanup * Update translations * Update API permissions * Correct permissions * Update warning message * Update translation * Return 401 for deleted query without admin role
This commit is contained in:
+3
-3
@@ -170,7 +170,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
}
|
||||
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagDashboardRestore) {
|
||||
r.Get("/dashboard/recently-deleted", reqSignedIn, hs.Index)
|
||||
r.Get("/dashboard/recently-deleted", reqOrgAdmin, hs.Index)
|
||||
}
|
||||
|
||||
r.Get("/explore", authorize(ac.EvalPermission(ac.ActionDatasourcesExplore)), hs.Index)
|
||||
@@ -477,8 +477,8 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
dashUidRoute.Get("/versions/:id", authorize(ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.GetDashboardVersion))
|
||||
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagDashboardRestore) {
|
||||
dashUidRoute.Patch("/trash", authorize(ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.RestoreDeletedDashboard))
|
||||
dashUidRoute.Delete("/trash", authorize(ac.EvalPermission(dashboards.ActionDashboardsDelete)), routing.Wrap(hs.HardDeleteDashboardByUID))
|
||||
dashUidRoute.Patch("/trash", reqOrgAdmin, routing.Wrap(hs.RestoreDeletedDashboard))
|
||||
dashUidRoute.Delete("/trash", reqOrgAdmin, routing.Wrap(hs.HardDeleteDashboardByUID))
|
||||
}
|
||||
|
||||
dashUidRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
@@ -31,6 +33,10 @@ func (hs *HTTPServer) Search(c *contextmodel.ReqContext) response.Response {
|
||||
deleted := c.Query("deleted")
|
||||
permission := dashboardaccess.PERMISSION_VIEW
|
||||
|
||||
if deleted == "true" && c.SignedInUser.GetOrgRole() != org.RoleAdmin {
|
||||
return response.Error(http.StatusUnauthorized, "Unauthorized", nil)
|
||||
}
|
||||
|
||||
if limit > 5000 {
|
||||
return response.Error(http.StatusUnprocessableEntity, "Limit is above maximum allowed (5000), use page parameter to access hits beyond limit", nil)
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
|
||||
if s.cfg.SnapshotEnabled {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
||||
Text: "Snapshots",
|
||||
SubTitle: "Interactive, publically available, point-in-time representations of dashboards",
|
||||
SubTitle: "Interactive, publicly available, point-in-time representations of dashboards",
|
||||
Id: "dashboards/snapshots",
|
||||
Url: s.cfg.AppSubURL + "/dashboard/snapshots",
|
||||
Icon: "camera",
|
||||
@@ -354,9 +354,9 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
|
||||
})
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagDashboardRestoreUI) && hasAccess(ac.EvalPermission(dashboards.ActionDashboardsDelete)) {
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagDashboardRestoreUI) && c.SignedInUser.GetOrgRole() == org.RoleAdmin {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
||||
Text: "Recently Deleted",
|
||||
Text: "Recently deleted",
|
||||
SubTitle: "Any items listed here for more than 30 days will be automatically deleted.",
|
||||
Id: "dashboards/recently-deleted",
|
||||
Url: s.cfg.AppSubURL + "/dashboard/recently-deleted",
|
||||
|
||||
@@ -41,7 +41,7 @@ export function getNavTitle(navId: string | undefined) {
|
||||
case 'dashboards/public':
|
||||
return t('nav.public.title', 'Public dashboards');
|
||||
case 'dashboards/recently-deleted':
|
||||
return t('nav.recently-deleted.title', 'Recently Deleted');
|
||||
return t('nav.recently-deleted.title', 'Recently deleted');
|
||||
case 'dashboards/new':
|
||||
return t('nav.new-dashboard.title', 'New dashboard');
|
||||
case 'dashboards/folder/new':
|
||||
|
||||
@@ -40,8 +40,8 @@ export const DeleteModal = ({ onConfirm, onDismiss, selectedItems, ...props }: P
|
||||
<Text element="p">
|
||||
<Trans i18nKey="browse-dashboards.action.delete-modal-restore-dashboards-text">
|
||||
This action will delete the selected folders immediately but the selected dashboards will be marked
|
||||
for deletion in 30 days. You can restore the dashboards anytime before the 30 days expires. Folders
|
||||
cannot be restored.
|
||||
for deletion in 30 days. Your organization administrator can restore the dashboards anytime before the
|
||||
30 days expire. Folders cannot be restored.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Space v={2} />
|
||||
|
||||
@@ -436,7 +436,7 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
},
|
||||
config.featureToggles.dashboardRestoreUI && {
|
||||
path: '/dashboard/recently-deleted',
|
||||
roles: () => contextSrv.evaluatePermission([AccessControlAction.DashboardsDelete]),
|
||||
roles: () => ['Admin'],
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "RecentlyDeletedPage" */ 'app/features/browse-dashboards/RecentlyDeletedPage')
|
||||
),
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
"delete-button": "Delete",
|
||||
"delete-modal-invalid-text": "One or more folders contain library panels or alert rules. Delete these first in order to proceed.",
|
||||
"delete-modal-invalid-title": "Cannot delete folder",
|
||||
"delete-modal-restore-dashboards-text": "This action will delete the selected folders immediately but the selected dashboards will be marked for deletion in 30 days. You can restore the dashboards anytime before the 30 days expires. Folders cannot be restored.",
|
||||
"delete-modal-restore-dashboards-text": "This action will delete the selected folders immediately but the selected dashboards will be marked for deletion in 30 days. Your organization administrator can restore the dashboards anytime before the 30 days expire. Folders cannot be restored.",
|
||||
"delete-modal-text": "This action will delete the following content:",
|
||||
"delete-modal-title": "Delete",
|
||||
"deleting": "Deleting...",
|
||||
@@ -1309,7 +1309,7 @@
|
||||
},
|
||||
"recently-deleted": {
|
||||
"subtitle": "Any items listed here for more than 30 days will be automatically deleted.",
|
||||
"title": "Recently Deleted"
|
||||
"title": "Recently deleted"
|
||||
},
|
||||
"recorded-queries": {
|
||||
"title": "Recorded queries"
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
"delete-button": "Đęľęŧę",
|
||||
"delete-modal-invalid-text": "Øʼnę őř mőřę ƒőľđęřş čőʼnŧäįʼn ľįþřäřy päʼnęľş őř äľęřŧ řūľęş. Đęľęŧę ŧĥęşę ƒįřşŧ įʼn őřđęř ŧő přőčęęđ.",
|
||||
"delete-modal-invalid-title": "Cäʼnʼnőŧ đęľęŧę ƒőľđęř",
|
||||
"delete-modal-restore-dashboards-text": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę ŧĥę şęľęčŧęđ ƒőľđęřş įmmęđįäŧęľy þūŧ ŧĥę şęľęčŧęđ đäşĥþőäřđş ŵįľľ þę mäřĸęđ ƒőř đęľęŧįőʼn įʼn 30 đäyş. Ÿőū čäʼn řęşŧőřę ŧĥę đäşĥþőäřđş äʼnyŧįmę þęƒőřę ŧĥę 30 đäyş ęχpįřęş. Főľđęřş čäʼnʼnőŧ þę řęşŧőřęđ.",
|
||||
"delete-modal-restore-dashboards-text": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę ŧĥę şęľęčŧęđ ƒőľđęřş įmmęđįäŧęľy þūŧ ŧĥę şęľęčŧęđ đäşĥþőäřđş ŵįľľ þę mäřĸęđ ƒőř đęľęŧįőʼn įʼn 30 đäyş. Ÿőūř őřģäʼnįžäŧįőʼn äđmįʼnįşŧřäŧőř čäʼn řęşŧőřę ŧĥę đäşĥþőäřđş äʼnyŧįmę þęƒőřę ŧĥę 30 đäyş ęχpįřę. Főľđęřş čäʼnʼnőŧ þę řęşŧőřęđ.",
|
||||
"delete-modal-text": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę ŧĥę ƒőľľőŵįʼnģ čőʼnŧęʼnŧ:",
|
||||
"delete-modal-title": "Đęľęŧę",
|
||||
"deleting": "Đęľęŧįʼnģ...",
|
||||
@@ -1309,7 +1309,7 @@
|
||||
},
|
||||
"recently-deleted": {
|
||||
"subtitle": "Åʼny įŧęmş ľįşŧęđ ĥęřę ƒőř mőřę ŧĥäʼn 30 đäyş ŵįľľ þę äūŧőmäŧįčäľľy đęľęŧęđ.",
|
||||
"title": "Ŗęčęʼnŧľy Đęľęŧęđ"
|
||||
"title": "Ŗęčęʼnŧľy đęľęŧęđ"
|
||||
},
|
||||
"recorded-queries": {
|
||||
"title": "Ŗęčőřđęđ qūęřįęş"
|
||||
|
||||
Reference in New Issue
Block a user