mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			448 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			448 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
package plugins
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	"github.com/grafana/grafana/pkg/services/org"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	TypeDashboard = "dashboard"
 | 
						|
 | 
						|
	ActionAppAccess = "plugins.app:access"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	ErrInstallCorePlugin   = errors.New("cannot install a Core plugin")
 | 
						|
	ErrUninstallCorePlugin = errors.New("cannot uninstall a Core plugin")
 | 
						|
	ErrPluginNotInstalled  = errors.New("plugin is not installed")
 | 
						|
)
 | 
						|
 | 
						|
type NotFoundError struct {
 | 
						|
	PluginID string
 | 
						|
}
 | 
						|
 | 
						|
func (e NotFoundError) Error() string {
 | 
						|
	return fmt.Sprintf("plugin with ID '%s' not found", e.PluginID)
 | 
						|
}
 | 
						|
 | 
						|
type DuplicateError struct {
 | 
						|
	PluginID string
 | 
						|
}
 | 
						|
 | 
						|
func (e DuplicateError) Error() string {
 | 
						|
	return fmt.Sprintf("plugin with ID '%s' already exists", e.PluginID)
 | 
						|
}
 | 
						|
 | 
						|
func (e DuplicateError) Is(err error) bool {
 | 
						|
	// nolint:errorlint
 | 
						|
	_, ok := err.(DuplicateError)
 | 
						|
	return ok
 | 
						|
}
 | 
						|
 | 
						|
type Dependencies struct {
 | 
						|
	GrafanaDependency string                 `json:"grafanaDependency"`
 | 
						|
	GrafanaVersion    string                 `json:"grafanaVersion"`
 | 
						|
	Plugins           []Dependency           `json:"plugins"`
 | 
						|
	Extensions        ExtensionsDependencies `json:"extensions"`
 | 
						|
}
 | 
						|
 | 
						|
// We need different versions for the Extensions struct because there is a now deprecated plugin.json schema out there, where the "extensions" prop
 | 
						|
// is in a different format (Extensions V1). In order to support those as well while reading the plugin.json, we need to add a custom unmarshaling logic for extensions.
 | 
						|
type ExtensionV1 struct {
 | 
						|
	ExtensionPointID string `json:"extensionPointId"`
 | 
						|
	Title            string `json:"title"`
 | 
						|
	Description      string `json:"description"`
 | 
						|
	Type             string `json:"type"`
 | 
						|
}
 | 
						|
 | 
						|
type ExtensionsV2 struct {
 | 
						|
	AddedLinks        []AddedLink        `json:"addedLinks"`
 | 
						|
	AddedComponents   []AddedComponent   `json:"addedComponents"`
 | 
						|
	ExposedComponents []ExposedComponent `json:"exposedComponents"`
 | 
						|
	ExtensionPoints   []ExtensionPoint   `json:"extensionPoints"`
 | 
						|
}
 | 
						|
 | 
						|
type Extensions ExtensionsV2
 | 
						|
 | 
						|
func (e *Extensions) UnmarshalJSON(data []byte) error {
 | 
						|
	var err error
 | 
						|
	var extensionsV2 ExtensionsV2
 | 
						|
 | 
						|
	if err = json.Unmarshal(data, &extensionsV2); err == nil {
 | 
						|
		e.AddedComponents = extensionsV2.AddedComponents
 | 
						|
		e.AddedLinks = extensionsV2.AddedLinks
 | 
						|
		e.ExposedComponents = extensionsV2.ExposedComponents
 | 
						|
		e.ExtensionPoints = extensionsV2.ExtensionPoints
 | 
						|
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Fallback (V1)
 | 
						|
	var extensionsV1 []ExtensionV1
 | 
						|
	if err = json.Unmarshal(data, &extensionsV1); err == nil {
 | 
						|
		// Trying to process old format and add them to `AddedLinks` and `AddedComponents`
 | 
						|
		for _, extensionV1 := range extensionsV1 {
 | 
						|
			if extensionV1.Type == "link" {
 | 
						|
				extensionV2 := AddedLink{
 | 
						|
					Targets:     []string{extensionV1.ExtensionPointID},
 | 
						|
					Title:       extensionV1.Title,
 | 
						|
					Description: extensionV1.Description,
 | 
						|
				}
 | 
						|
				e.AddedLinks = append(e.AddedLinks, extensionV2)
 | 
						|
			}
 | 
						|
 | 
						|
			if extensionV1.Type == "component" {
 | 
						|
				extensionV2 := AddedComponent{
 | 
						|
					Targets:     []string{extensionV1.ExtensionPointID},
 | 
						|
					Title:       extensionV1.Title,
 | 
						|
					Description: extensionV1.Description,
 | 
						|
				}
 | 
						|
 | 
						|
				e.AddedComponents = append(e.AddedComponents, extensionV2)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
type AddedLink struct {
 | 
						|
	Targets     []string `json:"targets"`
 | 
						|
	Title       string   `json:"title"`
 | 
						|
	Description string   `json:"description"`
 | 
						|
}
 | 
						|
 | 
						|
type AddedComponent struct {
 | 
						|
	Targets     []string `json:"targets"`
 | 
						|
	Title       string   `json:"title"`
 | 
						|
	Description string   `json:"description"`
 | 
						|
}
 | 
						|
 | 
						|
type ExposedComponent struct {
 | 
						|
	Id          string `json:"id"`
 | 
						|
	Title       string `json:"title"`
 | 
						|
	Description string `json:"description"`
 | 
						|
}
 | 
						|
 | 
						|
type ExtensionPoint struct {
 | 
						|
	Id          string `json:"id"`
 | 
						|
	Title       string `json:"title"`
 | 
						|
	Description string `json:"description"`
 | 
						|
}
 | 
						|
 | 
						|
type ExtensionsDependencies struct {
 | 
						|
	ExposedComponents []string `json:"exposedComponents"`
 | 
						|
}
 | 
						|
 | 
						|
type Includes struct {
 | 
						|
	Name       string       `json:"name"`
 | 
						|
	Path       string       `json:"path"`
 | 
						|
	Type       string       `json:"type"`
 | 
						|
	Component  string       `json:"component"`
 | 
						|
	Role       org.RoleType `json:"role"`
 | 
						|
	Action     string       `json:"action,omitempty"`
 | 
						|
	AddToNav   bool         `json:"addToNav"`
 | 
						|
	DefaultNav bool         `json:"defaultNav"`
 | 
						|
	Slug       string       `json:"slug"`
 | 
						|
	Icon       string       `json:"icon"`
 | 
						|
	UID        string       `json:"uid"`
 | 
						|
 | 
						|
	ID string `json:"-"`
 | 
						|
}
 | 
						|
 | 
						|
func (e Includes) DashboardURLPath() string {
 | 
						|
	if e.Type != "dashboard" || len(e.UID) == 0 {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	return "/d/" + e.UID
 | 
						|
}
 | 
						|
 | 
						|
func (e Includes) RequiresRBACAction() bool {
 | 
						|
	return e.Action != ""
 | 
						|
}
 | 
						|
 | 
						|
type Dependency struct {
 | 
						|
	ID      string `json:"id"`
 | 
						|
	Type    string `json:"type"`
 | 
						|
	Name    string `json:"name"`
 | 
						|
	Version string `json:"version"`
 | 
						|
}
 | 
						|
 | 
						|
type BuildInfo struct {
 | 
						|
	Time int64 `json:"time,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
type Info struct {
 | 
						|
	Author      InfoLink      `json:"author"`
 | 
						|
	Description string        `json:"description"`
 | 
						|
	Links       []InfoLink    `json:"links"`
 | 
						|
	Logos       Logos         `json:"logos"`
 | 
						|
	Build       BuildInfo     `json:"build"`
 | 
						|
	Screenshots []Screenshots `json:"screenshots"`
 | 
						|
	Version     string        `json:"version"`
 | 
						|
	Updated     string        `json:"updated"`
 | 
						|
	Keywords    []string      `json:"keywords"`
 | 
						|
}
 | 
						|
 | 
						|
type InfoLink struct {
 | 
						|
	Name string `json:"name"`
 | 
						|
	URL  string `json:"url"`
 | 
						|
}
 | 
						|
 | 
						|
type Logos struct {
 | 
						|
	Small string `json:"small"`
 | 
						|
	Large string `json:"large"`
 | 
						|
}
 | 
						|
 | 
						|
type Screenshots struct {
 | 
						|
	Name string `json:"name"`
 | 
						|
	Path string `json:"path"`
 | 
						|
}
 | 
						|
 | 
						|
type StaticRoute struct {
 | 
						|
	PluginID  string
 | 
						|
	Directory string
 | 
						|
}
 | 
						|
 | 
						|
type SignatureStatus string
 | 
						|
 | 
						|
func (ss SignatureStatus) IsValid() bool {
 | 
						|
	return ss == SignatureStatusValid
 | 
						|
}
 | 
						|
 | 
						|
func (ss SignatureStatus) IsInternal() bool {
 | 
						|
	return ss == SignatureStatusInternal
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	SignatureStatusInternal SignatureStatus = "internal" // core plugin, no signature
 | 
						|
	SignatureStatusValid    SignatureStatus = "valid"    // signed and accurate MANIFEST
 | 
						|
	SignatureStatusInvalid  SignatureStatus = "invalid"  // invalid signature
 | 
						|
	SignatureStatusModified SignatureStatus = "modified" // valid signature, but content mismatch
 | 
						|
	SignatureStatusUnsigned SignatureStatus = "unsigned" // no MANIFEST file
 | 
						|
)
 | 
						|
 | 
						|
type ReleaseState string
 | 
						|
 | 
						|
const (
 | 
						|
	ReleaseStateAlpha ReleaseState = "alpha"
 | 
						|
)
 | 
						|
 | 
						|
type SignatureType string
 | 
						|
 | 
						|
const (
 | 
						|
	SignatureTypeGrafana     SignatureType = "grafana"
 | 
						|
	SignatureTypeCommercial  SignatureType = "commercial"
 | 
						|
	SignatureTypeCommunity   SignatureType = "community"
 | 
						|
	SignatureTypePrivate     SignatureType = "private"
 | 
						|
	SignatureTypePrivateGlob SignatureType = "private-glob"
 | 
						|
)
 | 
						|
 | 
						|
func (s SignatureType) IsValid() bool {
 | 
						|
	switch s {
 | 
						|
	case SignatureTypeGrafana, SignatureTypeCommercial, SignatureTypeCommunity, SignatureTypePrivate,
 | 
						|
		SignatureTypePrivateGlob:
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
type Signature struct {
 | 
						|
	Status     SignatureStatus
 | 
						|
	Type       SignatureType
 | 
						|
	SigningOrg string
 | 
						|
}
 | 
						|
 | 
						|
type PluginMetaDTO struct {
 | 
						|
	JSONData
 | 
						|
	Signature                 SignatureStatus `json:"signature"`
 | 
						|
	Module                    string          `json:"module"`
 | 
						|
	ModuleHash                string          `json:"moduleHash,omitempty"`
 | 
						|
	BaseURL                   string          `json:"baseUrl"`
 | 
						|
	Angular                   AngularMeta     `json:"angular"`
 | 
						|
	MultiValueFilterOperators bool            `json:"multiValueFilterOperators"`
 | 
						|
	LoadingStrategy           LoadingStrategy `json:"loadingStrategy"`
 | 
						|
}
 | 
						|
 | 
						|
type DataSourceDTO struct {
 | 
						|
	ID         int64          `json:"id,omitempty"`
 | 
						|
	UID        string         `json:"uid,omitempty"`
 | 
						|
	Type       string         `json:"type"`
 | 
						|
	Name       string         `json:"name"`
 | 
						|
	PluginMeta *PluginMetaDTO `json:"meta"`
 | 
						|
	URL        string         `json:"url,omitempty"`
 | 
						|
	IsDefault  bool           `json:"isDefault"`
 | 
						|
	Access     string         `json:"access,omitempty"`
 | 
						|
	Preload    bool           `json:"preload"`
 | 
						|
	Module     string         `json:"module,omitempty"`
 | 
						|
	JSONData   map[string]any `json:"jsonData"`
 | 
						|
	ReadOnly   bool           `json:"readOnly"`
 | 
						|
	APIVersion string         `json:"apiVersion,omitempty"`
 | 
						|
 | 
						|
	BasicAuth       string `json:"basicAuth,omitempty"`
 | 
						|
	WithCredentials bool   `json:"withCredentials,omitempty"`
 | 
						|
 | 
						|
	// This is populated by an Enterprise hook
 | 
						|
	CachingConfig QueryCachingConfig `json:"cachingConfig,omitempty"`
 | 
						|
 | 
						|
	// InfluxDB
 | 
						|
	Username string `json:"username,omitempty"`
 | 
						|
	Password string `json:"password,omitempty"`
 | 
						|
 | 
						|
	// InfluxDB + Elasticsearch
 | 
						|
	Database string `json:"database,omitempty"`
 | 
						|
 | 
						|
	// Prometheus
 | 
						|
	DirectURL string `json:"directUrl,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
type PanelDTO struct {
 | 
						|
	ID              string          `json:"id"`
 | 
						|
	Name            string          `json:"name"`
 | 
						|
	AliasIDs        []string        `json:"aliasIds,omitempty"`
 | 
						|
	Info            Info            `json:"info"`
 | 
						|
	HideFromList    bool            `json:"hideFromList"`
 | 
						|
	Sort            int             `json:"sort"`
 | 
						|
	SkipDataQuery   bool            `json:"skipDataQuery"`
 | 
						|
	ReleaseState    string          `json:"state"`
 | 
						|
	BaseURL         string          `json:"baseUrl"`
 | 
						|
	Signature       string          `json:"signature"`
 | 
						|
	Module          string          `json:"module"`
 | 
						|
	Angular         AngularMeta     `json:"angular"`
 | 
						|
	LoadingStrategy LoadingStrategy `json:"loadingStrategy"`
 | 
						|
	ModuleHash      string          `json:"moduleHash,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
type AppDTO struct {
 | 
						|
	ID              string          `json:"id"`
 | 
						|
	Path            string          `json:"path"`
 | 
						|
	Version         string          `json:"version"`
 | 
						|
	Preload         bool            `json:"preload"`
 | 
						|
	Angular         AngularMeta     `json:"angular"`
 | 
						|
	LoadingStrategy LoadingStrategy `json:"loadingStrategy"`
 | 
						|
	Extensions      Extensions      `json:"extensions"`
 | 
						|
	Dependencies    Dependencies    `json:"dependencies"`
 | 
						|
	ModuleHash      string          `json:"moduleHash,omitempty"`
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	errorCodeSignatureMissing   ErrorCode = "signatureMissing"
 | 
						|
	errorCodeSignatureModified  ErrorCode = "signatureModified"
 | 
						|
	errorCodeSignatureInvalid   ErrorCode = "signatureInvalid"
 | 
						|
	ErrorCodeFailedBackendStart ErrorCode = "failedBackendStart"
 | 
						|
	ErrorAngular                ErrorCode = "angular"
 | 
						|
)
 | 
						|
 | 
						|
type ErrorCode string
 | 
						|
 | 
						|
type Error struct {
 | 
						|
	ErrorCode       `json:"errorCode"`
 | 
						|
	PluginID        string          `json:"pluginId,omitempty"`
 | 
						|
	SignatureStatus SignatureStatus `json:"status,omitempty"`
 | 
						|
	message         string          `json:"-"`
 | 
						|
}
 | 
						|
 | 
						|
type LoadingStrategy string
 | 
						|
 | 
						|
const (
 | 
						|
	LoadingStrategyFetch  LoadingStrategy = "fetch"
 | 
						|
	LoadingStrategyScript LoadingStrategy = "script"
 | 
						|
)
 | 
						|
 | 
						|
func (e Error) Error() string {
 | 
						|
	if e.message != "" {
 | 
						|
		return e.message
 | 
						|
	}
 | 
						|
 | 
						|
	if e.SignatureStatus != "" {
 | 
						|
		switch e.SignatureStatus {
 | 
						|
		case SignatureStatusInvalid:
 | 
						|
			return fmt.Sprintf("plugin '%s' has an invalid signature", e.PluginID)
 | 
						|
		case SignatureStatusModified:
 | 
						|
			return fmt.Sprintf("plugin '%s' has an modified signature", e.PluginID)
 | 
						|
		case SignatureStatusUnsigned:
 | 
						|
			return fmt.Sprintf("plugin '%s' has no signature", e.PluginID)
 | 
						|
		case SignatureStatusInternal, SignatureStatusValid:
 | 
						|
			return ""
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return fmt.Sprintf("plugin '%s' failed: %s", e.PluginID, e.ErrorCode)
 | 
						|
}
 | 
						|
 | 
						|
func (e Error) AsErrorCode() ErrorCode {
 | 
						|
	if e.ErrorCode != "" {
 | 
						|
		return e.ErrorCode
 | 
						|
	}
 | 
						|
 | 
						|
	switch e.SignatureStatus {
 | 
						|
	case SignatureStatusInvalid:
 | 
						|
		return errorCodeSignatureInvalid
 | 
						|
	case SignatureStatusModified:
 | 
						|
		return errorCodeSignatureModified
 | 
						|
	case SignatureStatusUnsigned:
 | 
						|
		return errorCodeSignatureMissing
 | 
						|
	case SignatureStatusInternal, SignatureStatusValid:
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func (e *Error) WithMessage(m string) *Error {
 | 
						|
	e.message = m
 | 
						|
	return e
 | 
						|
}
 | 
						|
 | 
						|
func (e Error) PublicMessage() string {
 | 
						|
	switch e.ErrorCode {
 | 
						|
	case errorCodeSignatureInvalid:
 | 
						|
		return "Invalid plugin signature"
 | 
						|
	case errorCodeSignatureModified:
 | 
						|
		return "Plugin signature does not match"
 | 
						|
	case errorCodeSignatureMissing:
 | 
						|
		return "Plugin signature is missing"
 | 
						|
	case ErrorCodeFailedBackendStart:
 | 
						|
		return "Plugin failed to start"
 | 
						|
	case ErrorAngular:
 | 
						|
		return "Angular plugins are not supported"
 | 
						|
	}
 | 
						|
 | 
						|
	return "Plugin failed to load"
 | 
						|
}
 | 
						|
 | 
						|
// RoleRegistration stores a role and its assignments to basic roles
 | 
						|
// (Viewer, Editor, Admin, Grafana Admin)
 | 
						|
type RoleRegistration struct {
 | 
						|
	Role   Role     `json:"role"`
 | 
						|
	Grants []string `json:"grants"`
 | 
						|
}
 | 
						|
 | 
						|
// Role is the model for Role in RBAC.
 | 
						|
type Role struct {
 | 
						|
	Name        string       `json:"name"`
 | 
						|
	Description string       `json:"description"`
 | 
						|
	Permissions []Permission `json:"permissions"`
 | 
						|
}
 | 
						|
 | 
						|
type Permission struct {
 | 
						|
	Action string `json:"action"`
 | 
						|
	Scope  string `json:"scope"`
 | 
						|
}
 | 
						|
 | 
						|
// ActionSet is the model for ActionSet in RBAC.
 | 
						|
type ActionSet struct {
 | 
						|
	Action  string   `json:"action"`
 | 
						|
	Actions []string `json:"actions"`
 | 
						|
}
 | 
						|
 | 
						|
type QueryCachingConfig struct {
 | 
						|
	Enabled bool  `json:"enabled"`
 | 
						|
	TTLMS   int64 `json:"TTLMs"`
 | 
						|
}
 |