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:
Gabriel MABILLE
2022-09-09 09:44:50 +02:00
committed by GitHub
parent 8c081d4523
commit 101349fe49
21 changed files with 397 additions and 53 deletions

View File

@ -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)
}
}