diff --git a/pkg/api/api.go b/pkg/api/api.go
index fdec78dc0ff..82734adfda4 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -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) {
diff --git a/pkg/api/search.go b/pkg/api/search.go
index e6fa99db380..2cd77a7d157 100644
--- a/pkg/api/search.go
+++ b/pkg/api/search.go
@@ -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)
}
diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go
index 69bd5de2aec..6e0440f88e3 100644
--- a/pkg/services/navtree/navtreeimpl/navtree.go
+++ b/pkg/services/navtree/navtreeimpl/navtree.go
@@ -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",
diff --git a/public/app/core/utils/navBarItem-translations.ts b/public/app/core/utils/navBarItem-translations.ts
index b79abe733fd..f9ee3dcd99f 100644
--- a/public/app/core/utils/navBarItem-translations.ts
+++ b/public/app/core/utils/navBarItem-translations.ts
@@ -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':
diff --git a/public/app/features/browse-dashboards/components/BrowseActions/DeleteModal.tsx b/public/app/features/browse-dashboards/components/BrowseActions/DeleteModal.tsx
index b9fbbcf9348..7cff90fd755 100644
--- a/public/app/features/browse-dashboards/components/BrowseActions/DeleteModal.tsx
+++ b/public/app/features/browse-dashboards/components/BrowseActions/DeleteModal.tsx
@@ -40,8 +40,8 @@ export const DeleteModal = ({ onConfirm, onDismiss, selectedItems, ...props }: P
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.
diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx
index fef33159025..98f7f6e5ff4 100644
--- a/public/app/routes/routes.tsx
+++ b/public/app/routes/routes.tsx
@@ -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')
),
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index 7b3fb3c778c..8c827594e52 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -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"
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index f4be7771099..20de0b7aaae 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -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ūęřįęş"