Access control: Reduce number of API calls for role picker (#44905)

* Restucture state for TeamRolePicker and UserRolePicker

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
Karl Persson 2022-02-04 14:54:42 +01:00 committed by GitHub
parent 61533a3cb4
commit 3cf31451ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 46 additions and 56 deletions

View File

@ -1,38 +1,29 @@
import React, { FC, useState } from 'react'; import React, { FC, useState } from 'react';
import { useAsync } from 'react-use'; import { useAsync } from 'react-use';
import { contextSrv } from 'app/core/core'; import { Role } from 'app/types';
import { AccessControlAction, Role } from 'app/types';
import { RolePicker } from './RolePicker'; import { RolePicker } from './RolePicker';
import { fetchRoleOptions, fetchTeamRoles, updateTeamRoles } from './api'; import { fetchTeamRoles, updateTeamRoles } from './api';
export interface Props { export interface Props {
teamId: number; teamId: number;
orgId?: number; orgId?: number;
getRoleOptions?: () => Promise<Role[]>; roleOptions: Role[];
disabled?: boolean; disabled?: boolean;
builtinRolesDisabled?: boolean; builtinRolesDisabled?: boolean;
} }
export const TeamRolePicker: FC<Props> = ({ teamId, orgId, getRoleOptions, disabled, builtinRolesDisabled }) => { export const TeamRolePicker: FC<Props> = ({ teamId, orgId, roleOptions, disabled, builtinRolesDisabled }) => {
const [roleOptions, setRoleOptions] = useState<Role[]>([]);
const [appliedRoles, setAppliedRoles] = useState<Role[]>([]); const [appliedRoles, setAppliedRoles] = useState<Role[]>([]);
const { loading } = useAsync(async () => { const { loading } = useAsync(async () => {
try { try {
if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
let options = await (getRoleOptions ? getRoleOptions() : fetchRoleOptions(orgId));
setRoleOptions(options.filter((option) => !option.name?.startsWith('managed:')));
} else {
setRoleOptions([]);
}
const teamRoles = await fetchTeamRoles(teamId, orgId); const teamRoles = await fetchTeamRoles(teamId, orgId);
setAppliedRoles(teamRoles); setAppliedRoles(teamRoles);
} catch (e) { } catch (e) {
// TODO handle error // TODO handle error
console.error('Error loading options'); console.error('Error loading options');
} }
}, [getRoleOptions, orgId, teamId]); }, [orgId, teamId]);
return ( return (
<RolePicker <RolePicker

View File

@ -3,15 +3,15 @@ import { useAsync } from 'react-use';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { Role, OrgRole, AccessControlAction } from 'app/types'; import { Role, OrgRole, AccessControlAction } from 'app/types';
import { RolePicker } from './RolePicker'; import { RolePicker } from './RolePicker';
import { fetchBuiltinRoles, fetchRoleOptions, fetchUserRoles, updateUserRoles } from './api'; import { fetchUserRoles, updateUserRoles } from './api';
export interface Props { export interface Props {
builtInRole: OrgRole; builtInRole: OrgRole;
userId: number; userId: number;
orgId?: number; orgId?: number;
onBuiltinRoleChange: (newRole: OrgRole) => void; onBuiltinRoleChange: (newRole: OrgRole) => void;
getRoleOptions?: () => Promise<Role[]>; roleOptions: Role[];
getBuiltinRoles?: () => Promise<{ [key: string]: Role[] }>; builtInRoles?: { [key: string]: Role[] };
disabled?: boolean; disabled?: boolean;
builtinRolesDisabled?: boolean; builtinRolesDisabled?: boolean;
} }
@ -21,31 +21,15 @@ export const UserRolePicker: FC<Props> = ({
userId, userId,
orgId, orgId,
onBuiltinRoleChange, onBuiltinRoleChange,
getRoleOptions, roleOptions,
getBuiltinRoles, builtInRoles,
disabled, disabled,
builtinRolesDisabled, builtinRolesDisabled,
}) => { }) => {
const [roleOptions, setRoleOptions] = useState<Role[]>([]);
const [appliedRoles, setAppliedRoles] = useState<Role[]>([]); const [appliedRoles, setAppliedRoles] = useState<Role[]>([]);
const [builtInRoles, setBuiltinRoles] = useState<Record<string, Role[]>>({});
const { loading } = useAsync(async () => { const { loading } = useAsync(async () => {
try { try {
if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
let options = await (getRoleOptions ? getRoleOptions() : fetchRoleOptions(orgId));
setRoleOptions(options.filter((option) => !option.name?.startsWith('managed:')));
} else {
setRoleOptions([]);
}
if (contextSrv.hasPermission(AccessControlAction.ActionBuiltinRolesList)) {
const builtInRoles = await (getBuiltinRoles ? getBuiltinRoles() : fetchBuiltinRoles(orgId));
setBuiltinRoles(builtInRoles);
} else {
setBuiltinRoles({});
}
if (contextSrv.hasPermission(AccessControlAction.ActionUserRolesList)) { if (contextSrv.hasPermission(AccessControlAction.ActionUserRolesList)) {
const userRoles = await fetchUserRoles(userId, orgId); const userRoles = await fetchUserRoles(userId, orgId);
setAppliedRoles(userRoles); setAppliedRoles(userRoles);
@ -56,7 +40,7 @@ export const UserRolePicker: FC<Props> = ({
// TODO handle error // TODO handle error
console.error('Error loading options'); console.error('Error loading options');
} }
}, [getBuiltinRoles, getRoleOptions, orgId, userId]); }, [orgId, userId]);
return ( return (
<RolePicker <RolePicker

View File

@ -20,6 +20,7 @@ import { OrgPicker, OrgSelectItem } from 'app/core/components/Select/OrgPicker';
import { OrgRolePicker } from './OrgRolePicker'; import { OrgRolePicker } from './OrgRolePicker';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
interface Props { interface Props {
orgs: UserOrg[]; orgs: UserOrg[];
@ -133,8 +134,25 @@ class UnThemedOrgRow extends PureComponent<OrgRowProps> {
state = { state = {
currentRole: this.props.org.role, currentRole: this.props.org.role,
isChangingRole: false, isChangingRole: false,
roleOptions: [],
builtInRoles: {},
}; };
componentDidMount() {
if (contextSrv.licensedAccessControlEnabled()) {
if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
fetchRoleOptions(this.props.org.orgId)
.then((roles) => this.setState({ roleOptions: roles }))
.catch((e) => console.error(e));
}
if (contextSrv.hasPermission(AccessControlAction.ActionBuiltinRolesList)) {
fetchRoleOptions(this.props.org.orgId)
.then((roles) => this.setState({ builtInRoles: roles }))
.catch((e) => console.error(e));
}
}
}
onOrgRemove = () => { onOrgRemove = () => {
const { org } = this.props; const { org } = this.props;
this.props.onOrgRemove(org.orgId); this.props.onOrgRemove(org.orgId);
@ -184,6 +202,8 @@ class UnThemedOrgRow extends PureComponent<OrgRowProps> {
userId={user?.id || 0} userId={user?.id || 0}
orgId={org.orgId} orgId={org.orgId}
builtInRole={org.role} builtInRole={org.role}
roleOptions={this.state.roleOptions}
builtInRoles={this.state.builtInRoles}
onBuiltinRoleChange={this.onBuiltinRoleChange} onBuiltinRoleChange={this.onBuiltinRoleChange}
builtinRolesDisabled={rolePickerDisabled} builtinRolesDisabled={rolePickerDisabled}
/> />

View File

@ -28,10 +28,15 @@ const ServiceAccountsTable: FC<Props> = (props) => {
useEffect(() => { useEffect(() => {
async function fetchOptions() { async function fetchOptions() {
try { try {
if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
let options = await fetchRoleOptions(orgId); let options = await fetchRoleOptions(orgId);
setRoleOptions(options); setRoleOptions(options);
}
if (contextSrv.hasPermission(AccessControlAction.ActionBuiltinRolesList)) {
const builtInRoles = await fetchBuiltinRoles(orgId); const builtInRoles = await fetchBuiltinRoles(orgId);
setBuiltinRoles(builtInRoles); setBuiltinRoles(builtInRoles);
}
} catch (e) { } catch (e) {
console.error('Error loading options'); console.error('Error loading options');
} }
@ -41,9 +46,6 @@ const ServiceAccountsTable: FC<Props> = (props) => {
} }
}, [orgId]); }, [orgId]);
const getRoleOptions = async () => roleOptions;
const getBuiltinRoles = async () => builtinRoles;
return ( return (
<> <>
<table className="filter-table form-inline"> <table className="filter-table form-inline">
@ -89,8 +91,8 @@ const ServiceAccountsTable: FC<Props> = (props) => {
orgId={orgId} orgId={orgId}
builtInRole={serviceAccount.role} builtInRole={serviceAccount.role}
onBuiltinRoleChange={(newRole) => onRoleChange(newRole, serviceAccount)} onBuiltinRoleChange={(newRole) => onRoleChange(newRole, serviceAccount)}
getRoleOptions={getRoleOptions} roleOptions={roleOptions}
getBuiltinRoles={getBuiltinRoles} builtInRoles={builtinRoles}
disabled={rolePickerDisabled} disabled={rolePickerDisabled}
/> />
) : ( ) : (

View File

@ -43,7 +43,7 @@ export class TeamList extends PureComponent<Props, State> {
componentDidMount() { componentDidMount() {
this.fetchTeams(); this.fetchTeams();
if (contextSrv.licensedAccessControlEnabled()) { if (contextSrv.licensedAccessControlEnabled() && contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
this.fetchRoleOptions(); this.fetchRoleOptions();
} }
} }
@ -95,7 +95,7 @@ export class TeamList extends PureComponent<Props, State> {
</td> </td>
{contextSrv.licensedAccessControlEnabled() && ( {contextSrv.licensedAccessControlEnabled() && (
<td> <td>
<TeamRolePicker teamId={team.id} getRoleOptions={async () => this.state.roleOptions} /> <TeamRolePicker teamId={team.id} roleOptions={this.state.roleOptions} />
</td> </td>
)} )}
<td className="text-right"> <td className="text-right">

View File

@ -26,15 +26,11 @@ const UsersTable: FC<Props> = (props) => {
if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) { if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) {
let options = await fetchRoleOptions(orgId); let options = await fetchRoleOptions(orgId);
setRoleOptions(options); setRoleOptions(options);
} else {
setRoleOptions([]);
} }
if (contextSrv.hasPermission(AccessControlAction.ActionBuiltinRolesList)) { if (contextSrv.hasPermission(AccessControlAction.ActionBuiltinRolesList)) {
const builtInRoles = await fetchBuiltinRoles(orgId); const builtInRoles = await fetchBuiltinRoles(orgId);
setBuiltinRoles(builtInRoles); setBuiltinRoles(builtInRoles);
} else {
setBuiltinRoles({});
} }
} catch (e) { } catch (e) {
console.error('Error loading options'); console.error('Error loading options');
@ -45,9 +41,6 @@ const UsersTable: FC<Props> = (props) => {
} }
}, [orgId]); }, [orgId]);
const getRoleOptions = async () => roleOptions;
const getBuiltinRoles = async () => builtinRoles;
return ( return (
<> <>
<table className="filter-table form-inline"> <table className="filter-table form-inline">
@ -94,8 +87,8 @@ const UsersTable: FC<Props> = (props) => {
orgId={orgId} orgId={orgId}
builtInRole={user.role} builtInRole={user.role}
onBuiltinRoleChange={(newRole) => onRoleChange(newRole, user)} onBuiltinRoleChange={(newRole) => onRoleChange(newRole, user)}
getRoleOptions={getRoleOptions} roleOptions={roleOptions}
getBuiltinRoles={getBuiltinRoles} builtInRoles={builtinRoles}
disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersRoleUpdate, user)} disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersRoleUpdate, user)}
/> />
) : ( ) : (