RBAC: Allow role registration for plugins (#57387)
* Picking role registration from OnCall POC branch * Fix test * Remove include actions from this PR * Removing unused permission * Adding test to DeclarePluginRoles * Add testcase to RegisterFixed role * Additional test case * Adding tests to validate plugins roles * Add test to plugin loader * Nit. * Scuemata validation * Changing the design to decouple accesscontrol from plugin management Co-authored-by: Kalle Persson <kalle.persson@grafana.com> * Fixing tests Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Add missing files Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Remove feature toggle check from loader * Remove feature toggleimport * Feedback Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Fix test' * Make plugins.RoleRegistry interface typed * Remove comment question * No need for json tags anymore * Nit. log * Adding the schema validation * Remove group to take plugin Name instead * Revert sqlstore -> db * Nit. * Nit. on tests Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Update pkg/services/accesscontrol/plugins.go Co-authored-by: Ieva <ieva.vasiljeva@grafana.com> * Log message Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Log message Co-Authored-By: marefr <marcus.efraimsson@gmail.com> * Remove unecessary method. Update test name. Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Fix linting * Update cue descriptions * Fix test Co-authored-by: Kalle Persson <kalle.persson@grafana.com> Co-authored-by: Jguer <joao.guerreiro@grafana.com> Co-authored-by: marefr <marcus.efraimsson@gmail.com> Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
This commit is contained in:
co-authored by
Kalle Persson
Jguer
marefr
ievaVasiljeva
parent
334b498632
commit
30fae33f66
@@ -42,6 +42,7 @@ type Loader struct {
|
||||
pluginFinder finder.Finder
|
||||
processManager process.Service
|
||||
pluginRegistry registry.Service
|
||||
roleRegistry plugins.RoleRegistry
|
||||
pluginInitializer initializer.Initializer
|
||||
signatureValidator signature.Validator
|
||||
pluginStorage storage.Manager
|
||||
@@ -51,14 +52,15 @@ type Loader struct {
|
||||
}
|
||||
|
||||
func ProvideService(cfg *config.Cfg, license models.Licensing, authorizer plugins.PluginLoaderAuthorizer,
|
||||
pluginRegistry registry.Service, backendProvider plugins.BackendFactoryProvider) *Loader {
|
||||
pluginRegistry registry.Service, backendProvider plugins.BackendFactoryProvider,
|
||||
roleRegistry plugins.RoleRegistry) *Loader {
|
||||
return New(cfg, license, authorizer, pluginRegistry, backendProvider, process.NewManager(pluginRegistry),
|
||||
storage.FileSystem(logger.NewLogger("loader.fs"), cfg.PluginsPath))
|
||||
storage.FileSystem(logger.NewLogger("loader.fs"), cfg.PluginsPath), roleRegistry)
|
||||
}
|
||||
|
||||
func New(cfg *config.Cfg, license models.Licensing, authorizer plugins.PluginLoaderAuthorizer,
|
||||
pluginRegistry registry.Service, backendProvider plugins.BackendFactoryProvider,
|
||||
processManager process.Service, pluginStorage storage.Manager) *Loader {
|
||||
processManager process.Service, pluginStorage storage.Manager, roleRegistry plugins.RoleRegistry) *Loader {
|
||||
return &Loader{
|
||||
pluginFinder: finder.New(),
|
||||
pluginRegistry: pluginRegistry,
|
||||
@@ -68,6 +70,7 @@ func New(cfg *config.Cfg, license models.Licensing, authorizer plugins.PluginLoa
|
||||
pluginStorage: pluginStorage,
|
||||
errs: make(map[string]*plugins.SignatureError),
|
||||
log: log.New("plugin.loader"),
|
||||
roleRegistry: roleRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +198,10 @@ func (l *Loader) loadPlugins(ctx context.Context, class plugins.Class, pluginJSO
|
||||
return nil, err
|
||||
}
|
||||
metrics.SetPluginBuildInformation(p.ID, string(p.Type), p.Info.Version, string(p.Signature))
|
||||
|
||||
if errDeclareRoles := l.roleRegistry.DeclarePluginRoles(ctx, p.ID, p.Name, p.Roles); errDeclareRoles != nil {
|
||||
l.log.Warn("Declare plugin roles failed.", "pluginID", p.ID, "path", p.PluginDir, "error", errDeclareRoles)
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range verifiedPlugins {
|
||||
|
||||
@@ -597,6 +597,111 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoader_Load_RBACReady(t *testing.T) {
|
||||
pluginDir, err := filepath.Abs("../testdata/test-app-with-roles")
|
||||
if err != nil {
|
||||
t.Errorf("could not construct absolute path of current dir")
|
||||
return
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *config.Cfg
|
||||
pluginPaths []string
|
||||
appURL string
|
||||
existingPlugins map[string]struct{}
|
||||
want []*plugins.Plugin
|
||||
}{
|
||||
{
|
||||
name: "Load plugin defining one RBAC role",
|
||||
cfg: &config.Cfg{},
|
||||
appURL: "http://localhost:3000",
|
||||
pluginPaths: []string{"../testdata/test-app-with-roles"},
|
||||
want: []*plugins.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-app",
|
||||
Type: "app",
|
||||
Name: "Test App",
|
||||
Info: plugins.Info{
|
||||
Author: plugins.InfoLink{
|
||||
Name: "Test Inc.",
|
||||
URL: "http://test.com",
|
||||
},
|
||||
Description: "Test App",
|
||||
Version: "1.0.0",
|
||||
Links: []plugins.InfoLink{},
|
||||
Logos: plugins.Logos{
|
||||
Small: "public/img/icn-app.svg",
|
||||
Large: "public/img/icn-app.svg",
|
||||
},
|
||||
Updated: "2015-02-10",
|
||||
},
|
||||
Dependencies: plugins.Dependencies{
|
||||
GrafanaVersion: "*",
|
||||
GrafanaDependency: ">=8.0.0",
|
||||
Plugins: []plugins.Dependency{},
|
||||
},
|
||||
Includes: []*plugins.Includes{},
|
||||
Roles: []plugins.RoleRegistration{
|
||||
{
|
||||
Role: plugins.Role{
|
||||
Name: "plugins.app:test-app:reader",
|
||||
DisplayName: "test-app reader",
|
||||
Description: "View everything in the test-app plugin",
|
||||
Permissions: []plugins.Permission{
|
||||
{Action: "plugins.app:access", Scope: "plugins.app:id:test-app"},
|
||||
{Action: "test-app.resource:read", Scope: "resources:*"},
|
||||
{Action: "test-app.otherresource:toggle"},
|
||||
},
|
||||
},
|
||||
Grants: []string{"Admin"},
|
||||
},
|
||||
},
|
||||
Backend: false,
|
||||
},
|
||||
PluginDir: pluginDir,
|
||||
Class: plugins.External,
|
||||
Signature: plugins.SignatureValid,
|
||||
SignatureType: plugins.PrivateSignature,
|
||||
SignatureOrg: "gabrielmabille",
|
||||
Module: "plugins/test-app/module",
|
||||
BaseURL: "public/plugins/test-app",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
origAppURL := setting.AppUrl
|
||||
t.Cleanup(func() {
|
||||
setting.AppUrl = origAppURL
|
||||
})
|
||||
setting.AppUrl = "http://localhost:3000"
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
storage := fakes.NewFakePluginStorage()
|
||||
procPrvdr := fakes.NewFakeBackendProcessProvider()
|
||||
procMgr := fakes.NewFakeProcessManager()
|
||||
l := newLoader(tt.cfg, func(l *Loader) {
|
||||
l.pluginRegistry = reg
|
||||
l.pluginStorage = storage
|
||||
l.processManager = procMgr
|
||||
l.pluginInitializer = initializer.New(tt.cfg, procPrvdr, fakes.NewFakeLicensingService())
|
||||
})
|
||||
|
||||
got, err := l.Load(context.Background(), plugins.External, tt.pluginPaths)
|
||||
require.NoError(t, err)
|
||||
|
||||
if !cmp.Equal(got, tt.want, compareOpts) {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", cmp.Diff(got, tt.want, compareOpts))
|
||||
}
|
||||
pluginErrs := l.PluginErrors()
|
||||
require.Len(t, pluginErrs, 0)
|
||||
|
||||
verifyState(t, tt.want, reg, procPrvdr, storage, procMgr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoader_Load_Signature_RootURL(t *testing.T) {
|
||||
const defaultAppURL = "http://localhost:3000/grafana"
|
||||
|
||||
@@ -1217,7 +1322,8 @@ func Test_setPathsBasedOnApp(t *testing.T) {
|
||||
|
||||
func newLoader(cfg *config.Cfg, cbs ...func(loader *Loader)) *Loader {
|
||||
l := New(cfg, &fakes.FakeLicensingService{}, signature.NewUnsignedAuthorizer(cfg), fakes.NewFakePluginRegistry(),
|
||||
fakes.NewFakeBackendProcessProvider(), fakes.NewFakeProcessManager(), fakes.NewFakePluginStorage())
|
||||
fakes.NewFakeBackendProcessProvider(), fakes.NewFakeProcessManager(), fakes.NewFakePluginStorage(),
|
||||
fakes.NewFakeRoleRegistry())
|
||||
|
||||
for _, cb := range cbs {
|
||||
cb(l)
|
||||
|
||||
Reference in New Issue
Block a user