1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-15 17:50:48 +02:00

feat: upgrade AdminAlert to PermissionGuard (#4074)

https://linear.app/unleash/issue/2-1165/improve-adminalert-usage-to-be-more-generic-accept-non-admin

Upgrades our `AdminAlert` to a new `PermissionGuard`.

**Question**: We don't **need** to, but **should** we be specific about
the `ADMIN` permission every time?
Technically `PermissionGuard` could have `permissions` as optional and
assume `[]` by default, which will add `ADMIN` anyways. However, I feel
like we may gain some readability if we're specific about it. WDYT?

Single permission:

![image](https://github.com/Unleash/unleash/assets/14320932/eab414ae-e798-4ab6-ba96-cde2977dc98b)

Multiple permissions:

![image](https://github.com/Unleash/unleash/assets/14320932/25302442-8fcc-4aa1-9525-d54f5f9350af)
This commit is contained in:
Nuno Góis 2023-06-23 13:25:35 +01:00 committed by GitHub
parent d2a98d0338
commit 95a0c7748f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 324 additions and 419 deletions

View File

@ -1,7 +1,4 @@
import { useContext } from 'react'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { ApiTokenTable } from 'component/common/ApiTokenTable/ApiTokenTable'; import { ApiTokenTable } from 'component/common/ApiTokenTable/ApiTokenTable';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageHeader } from 'component/common/PageHeader/PageHeader';
@ -24,7 +21,6 @@ import {
} from '@server/types/permissions'; } from '@server/types/permissions';
export const ApiTokenPage = () => { export const ApiTokenPage = () => {
const { hasAccess } = useContext(AccessContext);
const { tokens, loading, refetch } = useApiTokens(); const { tokens, loading, refetch } = useApiTokens();
const { deleteToken } = useApiTokensApi(); const { deleteToken } = useApiTokensApi();
@ -71,13 +67,13 @@ export const ApiTokenPage = () => {
}); });
return ( return (
<ConditionallyRender <PermissionGuard
condition={hasAccess([ permissions={[
READ_CLIENT_API_TOKEN, READ_CLIENT_API_TOKEN,
READ_FRONTEND_API_TOKEN, READ_FRONTEND_API_TOKEN,
ADMIN, ADMIN,
])} ]}
show={() => ( >
<PageContent <PageContent
header={ header={
<PageHeader <PageHeader
@ -114,8 +110,6 @@ export const ApiTokenPage = () => {
globalFilter={globalFilter} globalFilter={globalFilter}
/> />
</PageContent> </PageContent>
)} </PermissionGuard>
elseShow={() => <AdminAlert />}
/>
); );
}; };

View File

@ -1,4 +1,3 @@
import React from 'react';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -8,6 +7,8 @@ import { SamlAuth } from './SamlAuth/SamlAuth';
import { PasswordAuth } from './PasswordAuth/PasswordAuth'; import { PasswordAuth } from './PasswordAuth/PasswordAuth';
import { GoogleAuth } from './GoogleAuth/GoogleAuth'; import { GoogleAuth } from './GoogleAuth/GoogleAuth';
import { TabNav } from 'component/common/TabNav/TabNav/TabNav'; import { TabNav } from 'component/common/TabNav/TabNav/TabNav';
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { ADMIN } from '@server/types/permissions';
export const AuthSettings = () => { export const AuthSettings = () => {
const { authenticationType } = useUiConfig().uiConfig; const { authenticationType } = useUiConfig().uiConfig;
@ -36,6 +37,7 @@ export const AuthSettings = () => {
return ( return (
<div> <div>
<PermissionGuard permissions={ADMIN}>
<PageContent header="Single Sign-On"> <PageContent header="Single Sign-On">
<ConditionallyRender <ConditionallyRender
condition={authenticationType === 'enterprise'} condition={authenticationType === 'enterprise'}
@ -45,9 +47,9 @@ export const AuthSettings = () => {
condition={authenticationType === 'open-source'} condition={authenticationType === 'open-source'}
show={ show={
<Alert severity="warning"> <Alert severity="warning">
You are running the open-source version of Unleash. You are running the open-source version of
You have to use the Enterprise edition in order Unleash. You have to use the Enterprise edition
configure Single Sign-on. in order configure Single Sign-on.
</Alert> </Alert>
} }
/> />
@ -55,9 +57,9 @@ export const AuthSettings = () => {
condition={authenticationType === 'demo'} condition={authenticationType === 'demo'}
show={ show={
<Alert severity="warning"> <Alert severity="warning">
You are running Unleash in demo mode. You have to You are running Unleash in demo mode. You have
use the Enterprise edition in order configure Single to use the Enterprise edition in order configure
Sign-on. Single Sign-on.
</Alert> </Alert>
} }
/> />
@ -65,9 +67,10 @@ export const AuthSettings = () => {
condition={authenticationType === 'custom'} condition={authenticationType === 'custom'}
show={ show={
<Alert severity="warning"> <Alert severity="warning">
You have decided to use custom authentication type. You have decided to use custom authentication
You have to use the Enterprise edition in order type. You have to use the Enterprise edition in
configure Single Sign-on from the user interface. order configure Single Sign-on from the user
interface.
</Alert> </Alert>
} }
/> />
@ -81,6 +84,7 @@ export const AuthSettings = () => {
} }
/> />
</PageContent> </PageContent>
</PermissionGuard>
</div> </div>
); );
}; };

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import {
Box, Box,
Button, Button,
@ -9,8 +9,6 @@ import {
} from '@mui/material'; } from '@mui/material';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings'; import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings';
import useAuthSettingsApi from 'hooks/api/actions/useAuthSettingsApi/useAuthSettingsApi'; import useAuthSettingsApi from 'hooks/api/actions/useAuthSettingsApi/useAuthSettingsApi';
@ -31,7 +29,6 @@ export const GoogleAuth = () => {
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const [data, setData] = useState(initialState); const [data, setData] = useState(initialState);
const { hasAccess } = useContext(AccessContext);
const { config } = useAuthSettings('google'); const { config } = useAuthSettings('google');
const { updateSettings, errors, loading } = useAuthSettingsApi('google'); const { updateSettings, errors, loading } = useAuthSettingsApi('google');
@ -41,10 +38,6 @@ export const GoogleAuth = () => {
} }
}, [config]); }, [config]);
if (!hasAccess(ADMIN)) {
return <span>You need admin privileges to access this section.</span>;
}
const updateField = (event: React.ChangeEvent<HTMLInputElement>) => { const updateField = (event: React.ChangeEvent<HTMLInputElement>) => {
setData({ setData({
...data, ...data,

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import {
Button, Button,
FormControl, FormControl,
@ -12,8 +12,6 @@ import {
} from '@mui/material'; } from '@mui/material';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { AutoCreateForm } from '../AutoCreateForm/AutoCreateForm'; import { AutoCreateForm } from '../AutoCreateForm/AutoCreateForm';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useAuthSettingsApi from 'hooks/api/actions/useAuthSettingsApi/useAuthSettingsApi'; import useAuthSettingsApi from 'hooks/api/actions/useAuthSettingsApi/useAuthSettingsApi';
@ -41,7 +39,6 @@ export const OidcAuth = () => {
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const [data, setData] = useState(initialState); const [data, setData] = useState(initialState);
const { hasAccess } = useContext(AccessContext);
const { config } = useAuthSettings('oidc'); const { config } = useAuthSettings('oidc');
const { updateSettings, errors, loading } = useAuthSettingsApi('oidc'); const { updateSettings, errors, loading } = useAuthSettingsApi('oidc');
@ -51,14 +48,6 @@ export const OidcAuth = () => {
} }
}, [config]); }, [config]);
if (!hasAccess(ADMIN)) {
return (
<Alert severity="error">
You need to be a root admin to access this section.
</Alert>
);
}
const updateField = (event: React.ChangeEvent<HTMLInputElement>) => { const updateField = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.name, event.target.value); setValue(event.target.name, event.target.value);
}; };

View File

@ -1,9 +1,7 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, FormControlLabel, Grid, Switch } from '@mui/material'; import { Button, FormControlLabel, Grid, Switch } from '@mui/material';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings'; import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings';
import useAuthSettingsApi, { import useAuthSettingsApi, {
ISimpleAuthSettings, ISimpleAuthSettings,
@ -22,7 +20,6 @@ export const PasswordAuth = () => {
useState<boolean>(false); useState<boolean>(false);
const { updateSettings, errors, loading } = const { updateSettings, errors, loading } =
useAuthSettingsApi<ISimpleAuthSettings>('simple'); useAuthSettingsApi<ISimpleAuthSettings>('simple');
const { hasAccess } = useContext(AccessContext);
const [confirmationOpen, setConfirmationOpen] = useState(false); const [confirmationOpen, setConfirmationOpen] = useState(false);
const { data: adminCount } = useAdminCount(); const { data: adminCount } = useAdminCount();
const { tokens } = useApiTokens(); const { tokens } = useApiTokens();
@ -31,14 +28,6 @@ export const PasswordAuth = () => {
setDisablePasswordAuth(!!config.disabled); setDisablePasswordAuth(!!config.disabled);
}, [config.disabled]); }, [config.disabled]);
if (!hasAccess(ADMIN)) {
return (
<Alert severity="error">
You need to be a root admin to access this section.
</Alert>
);
}
const updateDisabled = () => { const updateDisabled = () => {
setDisablePasswordAuth(!disablePasswordAuth); setDisablePasswordAuth(!disablePasswordAuth);
}; };

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import {
Button, Button,
FormControlLabel, FormControlLabel,
@ -8,8 +8,6 @@ import {
} from '@mui/material'; } from '@mui/material';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { AutoCreateForm } from '../AutoCreateForm/AutoCreateForm'; import { AutoCreateForm } from '../AutoCreateForm/AutoCreateForm';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
@ -36,7 +34,6 @@ export const SamlAuth = () => {
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const [data, setData] = useState(initialState); const [data, setData] = useState(initialState);
const { hasAccess } = useContext(AccessContext);
const { config } = useAuthSettings('saml'); const { config } = useAuthSettings('saml');
const { updateSettings, errors, loading } = useAuthSettingsApi('saml'); const { updateSettings, errors, loading } = useAuthSettingsApi('saml');
@ -46,14 +43,6 @@ export const SamlAuth = () => {
} }
}, [config]); }, [config]);
if (!hasAccess(ADMIN)) {
return (
<Alert severity="error">
You need to be a root admin to access this section.
</Alert>
);
}
const updateField = (event: React.ChangeEvent<HTMLInputElement>) => { const updateField = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.name, event.target.value); setValue(event.target.name, event.target.value);
}; };

View File

@ -1,9 +1,8 @@
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { useContext, useEffect } from 'react'; import { useEffect } from 'react';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import AccessContext from 'contexts/AccessContext'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus'; import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { BillingDashboard } from './BillingDashboard/BillingDashboard'; import { BillingDashboard } from './BillingDashboard/BillingDashboard';
@ -19,7 +18,6 @@ export const Billing = () => {
loading, loading,
} = useInstanceStatus(); } = useInstanceStatus();
const { invoices } = useInvoices(); const { invoices } = useInvoices();
const { hasAccess } = useContext(AccessContext);
useEffect(() => { useEffect(() => {
const hardRefresh = async () => { const hardRefresh = async () => {
@ -35,18 +33,14 @@ export const Billing = () => {
<ConditionallyRender <ConditionallyRender
condition={isBilling} condition={isBilling}
show={ show={
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)}
show={() => (
<> <>
<BillingDashboard <BillingDashboard
instanceStatus={instanceStatus!} instanceStatus={instanceStatus!}
/> />
<BillingHistory data={invoices} /> <BillingHistory data={invoices} />
</> </>
)} </PermissionGuard>
elseShow={() => <AdminAlert />}
/>
} }
elseShow={ elseShow={
<Alert severity="error"> <Alert severity="error">

View File

@ -1,6 +1,6 @@
import { Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import InvoiceAdminPage from 'component/admin/invoice/InvoiceAdminPage'; import { InvoiceAdminPage } from 'component/admin/invoice/InvoiceAdminPage';
const FlaggedBillingRedirect = () => { const FlaggedBillingRedirect = () => {
const { uiConfig, loading } = useUiConfig(); const { uiConfig, loading } = useUiConfig();

View File

@ -1,8 +1,5 @@
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AccessContext from 'contexts/AccessContext';
import React, { useContext } from 'react';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
@ -10,19 +7,13 @@ import { CorsHelpAlert } from 'component/admin/cors/CorsHelpAlert';
import { CorsForm } from 'component/admin/cors/CorsForm'; import { CorsForm } from 'component/admin/cors/CorsForm';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
export const CorsAdmin = () => { export const CorsAdmin = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <CorsPage />
show={<CorsPage />} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
};
const CorsPage = () => { const CorsPage = () => {
const { uiConfig, loading } = useUiConfig(); const { uiConfig, loading } = useUiConfig();

View File

@ -1,20 +1,11 @@
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { GroupsList } from './GroupsList/GroupsList'; import { GroupsList } from './GroupsList/GroupsList';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useContext } from 'react';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from '@server/types/permissions'; import { ADMIN } from '@server/types/permissions';
export const GroupsAdmin = () => { export const GroupsAdmin = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <GroupsList />
show={<GroupsList />} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
};

View File

@ -1,25 +1,11 @@
import { useContext } from 'react';
import InvoiceList from './InvoiceList'; import InvoiceList from './InvoiceList';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { Alert } from '@mui/material';
const InvoiceAdminPage = () => { export const InvoiceAdminPage = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <InvoiceList />
show={<InvoiceList />} </PermissionGuard>
elseShow={
<Alert severity="error">
You need to be instance admin to access this section.
</Alert>
}
/>
</div> </div>
); );
};
export default InvoiceAdminPage;

View File

@ -1,8 +1,5 @@
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AccessContext from 'contexts/AccessContext';
import React, { useContext } from 'react';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { Box, styled } from '@mui/material'; import { Box, styled } from '@mui/material';
@ -10,19 +7,13 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { MaintenanceTooltip } from './MaintenanceTooltip'; import { MaintenanceTooltip } from './MaintenanceTooltip';
import { MaintenanceToggle } from './MaintenanceToggle'; import { MaintenanceToggle } from './MaintenanceToggle';
export const MaintenanceAdmin = () => { export const MaintenanceAdmin = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <MaintenancePage />
show={<MaintenancePage />} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
};
const StyledBox = styled(Box)(({ theme }) => ({ const StyledBox = styled(Box)(({ theme }) => ({
display: 'grid', display: 'grid',

View File

@ -1,9 +1,8 @@
import { useContext, useState } from 'react'; import { useState } from 'react';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { RolesTable } from './RolesTable/RolesTable'; import { RolesTable } from './RolesTable/RolesTable';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { Tab, Tabs, styled, useMediaQuery } from '@mui/material'; import { Tab, Tabs, styled, useMediaQuery } from '@mui/material';
import { Route, Routes, useLocation } from 'react-router-dom'; import { Route, Routes, useLocation } from 'react-router-dom';
@ -45,7 +44,6 @@ const StyledActions = styled('div')({
export const Roles = () => { export const Roles = () => {
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const { hasAccess } = useContext(AccessContext);
const { pathname } = useLocation(); const { pathname } = useLocation();
const { roles, projectRoles, loading } = useRoles(); const { roles, projectRoles, loading } = useRoles();
@ -84,9 +82,7 @@ export const Roles = () => {
return ( return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={[READ_ROLE, ADMIN]}>
condition={hasAccess(READ_ROLE)}
show={
<StyledPageContent <StyledPageContent
headerClass="page-header" headerClass="page-header"
bodyClass="page-body" bodyClass="page-body"
@ -102,24 +98,19 @@ export const Roles = () => {
variant="scrollable" variant="scrollable"
allowScrollButtonsMobile allowScrollButtonsMobile
> >
{tabs.map( {tabs.map(({ label, path, total }) => (
({ label, path, total }) => (
<Tab <Tab
key={label} key={label}
value={path} value={path}
label={ label={
<CenteredNavLink <CenteredNavLink to={path}>
to={path}
>
<span> <span>
{label} ( {label} ({total})
{total})
</span> </span>
</CenteredNavLink> </CenteredNavLink>
} }
/> />
) ))}
)}
</Tabs> </Tabs>
</StyledTabsContainer> </StyledTabsContainer>
<StyledActions> <StyledActions>
@ -128,12 +119,8 @@ export const Roles = () => {
show={ show={
<> <>
<Search <Search
initialValue={ initialValue={searchValue}
searchValue onChange={setSearchValue}
}
onChange={
setSearchValue
}
/> />
<PageHeader.Divider /> <PageHeader.Divider />
</> </>
@ -197,9 +184,7 @@ export const Roles = () => {
/> />
</Routes> </Routes>
</StyledPageContent> </StyledPageContent>
} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
}; };

View File

@ -1,20 +1,11 @@
import { useContext } from 'react';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { ServiceAccountsTable } from './ServiceAccountsTable/ServiceAccountsTable'; import { ServiceAccountsTable } from './ServiceAccountsTable/ServiceAccountsTable';
export const ServiceAccounts = () => { export const ServiceAccounts = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <ServiceAccountsTable />
show={<ServiceAccountsTable />} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
};

View File

@ -1,24 +1,15 @@
import { useContext } from 'react';
import UsersList from './UsersList/UsersList'; import UsersList from './UsersList/UsersList';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { InviteLinkBar } from './InviteLinkBar/InviteLinkBar'; import { InviteLinkBar } from './InviteLinkBar/InviteLinkBar';
export const UsersAdmin = () => { export const UsersAdmin = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<InviteLinkBar /> <InviteLinkBar />
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <UsersList />
show={<UsersList />} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
};
export default UsersAdmin; export default UsersAdmin;

View File

@ -1,9 +0,0 @@
import { Alert } from '@mui/material';
export const AdminAlert = () => {
return (
<Alert severity="error">
You need instance admin to access this section.
</Alert>
);
};

View File

@ -0,0 +1,54 @@
import { useContext } from 'react';
import { Alert, styled } from '@mui/material';
import { ADMIN } from '@server/types/permissions';
import AccessContext from 'contexts/AccessContext';
const StyledList = styled('ul')(({ theme }) => ({
paddingInlineStart: theme.spacing(2),
}));
interface IPermissionGuardProps {
permissions: string | string[];
children: JSX.Element;
}
export const PermissionGuard = ({
permissions,
children,
}: IPermissionGuardProps) => {
const { hasAccess } = useContext(AccessContext);
const permissionsArray = Array.isArray(permissions)
? permissions
: [permissions];
if (!permissionsArray.includes(ADMIN)) {
permissionsArray.push(ADMIN);
}
if (hasAccess(permissionsArray)) {
return children;
}
if (permissionsArray.length === 1) {
return (
<Alert severity="error">
You need the <strong>{permissionsArray[0]}</strong> permission
to access this section.
</Alert>
);
}
return (
<Alert severity="error">
You need one of the following permissions to access this section:
<StyledList>
{permissionsArray.sort().map(permission => (
<li key={permission}>
<strong>{permission}</strong>
</li>
))}
</StyledList>
</Alert>
);
};

View File

@ -1,18 +1,9 @@
import React, { useContext } from 'react';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import AccessContext from 'contexts/AccessContext';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { EventLog } from 'component/events/EventLog/EventLog'; import { EventLog } from 'component/events/EventLog/EventLog';
export const EventPage = () => { export const EventPage = () => (
const { hasAccess } = useContext(AccessContext); <PermissionGuard permissions={ADMIN}>
<EventLog title="Event log" />
return ( </PermissionGuard>
<ConditionallyRender );
condition={hasAccess(ADMIN)}
show={() => <EventLog title="Event log" />}
elseShow={<AdminAlert />}
/>
);
};

View File

@ -1,20 +1,11 @@
import { useContext } from 'react';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { LoginHistoryTable } from './LoginHistoryTable/LoginHistoryTable'; import { LoginHistoryTable } from './LoginHistoryTable/LoginHistoryTable';
export const LoginHistory = () => { export const LoginHistory = () => (
const { hasAccess } = useContext(AccessContext);
return (
<div> <div>
<ConditionallyRender <PermissionGuard permissions={ADMIN}>
condition={hasAccess(ADMIN)} <LoginHistoryTable />
show={<LoginHistoryTable />} </PermissionGuard>
elseShow={<AdminAlert />}
/>
</div> </div>
); );
};