RBAC: Fix plugins pages access-control (#76321)

* RBAC: Fix plugins pages access-control

* Better comment

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

* Add a small comment on connections/datasources routes

---------

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
Gabriel MABILLE
2023-10-12 10:46:43 +02:00
committed by GitHub
parent 6e0825d6e2
commit 420fb56fda
4 changed files with 41 additions and 12 deletions

View File

@@ -124,18 +124,19 @@ func (hs *HTTPServer) registerRoutes() {
r.Get("/live/pipeline", reqGrafanaAdmin, hs.Index)
r.Get("/live/cloud", reqGrafanaAdmin, hs.Index)
r.Get("/plugins", middleware.CanAdminPlugins(hs.Cfg), hs.Index)
r.Get("/plugins/:id/", middleware.CanAdminPlugins(hs.Cfg), hs.Index)
r.Get("/plugins/:id/edit", middleware.CanAdminPlugins(hs.Cfg), hs.Index) // deprecated
r.Get("/plugins/:id/page/:page", middleware.CanAdminPlugins(hs.Cfg), hs.Index)
r.Get("/plugins", middleware.CanAdminPlugins(hs.Cfg, hs.AccessControl), hs.Index)
r.Get("/plugins/:id/", middleware.CanAdminPlugins(hs.Cfg, hs.AccessControl), hs.Index)
r.Get("/plugins/:id/edit", middleware.CanAdminPlugins(hs.Cfg, hs.AccessControl), hs.Index) // deprecated
r.Get("/plugins/:id/page/:page", middleware.CanAdminPlugins(hs.Cfg, hs.AccessControl), hs.Index)
r.Get("/connections/datasources", authorize(datasources.ConfigurationPageAccess), hs.Index)
r.Get("/connections/datasources/new", authorize(datasources.NewPageAccess), hs.Index)
r.Get("/connections/datasources/edit/*", authorize(datasources.EditPageAccess), hs.Index)
r.Get("/connections", authorize(datasources.ConfigurationPageAccess), hs.Index)
r.Get("/connections/add-new-connection", authorize(datasources.ConfigurationPageAccess), hs.Index)
r.Get("/connections/datasources/:id", middleware.CanAdminPlugins(hs.Cfg), hs.Index)
r.Get("/connections/datasources/:id/page/:page", middleware.CanAdminPlugins(hs.Cfg), hs.Index)
// Plugin details pages
r.Get("/connections/datasources/:id", middleware.CanAdminPlugins(hs.Cfg, hs.AccessControl), hs.Index)
r.Get("/connections/datasources/:id/page/:page", middleware.CanAdminPlugins(hs.Cfg, hs.AccessControl), hs.Index)
// App Root Page
appPluginIDScope := pluginaccesscontrol.ScopeProvider.GetResourceScope(ac.Parameter(":id"))

View File

@@ -9,6 +9,7 @@ import (
"strings"
"github.com/grafana/grafana/pkg/middleware/cookies"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/authn"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
@@ -86,9 +87,10 @@ func removeForceLoginParams(str string) string {
return forceLoginParamsRegexp.ReplaceAllString(str, "")
}
func CanAdminPlugins(cfg *setting.Cfg) func(c *contextmodel.ReqContext) {
func CanAdminPlugins(cfg *setting.Cfg, accessControl ac.AccessControl) func(c *contextmodel.ReqContext) {
return func(c *contextmodel.ReqContext) {
if !pluginaccesscontrol.ReqCanAdminPlugins(cfg)(c) {
hasAccess := ac.HasAccess(accessControl, c)
if !pluginaccesscontrol.ReqCanAdminPlugins(cfg)(c) && !hasAccess(pluginaccesscontrol.AdminAccessEvaluator) {
accessForbidden(c)
return
}

View File

@@ -17,7 +17,8 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
orgsAccessEvaluator := ac.EvalPermission(ac.ActionOrgsRead)
authConfigUIAvailable := s.license.FeatureEnabled("saml") || s.cfg.LDAPAuthEnabled
// FIXME: while we don't have a permissions for listing plugins the legacy check has to stay as a default
// FIXME: If plugin admin is disabled or externally managed, server admins still need to access the page, this is why
// while we don't have a permissions for listing plugins the legacy check has to stay as a default
if pluginaccesscontrol.ReqCanAdminPlugins(s.cfg)(c) || hasAccess(pluginaccesscontrol.AdminAccessEvaluator) {
configNodes = append(configNodes, &navtree.NavLink{
Text: "Plugins",

View File

@@ -1,5 +1,7 @@
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
import { contextSrv } from 'app/core/core';
import { RouteDescriptor } from 'app/core/navigation/types';
import { AccessControlAction } from 'app/types';
import { PluginAdminRoutes } from './types';
@@ -7,26 +9,49 @@ const DEFAULT_ROUTES = [
{
path: '/plugins',
navId: 'plugins',
roles: () => ['Admin', 'ServerAdmin'],
roles: evaluateAccess(
[AccessControlAction.PluginsInstall, AccessControlAction.PluginsWrite],
['Admin', 'ServerAdmin']
),
routeName: PluginAdminRoutes.Home,
component: SafeDynamicImport(() => import(/* webpackChunkName: "PluginListPage" */ './pages/Browse')),
},
{
path: '/plugins/browse',
navId: 'plugins',
roles: () => ['Admin', 'ServerAdmin'],
roles: evaluateAccess(
[AccessControlAction.PluginsInstall, AccessControlAction.PluginsWrite],
['Admin', 'ServerAdmin']
),
routeName: PluginAdminRoutes.Browse,
component: SafeDynamicImport(() => import(/* webpackChunkName: "PluginListPage" */ './pages/Browse')),
},
{
path: '/plugins/:pluginId/',
navId: 'plugins',
roles: () => ['Admin', 'ServerAdmin'],
roles: evaluateAccess(
[AccessControlAction.PluginsInstall, AccessControlAction.PluginsWrite],
['Admin', 'ServerAdmin']
),
routeName: PluginAdminRoutes.Details,
component: SafeDynamicImport(() => import(/* webpackChunkName: "PluginPage" */ './pages/PluginDetails')),
},
];
// FIXME: If plugin admin is disabled or externally managed, server admins still need to access the page, this is why
// while we don't have a permissions for listing plugins the legacy check has to stay as a default
function evaluateAccess(actions: AccessControlAction[], userRoles: string[]): () => string[] {
return () => {
if (actions.some((action) => contextSrv.hasPermission(action))) {
// If the user has any of the required actions, no need to check the role
return [];
} else {
// User had no action, check the org role
return userRoles;
}
};
}
export function getRoutes(): RouteDescriptor[] {
return DEFAULT_ROUTES;
}