Commit 9b90ff29 by Alex Khomenko Committed by GitHub

Disable selecting enterprise plugins with no license (#28758)

* Add unlicensed property to plugins

* Disable selecting unlicensed plugin

* Add customizable plugin market place url

* License: workaround enabled only in enterprise

* linter

* Move licensing info to front end

* Update pkg/services/licensing/oss.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/licensing/oss.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/setting/setting.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/setting/setting.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/api/frontendsettings.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update sample.ini

* Update docs

* Update packages/grafana-runtime/src/config.ts

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* Update public/app/features/datasources/state/buildCategories.ts

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* Update pkg/api/frontendsettings.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/setting/setting.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix spelling

Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
parent 62138e8a
......@@ -769,6 +769,7 @@ enable_alpha = false
app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
allow_loading_unsigned_plugins =
marketplace_url = https://grafana.com/grafana/plugins/
#################################### Grafana Image Renderer Plugin ##########################
[plugin.grafana-image-renderer]
......
......@@ -757,6 +757,7 @@
;app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
;allow_loading_unsigned_plugins =
;marketplace_url = https://grafana.com/grafana/plugins/
#################################### Grafana Image Renderer Plugin ##########################
[plugin.grafana-image-renderer]
......
......@@ -1336,6 +1336,10 @@ Set to `true` if you want to test alpha plugins that are not yet ready for gener
Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
### marketplace_url
Custom install/learn more url for enterprise plugins. Defaults to https://grafana.com/grafana/plugins/.
<hr>
## [plugin.grafana-image-renderer]
......
......@@ -55,6 +55,8 @@ export interface LicenseInfo {
expiry: number;
licenseUrl: string;
stateInfo: string;
hasValidLicense: boolean;
edition: string;
}
/**
......
......@@ -114,6 +114,7 @@ export interface DataSourcePluginMeta<T extends KeyValue = {}> extends PluginMet
queryOptions?: PluginMetaQueryOptions;
sort?: number;
streaming?: boolean;
unlicensed?: boolean;
}
interface PluginMetaQueryOptions {
......
......@@ -62,6 +62,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
rendererAvailable = false;
http2Enabled = false;
dateFormats?: SystemDateFormatSettings;
marketplaceUrl?: string;
constructor(options: GrafanaBootConfig) {
this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark);
......
......@@ -231,13 +231,16 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
},
"licenseInfo": map[string]interface{}{
"hasLicense": hs.License.HasLicense(),
"hasValidLicense": hs.License.HasValidLicense(),
"expiry": hs.License.Expiry(),
"stateInfo": hs.License.StateInfo(),
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
"edition": hs.License.Edition(),
},
"featureToggles": hs.Cfg.FeatureToggles,
"rendererAvailable": hs.RenderService.IsAvailable(),
"http2Enabled": hs.Cfg.Protocol == setting.HTTP2Scheme,
"marketplaceUrl": hs.Cfg.MarketplaceURL,
}
return jsonObj, nil
......
......@@ -8,7 +8,6 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
......
......@@ -7,6 +7,10 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
const (
openSource = "Open Source"
)
type OSSLicensingService struct {
Cfg *setting.Cfg `inject:""`
HooksService *hooks.HooksService `inject:""`
......@@ -21,7 +25,7 @@ func (*OSSLicensingService) Expiry() int64 {
}
func (*OSSLicensingService) Edition() string {
return "Open Source"
return openSource
}
func (*OSSLicensingService) StateInfo() string {
......
......@@ -271,6 +271,7 @@ type Cfg struct {
PluginsAppsSkipVerifyTLS bool
PluginSettings PluginSettings
PluginsAllowUnsigned []string
MarketplaceURL string
DisableSanitizeHtml bool
EnterpriseLicensePath string
......@@ -794,6 +795,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
plug = strings.TrimSpace(plug)
cfg.PluginsAllowUnsigned = append(cfg.PluginsAllowUnsigned, plug)
}
cfg.MarketplaceURL = pluginsSection.Key("marketplace_url").MustString("https://grafana.com/grafana/plugins/")
cfg.Protocol = Protocol
// Read and populate feature toggles list
......
......@@ -129,10 +129,9 @@ interface DataSourceTypeCardProps {
const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
const { plugin, onLearnMoreClick } = props;
const isPhantom = plugin.module === 'phantom';
const onClick = !isPhantom ? props.onClick : () => {};
const onClick = !isPhantom && !plugin.unlicensed ? props.onClick : () => {};
// find first plugin info link
const learnMoreLink = plugin.info.links && plugin.info.links.length > 0 ? plugin.info.links[0] : null;
const learnMoreLink = plugin.info?.links?.length > 0 ? plugin.info.links[0] : null;
return (
<Card
......@@ -154,7 +153,7 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
{learnMoreLink.name}
</LinkButton>
)}
{!isPhantom && <Button>Select</Button>}
{!isPhantom && <Button disabled={plugin.unlicensed}>Select</Button>}
</>
}
labels={
......
import { DataSourcePluginMeta, PluginType } from '@grafana/data';
import { DataSourcePluginCategory } from 'app/types';
import { config } from '../../../core/config';
export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePluginCategory[] {
const categories: DataSourcePluginCategory[] = [
......@@ -22,10 +23,15 @@ export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePlug
categoryIndex[category.id] = category;
}
const { edition, hasValidLicense } = config.licenseInfo;
for (const plugin of plugins) {
const enterprisePlugin = enterprisePlugins.find(item => item.id === plugin.id);
// Force category for enterprise plugins
if (plugin.enterprise || enterprisePlugins.find(item => item.id === plugin.id)) {
if (plugin.enterprise || enterprisePlugin) {
plugin.category = 'enterprise';
plugin.unlicensed = edition !== 'Open Source' && !hasValidLicense;
plugin.info.links = enterprisePlugin?.info?.links || plugin.info.links;
}
// Fix link name
......@@ -197,7 +203,7 @@ function getPhantomPlugin(options: GetPhantomPluginOptions): DataSourcePluginMet
author: { name: 'Grafana Labs' },
links: [
{
url: 'https://grafana.com/grafana/plugins/' + options.id,
url: config.marketplaceUrl + options.id,
name: 'Install now',
},
],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment