mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user