Authorization: Add group to role DisplayName to make filtered list more clear (#102950)

* add group to role DisplayName to make searching easier

* clean up more role names; add filtered display text when fetching

* pass filter state into role menu to decide how to display role name

* prop name better describes what it does
This commit is contained in:
Cory Forseth
2025-04-08 09:15:03 -05:00
committed by GitHub
parent a3c632b814
commit 4caa9853cb
18 changed files with 62 additions and 44 deletions
+10 -10
View File
@@ -99,7 +99,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
builtInDatasourceReader := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:datasources.builtin:reader",
DisplayName: "Built in reader",
DisplayName: "Built in data source reader",
Description: "Read and query Grafana's built in test data sources.",
Group: "Data sources",
Permissions: []ac.Permission{
@@ -321,7 +321,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
dashboardAnnotationsWriterRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:annotations.dashboard:writer",
DisplayName: "Dashboard annotation writer",
DisplayName: "Writer (dashboard)",
Description: "Update annotations associated with dashboards.",
Group: "Annotations",
Permissions: []ac.Permission{
@@ -353,7 +353,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
annotationsReaderRole = ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:annotations:reader",
DisplayName: "Organization annotation reader",
DisplayName: "Reader (organization)",
Description: "Read organization annotations and annotation tags",
Group: "Annotations",
Permissions: []ac.Permission{
@@ -370,7 +370,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
annotationsWriterRole = ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:annotations:writer",
DisplayName: "Organization annotation writer",
DisplayName: "Writer (organization)",
Description: "Update organization annotations.",
Group: "Annotations",
Permissions: []ac.Permission{
@@ -463,7 +463,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
generalFolderReaderRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:folders.general:reader",
DisplayName: "General folder reader",
DisplayName: "Reader (root)",
Description: "Access the general (root) folder.",
Group: "Folders",
Hidden: true,
@@ -526,7 +526,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
libraryPanelsGeneralReaderRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:library.panels:general.reader",
DisplayName: "General reader",
DisplayName: "Reader (root)",
Description: "Read all library panels under the root folder.",
Group: "Library panels",
Permissions: []ac.Permission{
@@ -554,7 +554,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
libraryPanelsGeneralWriterRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:library.panels:general.writer",
DisplayName: "Root level writer",
DisplayName: "Writer (root)",
Group: "Library panels",
Description: "Create, read, write or delete all library panels and their permissions under the root folder.",
Permissions: ac.ConcatPermissions(libraryPanelsGeneralReaderRole.Role.Permissions, []ac.Permission{
@@ -569,7 +569,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
publicDashboardsWriterRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:dashboards.public:writer",
DisplayName: "Public Dashboard writer",
DisplayName: "Writer (public)",
Description: "Create, write or disable a public dashboard.",
Group: "Dashboards",
Permissions: []ac.Permission{
@@ -658,7 +658,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
allAnnotationsReaderRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:annotations.all:reader",
DisplayName: "Reader",
DisplayName: "Reader (all)",
Description: "Read all annotations and tags",
Group: "Annotations",
Permissions: []ac.Permission{
@@ -672,7 +672,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
allAnnotationsWriterRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: "fixed:annotations.all:writer",
DisplayName: "Writer",
DisplayName: "Writer (all)",
Description: "Update all annotations.",
Group: "Annotations",
Permissions: []ac.Permission{
@@ -163,8 +163,8 @@ func ProvideDashboardPermissions(
"Edit": getDashboardEditActions(features),
"Admin": getDashboardAdminActions(features),
},
ReaderRoleName: "Dashboard permission reader",
WriterRoleName: "Dashboard permission writer",
ReaderRoleName: "Permission reader",
WriterRoleName: "Permission writer",
RoleGroup: "Dashboards",
}
@@ -126,8 +126,8 @@ func ProvideFolderPermissions(
"Edit": append(getDashboardEditActions(features), FolderEditActions...),
"Admin": append(getDashboardAdminActions(features), FolderAdminActions...),
},
ReaderRoleName: "Folder permission reader",
WriterRoleName: "Folder permission writer",
ReaderRoleName: "Permission reader",
WriterRoleName: "Permission writer",
RoleGroup: "Folders",
}
srv, err := resourcepermissions.New(cfg, options, features, router, license, accesscontrol, service, sql, teamService, userService, actionSetService)
@@ -67,8 +67,8 @@ func ProvideServiceAccountPermissions(
"Edit": ServiceAccountEditActions,
"Admin": ServiceAccountAdminActions,
},
ReaderRoleName: "Service account permission reader",
WriterRoleName: "Service account permission writer",
ReaderRoleName: "Permission reader",
WriterRoleName: "Permission writer",
RoleGroup: "Service accounts",
}
@@ -78,8 +78,8 @@ func ProvideTeamPermissions(
"Member": TeamMemberActions,
"Admin": TeamAdminActions,
},
ReaderRoleName: "Team permission reader",
WriterRoleName: "Team permission writer",
ReaderRoleName: "Permission reader",
WriterRoleName: "Permission writer",
RoleGroup: "Teams",
OnSetUser: func(session *db.Session, orgID int64, user accesscontrol.User, resourceID, permission string) error {
teamId, err := strconv.ParseInt(resourceID, 10, 64)
+13 -13
View File
@@ -43,7 +43,7 @@ const (
var (
ldapReaderRole = RoleDTO{
Name: "fixed:ldap:reader",
DisplayName: "LDAP reader",
DisplayName: "Reader",
Description: "Read LDAP configuration and status.",
Group: "LDAP",
Permissions: []Permission{
@@ -58,7 +58,7 @@ var (
ldapWriterRole = RoleDTO{
Name: "fixed:ldap:writer",
DisplayName: "LDAP writer",
DisplayName: "Writer",
Description: "Read and update LDAP configuration and read LDAP status.",
Group: "LDAP",
Permissions: ConcatPermissions(ldapReaderRole.Permissions, []Permission{
@@ -73,9 +73,9 @@ var (
orgUsersWriterRole = RoleDTO{
Name: "fixed:org.users:writer",
DisplayName: "Organization user writer",
DisplayName: "Writer (organizational)",
Description: "Within a single organization, add a user, invite a user, read information about a user and their role, remove a user from that organization, or change the role of a user.",
Group: "User administration (organizational)",
Group: "User administration",
Permissions: ConcatPermissions(orgUsersReaderRole.Permissions, []Permission{
{
Action: ActionOrgUsersAdd,
@@ -94,9 +94,9 @@ var (
orgUsersReaderRole = RoleDTO{
Name: "fixed:org.users:reader",
DisplayName: "Organization user reader",
DisplayName: "Reader (organizational)",
Description: "Read users within a single organization.",
Group: "User administration (organizational)",
Group: "User administration",
Permissions: []Permission{
{
Action: ActionOrgUsersRead,
@@ -111,7 +111,7 @@ var (
SettingsReaderRole = RoleDTO{
Name: "fixed:settings:reader",
DisplayName: "Setting reader",
DisplayName: "Reader",
Description: "Read Grafana instance settings.",
Group: "Settings",
Permissions: []Permission{
@@ -124,7 +124,7 @@ var (
statsReaderRole = RoleDTO{
Name: "fixed:stats:reader",
DisplayName: "Statistics reader",
DisplayName: "Reader",
Description: "Read Grafana instance statistics.",
Group: "Statistics",
Permissions: []Permission{
@@ -136,9 +136,9 @@ var (
usersReaderRole = RoleDTO{
Name: "fixed:users:reader",
DisplayName: "User reader",
DisplayName: "Reader (global)",
Description: "Read all users and their information, such as team memberships, authentication tokens, and quotas.",
Group: "User administration (global)",
Group: "User administration",
Permissions: []Permission{
{
Action: ActionUsersRead,
@@ -157,9 +157,9 @@ var (
usersWriterRole = RoleDTO{
Name: "fixed:users:writer",
DisplayName: "User writer",
DisplayName: "Writer (global)",
Description: "Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a user’s authentication token, or update quotas for all users.",
Group: "User administration (global)",
Group: "User administration",
Permissions: ConcatPermissions(usersReaderRole.Permissions, []Permission{
{
Action: ActionUsersPasswordUpdate,
@@ -295,7 +295,7 @@ var (
usagestatsReaderRole = RoleDTO{
Name: "fixed:usagestats:reader",
DisplayName: "Usage stats report reader",
DisplayName: "Usage report reader",
Description: "View usage statistics report",
Group: "Statistics",
Permissions: []Permission{
@@ -60,7 +60,7 @@ func DeclareRBACRoles(service ac.Service, cfg *setting.Cfg, features featuremgmt
PluginsWriter := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: ac.FixedRolePrefix + "plugins:writer",
DisplayName: "Plugin Writer",
DisplayName: "Writer",
Description: "Enable and disable plugins and edit plugins' settings",
Group: "Plugins",
Permissions: []ac.Permission{
@@ -72,7 +72,7 @@ func DeclareRBACRoles(service ac.Service, cfg *setting.Cfg, features featuremgmt
PluginsMaintainer := ac.RoleRegistration{
Role: ac.RoleDTO{
Name: ac.FixedRolePrefix + "plugins:maintainer",
DisplayName: "Plugin Maintainer",
DisplayName: "Maintainer",
Description: "Install, uninstall plugins. Needs to be assigned globally.",
Group: "Plugins",
Permissions: []ac.Permission{
@@ -10,7 +10,7 @@ func RegisterRoles(service accesscontrol.Service) error {
saReader := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:serviceaccounts:reader",
DisplayName: "Service accounts reader",
DisplayName: "Reader",
Description: "Read service accounts and service account tokens.",
Group: "Service accounts",
Permissions: []accesscontrol.Permission{
@@ -26,7 +26,7 @@ func RegisterRoles(service accesscontrol.Service) error {
saCreator := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:serviceaccounts:creator",
DisplayName: "Service accounts creator",
DisplayName: "Creator",
Description: "Create service accounts.",
Group: "Service accounts",
Permissions: []accesscontrol.Permission{
@@ -41,7 +41,7 @@ func RegisterRoles(service accesscontrol.Service) error {
saWriter := accesscontrol.RoleRegistration{
Role: accesscontrol.RoleDTO{
Name: "fixed:serviceaccounts:writer",
DisplayName: "Service accounts writer",
DisplayName: "Writer",
Description: "Create, delete and read service accounts, manage service account permissions.",
Group: "Service accounts",
Permissions: accesscontrol.ConcatPermissions(saReader.Role.Permissions, []accesscontrol.Permission{
@@ -14,7 +14,7 @@ const (
var (
bundleReaderRole = accesscontrol.RoleDTO{
Name: "fixed:support.bundles:reader",
DisplayName: "Support bundle reader",
DisplayName: "Reader",
Description: "List and download support bundles",
Group: "Support bundles",
Permissions: []accesscontrol.Permission{
@@ -24,7 +24,7 @@ var (
bundleWriterRole = accesscontrol.RoleDTO{
Name: "fixed:support.bundles:writer",
DisplayName: "Support bundle writer",
DisplayName: "Writer",
Description: "Create, delete, list and download support bundles",
Group: "Support bundles",
Permissions: []accesscontrol.Permission{
@@ -11,6 +11,7 @@ import { isNotDelegatable } from './utils';
interface RoleMenuGroupsSectionProps {
roles: Role[];
isFiltered?: boolean;
renderedName: string;
showGroups?: boolean;
optionGroups: Array<{
@@ -33,6 +34,7 @@ export const RoleMenuGroupsSection = forwardRef<HTMLDivElement, RoleMenuGroupsSe
(
{
roles,
isFiltered,
renderedName,
showGroups,
optionGroups,
@@ -101,6 +103,7 @@ export const RoleMenuGroupsSection = forwardRef<HTMLDivElement, RoleMenuGroupsSe
))
: roles.map((option) => (
<RoleMenuOption
useFilteredDisplayName={isFiltered}
data={option}
key={option.uid}
isSelected={!!(option.uid && !!selectedOptions.find((opt) => opt.uid === option.uid))}
@@ -12,6 +12,7 @@ import { getStyles } from './styles';
interface RoleMenuOptionProps {
data: Role;
onChange: (value: Role) => void;
useFilteredDisplayName?: boolean;
isSelected?: boolean;
isFocused?: boolean;
disabled?: boolean;
@@ -20,7 +21,7 @@ interface RoleMenuOptionProps {
}
export const RoleMenuOption = forwardRef<HTMLDivElement, React.PropsWithChildren<RoleMenuOptionProps>>(
({ data, isFocused, isSelected, disabled, mapped, onChange, hideDescription }, ref) => {
({ data, isFocused, isSelected, useFilteredDisplayName, disabled, mapped, onChange, hideDescription }, ref) => {
const theme = useTheme2();
const styles = getSelectStyles(theme);
const customStyles = useStyles2(getStyles);
@@ -64,7 +65,7 @@ export const RoleMenuOption = forwardRef<HTMLDivElement, React.PropsWithChildren
disabled={disabled}
/>
<div className={cx(styles.optionBody, customStyles.menuOptionBody)}>
<span>{data.displayName || data.name}</span>
<span>{(useFilteredDisplayName && data.filteredDisplayName) || data.displayName || data.name}</span>
{!hideDescription && data.description && <div className={styles.optionDescription}>{data.description}</div>}
</div>
{disabledMessage && (
@@ -173,6 +173,7 @@ export const RolePicker = ({
const options = roleOptions.map((r) => ({ ...r, delegatable: canUpdateRoles && r.delegatable }));
if (query && query.trim() !== '') {
// TODO should this filter on `displayName` not (or in addition to) `name`?
return options.filter((option) => option.name?.toLowerCase().includes(query.toLowerCase()));
}
return options;
@@ -210,6 +211,7 @@ export const RolePicker = ({
<div onClick={(e) => e.stopPropagation()}>
<RolePickerMenu
options={getOptions()}
isFiltered={query.trim() !== ''}
basicRole={selectedBuiltInRole}
appliedRoles={appliedRoles}
onBasicRoleSelect={onBasicRoleSelect}
@@ -53,6 +53,7 @@ const tooltipMessage = (
interface RolePickerMenuProps {
basicRole?: OrgRole;
options: Role[];
isFiltered?: boolean;
appliedRoles: Role[];
showGroups?: boolean;
basicRoleDisabled?: boolean;
@@ -70,6 +71,7 @@ interface RolePickerMenuProps {
export const RolePickerMenu = ({
basicRole,
options,
isFiltered,
appliedRoles,
showGroups,
basicRoleDisabled,
@@ -259,6 +261,7 @@ export const RolePickerMenu = ({
<RoleMenuGroupsSection
key={groupId}
roles={collection.roles}
isFiltered={isFiltered}
renderedName={collection.renderedName}
showGroups={showGroups}
optionGroups={collection.optionGroup}
@@ -47,6 +47,7 @@ export const RolePickerSubMenu = ({
{options.map((option, i) => (
<RoleMenuOption
data={option}
useFilteredDisplayName={false}
key={i}
isSelected={
!!(
+4 -4
View File
@@ -1,7 +1,7 @@
import { getBackendSrv, isFetchError } from '@grafana/runtime';
import { Role } from 'app/types';
import { addDisplayNameForFixedRole } from './utils';
import { addDisplayNameForFixedRole, addFilteredDisplayName } from './utils';
export const fetchRoleOptions = async (orgId?: number): Promise<Role[]> => {
let rolesUrl = '/api/access-control/roles?delegatable=true';
@@ -12,7 +12,7 @@ export const fetchRoleOptions = async (orgId?: number): Promise<Role[]> => {
if (!roles || !roles.length) {
return [];
}
return roles.map(addDisplayNameForFixedRole);
return roles.map(addDisplayNameForFixedRole).map(addFilteredDisplayName);
};
export const fetchUserRoles = async (userId: number, orgId?: number): Promise<Role[]> => {
@@ -25,7 +25,7 @@ export const fetchUserRoles = async (userId: number, orgId?: number): Promise<Ro
if (!roles || !roles.length) {
return [];
}
return roles.map(addDisplayNameForFixedRole);
return roles.map(addDisplayNameForFixedRole).map(addFilteredDisplayName);
} catch (error) {
if (isFetchError(error)) {
error.isHandled = true;
@@ -57,7 +57,7 @@ export const fetchTeamRoles = async (teamId: number, orgId?: number): Promise<Ro
if (!roles || !roles.length) {
return [];
}
return roles.map(addDisplayNameForFixedRole);
return roles.map(addDisplayNameForFixedRole).map(addFilteredDisplayName);
} catch (error) {
if (isFetchError(error)) {
error.isHandled = true;
@@ -19,3 +19,9 @@ export const addDisplayNameForFixedRole = (role: Role) => {
}
return role;
};
// Adds a display name for use when the list of roles is filtered
export const addFilteredDisplayName = (role: Role) => {
role.filteredDisplayName = role.group + ':' + role.displayName;
return role;
};
@@ -22,6 +22,7 @@ const props = {
uid: 'uid',
name: 'admin',
displayName: 'Admin',
filteredDisplayName: 'group:Admin',
description: 'description',
group: 'group',
global: true,
+1
View File
@@ -173,6 +173,7 @@ export interface Role {
uid: string;
name: string;
displayName: string;
filteredDisplayName: string; // name to be shown in filtered role list
description: string;
group: string;
global: boolean;