@strapi/plugin-users-permissions 0.0.0-de3d4081f5 → 0.0.0-deb656d24792ef3c29d49f986ce382fd655cd104
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/components/BoundRoute/index.js +1 -3
- package/admin/src/components/FormModal/Input/index.js +1 -2
- package/admin/src/components/FormModal/index.js +7 -5
- package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +1 -1
- package/admin/src/components/Permissions/PermissionRow/SubCategory.js +2 -6
- package/admin/src/components/Permissions/PermissionRow/index.js +1 -1
- package/admin/src/components/Permissions/index.js +1 -3
- package/admin/src/components/Policies/index.js +1 -3
- package/admin/src/components/UsersPermissions/index.js +1 -3
- package/admin/src/hooks/useFetchRole/index.js +3 -3
- package/admin/src/hooks/usePlugins/index.js +5 -3
- package/admin/src/pages/AdvancedSettings/index.js +15 -10
- package/admin/src/pages/AdvancedSettings/utils/api.js +7 -3
- package/admin/src/pages/EmailTemplates/components/EmailForm.js +7 -5
- package/admin/src/pages/EmailTemplates/components/EmailTable.js +14 -9
- package/admin/src/pages/EmailTemplates/index.js +1 -3
- package/admin/src/pages/EmailTemplates/utils/api.js +7 -3
- package/admin/src/pages/Providers/index.js +18 -20
- package/admin/src/pages/Providers/utils/api.js +7 -3
- package/admin/src/pages/Providers/utils/forms.js +15 -0
- package/admin/src/pages/Roles/CreatePage/index.js +17 -12
- package/admin/src/pages/Roles/EditPage/index.js +17 -13
- package/admin/src/pages/Roles/ListPage/components/TableBody.js +2 -6
- package/admin/src/pages/Roles/ListPage/index.js +16 -8
- package/admin/src/pages/Roles/ListPage/utils/api.js +6 -3
- package/admin/src/translations/dk.json +0 -1
- package/admin/src/translations/en.json +0 -1
- package/admin/src/translations/es.json +0 -1
- package/admin/src/translations/ko.json +0 -1
- package/admin/src/translations/pl.json +0 -1
- package/admin/src/translations/sv.json +0 -1
- package/admin/src/translations/tr.json +38 -1
- package/admin/src/translations/zh.json +40 -2
- package/admin/src/utils/index.js +0 -1
- package/package.json +10 -10
- package/server/bootstrap/grant-config.js +8 -0
- package/server/controllers/validation/email-template.js +32 -8
- package/server/services/providers-registry.js +79 -12
- package/server/services/providers.js +1 -1
- package/server/services/user.js +18 -10
- package/server/services/users-permissions.js +15 -2
- package/admin/src/utils/axiosInstance.js +0 -36
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
|
-
import { Stack } from '@strapi/design-system
|
|
4
|
-
import { Box } from '@strapi/design-system/Box';
|
|
5
|
-
import { Typography } from '@strapi/design-system/Typography';
|
|
3
|
+
import { Stack, Box, Typography } from '@strapi/design-system';
|
|
6
4
|
import map from 'lodash/map';
|
|
7
5
|
import tail from 'lodash/tail';
|
|
8
6
|
import { useIntl } from 'react-intl';
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import React from 'react';
|
|
8
8
|
import { useIntl } from 'react-intl';
|
|
9
|
-
import { ToggleInput } from '@strapi/design-system
|
|
10
|
-
import { TextInput } from '@strapi/design-system/TextInput';
|
|
9
|
+
import { ToggleInput, TextInput } from '@strapi/design-system';
|
|
11
10
|
import PropTypes from 'prop-types';
|
|
12
11
|
|
|
13
12
|
const Input = ({
|
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
import React from 'react';
|
|
8
8
|
import { useIntl } from 'react-intl';
|
|
9
|
-
import { Button } from '@strapi/design-system/Button';
|
|
10
|
-
import { Stack } from '@strapi/design-system/Stack';
|
|
11
|
-
import { Breadcrumbs, Crumb } from '@strapi/design-system/Breadcrumbs';
|
|
12
|
-
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
13
9
|
import {
|
|
10
|
+
Button,
|
|
11
|
+
Stack,
|
|
12
|
+
Breadcrumbs,
|
|
13
|
+
Crumb,
|
|
14
|
+
Grid,
|
|
15
|
+
GridItem,
|
|
14
16
|
ModalLayout,
|
|
15
17
|
ModalHeader,
|
|
16
18
|
ModalFooter,
|
|
17
19
|
ModalBody,
|
|
18
|
-
} from '@strapi/design-system
|
|
20
|
+
} from '@strapi/design-system';
|
|
19
21
|
import PropTypes from 'prop-types';
|
|
20
22
|
import { Formik } from 'formik';
|
|
21
23
|
import { Form } from '@strapi/helper-plugin';
|
|
@@ -2,12 +2,8 @@ import React, { useCallback, useMemo } from 'react';
|
|
|
2
2
|
import { get } from 'lodash';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import { Box } from '@strapi/design-system
|
|
6
|
-
import {
|
|
7
|
-
import { Flex } from '@strapi/design-system/Flex';
|
|
8
|
-
import { Typography } from '@strapi/design-system/Typography';
|
|
9
|
-
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
10
|
-
import CogIcon from '@strapi/icons/Cog';
|
|
5
|
+
import { Box, Checkbox, Flex, Typography, Grid, GridItem } from '@strapi/design-system';
|
|
6
|
+
import { Cog as CogIcon } from '@strapi/icons';
|
|
11
7
|
import { useIntl } from 'react-intl';
|
|
12
8
|
import CheckboxWrapper from './CheckboxWrapper';
|
|
13
9
|
import { useUsersPermissions } from '../../../contexts/UsersPermissionsContext';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import sortBy from 'lodash/sortBy';
|
|
4
|
-
import { Box } from '@strapi/design-system
|
|
4
|
+
import { Box } from '@strapi/design-system';
|
|
5
5
|
import SubCategory from './SubCategory';
|
|
6
6
|
|
|
7
7
|
const PermissionRow = ({ name, permissions }) => {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React, { useReducer } from 'react';
|
|
2
|
-
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system
|
|
2
|
+
import { Accordion, AccordionToggle, AccordionContent, Box, Stack } from '@strapi/design-system';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
|
-
import { Box } from '@strapi/design-system/Box';
|
|
5
|
-
import { Stack } from '@strapi/design-system/Stack';
|
|
6
4
|
import { useUsersPermissions } from '../../contexts/UsersPermissionsContext';
|
|
7
5
|
import formatPluginName from '../../utils/formatPluginName';
|
|
8
6
|
import PermissionRow from './PermissionRow';
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
|
-
import { Typography } from '@strapi/design-system
|
|
4
|
-
import { Stack } from '@strapi/design-system/Stack';
|
|
5
|
-
import { GridItem } from '@strapi/design-system/Grid';
|
|
3
|
+
import { Typography, Stack, GridItem } from '@strapi/design-system';
|
|
6
4
|
import { get, isEmpty, without } from 'lodash';
|
|
7
5
|
import { useUsersPermissions } from '../../contexts/UsersPermissionsContext';
|
|
8
6
|
import BoundRoute from '../BoundRoute';
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React, { memo, useReducer, forwardRef, useImperativeHandle } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { Typography } from '@strapi/design-system
|
|
4
|
-
import { Stack } from '@strapi/design-system/Stack';
|
|
5
|
-
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
3
|
+
import { Typography, Stack, Grid, GridItem } from '@strapi/design-system';
|
|
6
4
|
import { useIntl } from 'react-intl';
|
|
7
5
|
import getTrad from '../../utils/getTrad';
|
|
8
6
|
import Policies from '../Policies';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useCallback, useReducer, useEffect, useRef } from 'react';
|
|
2
|
-
import { useNotification } from '@strapi/helper-plugin';
|
|
2
|
+
import { useNotification, useFetchClient } from '@strapi/helper-plugin';
|
|
3
3
|
import reducer, { initialState } from './reducer';
|
|
4
|
-
import axiosIntance from '../../utils/axiosInstance';
|
|
5
4
|
import pluginId from '../../pluginId';
|
|
6
5
|
|
|
7
6
|
const useFetchRole = (id) => {
|
|
8
7
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
9
8
|
const toggleNotification = useNotification();
|
|
10
9
|
const isMounted = useRef(null);
|
|
10
|
+
const { get } = useFetchClient();
|
|
11
11
|
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
isMounted.current = true;
|
|
@@ -29,7 +29,7 @@ const useFetchRole = (id) => {
|
|
|
29
29
|
try {
|
|
30
30
|
const {
|
|
31
31
|
data: { role },
|
|
32
|
-
} = await
|
|
32
|
+
} = await get(`/${pluginId}/roles/${roleId}`);
|
|
33
33
|
|
|
34
34
|
// Prevent updating state on an unmounted component
|
|
35
35
|
if (isMounted.current) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { useCallback, useEffect, useReducer } from 'react';
|
|
2
|
-
import { useNotification } from '@strapi/helper-plugin';
|
|
2
|
+
import { useNotification, useFetchClient } from '@strapi/helper-plugin';
|
|
3
3
|
import { get } from 'lodash';
|
|
4
4
|
import init from './init';
|
|
5
5
|
import pluginId from '../../pluginId';
|
|
6
6
|
import { cleanPermissions } from '../../utils';
|
|
7
|
-
import axiosInstance from '../../utils/axiosInstance';
|
|
8
7
|
import reducer, { initialState } from './reducer';
|
|
9
8
|
|
|
10
9
|
const usePlugins = (shouldFetchData = true) => {
|
|
@@ -12,6 +11,7 @@ const usePlugins = (shouldFetchData = true) => {
|
|
|
12
11
|
const [{ permissions, routes, isLoading }, dispatch] = useReducer(reducer, initialState, () =>
|
|
13
12
|
init(initialState, shouldFetchData)
|
|
14
13
|
);
|
|
14
|
+
const fetchClient = useFetchClient();
|
|
15
15
|
|
|
16
16
|
const fetchPlugins = useCallback(async () => {
|
|
17
17
|
try {
|
|
@@ -21,7 +21,7 @@ const usePlugins = (shouldFetchData = true) => {
|
|
|
21
21
|
|
|
22
22
|
const [{ permissions }, { routes }] = await Promise.all(
|
|
23
23
|
[`/${pluginId}/permissions`, `/${pluginId}/routes`].map(async (endpoint) => {
|
|
24
|
-
const res = await
|
|
24
|
+
const res = await fetchClient.get(endpoint);
|
|
25
25
|
|
|
26
26
|
return res.data;
|
|
27
27
|
})
|
|
@@ -46,6 +46,8 @@ const usePlugins = (shouldFetchData = true) => {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
51
|
}, [toggleNotification]);
|
|
50
52
|
|
|
51
53
|
useEffect(() => {
|
|
@@ -13,16 +13,21 @@ import {
|
|
|
13
13
|
useOverlayBlocker,
|
|
14
14
|
useRBAC,
|
|
15
15
|
} from '@strapi/helper-plugin';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
import {
|
|
17
|
+
useNotifyAT,
|
|
18
|
+
Main,
|
|
19
|
+
HeaderLayout,
|
|
20
|
+
ContentLayout,
|
|
21
|
+
Button,
|
|
22
|
+
Box,
|
|
23
|
+
Stack,
|
|
24
|
+
Select,
|
|
25
|
+
Option,
|
|
26
|
+
Typography,
|
|
27
|
+
Grid,
|
|
28
|
+
GridItem,
|
|
29
|
+
} from '@strapi/design-system';
|
|
30
|
+
import { Check } from '@strapi/icons';
|
|
26
31
|
import pluginPermissions from '../../permissions';
|
|
27
32
|
import { getTrad } from '../../utils';
|
|
28
33
|
import layout from './utils/layout';
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFetchClient } from '@strapi/helper-plugin';
|
|
2
|
+
import { getRequestURL } from '../../../utils';
|
|
2
3
|
|
|
3
4
|
const fetchData = async () => {
|
|
4
|
-
const {
|
|
5
|
+
const { get } = getFetchClient();
|
|
6
|
+
const { data } = await get(getRequestURL('advanced'));
|
|
5
7
|
|
|
6
8
|
return data;
|
|
7
9
|
};
|
|
8
10
|
|
|
9
11
|
const putAdvancedSettings = (body) => {
|
|
10
|
-
|
|
12
|
+
const { put } = getFetchClient();
|
|
13
|
+
|
|
14
|
+
return put(getRequestURL('advanced'), body);
|
|
11
15
|
};
|
|
12
16
|
|
|
13
17
|
export { fetchData, putAdvancedSettings };
|
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
ModalHeader,
|
|
9
9
|
ModalFooter,
|
|
10
10
|
ModalBody,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
Grid,
|
|
12
|
+
GridItem,
|
|
13
|
+
Button,
|
|
14
|
+
Breadcrumbs,
|
|
15
|
+
Crumb,
|
|
16
|
+
Textarea,
|
|
17
|
+
} from '@strapi/design-system';
|
|
16
18
|
import { getTrad } from '../../../utils';
|
|
17
19
|
import schema from '../utils/schema';
|
|
18
20
|
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import {
|
|
5
|
+
Table,
|
|
6
|
+
Thead,
|
|
7
|
+
Tbody,
|
|
8
|
+
Tr,
|
|
9
|
+
Td,
|
|
10
|
+
Th,
|
|
11
|
+
Typography,
|
|
12
|
+
IconButton,
|
|
13
|
+
Icon,
|
|
14
|
+
VisuallyHidden,
|
|
15
|
+
} from '@strapi/design-system';
|
|
16
|
+
import { Pencil, Refresh, Check } from '@strapi/icons';
|
|
11
17
|
import { onRowClick, stopPropagation } from '@strapi/helper-plugin';
|
|
12
|
-
import Check from '@strapi/icons/Check';
|
|
13
18
|
import { getTrad } from '../../../utils';
|
|
14
19
|
|
|
15
20
|
const EmailTable = ({ canUpdate, onEditClick }) => {
|
|
@@ -49,7 +54,7 @@ const EmailTable = ({ canUpdate, onEditClick }) => {
|
|
|
49
54
|
<Tr {...onRowClick({ fn: () => onEditClick('reset_password') })}>
|
|
50
55
|
<Td>
|
|
51
56
|
<Icon>
|
|
52
|
-
<
|
|
57
|
+
<Refresh
|
|
53
58
|
aria-label={formatMessage({
|
|
54
59
|
id: 'global.reset-password',
|
|
55
60
|
defaultMessage: 'Reset password',
|
|
@@ -11,9 +11,7 @@ import {
|
|
|
11
11
|
useFocusWhenNavigate,
|
|
12
12
|
LoadingIndicatorPage,
|
|
13
13
|
} from '@strapi/helper-plugin';
|
|
14
|
-
import { useNotifyAT } from '@strapi/design-system
|
|
15
|
-
import { Main } from '@strapi/design-system/Main';
|
|
16
|
-
import { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
|
|
14
|
+
import { useNotifyAT, Main, ContentLayout, HeaderLayout } from '@strapi/design-system';
|
|
17
15
|
import pluginPermissions from '../../permissions';
|
|
18
16
|
import { getTrad } from '../../utils';
|
|
19
17
|
import { fetchData, putEmailTemplate } from './utils/api';
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFetchClient } from '@strapi/helper-plugin';
|
|
2
|
+
import { getRequestURL } from '../../../utils';
|
|
2
3
|
|
|
3
4
|
const fetchData = async () => {
|
|
4
|
-
const {
|
|
5
|
+
const { get } = getFetchClient();
|
|
6
|
+
const { data } = await get(getRequestURL('email-templates'));
|
|
5
7
|
|
|
6
8
|
return data;
|
|
7
9
|
};
|
|
8
10
|
|
|
9
11
|
const putEmailTemplate = (body) => {
|
|
10
|
-
|
|
12
|
+
const { put } = getFetchClient();
|
|
13
|
+
|
|
14
|
+
return put(getRequestURL('email-templates'), body);
|
|
11
15
|
};
|
|
12
16
|
|
|
13
17
|
export { fetchData, putEmailTemplate };
|
|
@@ -14,15 +14,23 @@ import {
|
|
|
14
14
|
} from '@strapi/helper-plugin';
|
|
15
15
|
import has from 'lodash/has';
|
|
16
16
|
import upperFirst from 'lodash/upperFirst';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
import {
|
|
18
|
+
HeaderLayout,
|
|
19
|
+
Layout,
|
|
20
|
+
ContentLayout,
|
|
21
|
+
Main,
|
|
22
|
+
useNotifyAT,
|
|
23
|
+
Table,
|
|
24
|
+
Thead,
|
|
25
|
+
Tr,
|
|
26
|
+
Th,
|
|
27
|
+
Tbody,
|
|
28
|
+
Td,
|
|
29
|
+
Typography,
|
|
30
|
+
IconButton,
|
|
31
|
+
VisuallyHidden,
|
|
32
|
+
} from '@strapi/design-system';
|
|
33
|
+
import { Pencil } from '@strapi/icons';
|
|
26
34
|
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
|
27
35
|
import forms from './utils/forms';
|
|
28
36
|
import { fetchData, putProvider } from './utils/api';
|
|
@@ -163,16 +171,9 @@ export const ProvidersPage = () => {
|
|
|
163
171
|
<LoadingIndicatorPage />
|
|
164
172
|
) : (
|
|
165
173
|
<ContentLayout>
|
|
166
|
-
<Table colCount={
|
|
174
|
+
<Table colCount={3} rowCount={rowCount + 1}>
|
|
167
175
|
<Thead>
|
|
168
176
|
<Tr>
|
|
169
|
-
<Th>
|
|
170
|
-
<Typography variant="sigma" textColor="neutral600">
|
|
171
|
-
<VisuallyHidden>
|
|
172
|
-
{formatMessage({ id: getTrad('Providers.image'), defaultMessage: 'Image' })}
|
|
173
|
-
</VisuallyHidden>
|
|
174
|
-
</Typography>
|
|
175
|
-
</Th>
|
|
176
177
|
<Th>
|
|
177
178
|
<Typography variant="sigma" textColor="neutral600">
|
|
178
179
|
{formatMessage({ id: 'global.name', defaultMessage: 'Name' })}
|
|
@@ -204,9 +205,6 @@ export const ProvidersPage = () => {
|
|
|
204
205
|
condition: canUpdate,
|
|
205
206
|
})}
|
|
206
207
|
>
|
|
207
|
-
<Td width="">
|
|
208
|
-
<FontAwesomeIcon icon={provider.icon} />
|
|
209
|
-
</Td>
|
|
210
208
|
<Td width="45%">
|
|
211
209
|
<Typography fontWeight="semiBold" textColor="neutral800">
|
|
212
210
|
{provider.name}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFetchClient } from '@strapi/helper-plugin';
|
|
2
|
+
import { getRequestURL } from '../../../utils';
|
|
2
3
|
|
|
3
4
|
// eslint-disable-next-line import/prefer-default-export
|
|
4
5
|
export const fetchData = async (toggleNotification) => {
|
|
5
6
|
try {
|
|
6
|
-
const {
|
|
7
|
+
const { get } = getFetchClient();
|
|
8
|
+
const { data } = await get(getRequestURL('providers'));
|
|
7
9
|
|
|
8
10
|
return data;
|
|
9
11
|
} catch (err) {
|
|
@@ -17,5 +19,7 @@ export const fetchData = async (toggleNotification) => {
|
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
export const putProvider = (body) => {
|
|
20
|
-
|
|
22
|
+
const { put } = getFetchClient();
|
|
23
|
+
|
|
24
|
+
return put(getRequestURL('providers'), body);
|
|
21
25
|
};
|
|
@@ -173,6 +173,21 @@ const forms = {
|
|
|
173
173
|
},
|
|
174
174
|
},
|
|
175
175
|
],
|
|
176
|
+
[
|
|
177
|
+
{
|
|
178
|
+
intlLabel: {
|
|
179
|
+
id: getTrad({ id: 'PopUpForm.Providers.jwksurl.label' }),
|
|
180
|
+
defaultMessage: 'JWKS URL',
|
|
181
|
+
},
|
|
182
|
+
name: 'jwksurl',
|
|
183
|
+
type: 'text',
|
|
184
|
+
placeholder: textPlaceholder,
|
|
185
|
+
size: 12,
|
|
186
|
+
validations: {
|
|
187
|
+
required: false,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
],
|
|
176
191
|
|
|
177
192
|
[
|
|
178
193
|
{
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import React, { useState, useRef } from 'react';
|
|
2
2
|
import { useHistory } from 'react-router-dom';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
import {
|
|
4
|
+
ContentLayout,
|
|
5
|
+
HeaderLayout,
|
|
6
|
+
Main,
|
|
7
|
+
Button,
|
|
8
|
+
Stack,
|
|
9
|
+
Box,
|
|
10
|
+
TextInput,
|
|
11
|
+
Textarea,
|
|
12
|
+
Typography,
|
|
13
|
+
GridItem,
|
|
14
|
+
Grid,
|
|
15
|
+
} from '@strapi/design-system';
|
|
16
|
+
import { Check } from '@strapi/icons';
|
|
13
17
|
import { Formik } from 'formik';
|
|
14
18
|
import { useIntl } from 'react-intl';
|
|
15
19
|
import {
|
|
16
20
|
useOverlayBlocker,
|
|
17
21
|
SettingsPageTitle,
|
|
22
|
+
useFetchClient,
|
|
18
23
|
useTracking,
|
|
19
24
|
Form,
|
|
20
25
|
useNotification,
|
|
@@ -24,7 +29,6 @@ import getTrad from '../../../utils/getTrad';
|
|
|
24
29
|
import pluginId from '../../../pluginId';
|
|
25
30
|
import { usePlugins } from '../../../hooks';
|
|
26
31
|
import schema from './utils/schema';
|
|
27
|
-
import axiosInstance from '../../../utils/axiosInstance';
|
|
28
32
|
|
|
29
33
|
const EditPage = () => {
|
|
30
34
|
const { formatMessage } = useIntl();
|
|
@@ -35,6 +39,7 @@ const EditPage = () => {
|
|
|
35
39
|
const { isLoading: isLoadingPlugins, permissions, routes } = usePlugins();
|
|
36
40
|
const { trackUsage } = useTracking();
|
|
37
41
|
const permissionsRef = useRef();
|
|
42
|
+
const { post } = useFetchClient();
|
|
38
43
|
|
|
39
44
|
const handleCreateRoleSubmit = async (data) => {
|
|
40
45
|
// Set loading state
|
|
@@ -43,7 +48,7 @@ const EditPage = () => {
|
|
|
43
48
|
try {
|
|
44
49
|
const permissions = permissionsRef.current.getPermissions();
|
|
45
50
|
// Update role in Strapi
|
|
46
|
-
await
|
|
51
|
+
await post(`/${pluginId}/roles`, { ...data, ...permissions, users: [] });
|
|
47
52
|
// Notify success
|
|
48
53
|
trackUsage('didCreateRole');
|
|
49
54
|
toggleNotification({
|
|
@@ -3,6 +3,7 @@ import { Formik } from 'formik';
|
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import { useRouteMatch } from 'react-router-dom';
|
|
5
5
|
import {
|
|
6
|
+
useFetchClient,
|
|
6
7
|
useOverlayBlocker,
|
|
7
8
|
SettingsPageTitle,
|
|
8
9
|
LoadingIndicatorPage,
|
|
@@ -10,23 +11,25 @@ import {
|
|
|
10
11
|
useNotification,
|
|
11
12
|
Link,
|
|
12
13
|
} from '@strapi/helper-plugin';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
import {
|
|
15
|
+
ContentLayout,
|
|
16
|
+
HeaderLayout,
|
|
17
|
+
Main,
|
|
18
|
+
Button,
|
|
19
|
+
Stack,
|
|
20
|
+
Box,
|
|
21
|
+
TextInput,
|
|
22
|
+
Textarea,
|
|
23
|
+
Typography,
|
|
24
|
+
GridItem,
|
|
25
|
+
Grid,
|
|
26
|
+
} from '@strapi/design-system';
|
|
27
|
+
import { ArrowLeft, Check } from '@strapi/icons';
|
|
24
28
|
import UsersPermissions from '../../../components/UsersPermissions';
|
|
25
29
|
import getTrad from '../../../utils/getTrad';
|
|
26
30
|
import pluginId from '../../../pluginId';
|
|
27
31
|
import { usePlugins, useFetchRole } from '../../../hooks';
|
|
28
32
|
import schema from './utils/schema';
|
|
29
|
-
import axiosInstance from '../../../utils/axiosInstance';
|
|
30
33
|
|
|
31
34
|
const EditPage = () => {
|
|
32
35
|
const { formatMessage } = useIntl();
|
|
@@ -39,6 +42,7 @@ const EditPage = () => {
|
|
|
39
42
|
const { isLoading: isLoadingPlugins, routes } = usePlugins();
|
|
40
43
|
const { role, onSubmitSucceeded, isLoading: isLoadingRole } = useFetchRole(id);
|
|
41
44
|
const permissionsRef = useRef();
|
|
45
|
+
const { put } = useFetchClient();
|
|
42
46
|
|
|
43
47
|
const handleEditRoleSubmit = async (data) => {
|
|
44
48
|
// Set loading state
|
|
@@ -47,7 +51,7 @@ const EditPage = () => {
|
|
|
47
51
|
try {
|
|
48
52
|
const permissions = permissionsRef.current.getPermissions();
|
|
49
53
|
// Update role in Strapi
|
|
50
|
-
await
|
|
54
|
+
await put(`/${pluginId}/roles/${id}`, { ...data, ...permissions, users: [] });
|
|
51
55
|
// Notify success
|
|
52
56
|
onSubmitSucceeded({ name: data.name, description: data.description });
|
|
53
57
|
toggleNotification({
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { IconButton } from '@strapi/design-system
|
|
4
|
-
import {
|
|
5
|
-
import { Flex } from '@strapi/design-system/Flex';
|
|
6
|
-
import { Tbody, Tr, Td } from '@strapi/design-system/Table';
|
|
7
|
-
import Pencil from '@strapi/icons/Pencil';
|
|
8
|
-
import Trash from '@strapi/icons/Trash';
|
|
3
|
+
import { IconButton, Typography, Flex, Tbody, Tr, Td } from '@strapi/design-system';
|
|
4
|
+
import { Pencil, Trash } from '@strapi/icons';
|
|
9
5
|
import { CheckPermissions, onRowClick, stopPropagation } from '@strapi/helper-plugin';
|
|
10
6
|
import { useIntl } from 'react-intl';
|
|
11
7
|
import { useHistory } from 'react-router-dom';
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
HeaderLayout,
|
|
5
|
+
Layout,
|
|
6
|
+
ContentLayout,
|
|
7
|
+
ActionLayout,
|
|
8
|
+
Main,
|
|
9
|
+
Table,
|
|
10
|
+
Tr,
|
|
11
|
+
Thead,
|
|
12
|
+
Th,
|
|
13
|
+
Typography,
|
|
14
|
+
useNotifyAT,
|
|
15
|
+
VisuallyHidden,
|
|
16
|
+
} from '@strapi/design-system';
|
|
17
|
+
import { Plus } from '@strapi/icons';
|
|
10
18
|
import {
|
|
11
19
|
useTracking,
|
|
12
20
|
SettingsPageTitle,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFetchClient } from '@strapi/helper-plugin';
|
|
2
|
+
import { getRequestURL } from '../../../../utils';
|
|
2
3
|
|
|
3
4
|
export const fetchData = async (toggleNotification, notifyStatus) => {
|
|
4
5
|
try {
|
|
5
|
-
const {
|
|
6
|
+
const { get } = getFetchClient();
|
|
7
|
+
const { data } = await get(getRequestURL('roles'));
|
|
6
8
|
notifyStatus('The roles have loaded successfully');
|
|
7
9
|
|
|
8
10
|
return data;
|
|
@@ -18,7 +20,8 @@ export const fetchData = async (toggleNotification, notifyStatus) => {
|
|
|
18
20
|
|
|
19
21
|
export const deleteData = async (id, toggleNotification) => {
|
|
20
22
|
try {
|
|
21
|
-
|
|
23
|
+
const { del } = getFetchClient();
|
|
24
|
+
await del(`${getRequestURL('roles')}/${id}`);
|
|
22
25
|
} catch (error) {
|
|
23
26
|
toggleNotification({
|
|
24
27
|
type: 'warning',
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"PopUpForm.header.edit.email-templates": "Redigér e-mail skabeloner",
|
|
55
55
|
"PopUpForm.header.edit.providers": "Redigér udbydere",
|
|
56
56
|
"Providers.data.loaded": "Providers hentet",
|
|
57
|
-
"Providers.image": "Billede",
|
|
58
57
|
"Providers.status": "Status",
|
|
59
58
|
"Roles.empty": "Du har endnu ingen roller.",
|
|
60
59
|
"Roles.empty.search": "Ingen roller matcher søgningen.",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"PopUpForm.header.edit.email-templates": "Edit email template",
|
|
55
55
|
"PopUpForm.header.edit.providers": "Edit Provider",
|
|
56
56
|
"Providers.data.loaded": "Providers have been loaded",
|
|
57
|
-
"Providers.image": "Image",
|
|
58
57
|
"Providers.status": "Status",
|
|
59
58
|
"Roles.empty": "You don't have any roles yet.",
|
|
60
59
|
"Roles.empty.search": "No roles match the search.",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"PopUpForm.header.edit.email-templates": "Editar Plantillas de Email",
|
|
55
55
|
"PopUpForm.header.edit.providers": "Editar proveedor",
|
|
56
56
|
"Providers.data.loaded": "Los proveedores se han cargado",
|
|
57
|
-
"Providers.image": "Imagen",
|
|
58
57
|
"Providers.status": "Estado",
|
|
59
58
|
"Roles.empty": "Aún no tienes ningún rol.",
|
|
60
59
|
"Roles.empty.search": "Ningún rol coincide con la búsqueda.",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"PopUpForm.header.edit.email-templates": "이메일 템플릿 수정",
|
|
55
55
|
"PopUpForm.header.edit.providers": "프로바이더 수정",
|
|
56
56
|
"Providers.data.loaded": "프로바이더를 불러왔습니다.",
|
|
57
|
-
"Providers.image": "이미지",
|
|
58
57
|
"Providers.status": "상태",
|
|
59
58
|
"Roles.empty": "아직 역할이 없습니다.",
|
|
60
59
|
"Roles.empty.search": "검색과 일치하는 역할이 없습니다.",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"PopUpForm.header.edit.email-templates": "Zmień szablony e-mail",
|
|
55
55
|
"PopUpForm.header.edit.providers": "Edytuj dostawcę",
|
|
56
56
|
"Providers.data.loaded": "Dostawcy zostali załadowani",
|
|
57
|
-
"Providers.image": "Obrazek",
|
|
58
57
|
"Providers.status": "Status",
|
|
59
58
|
"Roles.empty": "Nie masz jeszcze żadnych ról.",
|
|
60
59
|
"Roles.empty.search": "Żadne role nie pasują do wyszukiwania.",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"PopUpForm.header.edit.email-templates": "Redigera e-postmallar",
|
|
55
55
|
"PopUpForm.header.edit.providers": "Redigera tjänst",
|
|
56
56
|
"Providers.data.loaded": "Tjänster har laddats in",
|
|
57
|
-
"Providers.image": "Bild",
|
|
58
57
|
"Providers.status": "Status",
|
|
59
58
|
"Roles.empty": "Du har inga roller än.",
|
|
60
59
|
"Roles.empty.search": "Inga roller matchar sökningen.",
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
"EditForm.inputToggle.label.email-confirmation": "E-posta onayını etkinleştir",
|
|
11
11
|
"EditForm.inputToggle.label.email-confirmation-redirection": "Yönlendirme URL'si",
|
|
12
12
|
"EditForm.inputToggle.label.sign-up": "Kayıtları etkinleştir",
|
|
13
|
+
"EditForm.inputToggle.placeholder.email-confirmation-redirection": "ör: https://yourfrontend.com/email-confirmation-redirection",
|
|
14
|
+
"EditForm.inputToggle.placeholder.email-reset-password": "ör: https://yourfrontend.com/reset-password",
|
|
15
|
+
"EditPage.form.roles": "Rol detayları",
|
|
16
|
+
"Email.template.data.loaded": "E-posta şablonları yüklendi",
|
|
17
|
+
"Email.template.email_confirmation": "E-posta adresi doğrulaması",
|
|
18
|
+
"Email.template.form.edit.label": "Şablonu düzenle",
|
|
19
|
+
"Email.template.table.action.label": "eylem",
|
|
20
|
+
"Email.template.table.icon.label": "ikon",
|
|
21
|
+
"Email.template.table.name.label": "ad",
|
|
22
|
+
"Form.advancedSettings.data.loaded": "Gelişmiş ayarlar verisi yüklendi",
|
|
13
23
|
"HeaderNav.link.advancedSettings": "Gelişmiş Ayarlar",
|
|
14
24
|
"HeaderNav.link.emailTemplates": "E-posta Şablonları",
|
|
15
25
|
"HeaderNav.link.providers": "Sağlayıcıları",
|
|
@@ -19,12 +29,14 @@
|
|
|
19
29
|
"Policies.header.hint": "Uygulamanın eylemlerini veya eklentinin eylemlerini seçin ve bağlı rotayı görüntülemek için dişli çark simgesini tıklayın",
|
|
20
30
|
"Policies.header.title": "Gelişmiş Ayarlar",
|
|
21
31
|
"PopUpForm.Email.email_templates.inputDescription": "Değişkenleri nasıl kullanacağınızdan emin değilseniz, {link}",
|
|
32
|
+
"PopUpForm.Email.link.documentation": "dokümantasyonu kontrol et.",
|
|
22
33
|
"PopUpForm.Email.options.from.email.label": "Gönderenin E-posta",
|
|
23
34
|
"PopUpForm.Email.options.from.email.placeholder": "kai@doe.com",
|
|
24
35
|
"PopUpForm.Email.options.from.name.label": "Gönderenin adı",
|
|
25
36
|
"PopUpForm.Email.options.from.name.placeholder": "Kai Doe",
|
|
26
37
|
"PopUpForm.Email.options.message.label": "Mesaj",
|
|
27
38
|
"PopUpForm.Email.options.object.label": "Konu",
|
|
39
|
+
"PopUpForm.Email.options.object.placeholder": "%APP_NAME% için e-posta adresini doğrula",
|
|
28
40
|
"PopUpForm.Email.options.response_email.label": "Yanıt e-postası",
|
|
29
41
|
"PopUpForm.Email.options.response_email.placeholder": "kai@doe.com",
|
|
30
42
|
"PopUpForm.Providers.enabled.description": "Devre dışı bırakıldıysa kullanıcılar bu sağlayıcıyı kullanamaz.",
|
|
@@ -32,13 +44,38 @@
|
|
|
32
44
|
"PopUpForm.Providers.key.label": "Web istemcisi ID",
|
|
33
45
|
"PopUpForm.Providers.key.placeholder": "METİN",
|
|
34
46
|
"PopUpForm.Providers.redirectURL.front-end.label": "Arayüz uygulamanızın yönlendirme URL'si",
|
|
47
|
+
"PopUpForm.Providers.redirectURL.label": "{provider} uygulama ayarlarına ekleyeceğin yönlendirme URLi",
|
|
35
48
|
"PopUpForm.Providers.secret.label": "Web istemcisi Secret",
|
|
36
49
|
"PopUpForm.Providers.secret.placeholder": "METİN",
|
|
37
50
|
"PopUpForm.Providers.subdomain.label": "Host URI (Subdomain)",
|
|
38
51
|
"PopUpForm.Providers.subdomain.placeholder": "my.subdomain.com",
|
|
39
52
|
"PopUpForm.header.edit.email-templates": "E-posta Şablonlarını Düzenle",
|
|
53
|
+
"PopUpForm.header.edit.providers": "Sağlayıcıyı Düzenle",
|
|
54
|
+
"Providers.data.loaded": "Sağlayıcılar yüklendi",
|
|
55
|
+
"Providers.image": "Görsel",
|
|
56
|
+
"Providers.status": "Durum",
|
|
57
|
+
"Roles.empty": "Henüz hiç rolün yok.",
|
|
58
|
+
"Roles.empty.search": "Aramaya uygun rol bulunmadı.",
|
|
59
|
+
"Settings.roles.deleted": "Rol silindi",
|
|
60
|
+
"Settings.roles.edited": "Rol düzenlendi",
|
|
61
|
+
"Settings.section-label": "Kullanıcılar ve İzinler eklentisi",
|
|
62
|
+
"components.Input.error.validation.email": "Bu geçersiz bir e-posta",
|
|
63
|
+
"components.Input.error.validation.json": "Bu JSON biçimine uymuyor",
|
|
64
|
+
"components.Input.error.validation.max": "Değer çok yüksek.",
|
|
65
|
+
"components.Input.error.validation.maxLength": "Değer çok uzun.",
|
|
66
|
+
"components.Input.error.validation.min": "Değer çok düşük.",
|
|
67
|
+
"components.Input.error.validation.minLength": "Değer çok kısa.",
|
|
68
|
+
"components.Input.error.validation.minSupMax": "Üst olamaz",
|
|
69
|
+
"components.Input.error.validation.regex": "Değer RegExp'e uymuyor.",
|
|
70
|
+
"components.Input.error.validation.required": "Değer gerekli.",
|
|
71
|
+
"components.Input.error.validation.unique": "Değer zaten kullanılıyor.",
|
|
40
72
|
"notification.success.submit": "Ayarlar güncellendi",
|
|
73
|
+
"page.title": "Ayarlar - Roller",
|
|
41
74
|
"plugin.description.long": "Servisinizi JWT'ye dayalı tam bir kimlik doğrulama işlemi ile koruyun. Bu eklenti, kullanıcı grupları arasındaki izinleri yönetmenize izin veren bir ACL stratejisiyle de gelir.",
|
|
42
75
|
"plugin.description.short": "Servisinizi JWT'ye dayalı tam bir kimlik doğrulama işlemi ile koruyun",
|
|
43
|
-
"plugin.name": "Roller
|
|
76
|
+
"plugin.name": "Roller ve İzinler",
|
|
77
|
+
"popUpWarning.button.cancel": "İptal Et",
|
|
78
|
+
"popUpWarning.button.confirm": "Onayla",
|
|
79
|
+
"popUpWarning.title": "Lütfen onayla",
|
|
80
|
+
"popUpWarning.warning.cancel": "Değişiklikleri iptal etmek istediğinden emin misin?"
|
|
44
81
|
}
|
|
@@ -5,11 +5,23 @@
|
|
|
5
5
|
"EditForm.inputToggle.description.email": "禁止使用者使用同一個電子郵件地址 + 不同的驗證方式註冊多個帳號",
|
|
6
6
|
"EditForm.inputToggle.description.email-confirmation": "當啟用後,新註冊的使用者將會收到一封認證郵件。",
|
|
7
7
|
"EditForm.inputToggle.description.email-confirmation-redirection": "認證完後後,使用者被重新導向的網址。",
|
|
8
|
+
"EditForm.inputToggle.description.email-reset-password": "您的應用程式的重設密碼頁面的網址",
|
|
8
9
|
"EditForm.inputToggle.description.sign-up": "當停用後,不論使用任何驗證方式使用者將無法註冊。",
|
|
9
10
|
"EditForm.inputToggle.label.email": "電子郵件地址單一帳號限制",
|
|
10
11
|
"EditForm.inputToggle.label.email-confirmation": "啟用電子郵件地址驗證",
|
|
11
12
|
"EditForm.inputToggle.label.email-confirmation-redirection": "重新導向網址",
|
|
13
|
+
"EditForm.inputToggle.label.email-reset-password": "密碼重設頁面",
|
|
12
14
|
"EditForm.inputToggle.label.sign-up": "啟用註冊",
|
|
15
|
+
"EditForm.inputToggle.placeholder.email-confirmation-redirection": "例如:https://yourfrontend.com/email-confirmation-redirection",
|
|
16
|
+
"EditForm.inputToggle.placeholder.email-reset-password": "例如:https://yourfrontend.com/reset-password",
|
|
17
|
+
"EditPage.form.roles": "角色詳細資訊",
|
|
18
|
+
"Email.template.data.loaded": "已載入電子郵件範本",
|
|
19
|
+
"Email.template.email_confirmation": "電子郵件地址確認",
|
|
20
|
+
"Email.template.form.edit.label": "編輯範本",
|
|
21
|
+
"Email.template.table.action.label": "操作",
|
|
22
|
+
"Email.template.table.icon.label": "圖示",
|
|
23
|
+
"Email.template.table.name.label": "名稱",
|
|
24
|
+
"Form.advancedSettings.data.loaded": "已載入進階設定資料",
|
|
13
25
|
"HeaderNav.link.advancedSettings": "進階設定",
|
|
14
26
|
"HeaderNav.link.emailTemplates": "郵件範本",
|
|
15
27
|
"HeaderNav.link.providers": "驗證方式",
|
|
@@ -19,12 +31,14 @@
|
|
|
19
31
|
"Policies.header.hint": "選取應用程式或擴充功能的操作然後點擊齒輪圖示以顯示綁定路徑",
|
|
20
32
|
"Policies.header.title": "進階設定",
|
|
21
33
|
"PopUpForm.Email.email_templates.inputDescription": "如果您不確定要怎麼使用變數,請 {link}",
|
|
34
|
+
"PopUpForm.Email.link.documentation": "查閱我們的說明文件。",
|
|
22
35
|
"PopUpForm.Email.options.from.email.label": "寄件人地址",
|
|
23
36
|
"PopUpForm.Email.options.from.email.placeholder": "kai@doe.com",
|
|
24
37
|
"PopUpForm.Email.options.from.name.label": "寄件人名稱",
|
|
25
38
|
"PopUpForm.Email.options.from.name.placeholder": "Kai Doe",
|
|
26
39
|
"PopUpForm.Email.options.message.label": "訊息",
|
|
27
40
|
"PopUpForm.Email.options.object.label": "主旨",
|
|
41
|
+
"PopUpForm.Email.options.object.placeholder": "請驗證 %APP_NAME% 的電子郵件地址",
|
|
28
42
|
"PopUpForm.Email.options.response_email.label": "回覆地址",
|
|
29
43
|
"PopUpForm.Email.options.response_email.placeholder": "kai@doe.com",
|
|
30
44
|
"PopUpForm.Providers.enabled.description": "如果停用,使用者將無法使用這個驗證方式",
|
|
@@ -32,13 +46,37 @@
|
|
|
32
46
|
"PopUpForm.Providers.key.label": "客戶端 ID",
|
|
33
47
|
"PopUpForm.Providers.key.placeholder": "TEXT",
|
|
34
48
|
"PopUpForm.Providers.redirectURL.front-end.label": "您應用程式的前端頁面網址",
|
|
49
|
+
"PopUpForm.Providers.redirectURL.label": "The redirect URL to add in your {provider} application configurations",
|
|
35
50
|
"PopUpForm.Providers.secret.label": "客戶端密鑰",
|
|
36
51
|
"PopUpForm.Providers.secret.placeholder": "TEXT",
|
|
37
|
-
"PopUpForm.Providers.subdomain.label": "Host URI (
|
|
52
|
+
"PopUpForm.Providers.subdomain.label": "Host URI (子網域)",
|
|
38
53
|
"PopUpForm.Providers.subdomain.placeholder": "my.subdomain.com",
|
|
39
54
|
"PopUpForm.header.edit.email-templates": "編輯郵件範本",
|
|
55
|
+
"PopUpForm.header.edit.providers": "編輯供應者",
|
|
56
|
+
"Providers.data.loaded": "已載入供應者",
|
|
57
|
+
"Providers.status": "狀態",
|
|
58
|
+
"Roles.empty": "您目前沒有任何角色。",
|
|
59
|
+
"Roles.empty.search": "沒有符合搜尋的角色。",
|
|
60
|
+
"Settings.roles.deleted": "已刪除角色",
|
|
61
|
+
"Settings.roles.edited": "已編輯角色",
|
|
62
|
+
"Settings.section-label": "使用者與權限外掛程式",
|
|
63
|
+
"components.Input.error.validation.email": "此電子郵件地址無效",
|
|
64
|
+
"components.Input.error.validation.json": "不符合 JSON 格式",
|
|
65
|
+
"components.Input.error.validation.max": "數值過高。",
|
|
66
|
+
"components.Input.error.validation.maxLength": "數值過長。",
|
|
67
|
+
"components.Input.error.validation.min": "數值過低。",
|
|
68
|
+
"components.Input.error.validation.minLength": "數值過短。",
|
|
69
|
+
"components.Input.error.validation.minSupMax": "Can't be superior",
|
|
70
|
+
"components.Input.error.validation.regex": "數值與 regex 不符。",
|
|
71
|
+
"components.Input.error.validation.required": "此數值為必填。",
|
|
72
|
+
"components.Input.error.validation.unique": "此值已被使用。",
|
|
40
73
|
"notification.success.submit": "設定已更新",
|
|
74
|
+
"page.title": "設定 - 角色",
|
|
41
75
|
"plugin.description.long": "使用 JWT 認證保護您的 API。這個擴充功能也使用 ACL 來讓你管理不同群組使用者的權限。",
|
|
42
76
|
"plugin.description.short": "使用 JWT 認證保護您的 API",
|
|
43
|
-
"plugin.name": "身份與權限"
|
|
77
|
+
"plugin.name": "身份與權限",
|
|
78
|
+
"popUpWarning.button.cancel": "取消",
|
|
79
|
+
"popUpWarning.button.confirm": "確認",
|
|
80
|
+
"popUpWarning.title": "請確認",
|
|
81
|
+
"popUpWarning.warning.cancel": "您確定要取消變更嗎?"
|
|
44
82
|
}
|
package/admin/src/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/plugin-users-permissions",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-deb656d24792ef3c29d49f986ce382fd655cd104",
|
|
4
4
|
"description": "Protect your API with a full-authentication process based on JWT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,22 +27,22 @@
|
|
|
27
27
|
"test:front:watch:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@strapi/helper-plugin": "0.0.0-
|
|
31
|
-
"@strapi/utils": "0.0.0-
|
|
30
|
+
"@strapi/helper-plugin": "0.0.0-deb656d24792ef3c29d49f986ce382fd655cd104",
|
|
31
|
+
"@strapi/utils": "0.0.0-deb656d24792ef3c29d49f986ce382fd655cd104",
|
|
32
32
|
"bcryptjs": "2.4.3",
|
|
33
33
|
"grant-koa": "5.4.8",
|
|
34
|
-
"jsonwebtoken": "
|
|
34
|
+
"jsonwebtoken": "9.0.0",
|
|
35
|
+
"jwk-to-pem": "2.0.5",
|
|
35
36
|
"koa": "^2.13.4",
|
|
36
37
|
"koa2-ratelimit": "^1.1.2",
|
|
37
38
|
"lodash": "4.17.21",
|
|
38
39
|
"purest": "4.0.2",
|
|
39
40
|
"react": "^17.0.2",
|
|
40
41
|
"react-dom": "^17.0.2",
|
|
41
|
-
"react-intl": "
|
|
42
|
-
"react-redux": "
|
|
42
|
+
"react-intl": "6.2.8",
|
|
43
|
+
"react-redux": "8.0.5",
|
|
43
44
|
"react-router": "^5.2.0",
|
|
44
|
-
"react-router-dom": "5.
|
|
45
|
-
"request": "^2.83.0",
|
|
45
|
+
"react-router-dom": "5.3.4",
|
|
46
46
|
"url-join": "4.0.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@testing-library/react": "12.1.4",
|
|
51
51
|
"@testing-library/react-hooks": "8.0.1",
|
|
52
52
|
"@testing-library/user-event": "14.4.3",
|
|
53
|
-
"msw": "0.
|
|
53
|
+
"msw": "1.0.1",
|
|
54
54
|
"react-test-renderer": "^17.0.2"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"required": true,
|
|
65
65
|
"kind": "plugin"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "deb656d24792ef3c29d49f986ce382fd655cd104"
|
|
68
68
|
}
|
|
@@ -120,4 +120,12 @@ module.exports = (baseURL) => ({
|
|
|
120
120
|
scope: ['openid email'], // scopes should be space delimited
|
|
121
121
|
subdomain: 'my.subdomain.com/cas',
|
|
122
122
|
},
|
|
123
|
+
patreon: {
|
|
124
|
+
enabled: false,
|
|
125
|
+
icon: '',
|
|
126
|
+
key: '',
|
|
127
|
+
secret: '',
|
|
128
|
+
callback: `${baseURL}/patreon/callback`,
|
|
129
|
+
scope: ['identity', 'identity[email]'],
|
|
130
|
+
},
|
|
123
131
|
});
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { trim } = require('lodash/fp');
|
|
4
|
+
const {
|
|
5
|
+
template: { createLooseInterpolationRegExp, createStrictInterpolationRegExp },
|
|
6
|
+
} = require('@strapi/utils');
|
|
7
|
+
|
|
8
|
+
const invalidPatternsRegexes = [
|
|
9
|
+
// Ignore "evaluation" patterns: <% ... %>
|
|
10
|
+
/<%[^=]([\s\S]*?)%>/m,
|
|
11
|
+
// Ignore basic string interpolations
|
|
12
|
+
/\${([^{}]*)}/m,
|
|
13
|
+
];
|
|
4
14
|
|
|
5
|
-
const invalidPatternsRegexes = [/<%[^=]([^<>%]*)%>/m, /\${([^{}]*)}/m];
|
|
6
15
|
const authorizedKeys = [
|
|
7
16
|
'URL',
|
|
8
17
|
'ADMIN_URL',
|
|
@@ -19,27 +28,42 @@ const matchAll = (pattern, src) => {
|
|
|
19
28
|
let match;
|
|
20
29
|
|
|
21
30
|
const regexPatternWithGlobal = RegExp(pattern, 'g');
|
|
31
|
+
|
|
22
32
|
// eslint-disable-next-line no-cond-assign
|
|
23
33
|
while ((match = regexPatternWithGlobal.exec(src))) {
|
|
24
34
|
const [, group] = match;
|
|
25
35
|
|
|
26
|
-
matches.push(
|
|
36
|
+
matches.push(trim(group));
|
|
27
37
|
}
|
|
38
|
+
|
|
28
39
|
return matches;
|
|
29
40
|
};
|
|
30
41
|
|
|
31
42
|
const isValidEmailTemplate = (template) => {
|
|
43
|
+
// Check for known invalid patterns
|
|
32
44
|
for (const reg of invalidPatternsRegexes) {
|
|
33
45
|
if (reg.test(template)) {
|
|
34
46
|
return false;
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
const interpolation = {
|
|
51
|
+
// Strict interpolation pattern to match only valid groups
|
|
52
|
+
strict: createStrictInterpolationRegExp(authorizedKeys),
|
|
53
|
+
// Weak interpolation pattern to match as many group as possible.
|
|
54
|
+
loose: createLooseInterpolationRegExp(),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Compute both strict & loose matches
|
|
58
|
+
const strictMatches = matchAll(interpolation.strict, template);
|
|
59
|
+
const looseMatches = matchAll(interpolation.loose, template);
|
|
60
|
+
|
|
61
|
+
// If we have more matches with the loose RegExp than with the strict one,
|
|
62
|
+
// then it means that at least one of the interpolation group is invalid
|
|
63
|
+
// Note: In the future, if we wanted to give more details for error formatting
|
|
64
|
+
// purposes, we could return the difference between the two arrays
|
|
65
|
+
if (looseMatches.length > strictMatches.length) {
|
|
66
|
+
return false;
|
|
43
67
|
}
|
|
44
68
|
|
|
45
69
|
return true;
|
|
@@ -2,10 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
const { strict: assert } = require('assert');
|
|
4
4
|
const jwt = require('jsonwebtoken');
|
|
5
|
+
const jwkToPem = require('jwk-to-pem');
|
|
6
|
+
|
|
7
|
+
const getCognitoPayload = async ({ idToken, jwksUrl, purest }) => {
|
|
8
|
+
const {
|
|
9
|
+
header: { kid },
|
|
10
|
+
payload,
|
|
11
|
+
} = jwt.decode(idToken, { complete: true });
|
|
12
|
+
|
|
13
|
+
if (!payload || !kid) {
|
|
14
|
+
throw new Error('The provided token is not valid');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const config = {
|
|
18
|
+
cognito: {
|
|
19
|
+
discovery: {
|
|
20
|
+
origin: jwksUrl.origin,
|
|
21
|
+
path: jwksUrl.pathname,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
try {
|
|
26
|
+
const cognito = purest({ provider: 'cognito', config });
|
|
27
|
+
// get the JSON Web Key (JWK) for the user pool
|
|
28
|
+
const { body: jwk } = await cognito('discovery').request();
|
|
29
|
+
// Get the key with the same Key ID as the provided token
|
|
30
|
+
const key = jwk.keys.find(({ kid: jwkKid }) => jwkKid === kid);
|
|
31
|
+
const pem = jwkToPem(key);
|
|
32
|
+
|
|
33
|
+
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
|
|
34
|
+
const decodedToken = await new Promise((resolve, reject) => {
|
|
35
|
+
jwt.verify(idToken, pem, { algorithms: ['RS256'] }, (err, decodedToken) => {
|
|
36
|
+
if (err) {
|
|
37
|
+
reject();
|
|
38
|
+
}
|
|
39
|
+
resolve(decodedToken);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
return decodedToken;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error('There was an error verifying the token');
|
|
45
|
+
}
|
|
46
|
+
};
|
|
5
47
|
|
|
6
48
|
const getInitialProviders = ({ purest }) => ({
|
|
7
49
|
async discord({ accessToken }) {
|
|
8
50
|
const discord = purest({ provider: 'discord' });
|
|
51
|
+
|
|
9
52
|
return discord
|
|
10
53
|
.get('users/@me')
|
|
11
54
|
.auth(accessToken)
|
|
@@ -19,19 +62,14 @@ const getInitialProviders = ({ purest }) => ({
|
|
|
19
62
|
};
|
|
20
63
|
});
|
|
21
64
|
},
|
|
22
|
-
async cognito({ query }) {
|
|
23
|
-
|
|
65
|
+
async cognito({ query, providers }) {
|
|
66
|
+
const jwksUrl = new URL(providers.cognito.jwksurl);
|
|
24
67
|
const idToken = query.id_token;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
username: tokenPayload['cognito:username'],
|
|
32
|
-
email: tokenPayload.email,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
68
|
+
const tokenPayload = await getCognitoPayload({ idToken, jwksUrl, purest });
|
|
69
|
+
return {
|
|
70
|
+
username: tokenPayload['cognito:username'],
|
|
71
|
+
email: tokenPayload.email,
|
|
72
|
+
};
|
|
35
73
|
},
|
|
36
74
|
async facebook({ accessToken }) {
|
|
37
75
|
const facebook = purest({ provider: 'facebook' });
|
|
@@ -264,6 +302,35 @@ const getInitialProviders = ({ purest }) => ({
|
|
|
264
302
|
};
|
|
265
303
|
});
|
|
266
304
|
},
|
|
305
|
+
async patreon({ accessToken }) {
|
|
306
|
+
const patreon = purest({
|
|
307
|
+
provider: 'patreon',
|
|
308
|
+
config: {
|
|
309
|
+
patreon: {
|
|
310
|
+
default: {
|
|
311
|
+
origin: 'https://www.patreon.com',
|
|
312
|
+
path: 'api/oauth2/{path}',
|
|
313
|
+
headers: {
|
|
314
|
+
authorization: 'Bearer {auth}',
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return patreon
|
|
322
|
+
.get('v2/identity')
|
|
323
|
+
.auth(accessToken)
|
|
324
|
+
.qs(new URLSearchParams({ 'fields[user]': 'full_name,email' }).toString())
|
|
325
|
+
.request()
|
|
326
|
+
.then(({ body }) => {
|
|
327
|
+
const patreonData = body.data.attributes;
|
|
328
|
+
return {
|
|
329
|
+
username: patreonData.full_name,
|
|
330
|
+
email: patreonData.email,
|
|
331
|
+
};
|
|
332
|
+
});
|
|
333
|
+
},
|
|
267
334
|
});
|
|
268
335
|
|
|
269
336
|
module.exports = () => {
|
package/server/services/user.js
CHANGED
|
@@ -109,17 +109,25 @@ module.exports = ({ strapi }) => ({
|
|
|
109
109
|
await this.edit(user.id, { confirmationToken });
|
|
110
110
|
|
|
111
111
|
const apiPrefix = strapi.config.get('api.rest.prefix');
|
|
112
|
-
settings.message = await userPermissionService.template(settings.message, {
|
|
113
|
-
URL: urlJoin(getAbsoluteServerUrl(strapi.config), apiPrefix, '/auth/email-confirmation'),
|
|
114
|
-
SERVER_URL: getAbsoluteServerUrl(strapi.config),
|
|
115
|
-
ADMIN_URL: getAbsoluteAdminUrl(strapi.config),
|
|
116
|
-
USER: sanitizedUserInfo,
|
|
117
|
-
CODE: confirmationToken,
|
|
118
|
-
});
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
try {
|
|
114
|
+
settings.message = await userPermissionService.template(settings.message, {
|
|
115
|
+
URL: urlJoin(getAbsoluteServerUrl(strapi.config), apiPrefix, '/auth/email-confirmation'),
|
|
116
|
+
SERVER_URL: getAbsoluteServerUrl(strapi.config),
|
|
117
|
+
ADMIN_URL: getAbsoluteAdminUrl(strapi.config),
|
|
118
|
+
USER: sanitizedUserInfo,
|
|
119
|
+
CODE: confirmationToken,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
settings.object = await userPermissionService.template(settings.object, {
|
|
123
|
+
USER: sanitizedUserInfo,
|
|
124
|
+
});
|
|
125
|
+
} catch {
|
|
126
|
+
strapi.log.error(
|
|
127
|
+
'[plugin::users-permissions.sendConfirmationEmail]: Failed to generate a template for "user confirmation email". Please make sure your email template is valid and does not contain invalid characters or patterns'
|
|
128
|
+
);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
123
131
|
|
|
124
132
|
// Send an email to the user.
|
|
125
133
|
await strapi
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const { filter, map, pipe, prop } = require('lodash/fp');
|
|
5
5
|
const urlJoin = require('url-join');
|
|
6
|
+
const {
|
|
7
|
+
template: { createStrictInterpolationRegExp },
|
|
8
|
+
errors,
|
|
9
|
+
keysDeep,
|
|
10
|
+
} = require('@strapi/utils');
|
|
6
11
|
|
|
7
12
|
const { getService } = require('../utils');
|
|
8
13
|
|
|
@@ -230,7 +235,15 @@ module.exports = ({ strapi }) => ({
|
|
|
230
235
|
},
|
|
231
236
|
|
|
232
237
|
template(layout, data) {
|
|
233
|
-
const
|
|
234
|
-
|
|
238
|
+
const allowedTemplateVariables = keysDeep(data);
|
|
239
|
+
|
|
240
|
+
// Create a strict interpolation RegExp based on possible variable names
|
|
241
|
+
const interpolate = createStrictInterpolationRegExp(allowedTemplateVariables, 'g');
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
return _.template(layout, { interpolate, evaluate: false, escape: false })(data);
|
|
245
|
+
} catch (e) {
|
|
246
|
+
throw new errors.ApplicationError('Invalid email template');
|
|
247
|
+
}
|
|
235
248
|
},
|
|
236
249
|
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import { auth } from '@strapi/helper-plugin';
|
|
3
|
-
|
|
4
|
-
const instance = axios.create({
|
|
5
|
-
baseURL: process.env.STRAPI_ADMIN_BACKEND_URL,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
instance.interceptors.request.use(
|
|
9
|
-
async (config) => {
|
|
10
|
-
config.headers = {
|
|
11
|
-
Authorization: `Bearer ${auth.getToken()}`,
|
|
12
|
-
Accept: 'application/json',
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
return config;
|
|
17
|
-
},
|
|
18
|
-
(error) => {
|
|
19
|
-
Promise.reject(error);
|
|
20
|
-
}
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
instance.interceptors.response.use(
|
|
24
|
-
(response) => response,
|
|
25
|
-
(error) => {
|
|
26
|
-
// whatever you want to do with the error
|
|
27
|
-
if (error.response?.status === 401) {
|
|
28
|
-
auth.clearAppStorage();
|
|
29
|
-
window.location.reload();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
export default instance;
|