1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

chore: rename access matrix to access overview (#9531)

https://linear.app/unleash/issue/2-3344/new-name-access-overview-instead-of-access-matrix

Renames Access Matrix to Access Overview, both internally (code) and
externally (UI).
This commit is contained in:
Nuno Góis 2025-03-13 15:23:58 +00:00 committed by GitHub
parent dadda7b648
commit 0d0530b61c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 60 additions and 59 deletions

View File

@ -2,7 +2,7 @@ import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useUserInfo from 'hooks/api/getters/useUserInfo/useUserInfo'; import useUserInfo from 'hooks/api/getters/useUserInfo/useUserInfo';
import { PermissionsTable } from './PermissionsTable'; import { AccessOverviewTable } from './AccessOverviewTable';
import { styled, useMediaQuery } from '@mui/material'; import { styled, useMediaQuery } from '@mui/material';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
@ -10,8 +10,8 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import theme from 'themes/theme'; import theme from 'themes/theme';
import { StringParam, useQueryParams } from 'use-query-params'; import { StringParam, useQueryParams } from 'use-query-params';
import useProjects from 'hooks/api/getters/useProjects/useProjects'; import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { AccessMatrixSelect } from './AccessMatrixSelect'; import { AccessOverviewSelect } from './AccessOverviewSelect';
import { useUserAccessMatrix } from 'hooks/api/getters/useUserAccessMatrix/useUserAccessMatrix'; import { useUserAccessOverview } from 'hooks/api/getters/useUserAccessOverview/useUserAccessOverview';
const StyledActionsContainer = styled('div')(({ theme }) => ({ const StyledActionsContainer = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
@ -28,7 +28,7 @@ const StyledTitle = styled('h2')(({ theme }) => ({
margin: theme.spacing(2, 0), margin: theme.spacing(2, 0),
})); }));
export const AccessMatrix = () => { export const AccessOverview = () => {
const id = useRequiredPathParam('id'); const id = useRequiredPathParam('id');
const [query, setQuery] = useQueryParams({ const [query, setQuery] = useQueryParams({
project: StringParam, project: StringParam,
@ -45,7 +45,7 @@ export const AccessMatrix = () => {
query.environment ?? undefined, query.environment ?? undefined,
); );
const { matrix, rootRole, projectRoles } = useUserAccessMatrix( const { overview, rootRole, projectRoles } = useUserAccessOverview(
id, id,
project, project,
environment, environment,
@ -63,14 +63,14 @@ export const AccessMatrix = () => {
const AccessActions = ( const AccessActions = (
<StyledActionsContainer> <StyledActionsContainer>
<AccessMatrixSelect <AccessOverviewSelect
label='Project' label='Project'
options={projects} options={projects}
getOptionLabel={(option) => option?.name ?? ''} getOptionLabel={(option) => option?.name ?? ''}
value={projects.find(({ id }) => id === project)} value={projects.find(({ id }) => id === project)}
setValue={(value) => setProject(value?.id ?? '')} setValue={(value) => setProject(value?.id ?? '')}
/> />
<AccessMatrixSelect <AccessOverviewSelect
label='Environment' label='Environment'
options={environments} options={environments}
getOptionLabel={(option) => getOptionLabel={(option) =>
@ -89,7 +89,7 @@ export const AccessMatrix = () => {
isLoading={loading} isLoading={loading}
header={ header={
<PageHeader <PageHeader
title={`Access for ${user.name ?? user.username}`} title={`Access overview for ${user.name ?? user.username}`}
actions={ actions={
<ConditionallyRender <ConditionallyRender
condition={!isSmallScreen} condition={!isSmallScreen}
@ -107,16 +107,16 @@ export const AccessMatrix = () => {
<StyledTitle> <StyledTitle>
Root permissions for role {rootRole?.name} Root permissions for role {rootRole?.name}
</StyledTitle> </StyledTitle>
<PermissionsTable permissions={matrix?.root ?? []} /> <AccessOverviewTable permissions={overview?.root ?? []} />
<StyledTitle> <StyledTitle>
Project permissions for project {project} with project roles [ Project permissions for project {project} with project roles [
{projectRoles?.map((role: any) => role.name).join(', ')}] {projectRoles?.map((role: any) => role.name).join(', ')}]
</StyledTitle> </StyledTitle>
<PermissionsTable permissions={matrix?.project ?? []} /> <AccessOverviewTable permissions={overview?.project ?? []} />
<StyledTitle> <StyledTitle>
Environment permissions for environment {environment} Environment permissions for environment {environment}
</StyledTitle> </StyledTitle>
<PermissionsTable permissions={matrix?.environment ?? []} /> <AccessOverviewTable permissions={overview?.environment ?? []} />
</PageContent> </PageContent>
); );
}; };

View File

@ -1,6 +1,6 @@
import { Autocomplete, type AutocompleteProps, TextField } from '@mui/material'; import { Autocomplete, type AutocompleteProps, TextField } from '@mui/material';
interface IAccessMatrixSelectProps<T> interface IAccessOverviewSelectProps<T>
extends Partial<AutocompleteProps<T, false, false, false>> { extends Partial<AutocompleteProps<T, false, false, false>> {
label: string; label: string;
options: T[]; options: T[];
@ -8,13 +8,13 @@ interface IAccessMatrixSelectProps<T>
setValue: (role: T | null) => void; setValue: (role: T | null) => void;
} }
export const AccessMatrixSelect = <T,>({ export const AccessOverviewSelect = <T,>({
label, label,
options, options,
value, value,
setValue, setValue,
...rest ...rest
}: IAccessMatrixSelectProps<T>) => ( }: IAccessOverviewSelectProps<T>) => (
<Autocomplete <Autocomplete
options={options} options={options}
value={value} value={value}

View File

@ -7,12 +7,12 @@ import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import Check from '@mui/icons-material/Check'; import Check from '@mui/icons-material/Check';
import Close from '@mui/icons-material/Close'; import Close from '@mui/icons-material/Close';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import type { IMatrixPermission } from 'interfaces/permissions'; import type { IAccessOverviewPermission } from 'interfaces/permissions';
export const PermissionsTable = ({ export const AccessOverviewTable = ({
permissions, permissions,
}: { }: {
permissions: IMatrixPermission[]; permissions: IAccessOverviewPermission[];
}) => { }) => {
const columns = useMemo( const columns = useMemo(
() => [ () => [

View File

@ -5,7 +5,7 @@ import { Route, Routes } from 'react-router-dom';
import EditUser from './EditUser/EditUser'; import EditUser from './EditUser/EditUser';
import NotFound from 'component/common/NotFound/NotFound'; import NotFound from 'component/common/NotFound/NotFound';
import { InactiveUsersList } from './InactiveUsersList/InactiveUsersList'; import { InactiveUsersList } from './InactiveUsersList/InactiveUsersList';
import { AccessMatrix } from './AccessMatrix/AccessMatrix'; import { AccessOverview } from './AccessOverview/AccessOverview';
import { PremiumFeature } from '../../common/PremiumFeature/PremiumFeature'; import { PremiumFeature } from '../../common/PremiumFeature/PremiumFeature';
import { ConditionallyRender } from '../../common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from '../../common/ConditionallyRender/ConditionallyRender';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
@ -25,7 +25,7 @@ export const UsersAdmin = () => {
} }
/> />
<Route path=':id/edit' element={<EditUser />} /> <Route path=':id/edit' element={<EditUser />} />
<Route path=':id/access' element={<AccessMatrix />} /> <Route path=':id/access' element={<AccessOverview />} />
<Route <Route
path='inactive' path='inactive'
element={ element={

View File

@ -4,7 +4,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import type { ActionsActionState } from '../../useProjectActionsForm'; import type { ActionsActionState } from '../../useProjectActionsForm';
import { ProjectActionsFormItem } from '../ProjectActionsFormItem'; import { ProjectActionsFormItem } from '../ProjectActionsFormItem';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useServiceAccountAccessMatrix } from 'hooks/api/getters/useServiceAccountAccessMatrix/useServiceAccountAccessMatrix'; import { useServiceAccountAccessOverview } from 'hooks/api/getters/useServiceAccountAccessOverview/useServiceAccountAccessOverview';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { ProjectActionsActionParameter } from './ProjectActionsActionParameter/ProjectActionsActionParameter'; import { ProjectActionsActionParameter } from './ProjectActionsActionParameter/ProjectActionsActionParameter';
import type { ActionConfigurations } from 'interfaces/action'; import type { ActionConfigurations } from 'interfaces/action';
@ -51,7 +51,7 @@ export const ProjectActionsActionItem = ({
}: IProjectActionsItemProps) => { }: IProjectActionsItemProps) => {
const { action: actionName, executionParams, error } = action; const { action: actionName, executionParams, error } = action;
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const { permissions } = useServiceAccountAccessMatrix( const { permissions } = useServiceAccountAccessOverview(
actorId, actorId,
projectId, projectId,
executionParams.environment as string, executionParams.environment as string,

View File

@ -3,49 +3,49 @@ import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler'; import handleErrorResponses from '../httpErrorResponseHandler';
import type { IRole } from 'interfaces/role'; import type { IRole } from 'interfaces/role';
import type { IServiceAccount } from 'interfaces/service-account'; import type { IServiceAccount } from 'interfaces/service-account';
import type { IMatrixPermission } from 'interfaces/permissions'; import type { IAccessOverviewPermission } from 'interfaces/permissions';
import type { IPermission } from 'interfaces/user'; import type { IPermission } from 'interfaces/user';
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR'; import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
interface IServiceAccountAccessMatrix { interface IServiceAccountAccessOverview {
root: IMatrixPermission[]; root: IAccessOverviewPermission[];
project: IMatrixPermission[]; project: IAccessOverviewPermission[];
environment: IMatrixPermission[]; environment: IAccessOverviewPermission[];
} }
interface IServiceAccountAccessMatrixResponse { interface IServiceAccountAccessOverviewResponse {
matrix: IServiceAccountAccessMatrix; overview: IServiceAccountAccessOverview;
projectRoles: IRole[]; projectRoles: IRole[];
rootRole: IRole; rootRole: IRole;
serviceAccount: IServiceAccount; serviceAccount: IServiceAccount;
permissions: IPermission[]; permissions: IPermission[];
} }
interface IServiceAccountAccessMatrixOutput interface IServiceAccountAccessOverviewOutput
extends Partial<IServiceAccountAccessMatrixResponse> { extends Partial<IServiceAccountAccessOverviewResponse> {
permissions: IPermission[]; permissions: IPermission[];
loading: boolean; loading: boolean;
refetch: () => void; refetch: () => void;
error?: Error; error?: Error;
} }
export const useServiceAccountAccessMatrix = ( export const useServiceAccountAccessOverview = (
id?: number, id?: number,
project?: string, project?: string,
environment?: string, environment?: string,
): IServiceAccountAccessMatrixOutput => { ): IServiceAccountAccessOverviewOutput => {
const queryParams = `${project ? `?project=${project}` : ''}${ const queryParams = `${project ? `?project=${project}` : ''}${
environment ? `${project ? '&' : '?'}environment=${environment}` : '' environment ? `${project ? '&' : '?'}environment=${environment}` : ''
}`; }`;
const url = `api/admin/service-account/${id}/permissions${queryParams}`; const url = `api/admin/service-account/${id}/permissions${queryParams}`;
const { data, error, mutate } = useConditionalSWR< const { data, error, mutate } = useConditionalSWR<
IServiceAccountAccessMatrixResponse | undefined IServiceAccountAccessOverviewResponse | undefined
>(Boolean(id), undefined, formatApiPath(url), fetcher); >(Boolean(id), undefined, formatApiPath(url), fetcher);
return useMemo( return useMemo(
() => ({ () => ({
matrix: data?.matrix, overview: data?.overview,
projectRoles: data?.projectRoles, projectRoles: data?.projectRoles,
rootRole: data?.rootRole, rootRole: data?.rootRole,
serviceAccount: data?.serviceAccount, serviceAccount: data?.serviceAccount,
@ -60,6 +60,6 @@ export const useServiceAccountAccessMatrix = (
const fetcher = (path: string) => { const fetcher = (path: string) => {
return fetch(path) return fetch(path)
.then(handleErrorResponses('Service account access matrix')) .then(handleErrorResponses('Service account access overview'))
.then((res) => res.json()); .then((res) => res.json());
}; };

View File

@ -4,45 +4,46 @@ import handleErrorResponses from '../httpErrorResponseHandler';
import useSWR from 'swr'; import useSWR from 'swr';
import type { IRole } from 'interfaces/role'; import type { IRole } from 'interfaces/role';
import type { IUser } from 'interfaces/user'; import type { IUser } from 'interfaces/user';
import type { IMatrixPermission } from 'interfaces/permissions'; import type { IAccessOverviewPermission } from 'interfaces/permissions';
interface IUserAccessMatrix { interface IUserAccessOverview {
root: IMatrixPermission[]; root: IAccessOverviewPermission[];
project: IMatrixPermission[]; project: IAccessOverviewPermission[];
environment: IMatrixPermission[]; environment: IAccessOverviewPermission[];
} }
interface IUserAccessMatrixResponse { interface IUserAccessOverviewResponse {
matrix: IUserAccessMatrix; overview: IUserAccessOverview;
projectRoles: IRole[]; projectRoles: IRole[];
rootRole: IRole; rootRole: IRole;
user: IUser; user: IUser;
} }
interface IUserAccessMatrixOutput extends Partial<IUserAccessMatrixResponse> { interface IUserAccessOverviewOutput
extends Partial<IUserAccessOverviewResponse> {
loading: boolean; loading: boolean;
refetch: () => void; refetch: () => void;
error?: Error; error?: Error;
} }
export const useUserAccessMatrix = ( export const useUserAccessOverview = (
id: string, id: string,
project?: string, project?: string,
environment?: string, environment?: string,
): IUserAccessMatrixOutput => { ): IUserAccessOverviewOutput => {
const queryParams = `${project ? `?project=${project}` : ''}${ const queryParams = `${project ? `?project=${project}` : ''}${
environment ? `${project ? '&' : '?'}environment=${environment}` : '' environment ? `${project ? '&' : '?'}environment=${environment}` : ''
}`; }`;
const url = `api/admin/user-admin/${id}/permissions${queryParams}`; const url = `api/admin/user-admin/${id}/permissions${queryParams}`;
const { data, error, mutate } = useSWR<IUserAccessMatrixResponse>( const { data, error, mutate } = useSWR<IUserAccessOverviewResponse>(
formatApiPath(url), formatApiPath(url),
fetcher, fetcher,
); );
return useMemo( return useMemo(
() => ({ () => ({
matrix: data?.matrix, overview: data?.overview,
projectRoles: data?.projectRoles, projectRoles: data?.projectRoles,
rootRole: data?.rootRole, rootRole: data?.rootRole,
user: data?.user, user: data?.user,

View File

@ -37,6 +37,6 @@ export interface IPermissionCategory {
permissions: IPermission[]; permissions: IPermission[];
} }
export interface IMatrixPermission extends IPermission { export interface IAccessOverviewPermission extends IPermission {
hasPermission: boolean; hasPermission: boolean;
} }

View File

@ -741,7 +741,7 @@ export default class UserAdminController extends Controller {
), ),
); );
} }
const matrix = await this.accessService.permissionsMatrixForUser( const overview = await this.accessService.getAccessOverviewForUser(
user, user,
project, project,
environment, environment,
@ -749,7 +749,7 @@ export default class UserAdminController extends Controller {
// TODO add response validation based on the schema // TODO add response validation based on the schema
res.status(200).json({ res.status(200).json({
matrix, overview,
user, user,
rootRole, rootRole,
projectRoles, projectRoles,

View File

@ -63,13 +63,13 @@ const PROJECT_ADMIN = [
export type IdPermissionRef = Pick<IPermission, 'id' | 'environment'>; export type IdPermissionRef = Pick<IPermission, 'id' | 'environment'>;
export type NamePermissionRef = Pick<IPermission, 'name' | 'environment'>; export type NamePermissionRef = Pick<IPermission, 'name' | 'environment'>;
export type PermissionRef = IdPermissionRef | NamePermissionRef; export type PermissionRef = IdPermissionRef | NamePermissionRef;
type MatrixPermission = IPermission & { type AccessOverviewPermission = IPermission & {
hasPermission: boolean; hasPermission: boolean;
}; };
type PermissionMatrix = { type AccessOverview = {
root: MatrixPermission[]; root: AccessOverviewPermission[];
project: MatrixPermission[]; project: AccessOverviewPermission[];
environment: MatrixPermission[]; environment: AccessOverviewPermission[];
}; };
type APIUser = Pick<IUser, 'id' | 'permissions'> & { isAPI: true }; type APIUser = Pick<IUser, 'id' | 'permissions'> & { isAPI: true };
@ -244,14 +244,14 @@ export class AccessService {
* Provided a project, project permissions will be checked against that project. * Provided a project, project permissions will be checked against that project.
* Provided an environment, environment permissions will be checked against that environment (and project). * Provided an environment, environment permissions will be checked against that environment (and project).
*/ */
async permissionsMatrixForUser( async getAccessOverviewForUser(
user: APIUser | NonAPIUser, user: APIUser | NonAPIUser,
projectId?: string, projectId?: string,
environment?: string, environment?: string,
): Promise<PermissionMatrix> { ): Promise<AccessOverview> {
const permissions = await this.getPermissions(); const permissions = await this.getPermissions();
const userP = await this.getPermissionsForUser(user); const userP = await this.getPermissionsForUser(user);
const matrix: PermissionMatrix = { const overview: AccessOverview = {
root: permissions.root.map((p) => ({ root: permissions.root.map((p) => ({
...p, ...p,
hasPermission: this.meetsAllPermissions(userP, [p.name]), hasPermission: this.meetsAllPermissions(userP, [p.name]),
@ -278,7 +278,7 @@ export class AccessService {
})) ?? [], })) ?? [],
}; };
return matrix; return overview;
} }
async getPermissionsForUser( async getPermissionsForUser(