From 824e0f9ce80d2f7bff16d3903b9290ccd5973137 Mon Sep 17 00:00:00 2001 From: linoman <2051016+linoman@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:29:13 -0600 Subject: [PATCH] Plugins: Display plugin permissions required (#78355) * Add definition of external service registration * Add style and tables for permissions needed * Add external service registration to local without counterpart * Add feature toggle check * Add feature flag check in the backend as well * Add the disclaimer for permissions --------- Co-authored-by: Gabriel MABILLE --- pkg/api/dtos/plugins.go | 2 + pkg/api/plugins.go | 4 ++ public/app/features/plugins/admin/api.ts | 1 + .../admin/components/PluginDetailsBody.tsx | 50 +++++++++++++++++-- public/app/features/plugins/admin/helpers.ts | 2 + .../admin/hooks/usePluginDetailsTabs.tsx | 10 ++++ public/app/features/plugins/admin/types.ts | 15 ++++++ 7 files changed, 81 insertions(+), 3 deletions(-) diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index 325e3fd40c9..0f50b6cbd7c 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -2,6 +2,7 @@ package dtos import ( "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/plugindef" "github.com/grafana/grafana/pkg/services/accesscontrol" ) @@ -47,6 +48,7 @@ type PluginListItem struct { SignatureOrg string `json:"signatureOrg"` AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"` AngularDetected bool `json:"angularDetected"` + IAM *plugindef.IAM `json:"iam,omitempty"` } type PluginList []PluginListItem diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 2edcfe3637b..f8cbf7ac66e 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -144,6 +144,10 @@ func (hs *HTTPServer) GetPluginList(c *contextmodel.ReqContext) response.Respons AngularDetected: pluginDef.Angular.Detected, } + if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagExternalServiceAccounts) { + listItem.IAM = pluginDef.IAM + } + update, exists := hs.pluginsUpdateChecker.HasUpdate(c.Req.Context(), pluginDef.ID) if exists { listItem.LatestVersion = update diff --git a/public/app/features/plugins/admin/api.ts b/public/app/features/plugins/admin/api.ts index 40344e2932a..f90d1c581b9 100644 --- a/public/app/features/plugins/admin/api.ts +++ b/public/app/features/plugins/admin/api.ts @@ -26,6 +26,7 @@ export async function getPluginDetails(id: string): Promise = CellProps; + export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.Element { const styles = useStyles2(getStyles); const { value: pluginConfig } = usePluginConfig(plugin); + const columns: Array> = useMemo( + () => [ + { + id: 'action', + header: 'Action', + cell: ({ cell: { value } }: Cell<'action'>) => value, + }, + { + id: 'scope', + header: 'Scope', + cell: ({ cell: { value } }: Cell<'scope'>) => value, + }, + ], + [] + ); + if (pageId === PluginTabIds.OVERVIEW) { return (
0; + + if (displayPermissions) { + return ( + <> + + The {plugin.name} plugin needs a service account to be able to query Grafana. The following list contains the + permissions available to the service account: + + String(permission.action)} + /> + + ); + } + if (pluginConfig?.configPages) { for (const configPage of pluginConfig.configPages) { if (pageId === configPage.id) { diff --git a/public/app/features/plugins/admin/helpers.ts b/public/app/features/plugins/admin/helpers.ts index c870fa9fbe0..2a4b6446900 100644 --- a/public/app/features/plugins/admin/helpers.ts +++ b/public/app/features/plugins/admin/helpers.ts @@ -161,6 +161,7 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat accessControl: accessControl, angularDetected, isFullyInstalled: true, + iam: plugin.iam, }; } @@ -218,6 +219,7 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e accessControl: local?.accessControl, angularDetected: local?.angularDetected || remote?.angularDetected, isFullyInstalled: Boolean(local) || isDisabled, + iam: local?.iam, }; } diff --git a/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx b/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx index 283d52abdfe..63f3008a5ab 100644 --- a/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx +++ b/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx @@ -41,6 +41,16 @@ export const usePluginDetailsTabs = (plugin?: CatalogPlugin, pageId?: PluginTabI return navModelChildren; } + if (config.featureToggles.externalServiceAccounts && (plugin?.iam || plugin?.details?.iam)) { + navModelChildren.push({ + text: PluginTabLabels.IAM, + icon: 'shield', + id: PluginTabIds.IAM, + url: `${pathname}?page=${PluginTabIds.IAM}`, + active: PluginTabIds.IAM === currentPageId, + }); + } + if (config.featureToggles.panelTitleSearch && pluginConfig.meta.type === PluginType.panel) { navModelChildren.push({ text: PluginTabLabels.USAGE, diff --git a/public/app/features/plugins/admin/types.ts b/public/app/features/plugins/admin/types.ts index 6966ac07cf2..d359d1ffaf1 100644 --- a/public/app/features/plugins/admin/types.ts +++ b/public/app/features/plugins/admin/types.ts @@ -62,6 +62,7 @@ export interface CatalogPlugin extends WithAccessControlMetadata { // instance plugins may not be fully installed, which means a new instance // running the plugin didn't started yet isFullyInstalled?: boolean; + iam?: IdentityAccessManagement; } export interface CatalogPluginDetails { @@ -74,6 +75,7 @@ export interface CatalogPluginDetails { grafanaDependency?: string; pluginDependencies?: PluginDependencies['plugins']; statusContext?: string; + iam?: IdentityAccessManagement; } export interface CatalogPluginInfo { @@ -93,6 +95,7 @@ export type RemotePlugin = { internal: boolean; json?: { dependencies: PluginDependencies; + iam?: IdentityAccessManagement; info: { links: Array<{ name: string; @@ -175,8 +178,18 @@ export type LocalPlugin = WithAccessControlMetadata & { type: PluginType; dependencies: PluginDependencies; angularDetected: boolean; + iam?: IdentityAccessManagement; }; +interface IdentityAccessManagement { + permissions: Permission[]; +} + +export interface Permission { + action: string; + scope: string; +} + interface Rel { name: string; url: string; @@ -231,6 +244,7 @@ export enum PluginTabLabels { CONFIG = 'Config', DASHBOARDS = 'Dashboards', USAGE = 'Usage', + IAM = 'IAM', } export enum PluginTabIds { @@ -239,6 +253,7 @@ export enum PluginTabIds { CONFIG = 'config', DASHBOARDS = 'dashboards', USAGE = 'usage', + IAM = 'iam', } export enum RequestStatus {