2015-11-19 12:55:13 +01:00
package plugins
2015-12-22 00:23:24 +08:00
import (
2024-10-04 08:41:26 +02:00
"encoding/json"
2021-05-12 20:05:16 +02:00
"errors"
2016-03-10 19:57:48 +01:00
"fmt"
2016-02-10 11:03:12 +01:00
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/org"
2015-12-22 00:23:24 +08:00
)
2015-11-19 12:55:13 +01:00
2021-03-08 07:02:49 +01:00
const (
2021-11-01 09:53:33 +00:00
TypeDashboard = "dashboard"
2024-07-29 20:56:09 +02:00
ActionAppAccess = "plugins.app:access"
2016-03-10 19:57:48 +01:00
)
2021-05-12 20:05:16 +02:00
var (
2022-08-23 11:50:50 +02:00
ErrInstallCorePlugin = errors . New ( "cannot install a Core plugin" )
ErrUninstallCorePlugin = errors . New ( "cannot uninstall a Core plugin" )
ErrPluginNotInstalled = errors . New ( "plugin is not installed" )
2021-05-12 20:05:16 +02:00
)
2021-11-01 09:53:33 +00:00
type NotFoundError struct {
2020-10-21 12:39:41 +02:00
PluginID string
2016-03-10 19:57:48 +01:00
}
2021-11-01 09:53:33 +00:00
func ( e NotFoundError ) Error () string {
2021-05-12 20:05:16 +02:00
return fmt . Sprintf ( "plugin with ID '%s' not found" , e . PluginID )
2020-10-21 12:39:41 +02:00
}
2021-11-01 09:53:33 +00:00
type DuplicateError struct {
2022-12-02 12:46:55 +00:00
PluginID string
2020-10-21 12:39:41 +02:00
}
2021-11-01 09:53:33 +00:00
func ( e DuplicateError ) Error () string {
2022-12-02 12:46:55 +00:00
return fmt . Sprintf ( "plugin with ID '%s' already exists" , e . PluginID )
2020-10-21 12:39:41 +02:00
}
2021-11-01 09:53:33 +00:00
func ( e DuplicateError ) Is ( err error ) bool {
2020-11-19 13:34:28 +01:00
// nolint:errorlint
2021-11-01 09:53:33 +00:00
_ , ok := err .( DuplicateError )
2020-10-21 12:39:41 +02:00
return ok
2016-03-10 19:57:48 +01:00
}
2021-11-01 09:53:33 +00:00
type Dependencies struct {
2024-10-04 08:41:26 +02:00
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"`
2016-03-07 14:31:02 +01:00
}
2021-11-01 09:53:33 +00:00
type Includes struct {
2022-08-10 11:56:48 +02:00
Name string `json:"name"`
Path string `json:"path"`
Type string `json:"type"`
Component string `json:"component"`
Role org . RoleType `json:"role"`
2022-11-16 15:54:04 +01:00
Action string `json:"action,omitempty"`
2022-08-10 11:56:48 +02:00
AddToNav bool `json:"addToNav"`
DefaultNav bool `json:"defaultNav"`
Slug string `json:"slug"`
Icon string `json:"icon"`
UID string `json:"uid"`
2016-03-21 19:07:08 +01:00
2021-11-01 09:53:33 +00:00
ID string `json:"-"`
2016-03-08 18:17:47 +01:00
}
2022-04-06 10:50:39 +02:00
func ( e Includes ) DashboardURLPath () string {
if e . Type != "dashboard" || len ( e . UID ) == 0 {
return ""
2021-07-22 09:11:33 +02:00
}
2022-04-06 10:50:39 +02:00
return "/d/" + e . UID
2021-07-22 09:11:33 +02:00
}
2022-11-16 15:54:04 +01:00
func ( e Includes ) RequiresRBACAction () bool {
return e . Action != ""
}
2021-11-01 09:53:33 +00:00
type Dependency struct {
ID string `json:"id"`
2018-02-16 09:49:29 +01:00
Type string `json:"type"`
Name string `json:"name"`
Version string `json:"version"`
}
2021-11-01 09:53:33 +00:00
type BuildInfo struct {
2024-09-20 10:20:59 +02:00
Time int64 `json:"time,omitempty"`
2019-07-18 11:52:34 -07:00
}
2021-11-01 09:53:33 +00:00
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"`
2024-02-14 14:30:24 +01:00
Keywords [] string `json:"keywords"`
2015-12-22 16:32:17 +01:00
}
2021-11-01 09:53:33 +00:00
type InfoLink struct {
2015-12-22 16:32:17 +01:00
Name string `json:"name"`
2021-11-01 09:53:33 +00:00
URL string `json:"url"`
2015-12-22 16:32:17 +01:00
}
2021-11-01 09:53:33 +00:00
type Logos struct {
2015-12-22 16:32:17 +01:00
Small string `json:"small"`
Large string `json:"large"`
}
2021-11-01 09:53:33 +00:00
type Screenshots struct {
2016-02-09 22:36:42 +08:00
Name string `json:"name"`
2021-11-01 09:53:33 +00:00
Path string `json:"path"`
2016-02-09 22:36:42 +08:00
}
2021-11-01 09:53:33 +00:00
type StaticRoute struct {
PluginID string
2016-01-09 08:12:27 +01:00
Directory string
2015-11-19 16:50:17 +01:00
}
2021-11-01 09:53:33 +00:00
type SignatureStatus string
func ( ss SignatureStatus ) IsValid () bool {
2023-06-08 12:21:19 +02:00
return ss == SignatureStatusValid
2021-11-01 09:53:33 +00:00
}
func ( ss SignatureStatus ) IsInternal () bool {
2023-06-08 12:21:19 +02:00
return ss == SignatureStatusInternal
2021-11-01 09:53:33 +00:00
}
const (
2023-06-08 12:21:19 +02:00
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
2021-11-01 09:53:33 +00:00
)
type ReleaseState string
const (
2023-06-08 12:21:19 +02:00
ReleaseStateAlpha ReleaseState = "alpha"
2021-11-01 09:53:33 +00:00
)
type SignatureType string
const (
2023-06-08 12:21:19 +02:00
SignatureTypeGrafana SignatureType = "grafana"
SignatureTypeCommercial SignatureType = "commercial"
SignatureTypeCommunity SignatureType = "community"
SignatureTypePrivate SignatureType = "private"
SignatureTypePrivateGlob SignatureType = "private-glob"
2021-11-01 09:53:33 +00:00
)
2022-07-27 12:56:52 +02:00
func ( s SignatureType ) IsValid () bool {
switch s {
2023-06-08 12:21:19 +02:00
case SignatureTypeGrafana , SignatureTypeCommercial , SignatureTypeCommunity , SignatureTypePrivate ,
SignatureTypePrivateGlob :
2022-07-27 12:56:52 +02:00
return true
}
return false
}
2021-11-01 09:53:33 +00:00
type Signature struct {
Status SignatureStatus
Type SignatureType
SigningOrg string
}
type PluginMetaDTO struct {
JSONData
2024-08-30 11:03:44 +01:00
Signature SignatureStatus `json:"signature"`
Module string `json:"module"`
2024-10-04 14:55:09 +02:00
ModuleHash string `json:"moduleHash,omitempty"`
2024-08-30 11:03:44 +01:00
BaseURL string `json:"baseUrl"`
Angular AngularMeta `json:"angular"`
MultiValueFilterOperators bool `json:"multiValueFilterOperators"`
2024-09-09 10:38:35 +01:00
LoadingStrategy LoadingStrategy `json:"loadingStrategy"`
2015-12-03 15:52:37 +08:00
}
2021-07-29 11:52:23 +02:00
2021-12-14 10:16:13 +00:00
type DataSourceDTO struct {
2023-11-10 11:44:54 +01:00
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"`
2024-07-04 16:44:19 +02:00
APIVersion string `json:"apiVersion,omitempty"`
2021-12-14 10:16:13 +00:00
BasicAuth string `json:"basicAuth,omitempty"`
WithCredentials bool `json:"withCredentials,omitempty"`
2023-02-02 23:39:54 -05:00
// This is populated by an Enterprise hook
CachingConfig QueryCachingConfig `json:"cachingConfig,omitempty"`
2021-12-14 10:16:13 +00:00
// 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 {
2024-09-09 10:38:35 +01:00
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"`
2024-10-04 14:55:09 +02:00
ModuleHash string `json:"moduleHash,omitempty"`
2021-12-14 10:16:13 +00:00
}
2023-02-07 17:20:05 +01:00
type AppDTO struct {
2024-09-09 10:38:35 +01:00
ID string `json:"id"`
Path string `json:"path"`
Version string `json:"version"`
Preload bool `json:"preload"`
Angular AngularMeta `json:"angular"`
LoadingStrategy LoadingStrategy `json:"loadingStrategy"`
2024-10-04 08:41:26 +02:00
Extensions Extensions `json:"extensions"`
Dependencies Dependencies `json:"dependencies"`
2024-10-04 14:55:09 +02:00
ModuleHash string `json:"moduleHash,omitempty"`
2023-02-07 17:20:05 +01:00
}
2021-11-01 09:53:33 +00:00
const (
2024-04-18 13:04:22 +02:00
errorCodeSignatureMissing ErrorCode = "signatureMissing"
errorCodeSignatureModified ErrorCode = "signatureModified"
errorCodeSignatureInvalid ErrorCode = "signatureInvalid"
ErrorCodeFailedBackendStart ErrorCode = "failedBackendStart"
ErrorAngular ErrorCode = "angular"
2021-11-01 09:53:33 +00:00
)
type ErrorCode string
type Error struct {
2024-04-11 16:18:04 +02:00
ErrorCode `json:"errorCode"`
PluginID string `json:"pluginId,omitempty"`
SignatureStatus SignatureStatus `json:"status,omitempty"`
2024-04-18 13:04:22 +02:00
message string `json:"-"`
2024-04-11 16:18:04 +02:00
}
2024-09-09 10:38:35 +01:00
type LoadingStrategy string
const (
LoadingStrategyFetch LoadingStrategy = "fetch"
LoadingStrategyScript LoadingStrategy = "script"
)
2024-04-11 16:18:04 +02:00
func ( e Error ) Error () string {
2024-04-18 13:04:22 +02:00
if e . message != "" {
return e . message
}
2024-04-11 16:18:04 +02:00
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 {
2024-04-18 13:04:22 +02:00
if e . ErrorCode != "" {
return e . ErrorCode
}
2024-04-11 16:18:04 +02:00
switch e . SignatureStatus {
case SignatureStatusInvalid :
return errorCodeSignatureInvalid
case SignatureStatusModified :
return errorCodeSignatureModified
case SignatureStatusUnsigned :
return errorCodeSignatureMissing
case SignatureStatusInternal , SignatureStatusValid :
return ""
}
return ""
2021-07-29 11:52:23 +02:00
}
2021-12-14 10:16:13 +00:00
2024-04-18 13:04:22 +02:00
func ( e * Error ) WithMessage ( m string ) * Error {
e . message = m
return e
}
2024-04-18 14:29:02 +02:00
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"
}
2022-11-07 11:30:45 +01:00
// 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"`
}
2023-02-02 23:39:54 -05:00
2024-07-19 16:16:23 +01:00
// ActionSet is the model for ActionSet in RBAC.
type ActionSet struct {
Action string `json:"action"`
Actions [] string `json:"actions"`
}
2023-02-02 23:39:54 -05:00
type QueryCachingConfig struct {
Enabled bool `json:"enabled"`
TTLMS int64 `json:"TTLMs"`
}