diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js
new file mode 100644
index 0000000000..2d08392ac6
--- /dev/null
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import { useIntl } from 'react-intl';
+import PropTypes from 'prop-types';
+import { Link } from '@strapi/helper-plugin';
+import { useHistory } from 'react-router-dom';
+import styled from 'styled-components';
+
+const MESSAGES_MAP = {
+ edit: {
+ id: 'app.component.table.edit',
+ defaultMessage: 'Edit {target}',
+ },
+ read: {
+ id: 'app.component.table.read',
+ defaultMessage: 'Read {target}',
+ },
+};
+
+const LinkStyled = styled(Link)`
+ svg {
+ path {
+ fill: ${({ theme }) => theme.colors.neutral500};
+ }
+ }
+
+ &:hover,
+ &:focus {
+ svg {
+ path {
+ fill: ${({ theme }) => theme.colors.neutral800};
+ }
+ }
+ }
+`;
+
+const DefaultButton = ({ tokenName, tokenId, buttonType, children }) => {
+ const { formatMessage } = useIntl();
+ const {
+ location: { pathname },
+ } = useHistory();
+
+ return (
+
+ {children}
+
+ );
+};
+
+DefaultButton.propTypes = {
+ tokenName: PropTypes.string.isRequired,
+ tokenId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ buttonType: PropTypes.string,
+ children: PropTypes.node.isRequired,
+};
+
+DefaultButton.defaultProps = {
+ buttonType: 'edit',
+};
+
+export default DefaultButton;
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js
index 2ed062fbfa..2ae7b441cb 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js
@@ -24,6 +24,7 @@ const DeleteButton = ({ tokenName, onClickDelete }) => {
},
{ target: `${tokenName}` }
)}
+ name="delete"
noBorder
icon={}
/>
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js
new file mode 100644
index 0000000000..85c9f96dbf
--- /dev/null
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import Eye from '@strapi/icons/Eye';
+import PropTypes from 'prop-types';
+import DefaultButton from '../DefaultButton';
+
+const ReadButton = ({ tokenName, tokenId }) => {
+ return (
+
+
+
+ );
+};
+
+ReadButton.propTypes = {
+ tokenName: PropTypes.string.isRequired,
+ tokenId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+};
+
+export default ReadButton;
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js
index 0c5a77ab5e..f781e03543 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js
@@ -1,46 +1,13 @@
import React from 'react';
import Pencil from '@strapi/icons/Pencil';
-import { useIntl } from 'react-intl';
import PropTypes from 'prop-types';
-import { Link } from '@strapi/helper-plugin';
-import { useHistory } from 'react-router-dom';
-import styled from 'styled-components';
-
-const LinkUpdate = styled(Link)`
- svg {
- path {
- fill: ${({ theme }) => theme.colors.neutral500};
- }
- }
-
- &:hover {
- svg {
- path {
- fill: ${({ theme }) => theme.colors.neutral800};
- }
- }
- }
-`;
+import DefaultButton from '../DefaultButton';
const UpdateButton = ({ tokenName, tokenId }) => {
- const { formatMessage } = useIntl();
- const {
- location: { pathname },
- } = useHistory();
-
return (
-
+
-
+
);
};
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js
index 7b65618724..e91430ab73 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js
@@ -14,8 +14,9 @@ import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import DeleteButton from './DeleteButton';
import UpdateButton from './UpdateButton';
+import ReadButton from './ReadButton';
-const TableRows = ({ canDelete, canUpdate, onClickDelete, withBulkActions, rows }) => {
+const TableRows = ({ canDelete, canUpdate, canRead, onClickDelete, withBulkActions, rows }) => {
const { formatMessage } = useIntl();
const [{ query }] = useQueryParams();
const [, sortOrder] = query.sort.split(':');
@@ -73,6 +74,9 @@ const TableRows = ({ canDelete, canUpdate, onClickDelete, withBulkActions, rows
{canUpdate && }
+ {!canUpdate && canRead && (
+
+ )}
{canDelete && (
{
headers={tableHeaders}
contentType="api-tokens"
rows={apiTokens}
- withBulkActions={canDelete || canUpdate}
+ withBulkActions={canDelete || canUpdate || canRead}
isLoading={isLoading}
onConfirmDelete={(id) => deleteMutation.mutateAsync(id)}
>
)}
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js
index fcb5769a3d..ed83bb207c 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js
@@ -15,9 +15,7 @@ jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useNotification: jest.fn(),
useFocusWhenNavigate: jest.fn(),
- useRBAC: jest.fn(() => ({
- allowedActions: { canCreate: true, canDelete: true, canRead: true, canUpdate: true },
- })),
+ useRBAC: jest.fn(),
useGuidedTour: jest.fn(() => ({
startSection: jest.fn(),
})),
@@ -73,6 +71,9 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
});
it('should show a list of api tokens', async () => {
+ useRBAC.mockImplementation(() => ({
+ allowedActions: { canCreate: true, canDelete: true, canRead: true, canUpdate: true },
+ }));
const history = createMemoryHistory();
history.push('/settings/api-tokens');
const app = makeApp(history);
@@ -631,7 +632,8 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
fill: #8e8ea9;
}
- .c36:hover svg path {
+ .c36:hover svg path,
+ .c36:focus svg path {
fill: #32324d;
}
@@ -956,6 +958,7 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
aria-disabled="false"
aria-labelledby="tooltip-3"
class="c25 c26"
+ name="delete"
tabindex="-1"
type="button"
>
@@ -988,7 +991,7 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
});
it('should not show the create button when the user does not have the rights to create', async () => {
- useRBAC.mockImplementationOnce(() => ({
+ useRBAC.mockImplementation(() => ({
allowedActions: { canCreate: false, canDelete: true, canRead: true, canUpdate: true },
}));
@@ -999,4 +1002,34 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
await waitFor(() => expect(queryByTestId('create-api-token-button')).not.toBeInTheDocument());
});
+
+ it('should show the delete button when the user have the rights to delete', async () => {
+ useRBAC.mockImplementation(() => ({
+ allowedActions: { canCreate: false, canDelete: true, canRead: true, canUpdate: false },
+ }));
+ const history = createMemoryHistory();
+ history.push('/settings/api-tokens');
+ const app = makeApp(history);
+
+ const { container } = render(app);
+
+ await waitFor(() => {
+ expect(container.querySelector('button[name="delete"]')).toBeInTheDocument();
+ });
+ });
+
+ it('should show the read button when the user have the rights to read and not to update', async () => {
+ useRBAC.mockImplementation(() => ({
+ allowedActions: { canCreate: false, canDelete: true, canRead: true, canUpdate: false },
+ }));
+ const history = createMemoryHistory();
+ history.push('/settings/api-tokens');
+ const app = makeApp(history);
+
+ const { container } = render(app);
+
+ await waitFor(() => {
+ expect(container.querySelector('a[title*="Read"]')).toBeInTheDocument();
+ });
+ });
});
diff --git a/packages/core/admin/admin/src/permissions/defaultPermissions.js b/packages/core/admin/admin/src/permissions/defaultPermissions.js
index a2ff388692..be3e6b9b9d 100644
--- a/packages/core/admin/admin/src/permissions/defaultPermissions.js
+++ b/packages/core/admin/admin/src/permissions/defaultPermissions.js
@@ -76,12 +76,7 @@ const permissions = {
update: [{ action: 'admin::webhooks.update', subject: null }],
},
'api-tokens': {
- main: [
- { action: 'admin::api-tokens.create', subject: null },
- { action: 'admin::api-tokens.read', subject: null },
- { action: 'admin::api-tokens.update', subject: null },
- { action: 'admin::api-tokens.delete', subject: null },
- ],
+ main: [],
create: [{ action: 'admin::api-tokens.create', subject: null }],
delete: [{ action: 'admin::api-tokens.delete', subject: null }],
read: [{ action: 'admin::api-tokens.read', subject: null }],
diff --git a/packages/core/admin/package.json b/packages/core/admin/package.json
index 86df18afa6..2935455ae7 100644
--- a/packages/core/admin/package.json
+++ b/packages/core/admin/package.json
@@ -34,7 +34,8 @@
"test:front": "cross-env IS_EE=true jest --config ./jest.config.front.js",
"test:front:watch": "cross-env IS_EE=true jest --config ./jest.config.front.js --watchAll",
"test:front:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js",
- "test:front:watch:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll"
+ "test:front:watch:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll",
+ "test:front:ce:cov": "cross-env IS_EE=false jest --config ./jest.config.front.js --coverage --collectCoverageFrom='/packages/core/admin/admin/**/*.js' --coverageDirectory='/packages/core/admin/coverage'"
},
"dependencies": {
"@babel/core": "7.18.10",
|