RBAC: Cover plugin includes (#57582)

* RBAC: Add action to plugin includes

* Adding the feature toggle check

* Cue update

* Extract include access control to method

* Suggestion to prevent log when RBAC is disabled

Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>

* Rename IsRBACReady to RequireRBACAction

Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
This commit is contained in:
Gabriel MABILLE 2022-11-16 15:54:04 +01:00 committed by GitHub
parent bce83485a9
commit a8c48b6801
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 3 deletions

View File

@ -87,6 +87,7 @@ type Includes struct {
Type string `json:"type"`
Component string `json:"component"`
Role org.RoleType `json:"role"`
Action string `json:"action,omitempty"`
AddToNav bool `json:"addToNav"`
DefaultNav bool `json:"defaultNav"`
Slug string `json:"slug"`
@ -103,6 +104,10 @@ func (e Includes) DashboardURLPath() string {
return "/d/" + e.UID
}
func (e Includes) RequiresRBACAction() bool {
return e.Action != ""
}
type Dependency struct {
ID string `json:"id"`
Type string `json:"type"`

View File

@ -88,6 +88,9 @@ seqs: [
component?: string
role?: "Admin" | "Editor" | "Viewer"
// RBAC action the user must have to access the route
action?: string
// Used for app plugins.
path?: string
@ -163,7 +166,7 @@ seqs: [
permissions: [...#Permission]
}
// Permission describes an RBAC permission on the plugin. A permission has an action and an option
// Permission describes an RBAC permission on the plugin. A permission has an action and an optional
// scope.
// Example: action: 'test-app.schedules:read', scope: 'test-app.schedules:*'
#Permission: {

View File

@ -362,6 +362,9 @@ type Header struct {
// A resource to be included in a plugin.
type Include struct {
// RBAC action the user must have to access the route
Action *string `json:"action,omitempty"`
// Add the include to the side menu.
AddToNav *bool `json:"addToNav,omitempty"`
@ -462,7 +465,7 @@ type JWTTokenAuth struct {
Url string `json:"url"`
}
// Permission describes an RBAC permission on the plugin. A permission has an action and an option
// Permission describes an RBAC permission on the plugin. A permission has an action and an optional
// scope.
// Example: action: 'test-app.schedules:read', scope: 'test-app.schedules:*'
type Permission struct {

View File

@ -65,6 +65,7 @@ func (s *ServiceImpl) addAppLinks(treeRoot *navtree.NavTreeRoot, c *models.ReqCo
}
func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqContext, topNavEnabled bool, treeRoot *navtree.NavTreeRoot) *navtree.NavLink {
hasAccessToInclude := s.hasAccessToInclude(c, plugin.ID)
appLink := &navtree.NavLink{
Text: plugin.Name,
Id: "plugin-page-" + plugin.ID,
@ -82,7 +83,7 @@ func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqCo
}
for _, include := range plugin.Includes {
if !c.HasUserRole(include.Role) {
if !hasAccessToInclude(include) {
continue
}
@ -230,6 +231,23 @@ func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqCo
return nil
}
func (s *ServiceImpl) hasAccessToInclude(c *models.ReqContext, pluginID string) func(include *plugins.Includes) bool {
hasAccess := ac.HasAccess(s.accessControl, c)
return func(include *plugins.Includes) bool {
useRBAC := s.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) &&
!s.accessControl.IsDisabled() && include.RequiresRBACAction()
if useRBAC && !hasAccess(ac.ReqHasRole(include.Role), ac.EvalPermission(include.Action)) {
s.log.Debug("plugin include is covered by RBAC, user doesn't have access",
"plugin", pluginID,
"include", include.Name)
return false
} else if !useRBAC && !c.HasUserRole(include.Role) {
return false
}
return true
}
}
func (s *ServiceImpl) readNavigationSettings() {
s.navigationAppConfig = map[string]NavigationAppConfig{
"grafana-k8s-app": {SectionID: navtree.NavIDMonitoring, SortWeight: 1, Text: "Kubernetes"},