@strapi/admin 4.3.7 → 4.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/contexts/ApiTokenPermissions/index.js +24 -0
- package/admin/src/hooks/index.js +1 -0
- package/admin/src/hooks/useRegenerate/index.js +34 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +56 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/getMethodColor.js +41 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/index.js +72 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/CheckBoxWrapper.js +30 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +150 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js +37 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +40 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +68 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +452 -180
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/init.js +13 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/reducer.js +55 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/getDateOfExpiration.js +16 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/index.js +5 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +2 -1
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/transformPermissionsData.js +36 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js +63 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js +1 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js +19 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js +3 -36
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +13 -11
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +3 -2
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/utils/tableHeaders.js +8 -8
- package/admin/src/pages/SettingsPage/pages/ApiTokens/ProtectedEditView/index.js +1 -1
- package/admin/src/permissions/defaultPermissions.js +2 -6
- package/admin/src/translations/en.json +17 -0
- package/admin/src/translations/fr.json +32 -0
- package/build/4235.982b5799.chunk.js +30 -0
- package/build/7379.d246dd38.chunk.js +1 -0
- package/build/{Admin-authenticatedApp.0d299d1a.chunk.js → Admin-authenticatedApp.3a31a087.chunk.js} +1 -1
- package/build/{Admin_homePage.118926e0.chunk.js → Admin_homePage.6d5e3236.chunk.js} +1 -1
- package/build/{Admin_profilePage.8617313a.chunk.js → Admin_profilePage.83991a6c.chunk.js} +1 -1
- package/build/{Admin_settingsPage.98a711e5.chunk.js → Admin_settingsPage.fc9c607a.chunk.js} +16 -16
- package/build/admin-app.41b6472c.chunk.js +112 -0
- package/build/admin-edit-roles-page.4dd6bcb9.chunk.js +1 -0
- package/build/api-tokens-create-page.29cc87b6.chunk.js +1 -0
- package/build/api-tokens-edit-page.c294a88f.chunk.js +1 -0
- package/build/api-tokens-list-page.bb36535f.chunk.js +16 -0
- package/build/en-json.a9918c93.chunk.js +1 -0
- package/build/{fr-json.6d5a7e14.chunk.js → fr-json.4ed1fc2c.chunk.js} +1 -1
- package/build/index.html +1 -1
- package/build/{main.e73468bf.js → main.cdfda31e.js} +1 -1
- package/build/{runtime~main.edd06c9f.js → runtime~main.fa8f8898.js} +2 -2
- package/build/sso-settings-page.9ceb0140.chunk.js +1 -0
- package/build/{webhook-edit-page.d2ea3351.chunk.js → webhook-edit-page.9e46fc3f.chunk.js} +1 -1
- package/package.json +9 -8
- package/scripts/build.js +2 -4
- package/server/bootstrap.js +19 -1
- package/server/config/admin-actions.js +20 -0
- package/server/content-types/api-token-permission.js +36 -0
- package/server/content-types/api-token.js +25 -1
- package/server/content-types/index.js +1 -0
- package/server/controllers/api-token.js +24 -1
- package/server/controllers/content-api.js +15 -0
- package/server/controllers/index.js +1 -0
- package/server/routes/api-tokens.js +11 -0
- package/server/routes/content-api.js +20 -0
- package/server/routes/index.js +2 -0
- package/server/services/api-token.js +310 -29
- package/server/services/constants.js +10 -0
- package/server/services/permission/engine.js +36 -226
- package/server/services/permission/permissions-manager/query-builers.js +3 -2
- package/server/services/permission/queries.js +1 -1
- package/server/services/permission.js +4 -1
- package/server/strategies/admin.js +7 -1
- package/server/strategies/api-token.js +71 -11
- package/server/validation/api-tokens.js +12 -2
- package/server/validation/common-functions/check-fields-are-correctly-nested.js +1 -1
- package/build/admin-app.05edc328.chunk.js +0 -112
- package/build/admin-edit-roles-page.554ba3fa.chunk.js +0 -1
- package/build/api-tokens-create-page.4c262d6e.chunk.js +0 -1
- package/build/api-tokens-edit-page.10a9d368.chunk.js +0 -1
- package/build/api-tokens-list-page.442c9f3c.chunk.js +0 -15
- package/build/en-json.12bc5a14.chunk.js +0 -1
- package/build/sso-settings-page.445184e0.chunk.js +0 -1
- package/server/services/permission/engine-hooks.js +0 -82
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
const ApiTokenPermissionsContext = createContext({});
|
|
5
|
+
|
|
6
|
+
const ApiTokenPermissionsContextProvider = ({ children, ...rest }) => {
|
|
7
|
+
return (
|
|
8
|
+
<ApiTokenPermissionsContext.Provider value={rest}>
|
|
9
|
+
{children}
|
|
10
|
+
</ApiTokenPermissionsContext.Provider>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const useApiTokenPermissionsContext = () => useContext(ApiTokenPermissionsContext);
|
|
15
|
+
|
|
16
|
+
ApiTokenPermissionsContextProvider.propTypes = {
|
|
17
|
+
children: PropTypes.node.isRequired,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
ApiTokenPermissionsContext,
|
|
22
|
+
ApiTokenPermissionsContextProvider,
|
|
23
|
+
useApiTokenPermissionsContext,
|
|
24
|
+
};
|
package/admin/src/hooks/index.js
CHANGED
|
@@ -9,3 +9,4 @@ export { default as useSettingsForm } from './useSettingsForm';
|
|
|
9
9
|
export { default as usePermissionsDataManager } from './usePermissionsDataManager';
|
|
10
10
|
export { default as useReleaseNotification } from './useReleaseNotification';
|
|
11
11
|
export { default as useThemeToggle } from './useThemeToggle';
|
|
12
|
+
export { default as useRegenerate } from './useRegenerate';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { get } from 'lodash';
|
|
3
|
+
import { useNotification } from '@strapi/helper-plugin';
|
|
4
|
+
import { axiosInstance } from '../../core/utils';
|
|
5
|
+
|
|
6
|
+
const useRegenerate = (id, onRegenerate) => {
|
|
7
|
+
const [isLoadingConfirmation, setIsLoadingConfirmation] = useState(false);
|
|
8
|
+
const toggleNotification = useNotification();
|
|
9
|
+
|
|
10
|
+
const regenerateData = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const {
|
|
13
|
+
data: {
|
|
14
|
+
data: { accessKey },
|
|
15
|
+
},
|
|
16
|
+
} = await axiosInstance.post(`/admin/api-tokens/${id}/regenerate`);
|
|
17
|
+
setIsLoadingConfirmation(false);
|
|
18
|
+
onRegenerate(accessKey);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
setIsLoadingConfirmation(false);
|
|
21
|
+
toggleNotification({
|
|
22
|
+
type: 'warning',
|
|
23
|
+
message: get(error, 'response.data.message', 'notification.error'),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
regenerateData,
|
|
30
|
+
isLoadingConfirmation,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default useRegenerate;
|
package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
4
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
5
|
+
import { GridItem } from '@strapi/design-system/Grid';
|
|
6
|
+
import BoundRoute from '../BoundRoute';
|
|
7
|
+
import { useApiTokenPermissionsContext } from '../../../../../../../contexts/ApiTokenPermissions';
|
|
8
|
+
|
|
9
|
+
const ActionBoundRoutes = () => {
|
|
10
|
+
const {
|
|
11
|
+
value: { selectedAction, routes },
|
|
12
|
+
} = useApiTokenPermissionsContext();
|
|
13
|
+
const { formatMessage } = useIntl();
|
|
14
|
+
const actionSection = selectedAction?.split('.')[0];
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<GridItem
|
|
18
|
+
col={5}
|
|
19
|
+
background="neutral150"
|
|
20
|
+
paddingTop={6}
|
|
21
|
+
paddingBottom={6}
|
|
22
|
+
paddingLeft={7}
|
|
23
|
+
paddingRight={7}
|
|
24
|
+
style={{ minHeight: '100%' }}
|
|
25
|
+
>
|
|
26
|
+
{selectedAction ? (
|
|
27
|
+
<Stack spacing={2}>
|
|
28
|
+
{routes[actionSection]?.map((route) => {
|
|
29
|
+
return route.config.auth?.scope?.includes(selectedAction) ||
|
|
30
|
+
route.handler === selectedAction ? (
|
|
31
|
+
<BoundRoute key={route.handler} route={route} />
|
|
32
|
+
) : null;
|
|
33
|
+
})}
|
|
34
|
+
</Stack>
|
|
35
|
+
) : (
|
|
36
|
+
<Stack spacing={2}>
|
|
37
|
+
<Typography variant="delta" as="h3">
|
|
38
|
+
{formatMessage({
|
|
39
|
+
id: 'Settings.apiTokens.createPage.permissions.header.title',
|
|
40
|
+
defaultMessage: 'Advanced settings',
|
|
41
|
+
})}
|
|
42
|
+
</Typography>
|
|
43
|
+
<Typography as="p" textColor="neutral600">
|
|
44
|
+
{formatMessage({
|
|
45
|
+
id: 'Settings.apiTokens.createPage.permissions.header.hint',
|
|
46
|
+
defaultMessage:
|
|
47
|
+
"Select the application's actions or the plugin's actions and click on the cog icon to display the bound route",
|
|
48
|
+
})}
|
|
49
|
+
</Typography>
|
|
50
|
+
</Stack>
|
|
51
|
+
)}
|
|
52
|
+
</GridItem>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default ActionBoundRoutes;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const getMethodColor = (verb) => {
|
|
2
|
+
switch (verb) {
|
|
3
|
+
case 'POST': {
|
|
4
|
+
return {
|
|
5
|
+
text: 'success600',
|
|
6
|
+
border: 'success200',
|
|
7
|
+
background: 'success100',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
case 'GET': {
|
|
11
|
+
return {
|
|
12
|
+
text: 'secondary600',
|
|
13
|
+
border: 'secondary200',
|
|
14
|
+
background: 'secondary100',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
case 'PUT': {
|
|
18
|
+
return {
|
|
19
|
+
text: 'warning600',
|
|
20
|
+
border: 'warning200',
|
|
21
|
+
background: 'warning100',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
case 'DELETE': {
|
|
25
|
+
return {
|
|
26
|
+
text: 'danger600',
|
|
27
|
+
border: 'danger200',
|
|
28
|
+
background: 'danger100',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
default: {
|
|
32
|
+
return {
|
|
33
|
+
text: 'neutral600',
|
|
34
|
+
border: 'neutral200',
|
|
35
|
+
background: 'neutral100',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default getMethodColor;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
4
|
+
import { Box } from '@strapi/design-system/Box';
|
|
5
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
6
|
+
import map from 'lodash/map';
|
|
7
|
+
import tail from 'lodash/tail';
|
|
8
|
+
import { useIntl } from 'react-intl';
|
|
9
|
+
import PropTypes from 'prop-types';
|
|
10
|
+
import getMethodColor from './getMethodColor';
|
|
11
|
+
|
|
12
|
+
const MethodBox = styled(Box)`
|
|
13
|
+
margin: -1px;
|
|
14
|
+
border-radius: ${({ theme }) => theme.spaces[1]} 0 0 ${({ theme }) => theme.spaces[1]};
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
function BoundRoute({ route }) {
|
|
18
|
+
const { formatMessage } = useIntl();
|
|
19
|
+
|
|
20
|
+
const { method, handler: title, path } = route;
|
|
21
|
+
const formattedRoute = path ? tail(path.split('/')) : [];
|
|
22
|
+
const [controller = '', action = ''] = title ? title.split('.') : [];
|
|
23
|
+
const colors = getMethodColor(route.method);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Stack spacing={2}>
|
|
27
|
+
<Typography variant="delta" as="h3">
|
|
28
|
+
{formatMessage({
|
|
29
|
+
id: 'Settings.apiTokens.createPage.BoundRoute.title',
|
|
30
|
+
defaultMessage: 'Bound route to',
|
|
31
|
+
})}
|
|
32
|
+
|
|
33
|
+
<span>{controller}</span>
|
|
34
|
+
<Typography variant="delta" textColor="primary600">
|
|
35
|
+
.{action}
|
|
36
|
+
</Typography>
|
|
37
|
+
</Typography>
|
|
38
|
+
<Stack horizontal hasRadius background="neutral0" borderColor="neutral200" spacing={0}>
|
|
39
|
+
<MethodBox background={colors.background} borderColor={colors.border} padding={2}>
|
|
40
|
+
<Typography fontWeight="bold" textColor={colors.text}>
|
|
41
|
+
{method}
|
|
42
|
+
</Typography>
|
|
43
|
+
</MethodBox>
|
|
44
|
+
<Box paddingLeft={2} paddingRight={2}>
|
|
45
|
+
{map(formattedRoute, (value) => (
|
|
46
|
+
<Typography key={value} textColor={value.includes(':') ? 'neutral600' : 'neutral900'}>
|
|
47
|
+
/{value}
|
|
48
|
+
</Typography>
|
|
49
|
+
))}
|
|
50
|
+
</Box>
|
|
51
|
+
</Stack>
|
|
52
|
+
</Stack>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
BoundRoute.defaultProps = {
|
|
57
|
+
route: {
|
|
58
|
+
handler: 'Nocontroller.error',
|
|
59
|
+
method: 'GET',
|
|
60
|
+
path: '/there-is-no-path',
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
BoundRoute.propTypes = {
|
|
65
|
+
route: PropTypes.shape({
|
|
66
|
+
handler: PropTypes.string,
|
|
67
|
+
method: PropTypes.string,
|
|
68
|
+
path: PropTypes.string,
|
|
69
|
+
}),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default BoundRoute;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
import { Box } from '@strapi/design-system/Box';
|
|
3
|
+
|
|
4
|
+
const activeCheckboxWrapperStyles = css`
|
|
5
|
+
background: ${(props) => props.theme.colors.primary100};
|
|
6
|
+
svg {
|
|
7
|
+
opacity: 1;
|
|
8
|
+
}
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
const CheckboxWrapper = styled(Box)`
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: space-between;
|
|
14
|
+
align-items: center;
|
|
15
|
+
|
|
16
|
+
svg {
|
|
17
|
+
opacity: 0;
|
|
18
|
+
path {
|
|
19
|
+
fill: ${(props) => props.theme.colors.primary600};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Show active style both on hover and when the action is selected */
|
|
24
|
+
${(props) => props.isActive && activeCheckboxWrapperStyles}
|
|
25
|
+
&:hover {
|
|
26
|
+
${activeCheckboxWrapperStyles}
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export default CheckboxWrapper;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { capitalize } from 'lodash';
|
|
3
|
+
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion';
|
|
4
|
+
import { Checkbox } from '@strapi/design-system/Checkbox';
|
|
5
|
+
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
6
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
7
|
+
import { Box } from '@strapi/design-system/Box';
|
|
8
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
9
|
+
import CogIcon from '@strapi/icons/Cog';
|
|
10
|
+
import styled from 'styled-components';
|
|
11
|
+
import PropTypes from 'prop-types';
|
|
12
|
+
import { useApiTokenPermissionsContext } from '../../../../../../../contexts/ApiTokenPermissions';
|
|
13
|
+
import CheckboxWrapper from './CheckBoxWrapper';
|
|
14
|
+
|
|
15
|
+
const Border = styled.div`
|
|
16
|
+
flex: 1;
|
|
17
|
+
align-self: center;
|
|
18
|
+
border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const CollapsableContentType = ({
|
|
22
|
+
controllers,
|
|
23
|
+
label,
|
|
24
|
+
orderNumber,
|
|
25
|
+
disabled,
|
|
26
|
+
onExpanded,
|
|
27
|
+
indexExpandendCollapsedContent,
|
|
28
|
+
}) => {
|
|
29
|
+
const {
|
|
30
|
+
value: { onChangeSelectAll, onChange, selectedActions, setSelectedAction, selectedAction },
|
|
31
|
+
} = useApiTokenPermissionsContext();
|
|
32
|
+
const [expanded, setExpanded] = useState(false);
|
|
33
|
+
|
|
34
|
+
const handleExpandedAccordion = () => {
|
|
35
|
+
setExpanded((s) => !s);
|
|
36
|
+
onExpanded(orderNumber);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (
|
|
41
|
+
indexExpandendCollapsedContent !== null &&
|
|
42
|
+
indexExpandendCollapsedContent !== orderNumber &&
|
|
43
|
+
expanded
|
|
44
|
+
) {
|
|
45
|
+
setExpanded(false);
|
|
46
|
+
}
|
|
47
|
+
}, [indexExpandendCollapsedContent, orderNumber, expanded]);
|
|
48
|
+
|
|
49
|
+
const isActionSelected = (actionId) => actionId === selectedAction;
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Accordion
|
|
53
|
+
expanded={expanded}
|
|
54
|
+
onToggle={handleExpandedAccordion}
|
|
55
|
+
variant={orderNumber % 2 ? 'primary' : 'secondary'}
|
|
56
|
+
>
|
|
57
|
+
<AccordionToggle title={capitalize(label)} />
|
|
58
|
+
<AccordionContent>
|
|
59
|
+
{controllers?.map((controller) => {
|
|
60
|
+
const allActionsSelected = controller.actions.every((action) =>
|
|
61
|
+
selectedActions.includes(action.actionId)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const someActionsSelected = controller.actions.some((action) =>
|
|
65
|
+
selectedActions.includes(action.actionId)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Box key={`${label}.${controller?.controller}`}>
|
|
70
|
+
<Flex justifyContent="space-between" alignItems="center" padding={4}>
|
|
71
|
+
<Box paddingRight={4}>
|
|
72
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
73
|
+
{controller?.controller}
|
|
74
|
+
</Typography>
|
|
75
|
+
</Box>
|
|
76
|
+
<Border />
|
|
77
|
+
<Box paddingLeft={4}>
|
|
78
|
+
<Checkbox
|
|
79
|
+
value={allActionsSelected}
|
|
80
|
+
indeterminate={!allActionsSelected && someActionsSelected}
|
|
81
|
+
onValueChange={() => {
|
|
82
|
+
onChangeSelectAll({ target: { value: [...controller.actions] } });
|
|
83
|
+
}}
|
|
84
|
+
disabled={disabled}
|
|
85
|
+
>
|
|
86
|
+
Select all
|
|
87
|
+
</Checkbox>
|
|
88
|
+
</Box>
|
|
89
|
+
</Flex>
|
|
90
|
+
<Grid gap={4} padding={4}>
|
|
91
|
+
{controller?.actions &&
|
|
92
|
+
controller?.actions.map((action) => {
|
|
93
|
+
return (
|
|
94
|
+
<GridItem col={6} key={action.actionId}>
|
|
95
|
+
<CheckboxWrapper
|
|
96
|
+
isActive={isActionSelected(action.actionId)}
|
|
97
|
+
padding={2}
|
|
98
|
+
hasRadius
|
|
99
|
+
>
|
|
100
|
+
<Checkbox
|
|
101
|
+
value={selectedActions.includes(action.actionId)}
|
|
102
|
+
name={action.actionId}
|
|
103
|
+
onValueChange={() => {
|
|
104
|
+
onChange({ target: { value: action.actionId } });
|
|
105
|
+
}}
|
|
106
|
+
disabled={disabled}
|
|
107
|
+
>
|
|
108
|
+
{action.action}
|
|
109
|
+
</Checkbox>
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
data-testid="action-cog"
|
|
113
|
+
onClick={() =>
|
|
114
|
+
setSelectedAction({ target: { value: action.actionId } })
|
|
115
|
+
}
|
|
116
|
+
style={{ display: 'inline-flex', alignItems: 'center' }}
|
|
117
|
+
>
|
|
118
|
+
<CogIcon />
|
|
119
|
+
</button>
|
|
120
|
+
</CheckboxWrapper>
|
|
121
|
+
</GridItem>
|
|
122
|
+
);
|
|
123
|
+
})}
|
|
124
|
+
</Grid>
|
|
125
|
+
</Box>
|
|
126
|
+
);
|
|
127
|
+
})}
|
|
128
|
+
</AccordionContent>
|
|
129
|
+
</Accordion>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
CollapsableContentType.defaultProps = {
|
|
134
|
+
controllers: [],
|
|
135
|
+
orderNumber: 0,
|
|
136
|
+
disabled: false,
|
|
137
|
+
onExpanded: () => null,
|
|
138
|
+
indexExpandendCollapsedContent: null,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
CollapsableContentType.propTypes = {
|
|
142
|
+
controllers: PropTypes.array,
|
|
143
|
+
orderNumber: PropTypes.number,
|
|
144
|
+
label: PropTypes.string.isRequired,
|
|
145
|
+
disabled: PropTypes.bool,
|
|
146
|
+
onExpanded: PropTypes.func,
|
|
147
|
+
indexExpandendCollapsedContent: PropTypes.number,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default CollapsableContentType;
|
package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Box } from '@strapi/design-system/Box';
|
|
4
|
+
import CollapsableContentType from '../CollapsableContentType';
|
|
5
|
+
|
|
6
|
+
const ContentTypesSection = ({ section, ...props }) => {
|
|
7
|
+
const [indexExpandedCollpsedContent, setIndexExpandedCollpsedContent] = useState(null);
|
|
8
|
+
const handleExpandedCollpsedContentIndex = (index) => setIndexExpandedCollpsedContent(index);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Box padding={4} background="neutral0">
|
|
12
|
+
{section &&
|
|
13
|
+
section.map((api, index) => (
|
|
14
|
+
<CollapsableContentType
|
|
15
|
+
key={api.apiId}
|
|
16
|
+
label={api.label}
|
|
17
|
+
controllers={api.controllers}
|
|
18
|
+
orderNumber={index}
|
|
19
|
+
indexExpandendCollapsedContent={indexExpandedCollpsedContent}
|
|
20
|
+
onExpanded={handleExpandedCollpsedContentIndex}
|
|
21
|
+
name={api.apiId}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
))}
|
|
25
|
+
</Box>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
ContentTypesSection.defaultProps = {
|
|
30
|
+
section: null,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
ContentTypesSection.propTypes = {
|
|
34
|
+
section: PropTypes.arrayOf(PropTypes.object),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default ContentTypesSection;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
4
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
5
|
+
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
6
|
+
import ContentTypesSection from '../ContenTypesSection';
|
|
7
|
+
import ActionBoundRoutes from '../ActionBoundRoutes';
|
|
8
|
+
import { useApiTokenPermissionsContext } from '../../../../../../../contexts/ApiTokenPermissions';
|
|
9
|
+
|
|
10
|
+
const Permissions = ({ ...props }) => {
|
|
11
|
+
const {
|
|
12
|
+
value: { data },
|
|
13
|
+
} = useApiTokenPermissionsContext();
|
|
14
|
+
const { formatMessage } = useIntl();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Grid gap={0} shadow="filterShadow" hasRadius background="neutral0">
|
|
18
|
+
<GridItem col={7} paddingTop={6} paddingBottom={6} paddingLeft={7} paddingRight={7}>
|
|
19
|
+
<Stack spacing={2}>
|
|
20
|
+
<Typography variant="delta" as="h2">
|
|
21
|
+
{formatMessage({
|
|
22
|
+
id: 'Settings.apiTokens.createPage.permissions.title',
|
|
23
|
+
defaultMessage: 'Permissions',
|
|
24
|
+
})}
|
|
25
|
+
</Typography>
|
|
26
|
+
<Typography as="p" textColor="neutral600">
|
|
27
|
+
{formatMessage({
|
|
28
|
+
id: 'Settings.apiTokens.createPage.permissions.description',
|
|
29
|
+
defaultMessage: 'Only actions bound by a route are listed below.',
|
|
30
|
+
})}
|
|
31
|
+
</Typography>
|
|
32
|
+
</Stack>
|
|
33
|
+
{data?.permissions && <ContentTypesSection section={data?.permissions} {...props} />}
|
|
34
|
+
</GridItem>
|
|
35
|
+
<ActionBoundRoutes />
|
|
36
|
+
</Grid>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default memo(Permissions);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import { Button } from '@strapi/design-system/Button';
|
|
5
|
+
import Refresh from '@strapi/icons/Refresh';
|
|
6
|
+
import { ConfirmDialog } from '@strapi/helper-plugin';
|
|
7
|
+
import { useRegenerate } from '../../../../../../../hooks';
|
|
8
|
+
|
|
9
|
+
export const Regenerate = ({ onRegenerate, idToRegenerate }) => {
|
|
10
|
+
const { formatMessage } = useIntl();
|
|
11
|
+
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
|
12
|
+
const { regenerateData, isLoadingConfirmation } = useRegenerate(idToRegenerate, onRegenerate);
|
|
13
|
+
const handleConfirmRegeneration = async () => {
|
|
14
|
+
regenerateData();
|
|
15
|
+
setShowConfirmDialog(false);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<Button
|
|
21
|
+
startIcon={<Refresh />}
|
|
22
|
+
type="button"
|
|
23
|
+
size="S"
|
|
24
|
+
variant="tertiary"
|
|
25
|
+
onClick={() => setShowConfirmDialog(true)}
|
|
26
|
+
name="regenerate"
|
|
27
|
+
>
|
|
28
|
+
{formatMessage({
|
|
29
|
+
id: 'Settings.apiTokens.regenerate',
|
|
30
|
+
defaultMessage: 'Regenerate',
|
|
31
|
+
})}
|
|
32
|
+
</Button>
|
|
33
|
+
|
|
34
|
+
<ConfirmDialog
|
|
35
|
+
bodyText={{
|
|
36
|
+
id: 'Settings.apiTokens.popUpWarning.message',
|
|
37
|
+
defaultMessage: 'Are you sure you want to regenerate this token?',
|
|
38
|
+
}}
|
|
39
|
+
iconRightButton={<Refresh />}
|
|
40
|
+
isConfirmButtonLoading={isLoadingConfirmation}
|
|
41
|
+
isOpen={showConfirmDialog}
|
|
42
|
+
onToggleDialog={() => setShowConfirmDialog(false)}
|
|
43
|
+
onConfirm={handleConfirmRegeneration}
|
|
44
|
+
leftButtonText={{
|
|
45
|
+
id: 'Settings.apiTokens.Button.cancel',
|
|
46
|
+
defaultMessage: 'Cancel',
|
|
47
|
+
}}
|
|
48
|
+
rightButtonText={{
|
|
49
|
+
id: 'Settings.apiTokens.Button.regenerate',
|
|
50
|
+
defaultMessage: 'Regenerate',
|
|
51
|
+
}}
|
|
52
|
+
title={{
|
|
53
|
+
id: 'Settings.apiTokens.RegenerateDialog.title',
|
|
54
|
+
defaultMessage: 'Regenerate token',
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
Regenerate.defaultProps = { onRegenerate() {} };
|
|
62
|
+
|
|
63
|
+
Regenerate.propTypes = {
|
|
64
|
+
onRegenerate: PropTypes.func,
|
|
65
|
+
idToRegenerate: PropTypes.string.isRequired,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default Regenerate;
|