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