mirror of
https://github.com/grafana/grafana.git
synced 2025-09-25 11:44:06 +08:00
RBAC: Add permissions to install and configure plugins (#51829)
* RBAC: Allow app plugins restriction Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * Moving declaration to HttpServer Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Picking changes from the other branch Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com> * Rename plugins.settings to plugins Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * Account for PluginAdminExternalManageEnabled Co-authored-by: Will Browne <will.browne@grafana.com> * Set metadata on instantiation Co-authored-by: Jguer <joao.guerreiro@grafana.com> Co-authored-by: Kalle Persson <kalle.persson@grafana.com> Co-authored-by: marefr <marcus.efraimsson@gmail.com> Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com> Co-authored-by: Will Browne <will.browne@grafana.com> Co-authored-by: Jguer <joao.guerreiro@grafana.com>
This commit is contained in:
@ -13,10 +13,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@ -26,6 +22,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/plugins/storage"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@ -35,22 +34,33 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
typeFilter := c.Query("type")
|
||||
enabledFilter := c.Query("enabled")
|
||||
embeddedFilter := c.Query("embedded")
|
||||
// "" => no filter
|
||||
// "0" => filter out core plugins
|
||||
// "1" => filter out non-core plugins
|
||||
coreFilter := c.Query("core")
|
||||
|
||||
// When using access control anyone that can create a data source should be able to list all data sources installed
|
||||
// FIXME: while we don't have permissions for listing plugins we need this complex check:
|
||||
// When using access control, should be able to list non-core plugins:
|
||||
// * anyone that can create a data source
|
||||
// * anyone that can install a plugin
|
||||
// Fallback to only letting admins list non-core plugins
|
||||
hasAccess := accesscontrol.HasAccess(hs.AccessControl, c)
|
||||
if !hasAccess(accesscontrol.ReqOrgAdmin, accesscontrol.EvalPermission(datasources.ActionCreate)) && !c.HasRole(org.RoleAdmin) {
|
||||
coreFilter = "1"
|
||||
}
|
||||
reqOrgAdmin := ac.ReqHasRole(org.RoleAdmin)
|
||||
hasAccess := ac.HasAccess(hs.AccessControl, c)
|
||||
canListNonCorePlugins := reqOrgAdmin(c) || hasAccess(reqOrgAdmin, ac.EvalAny(
|
||||
ac.EvalPermission(datasources.ActionCreate),
|
||||
ac.EvalPermission(plugins.ActionInstall),
|
||||
))
|
||||
|
||||
pluginSettingsMap, err := hs.pluginSettings(c.Req.Context(), c.OrgID)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to get list of plugins", err)
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get list of plugins", err)
|
||||
}
|
||||
|
||||
result := make(dtos.PluginList, 0)
|
||||
for _, pluginDef := range hs.pluginStore.Plugins(c.Req.Context()) {
|
||||
// Filter plugins
|
||||
pluginDefinitions := hs.pluginStore.Plugins(c.Req.Context())
|
||||
filteredPluginDefinitions := []plugins.PluginDTO{}
|
||||
filteredPluginIDs := map[string]bool{}
|
||||
for _, pluginDef := range pluginDefinitions {
|
||||
// filter out app sub plugins
|
||||
if embeddedFilter == "0" && pluginDef.IncludedInAppID != "" {
|
||||
continue
|
||||
@ -61,6 +71,17 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
continue
|
||||
}
|
||||
|
||||
// FIXME: while we don't have permissions for listing plugins we need this complex check:
|
||||
// When using access control, should be able to list non-core plugins:
|
||||
// * anyone that can create a data source
|
||||
// * anyone that can install a plugin
|
||||
// Should be able to list this installed plugin:
|
||||
// * anyone that can edit its settings
|
||||
if !pluginDef.IsCorePlugin() && !canListNonCorePlugins && !hasAccess(reqOrgAdmin,
|
||||
ac.EvalPermission(plugins.ActionWrite, plugins.ScopeProvider.GetResourceScope(pluginDef.ID))) {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter on type
|
||||
if typeFilter != "" && typeFilter != string(pluginDef.Type) {
|
||||
continue
|
||||
@ -70,6 +91,29 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter out built in plugins
|
||||
if pluginDef.BuiltIn {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter out disabled plugins
|
||||
if pluginSetting, exists := pluginSettingsMap[pluginDef.ID]; exists {
|
||||
if enabledFilter == "1" && !pluginSetting.Enabled {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
filteredPluginDefinitions = append(filteredPluginDefinitions, pluginDef)
|
||||
filteredPluginIDs[pluginDef.ID] = true
|
||||
}
|
||||
|
||||
// Compute metadata
|
||||
pluginsMetadata := hs.getMultiAccessControlMetadata(c, c.OrgID,
|
||||
plugins.ScopeProvider.GetResourceScope(""), filteredPluginIDs)
|
||||
|
||||
// Prepare DTO
|
||||
result := make(dtos.PluginList, 0)
|
||||
for _, pluginDef := range filteredPluginDefinitions {
|
||||
listItem := dtos.PluginListItem{
|
||||
Id: pluginDef.ID,
|
||||
Name: pluginDef.Name,
|
||||
@ -82,6 +126,7 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
Signature: pluginDef.Signature,
|
||||
SignatureType: pluginDef.SignatureType,
|
||||
SignatureOrg: pluginDef.SignatureOrg,
|
||||
AccessControl: pluginsMetadata[pluginDef.ID],
|
||||
}
|
||||
|
||||
update, exists := hs.pluginsUpdateChecker.HasUpdate(c.Req.Context(), pluginDef.ID)
|
||||
@ -99,16 +144,6 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
listItem.DefaultNavUrl = hs.Cfg.AppSubURL + "/plugins/" + listItem.Id + "/"
|
||||
}
|
||||
|
||||
// filter out disabled plugins
|
||||
if enabledFilter == "1" && !listItem.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter out built in plugins
|
||||
if pluginDef.BuiltIn {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, listItem)
|
||||
}
|
||||
|
||||
@ -127,9 +162,9 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon
|
||||
// In a first iteration, we only have one permission for app plugins.
|
||||
// We will need a different permission to allow users to configure the plugin without needing access to it.
|
||||
if plugin.IsApp() {
|
||||
hasAccess := accesscontrol.HasAccess(hs.AccessControl, c)
|
||||
if !hasAccess(accesscontrol.ReqSignedIn,
|
||||
accesscontrol.EvalPermission(plugins.ActionAppAccess, plugins.ScopeProvider.GetResourceScope(plugin.ID))) {
|
||||
hasAccess := ac.HasAccess(hs.AccessControl, c)
|
||||
if !hasAccess(ac.ReqSignedIn,
|
||||
ac.EvalPermission(plugins.ActionAppAccess, plugins.ScopeProvider.GetResourceScope(plugin.ID))) {
|
||||
return response.Error(http.StatusForbidden, "Access Denied", nil)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user