| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | package pipeline | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2023-08-10 00:25:28 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/metrics" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins" | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/auth" | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/config" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins/log" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization" | 
					
						
							| 
									
										
										
										
											2023-08-10 00:25:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/manager/pipeline/validation" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins/manager/signature" | 
					
						
							| 
									
										
										
										
											2023-10-27 20:27:06 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/plugindef" | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							| 
									
										
										
										
											2023-08-10 00:25:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExternalServiceRegistration implements an InitializeFunc for registering external services.
 | 
					
						
							|  |  |  | type ExternalServiceRegistration struct { | 
					
						
							|  |  |  | 	cfg                     *config.Cfg | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | 	externalServiceRegistry auth.ExternalServiceRegistry | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 	log                     log.Logger | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExternalServiceRegistrationStep returns an InitializeFunc for registering external services.
 | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | func ExternalServiceRegistrationStep(cfg *config.Cfg, externalServiceRegistry auth.ExternalServiceRegistry) initialization.InitializeFunc { | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 	return newExternalServiceRegistration(cfg, externalServiceRegistry).Register | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | func newExternalServiceRegistration(cfg *config.Cfg, serviceRegistry auth.ExternalServiceRegistry) *ExternalServiceRegistration { | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 	return &ExternalServiceRegistration{ | 
					
						
							|  |  |  | 		cfg:                     cfg, | 
					
						
							|  |  |  | 		externalServiceRegistry: serviceRegistry, | 
					
						
							|  |  |  | 		log:                     log.New("plugins.external.registration"), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Register registers the external service with the external service registry, if the feature is enabled.
 | 
					
						
							|  |  |  | func (r *ExternalServiceRegistration) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { | 
					
						
							| 
									
										
										
										
											2023-11-13 20:18:13 +08:00
										 |  |  | 	if p.ExternalServiceRegistration != nil { | 
					
						
							| 
									
										
										
										
											2023-10-27 20:27:06 +08:00
										 |  |  | 		s, err := r.externalServiceRegistry.RegisterExternalService(ctx, p.ID, plugindef.Type(p.Type), p.ExternalServiceRegistration) | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-08-10 20:46:38 +08:00
										 |  |  | 			r.log.Error("Could not register an external service. Initialization skipped", "pluginId", p.ID, "error", err) | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		p.ExternalService = s | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return p, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RegisterPluginRoles implements an InitializeFunc for registering plugin roles.
 | 
					
						
							|  |  |  | type RegisterPluginRoles struct { | 
					
						
							|  |  |  | 	log          log.Logger | 
					
						
							|  |  |  | 	roleRegistry plugins.RoleRegistry | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RegisterPluginRolesStep returns a new InitializeFunc for registering plugin roles.
 | 
					
						
							|  |  |  | func RegisterPluginRolesStep(roleRegistry plugins.RoleRegistry) initialization.InitializeFunc { | 
					
						
							|  |  |  | 	return newRegisterPluginRoles(roleRegistry).Register | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newRegisterPluginRoles(registry plugins.RoleRegistry) *RegisterPluginRoles { | 
					
						
							|  |  |  | 	return &RegisterPluginRoles{ | 
					
						
							|  |  |  | 		log:          log.New("plugins.roles.registration"), | 
					
						
							|  |  |  | 		roleRegistry: registry, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Register registers the plugin roles with the role registry.
 | 
					
						
							|  |  |  | func (r *RegisterPluginRoles) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { | 
					
						
							|  |  |  | 	if err := r.roleRegistry.DeclarePluginRoles(ctx, p.ID, p.Name, p.Roles); err != nil { | 
					
						
							| 
									
										
										
										
											2023-08-10 20:46:38 +08:00
										 |  |  | 		r.log.Warn("Declare plugin roles failed.", "pluginId", p.ID, "error", err) | 
					
						
							| 
									
										
										
										
											2023-08-04 18:35:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return p, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReportBuildMetrics reports build information for all plugins, except core and bundled plugins.
 | 
					
						
							|  |  |  | func ReportBuildMetrics(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { | 
					
						
							|  |  |  | 	if !p.IsCorePlugin() && !p.IsBundledPlugin() { | 
					
						
							|  |  |  | 		metrics.SetPluginBuildInformation(p.ID, string(p.Type), p.Info.Version, string(p.Signature)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return p, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-08-10 00:25:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // SignatureValidation implements a ValidateFunc for validating plugin signatures.
 | 
					
						
							|  |  |  | type SignatureValidation struct { | 
					
						
							|  |  |  | 	signatureValidator signature.Validator | 
					
						
							|  |  |  | 	errs               pluginerrs.SignatureErrorTracker | 
					
						
							|  |  |  | 	log                log.Logger | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignatureValidationStep returns a new ValidateFunc for validating plugin signatures.
 | 
					
						
							|  |  |  | func SignatureValidationStep(signatureValidator signature.Validator, | 
					
						
							|  |  |  | 	sigErr pluginerrs.SignatureErrorTracker) validation.ValidateFunc { | 
					
						
							|  |  |  | 	sv := &SignatureValidation{ | 
					
						
							|  |  |  | 		errs:               sigErr, | 
					
						
							|  |  |  | 		signatureValidator: signatureValidator, | 
					
						
							|  |  |  | 		log:                log.New("plugins.signature.validation"), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sv.Validate | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Validate validates the plugin signature. If a signature error is encountered, the error is recorded with the
 | 
					
						
							|  |  |  | // pluginerrs.SignatureErrorTracker.
 | 
					
						
							|  |  |  | func (v *SignatureValidation) Validate(ctx context.Context, p *plugins.Plugin) error { | 
					
						
							|  |  |  | 	err := v.signatureValidator.ValidateSignature(p) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		var sigErr *plugins.SignatureError | 
					
						
							|  |  |  | 		if errors.As(err, &sigErr) { | 
					
						
							|  |  |  | 			v.log.Warn("Skipping loading plugin due to problem with signature", | 
					
						
							| 
									
										
										
										
											2023-08-10 20:46:38 +08:00
										 |  |  | 				"pluginId", p.ID, "status", sigErr.SignatureStatus) | 
					
						
							| 
									
										
										
										
											2023-08-10 00:25:28 +08:00
										 |  |  | 			p.SignatureError = sigErr | 
					
						
							|  |  |  | 			v.errs.Record(ctx, sigErr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// clear plugin error if a pre-existing error has since been resolved
 | 
					
						
							|  |  |  | 	v.errs.Clear(ctx, p.ID) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-09-14 18:58:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // DisablePlugins is a filter step that will filter out any configured plugins
 | 
					
						
							|  |  |  | type DisablePlugins struct { | 
					
						
							|  |  |  | 	log log.Logger | 
					
						
							|  |  |  | 	cfg *config.Cfg | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewDisablePluginsStep returns a new DisablePlugins.
 | 
					
						
							|  |  |  | func NewDisablePluginsStep(cfg *config.Cfg) *DisablePlugins { | 
					
						
							|  |  |  | 	return &DisablePlugins{ | 
					
						
							|  |  |  | 		cfg: cfg, | 
					
						
							|  |  |  | 		log: log.New("plugins.disable"), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Filter will filter out any plugins that are marked to be disabled.
 | 
					
						
							|  |  |  | func (c *DisablePlugins) Filter(bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) { | 
					
						
							|  |  |  | 	if len(c.cfg.DisablePlugins) == 0 { | 
					
						
							|  |  |  | 		return bundles, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	disablePluginsMap := make(map[string]bool) | 
					
						
							|  |  |  | 	for _, pluginID := range c.cfg.DisablePlugins { | 
					
						
							|  |  |  | 		disablePluginsMap[pluginID] = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res := []*plugins.FoundBundle{} | 
					
						
							|  |  |  | 	for _, bundle := range bundles { | 
					
						
							|  |  |  | 		if disablePluginsMap[bundle.Primary.JSONData.ID] { | 
					
						
							|  |  |  | 			c.log.Debug("Disabling plugin load", "pluginID", bundle.Primary.JSONData.ID) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			res = append(res, bundle) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return res, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-09-22 16:50:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // AsExternal is a filter step that will skip loading a core plugin to use an external one.
 | 
					
						
							|  |  |  | type AsExternal struct { | 
					
						
							|  |  |  | 	log log.Logger | 
					
						
							|  |  |  | 	cfg *config.Cfg | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewDisablePluginsStep returns a new DisablePlugins.
 | 
					
						
							|  |  |  | func NewAsExternalStep(cfg *config.Cfg) *AsExternal { | 
					
						
							|  |  |  | 	return &AsExternal{ | 
					
						
							|  |  |  | 		cfg: cfg, | 
					
						
							|  |  |  | 		log: log.New("plugins.asExternal"), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Filter will filter out any plugins that are marked to be disabled.
 | 
					
						
							|  |  |  | func (c *AsExternal) Filter(cl plugins.Class, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) { | 
					
						
							|  |  |  | 	if c.cfg.Features == nil || !c.cfg.Features.IsEnabled(featuremgmt.FlagExternalCorePlugins) { | 
					
						
							|  |  |  | 		return bundles, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cl == plugins.ClassCore { | 
					
						
							|  |  |  | 		res := []*plugins.FoundBundle{} | 
					
						
							|  |  |  | 		for _, bundle := range bundles { | 
					
						
							|  |  |  | 			pluginCfg := c.cfg.PluginSettings[bundle.Primary.JSONData.ID] | 
					
						
							|  |  |  | 			// Skip core plugins if the feature flag is enabled and the plugin is in the skip list.
 | 
					
						
							|  |  |  | 			// It could be loaded later as an external plugin.
 | 
					
						
							|  |  |  | 			if pluginCfg["as_external"] == "true" { | 
					
						
							|  |  |  | 				c.log.Debug("Skipping the core plugin load", "pluginID", bundle.Primary.JSONData.ID) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				res = append(res, bundle) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return res, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cl == plugins.ClassExternal { | 
					
						
							|  |  |  | 		// Warn if the plugin is not found in the external plugins directory.
 | 
					
						
							|  |  |  | 		asExternal := map[string]bool{} | 
					
						
							|  |  |  | 		for pluginID, pluginCfg := range c.cfg.PluginSettings { | 
					
						
							|  |  |  | 			if pluginCfg["as_external"] == "true" { | 
					
						
							|  |  |  | 				asExternal[pluginID] = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, bundle := range bundles { | 
					
						
							|  |  |  | 			if asExternal[bundle.Primary.JSONData.ID] { | 
					
						
							|  |  |  | 				delete(asExternal, bundle.Primary.JSONData.ID) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(asExternal) > 0 { | 
					
						
							|  |  |  | 			for p := range asExternal { | 
					
						
							|  |  |  | 				c.log.Error("Core plugin expected to be loaded as external, but it is missing", "pluginID", p) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return bundles, nil | 
					
						
							|  |  |  | } |