mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Expose functions to plugins for checking RBAC permissions (#89047)
* feat(grafana-data): create rbac functions for checking permissions * feat(grafana-runtime): pass current user to runtime * feat(grafana-runtime): expose rbac functions to check permissions against current user * refactor(contextsrv): use functions from grafana/data to check rbac permissions against user * Apply suggestions from code review Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> * chore(rbac): fix missing types imports * refactor(rbac): make exposed functions return boolean --------- Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
This commit is contained in:
parent
7f4faaa45b
commit
40207c53ae
@ -50,3 +50,10 @@ export { CircularVector } from './vector/CircularVector';
|
||||
export { vectorator } from './vector/FunctionalVector';
|
||||
export { ArrayVector } from './vector/ArrayVector';
|
||||
export * from './dataframe/CircularDataFrame';
|
||||
export {
|
||||
type CurrentUser,
|
||||
userHasPermission,
|
||||
userHasPermissionInMetadata,
|
||||
userHasAllPermissions,
|
||||
userHasAnyPermission,
|
||||
} from './rbac/rbac';
|
||||
|
19
packages/grafana-data/src/rbac/rbac.ts
Normal file
19
packages/grafana-data/src/rbac/rbac.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { CurrentUserDTO, WithAccessControlMetadata } from '../types';
|
||||
|
||||
export interface CurrentUser extends Omit<CurrentUserDTO, 'lightTheme'> {}
|
||||
|
||||
export function userHasPermission(action: string, user: CurrentUser): boolean {
|
||||
return !!user.permissions?.[action];
|
||||
}
|
||||
|
||||
export function userHasPermissionInMetadata(action: string, object: WithAccessControlMetadata): boolean {
|
||||
return !!object.accessControl?.[action];
|
||||
}
|
||||
|
||||
export function userHasAllPermissions(actions: string[], user: CurrentUser) {
|
||||
return actions.every((action) => userHasPermission(action, user));
|
||||
}
|
||||
|
||||
export function userHasAnyPermission(actions: string[], user: CurrentUser) {
|
||||
return actions.some((action) => userHasPermission(action, user));
|
||||
}
|
@ -52,3 +52,4 @@ export {
|
||||
export { usePluginInteractionReporter } from './analytics/plugins/usePluginInteractionReporter';
|
||||
export { setReturnToPreviousHook, useReturnToPrevious } from './utils/returnToPrevious';
|
||||
export { type EmbeddedDashboardProps, EmbeddedDashboard, setEmbeddedDashboard } from './components/EmbeddedDashboard';
|
||||
export { hasPermission, hasPermissionInMetadata, hasAllPermissions, hasAnyPermission } from './utils/rbac';
|
||||
|
@ -33,3 +33,4 @@ export {
|
||||
export { setPluginComponentHook, usePluginComponent } from './pluginExtensions/usePluginComponent';
|
||||
|
||||
export { isPluginExtensionLink, isPluginExtensionComponent } from './pluginExtensions/utils';
|
||||
export { setCurrentUser } from './user';
|
||||
|
29
packages/grafana-runtime/src/services/user.ts
Normal file
29
packages/grafana-runtime/src/services/user.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { CurrentUser } from '@grafana/data';
|
||||
|
||||
let singletonInstance: CurrentUser | null = null;
|
||||
|
||||
/**
|
||||
* Used during startup by Grafana to set the current user so it is available
|
||||
* for rbac checks.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function setCurrentUser(instance: CurrentUser) {
|
||||
if (singletonInstance) {
|
||||
throw new Error('User should only be set once, when Grafana is starting.');
|
||||
}
|
||||
singletonInstance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to retrieve the current user.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
export function getCurrentUser(): CurrentUser {
|
||||
if (!singletonInstance) {
|
||||
throw new Error('User can only be used after Grafana instance has started.');
|
||||
}
|
||||
return singletonInstance;
|
||||
}
|
18
packages/grafana-runtime/src/utils/rbac.ts
Normal file
18
packages/grafana-runtime/src/utils/rbac.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {
|
||||
userHasPermission,
|
||||
userHasPermissionInMetadata,
|
||||
userHasAllPermissions,
|
||||
userHasAnyPermission,
|
||||
WithAccessControlMetadata,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { getCurrentUser } from '../services/user';
|
||||
|
||||
export const hasPermission = (action: string) => userHasPermission(action, getCurrentUser());
|
||||
|
||||
export const hasPermissionInMetadata = (action: string, object: WithAccessControlMetadata) =>
|
||||
userHasPermissionInMetadata(action, object);
|
||||
|
||||
export const hasAllPermissions = (actions: string[]) => userHasAllPermissions(actions, getCurrentUser());
|
||||
|
||||
export const hasAnyPermission = (actions: string[]) => userHasAnyPermission(actions, getCurrentUser());
|
@ -38,6 +38,7 @@ import {
|
||||
setReturnToPreviousHook,
|
||||
setPluginExtensionsHook,
|
||||
setPluginComponentHook,
|
||||
setCurrentUser,
|
||||
} from '@grafana/runtime';
|
||||
import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView';
|
||||
import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer';
|
||||
@ -144,6 +145,7 @@ export class GrafanaApp {
|
||||
setEmbeddedDashboard(EmbeddedDashboardLazy);
|
||||
setTimeZoneResolver(() => config.bootData.user.timezone);
|
||||
initGrafanaLive();
|
||||
setCurrentUser(contextSrv.user);
|
||||
|
||||
initAuthConfig();
|
||||
|
||||
|
@ -1,6 +1,14 @@
|
||||
import { extend } from 'lodash';
|
||||
|
||||
import { AnalyticsSettings, OrgRole, rangeUtil, WithAccessControlMetadata } from '@grafana/data';
|
||||
import {
|
||||
AnalyticsSettings,
|
||||
OrgRole,
|
||||
rangeUtil,
|
||||
WithAccessControlMetadata,
|
||||
userHasPermission,
|
||||
userHasPermissionInMetadata,
|
||||
userHasAnyPermission,
|
||||
} from '@grafana/data';
|
||||
import { featureEnabled, getBackendSrv } from '@grafana/runtime';
|
||||
import { getSessionExpiry } from 'app/core/utils/auth';
|
||||
import { AccessControlAction, UserPermission } from 'app/types';
|
||||
@ -131,12 +139,12 @@ export class ContextSrv {
|
||||
|
||||
// Checks whether user has required permission
|
||||
hasPermissionInMetadata(action: AccessControlAction | string, object: WithAccessControlMetadata): boolean {
|
||||
return !!object.accessControl?.[action];
|
||||
return userHasPermissionInMetadata(action, object);
|
||||
}
|
||||
|
||||
// Checks whether user has required permission
|
||||
hasPermission(action: AccessControlAction | string): boolean {
|
||||
return !!this.user.permissions?.[action];
|
||||
return userHasPermission(action, this.user);
|
||||
}
|
||||
|
||||
isGrafanaVisible() {
|
||||
@ -171,7 +179,7 @@ export class ContextSrv {
|
||||
|
||||
// evaluates access control permissions, granting access if the user has any of them
|
||||
evaluatePermission(actions: string[]) {
|
||||
if (actions.some((action) => this.hasPermission(action))) {
|
||||
if (userHasAnyPermission(actions, this.user)) {
|
||||
return [];
|
||||
}
|
||||
// Hack to reject when user does not have permission
|
||||
|
Loading…
Reference in New Issue
Block a user