Explore: Check for RBAC permissions when hitting query history endpoints (#91156)

* Check for RBAC permissions when hitting query history endpoints; extract checking logic into a middleware

* Fix lint errors

* Fix test

* Use permissions for patch path; rename callback handler
This commit is contained in:
Haris Rozajac
2024-07-31 12:10:52 -06:00
committed by GitHub
parent eef07aedc8
commit e81fa0e4c5
5 changed files with 71 additions and 44 deletions
+19 -24
View File
@@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util"
@@ -15,15 +16,27 @@ import (
func (s *QueryHistoryService) registerAPIEndpoints() {
s.RouteRegister.Group("/api/query-history", func(entities routing.RouteRegister) {
entities.Post("/", middleware.ReqSignedIn, routing.Wrap(s.createHandler))
entities.Get("/", middleware.ReqSignedIn, routing.Wrap(s.searchHandler))
entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(s.deleteHandler))
entities.Post("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.starHandler))
entities.Delete("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.unstarHandler))
entities.Patch("/:uid", middleware.ReqSignedIn, routing.Wrap(s.patchCommentHandler))
entities.Post("/", middleware.ReqSignedIn, routing.Wrap(s.permissionsMiddleware(s.createHandler, "Failed to create query history")))
entities.Get("/", middleware.ReqSignedIn, routing.Wrap(s.permissionsMiddleware(s.searchHandler, "Failed to get query history")))
entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(s.permissionsMiddleware(s.deleteHandler, "Failed to delete query history")))
entities.Post("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.permissionsMiddleware(s.starHandler, "Failed to star query history")))
entities.Delete("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.permissionsMiddleware(s.unstarHandler, "Failed to unstar query history")))
entities.Patch("/:uid", middleware.ReqSignedIn, routing.Wrap(s.permissionsMiddleware(s.patchCommentHandler, "Failed to update comment of query in query history")))
})
}
type CallbackHandler func(c *contextmodel.ReqContext) response.Response
func (s *QueryHistoryService) permissionsMiddleware(handler CallbackHandler, errorMessage string) CallbackHandler {
return func(c *contextmodel.ReqContext) response.Response {
hasAccess := ac.HasAccess(s.accessControl, c)
if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit && !hasAccess(ac.EvalPermission(ac.ActionDatasourcesExplore)) {
return response.Error(http.StatusUnauthorized, errorMessage, nil)
}
return handler(c)
}
}
// swagger:route POST /query-history query_history createQuery
//
// Add query to query history.
@@ -36,10 +49,6 @@ func (s *QueryHistoryService) registerAPIEndpoints() {
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) createHandler(c *contextmodel.ReqContext) response.Response {
if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit {
return response.Error(http.StatusUnauthorized, "Failed to create query history", nil)
}
cmd := CreateQueryInQueryHistoryCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
@@ -66,10 +75,6 @@ func (s *QueryHistoryService) createHandler(c *contextmodel.ReqContext) response
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) searchHandler(c *contextmodel.ReqContext) response.Response {
if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit {
return response.Error(http.StatusUnauthorized, "Failed to get query history", nil)
}
timeRange := gtime.NewTimeRange(c.Query("from"), c.Query("to"))
query := SearchInQueryHistoryQuery{
@@ -102,10 +107,6 @@ func (s *QueryHistoryService) searchHandler(c *contextmodel.ReqContext) response
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) deleteHandler(c *contextmodel.ReqContext) response.Response {
if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit {
return response.Error(http.StatusUnauthorized, "Failed to delete query history", nil)
}
queryUID := web.Params(c.Req)[":uid"]
if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
return response.Error(http.StatusNotFound, "Query in query history not found", nil)
@@ -163,9 +164,6 @@ func (s *QueryHistoryService) patchCommentHandler(c *contextmodel.ReqContext) re
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) starHandler(c *contextmodel.ReqContext) response.Response {
if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit {
return response.Error(http.StatusUnauthorized, "Failed to star query history", nil)
}
queryUID := web.Params(c.Req)[":uid"]
if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
return response.Error(http.StatusNotFound, "Query in query history not found", nil)
@@ -190,9 +188,6 @@ func (s *QueryHistoryService) starHandler(c *contextmodel.ReqContext) response.R
// 401: unauthorisedError
// 500: internalServerError
func (s *QueryHistoryService) unstarHandler(c *contextmodel.ReqContext) response.Response {
if c.GetOrgRole() == org.RoleViewer && !s.Cfg.ViewersCanEdit {
return response.Error(http.StatusUnauthorized, "Failed to unstar query history", nil)
}
queryUID := web.Params(c.Req)[":uid"]
if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
return response.Error(http.StatusNotFound, "Query in query history not found", nil)