1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-03 01:18:43 +02:00

feat: use READ_API_TOKEN permission (#906)

* refactor: extract AdminAlert component

* refactor: split ApiTokenPage from ApiTokenList

* refactor: display AdminMenu based on path instead of permissions

* feat: use the new READ_API_TOKEN permission
This commit is contained in:
olav 2022-04-26 10:24:26 +02:00 committed by GitHub
parent 49a63173f8
commit f6e42f99f9
10 changed files with 140 additions and 128 deletions

View File

@ -1,16 +1,19 @@
import { ApiTokenList } from '../apiToken/ApiTokenList/ApiTokenList';
import AdminMenu from '../menu/AdminMenu'; import AdminMenu from '../menu/AdminMenu';
import ConditionallyRender from 'component/common/ConditionallyRender'; import ConditionallyRender from 'component/common/ConditionallyRender';
import AccessContext from 'contexts/AccessContext'; import { ApiTokenPage } from 'component/admin/apiToken/ApiTokenPage/ApiTokenPage';
import { useContext } from 'react'; import { useLocation } from 'react-router-dom';
const ApiPage = () => { const ApiPage = () => {
const { isAdmin } = useContext(AccessContext); const { pathname } = useLocation();
const showAdminMenu = pathname.includes('/admin/');
return ( return (
<div> <div>
<ConditionallyRender condition={isAdmin} show={<AdminMenu />} /> <ConditionallyRender
<ApiTokenList /> condition={showAdminMenu}
show={<AdminMenu />}
/>
<ApiTokenPage />
</div> </div>
); );
}; };

View File

@ -13,10 +13,6 @@ export const useStyles = makeStyles(theme => ({
justifyContent: 'center', justifyContent: 'center',
}, },
}, },
apiError: {
maxWidth: '400px',
marginBottom: '1rem',
},
center: { center: {
textAlign: 'center', textAlign: 'center',
}, },
@ -25,9 +21,6 @@ export const useStyles = makeStyles(theme => ({
display: 'flex-inline', display: 'flex-inline',
flexWrap: 'nowrap', flexWrap: 'nowrap',
}, },
infoBoxContainer: {
marginBottom: 40,
},
hideSM: { hideSM: {
[theme.breakpoints.down('sm')]: { [theme.breakpoints.down('sm')]: {
display: 'none', display: 'none',

View File

@ -1,7 +1,5 @@
import { Fragment, useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { import {
Button,
IconButton, IconButton,
Table, Table,
TableBody, TableBody,
@ -17,19 +15,12 @@ import useApiTokens from 'hooks/api/getters/useApiTokens/useApiTokens';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useApiTokensApi from 'hooks/api/actions/useApiTokensApi/useApiTokensApi'; import useApiTokensApi from 'hooks/api/actions/useApiTokensApi/useApiTokensApi';
import ApiError from 'component/common/ApiError/ApiError'; import ApiError from 'component/common/ApiError/ApiError';
import PageContent from 'component/common/PageContent';
import HeaderTitle from 'component/common/HeaderTitle';
import ConditionallyRender from 'component/common/ConditionallyRender'; import ConditionallyRender from 'component/common/ConditionallyRender';
import { import { DELETE_API_TOKEN } from 'component/providers/AccessProvider/permissions';
CREATE_API_TOKEN,
DELETE_API_TOKEN,
} from 'component/providers/AccessProvider/permissions';
import { useStyles } from './ApiTokenList.styles'; import { useStyles } from './ApiTokenList.styles';
import Secret from './secret'; import Secret from './secret';
import { Delete, FileCopy } from '@material-ui/icons'; import { Delete, FileCopy } from '@material-ui/icons';
import Dialogue from 'component/common/Dialogue'; import Dialogue from 'component/common/Dialogue';
import { CREATE_API_TOKEN_BUTTON } from 'utils/testIds';
import { Alert } from '@material-ui/lab';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import { useLocationSettings } from 'hooks/useLocationSettings'; import { useLocationSettings } from 'hooks/useLocationSettings';
import { formatDateYMD } from 'utils/formatDate'; import { formatDateYMD } from 'utils/formatDate';
@ -56,16 +47,9 @@ export const ApiTokenList = () => {
const { tokens, loading, refetch, error } = useApiTokens(); const { tokens, loading, refetch, error } = useApiTokens();
const { deleteToken } = useApiTokensApi(); const { deleteToken } = useApiTokensApi();
const ref = useLoading(loading); const ref = useLoading(loading);
const history = useHistory();
const renderError = () => { const renderError = () => {
return ( return <ApiError onClick={refetch} text="Error fetching api tokens" />;
<ApiError
onClick={refetch}
// className={styles.apiError}
text="Error fetching api tokens"
/>
);
}; };
const copyToken = (value: string) => { const copyToken = (value: string) => {
@ -236,53 +220,6 @@ export const ApiTokenList = () => {
return ( return (
<div ref={ref}> <div ref={ref}>
<PageContent
headerContent={
<HeaderTitle
title="API Access"
actions={
<ConditionallyRender
condition={hasAccess(CREATE_API_TOKEN)}
show={
<Button
variant="contained"
color="primary"
onClick={() =>
history.push(
'/admin/api/create-token'
)
}
data-testid={CREATE_API_TOKEN_BUTTON}
>
New API token
</Button>
}
/>
}
/>
}
>
<Alert severity="info" className={styles.infoBoxContainer}>
<p>
Read the{' '}
<a
href="https://docs.getunleash.io/docs"
target="_blank"
rel="noreferrer"
>
Getting started guide
</a>{' '}
to learn how to connect to the Unleash API from your
application or programmatically. Please note it can take
up to 1 minute before a new API key is activated.
</p>
<br />
<strong>API URL: </strong>{' '}
<pre style={{ display: 'inline' }}>
{uiConfig.unleashUrl}/api/
</pre>
</Alert>
<ConditionallyRender condition={error} show={renderError()} /> <ConditionallyRender condition={error} show={renderError()} />
<div className={styles.container}> <div className={styles.container}>
<ConditionallyRender <ConditionallyRender
@ -291,7 +228,6 @@ export const ApiTokenList = () => {
elseShow={renderApiTokens(tokens)} elseShow={renderApiTokens(tokens)}
/> />
</div> </div>
<Dialogue <Dialogue
open={showDelete} open={showDelete}
onClick={onDeleteToken} onClick={onDeleteToken}
@ -310,13 +246,11 @@ export const ApiTokenList = () => {
<code>{delToken?.username}</code> <code>{delToken?.username}</code>
</li> </li>
<li> <li>
<strong>type</strong>:{' '} <strong>type</strong>: <code>{delToken?.type}</code>
<code>{delToken?.type}</code>
</li> </li>
</ul> </ul>
</div> </div>
</Dialogue> </Dialogue>
</PageContent>
</div> </div>
); );
}; };

View File

@ -0,0 +1,7 @@
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
infoBoxContainer: {
marginBottom: 40,
},
}));

View File

@ -0,0 +1,77 @@
import { useContext } from 'react';
import { useHistory } from 'react-router-dom';
import { Button } from '@material-ui/core';
import AccessContext from 'contexts/AccessContext';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import PageContent from 'component/common/PageContent';
import HeaderTitle from 'component/common/HeaderTitle';
import ConditionallyRender from 'component/common/ConditionallyRender';
import {
CREATE_API_TOKEN,
READ_API_TOKEN,
} from 'component/providers/AccessProvider/permissions';
import { useStyles } from './ApiTokenPage.styles';
import { CREATE_API_TOKEN_BUTTON } from 'utils/testIds';
import { Alert } from '@material-ui/lab';
import { ApiTokenList } from 'component/admin/apiToken/ApiTokenList/ApiTokenList';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
export const ApiTokenPage = () => {
const styles = useStyles();
const { hasAccess } = useContext(AccessContext);
const { uiConfig } = useUiConfig();
const history = useHistory();
return (
<PageContent
headerContent={
<HeaderTitle
title="API Access"
actions={
<ConditionallyRender
condition={hasAccess(CREATE_API_TOKEN)}
show={
<Button
variant="contained"
color="primary"
onClick={() =>
history.push('/admin/api/create-token')
}
data-testid={CREATE_API_TOKEN_BUTTON}
>
New API token
</Button>
}
/>
}
/>
}
>
<Alert severity="info" className={styles.infoBoxContainer}>
<p>
Read the{' '}
<a
href="https://docs.getunleash.io/docs"
target="_blank"
rel="noreferrer"
>
Getting started guide
</a>{' '}
to learn how to connect to the Unleash API from your
application or programmatically. Please note it can take up
to 1 minute before a new API key is activated.
</p>
<br />
<strong>API URL: </strong>{' '}
<pre style={{ display: 'inline' }}>
{uiConfig.unleashUrl}/api/
</pre>
</Alert>
<ConditionallyRender
condition={hasAccess(READ_API_TOKEN)}
show={() => <ApiTokenList />}
elseShow={() => <AdminAlert />}
/>
</PageContent>
);
};

View File

@ -1,5 +1,4 @@
import { Button } from '@material-ui/core'; import { Button } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import { useContext } from 'react'; import { useContext } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import AccessContext from 'contexts/AccessContext'; import AccessContext from 'contexts/AccessContext';
@ -10,6 +9,7 @@ import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AdminMenu from 'component/admin/menu/AdminMenu'; import AdminMenu from 'component/admin/menu/AdminMenu';
import { useStyles } from './ProjectRoles.styles'; import { useStyles } from './ProjectRoles.styles';
import ProjectRoleList from './ProjectRoleList/ProjectRoleList'; import ProjectRoleList from './ProjectRoleList/ProjectRoleList';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
const ProjectRoles = () => { const ProjectRoles = () => {
const { hasAccess } = useContext(AccessContext); const { hasAccess } = useContext(AccessContext);
@ -53,11 +53,7 @@ const ProjectRoles = () => {
<ConditionallyRender <ConditionallyRender
condition={hasAccess(ADMIN)} condition={hasAccess(ADMIN)}
show={<ProjectRoleList />} show={<ProjectRoleList />}
elseShow={ elseShow={<AdminAlert />}
<Alert severity="error">
You need instance admin to access this section.
</Alert>
}
/> />
</PageContent> </PageContent>
</div> </div>

View File

@ -5,12 +5,12 @@ import PageContent from 'component/common/PageContent/PageContent';
import AccessContext from 'contexts/AccessContext'; import AccessContext from 'contexts/AccessContext';
import ConditionallyRender from 'component/common/ConditionallyRender'; import ConditionallyRender from 'component/common/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { Alert } from '@material-ui/lab';
import HeaderTitle from 'component/common/HeaderTitle'; import HeaderTitle from 'component/common/HeaderTitle';
import { TableActions } from 'component/common/Table/TableActions/TableActions'; import { TableActions } from 'component/common/Table/TableActions/TableActions';
import { Button } from '@material-ui/core'; import { Button } from '@material-ui/core';
import { useStyles } from './UserAdmin.styles'; import { useStyles } from './UserAdmin.styles';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
const UsersAdmin = () => { const UsersAdmin = () => {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
@ -63,11 +63,7 @@ const UsersAdmin = () => {
<ConditionallyRender <ConditionallyRender
condition={hasAccess(ADMIN)} condition={hasAccess(ADMIN)}
show={<UsersList search={search} />} show={<UsersList search={search} />}
elseShow={ elseShow={<AdminAlert />}
<Alert severity="error">
You need instance admin to access this section.
</Alert>
}
/> />
</PageContent> </PageContent>
</div> </div>

View File

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

View File

@ -1,9 +1,9 @@
import { Alert } from '@material-ui/lab';
import React, { useContext } from 'react'; 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'; import ConditionallyRender from 'component/common/ConditionallyRender';
import AccessContext from 'contexts/AccessContext'; import AccessContext from 'contexts/AccessContext';
import { EventHistory } from '../EventHistory/EventHistory'; import { EventHistory } from '../EventHistory/EventHistory';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
export const EventHistoryPage = () => { export const EventHistoryPage = () => {
const { hasAccess } = useContext(AccessContext); const { hasAccess } = useContext(AccessContext);
@ -12,11 +12,7 @@ export const EventHistoryPage = () => {
<ConditionallyRender <ConditionallyRender
condition={hasAccess(ADMIN)} condition={hasAccess(ADMIN)}
show={<EventHistory />} show={<EventHistory />}
elseShow={ elseShow={<AdminAlert />}
<Alert severity="error">
You need instance admin to access this section.
</Alert>
}
/> />
); );
}; };

View File

@ -18,6 +18,7 @@ export const UPDATE_ADDON = 'UPDATE_ADDON';
export const DELETE_ADDON = 'DELETE_ADDON'; export const DELETE_ADDON = 'DELETE_ADDON';
export const CREATE_API_TOKEN = 'CREATE_API_TOKEN'; export const CREATE_API_TOKEN = 'CREATE_API_TOKEN';
export const DELETE_API_TOKEN = 'DELETE_API_TOKEN'; export const DELETE_API_TOKEN = 'DELETE_API_TOKEN';
export const READ_API_TOKEN = 'READ_API_TOKEN';
export const DELETE_ENVIRONMENT = 'DELETE_ENVIRONMENT'; export const DELETE_ENVIRONMENT = 'DELETE_ENVIRONMENT';
export const UPDATE_ENVIRONMENT = 'UPDATE_ENVIRONMENT'; export const UPDATE_ENVIRONMENT = 'UPDATE_ENVIRONMENT';
export const CREATE_FEATURE_STRATEGY = 'CREATE_FEATURE_STRATEGY'; export const CREATE_FEATURE_STRATEGY = 'CREATE_FEATURE_STRATEGY';