RBAC: Cover plugin routes (#80578)

* RBAC: Cover plugin routes

* Action instead of ReqAction

* Fix test initializations

* Fix NewPluginProxy call

* Duplicate test to add RBAC checks

* Cover legacy access control as well

* Fix typo

* action -> reqAction

* Add example

Co-authored-by: Andres Martinez Gotor <andres.martinez@grafana.com>

---------

Co-authored-by: Andres Martinez Gotor <andres.martinez@grafana.com>
This commit is contained in:
Gabriel MABILLE
2024-01-17 16:32:23 +01:00
committed by GitHub
parent 81a49e8016
commit 6b954165c5
6 changed files with 156 additions and 11 deletions
+22 -6
View File
@@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
@@ -22,6 +23,7 @@ import (
)
type PluginProxy struct {
accessControl ac.AccessControl
ps *pluginsettings.DTO
pluginRoutes []*plugins.Route
ctx *contextmodel.ReqContext
@@ -37,8 +39,9 @@ type PluginProxy struct {
// NewPluginProxy creates a plugin proxy.
func NewPluginProxy(ps *pluginsettings.DTO, routes []*plugins.Route, ctx *contextmodel.ReqContext,
proxyPath string, cfg *setting.Cfg, secretsService secrets.Service, tracer tracing.Tracer,
transport *http.Transport, features featuremgmt.FeatureToggles) (*PluginProxy, error) {
transport *http.Transport, accessControl ac.AccessControl, features featuremgmt.FeatureToggles) (*PluginProxy, error) {
return &PluginProxy{
accessControl: accessControl,
ps: ps,
pluginRoutes: routes,
ctx: ctx,
@@ -67,11 +70,9 @@ func (proxy *PluginProxy) HandleRequest() {
continue
}
if route.ReqRole.IsValid() {
if !proxy.ctx.HasUserRole(route.ReqRole) {
proxy.ctx.JsonApiErr(http.StatusForbidden, "plugin proxy route access denied", nil)
return
}
if !proxy.hasAccessToRoute(route) {
proxy.ctx.JsonApiErr(http.StatusForbidden, "plugin proxy route access denied", nil)
return
}
if path, exists := params["*"]; exists {
@@ -120,6 +121,21 @@ func (proxy *PluginProxy) HandleRequest() {
reverseProxy.ServeHTTP(proxy.ctx.Resp, proxy.ctx.Req)
}
func (proxy *PluginProxy) hasAccessToRoute(route *plugins.Route) bool {
useRBAC := proxy.features.IsEnabled(proxy.ctx.Req.Context(), featuremgmt.FlagAccessControlOnCall) && route.RequiresRBACAction()
if useRBAC {
hasAccess := ac.HasAccess(proxy.accessControl, proxy.ctx)(ac.EvalPermission(route.ReqAction))
if !hasAccess {
proxy.ctx.Logger.Debug("plugin route is covered by RBAC, user doesn't have access", "route", proxy.ctx.Req.URL.Path)
}
return hasAccess
}
if route.ReqRole.IsValid() {
return proxy.ctx.HasUserRole(route.ReqRole)
}
return true
}
func (proxy PluginProxy) director(req *http.Request) {
secureJsonData, err := proxy.secretsService.DecryptJsonData(proxy.ctx.Req.Context(), proxy.ps.SecureJSONData)
if err != nil {