From a8c48b6801b63750781b2bf151c5f587d0305c7e Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Wed, 16 Nov 2022 15:54:04 +0100 Subject: [PATCH] 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 * Rename IsRBACReady to RequireRBACAction Co-authored-by: ievaVasiljeva --- pkg/plugins/models.go | 5 +++++ pkg/plugins/plugindef/plugindef.cue | 5 ++++- pkg/plugins/plugindef/plugindef_types_gen.go | 5 ++++- pkg/services/navtree/navtreeimpl/applinks.go | 20 +++++++++++++++++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 674156de1ad..3eed57d64ca 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -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"` diff --git a/pkg/plugins/plugindef/plugindef.cue b/pkg/plugins/plugindef/plugindef.cue index 555f78531c8..b2b3df50e3e 100644 --- a/pkg/plugins/plugindef/plugindef.cue +++ b/pkg/plugins/plugindef/plugindef.cue @@ -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: { diff --git a/pkg/plugins/plugindef/plugindef_types_gen.go b/pkg/plugins/plugindef/plugindef_types_gen.go index b6532b774ea..39f2d1c9b17 100644 --- a/pkg/plugins/plugindef/plugindef_types_gen.go +++ b/pkg/plugins/plugindef/plugindef_types_gen.go @@ -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 { diff --git a/pkg/services/navtree/navtreeimpl/applinks.go b/pkg/services/navtree/navtreeimpl/applinks.go index 0366fdd6dc7..9ac62c7cfd4 100644 --- a/pkg/services/navtree/navtreeimpl/applinks.go +++ b/pkg/services/navtree/navtreeimpl/applinks.go @@ -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"},