@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.
Files changed (42) hide show
  1. package/admin/src/components/BoundRoute/index.js +1 -3
  2. package/admin/src/components/FormModal/Input/index.js +1 -2
  3. package/admin/src/components/FormModal/index.js +7 -5
  4. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +1 -1
  5. package/admin/src/components/Permissions/PermissionRow/SubCategory.js +2 -6
  6. package/admin/src/components/Permissions/PermissionRow/index.js +1 -1
  7. package/admin/src/components/Permissions/index.js +1 -3
  8. package/admin/src/components/Policies/index.js +1 -3
  9. package/admin/src/components/UsersPermissions/index.js +1 -3
  10. package/admin/src/hooks/useFetchRole/index.js +3 -3
  11. package/admin/src/hooks/usePlugins/index.js +5 -3
  12. package/admin/src/pages/AdvancedSettings/index.js +15 -10
  13. package/admin/src/pages/AdvancedSettings/utils/api.js +7 -3
  14. package/admin/src/pages/EmailTemplates/components/EmailForm.js +7 -5
  15. package/admin/src/pages/EmailTemplates/components/EmailTable.js +14 -9
  16. package/admin/src/pages/EmailTemplates/index.js +1 -3
  17. package/admin/src/pages/EmailTemplates/utils/api.js +7 -3
  18. package/admin/src/pages/Providers/index.js +18 -20
  19. package/admin/src/pages/Providers/utils/api.js +7 -3
  20. package/admin/src/pages/Providers/utils/forms.js +15 -0
  21. package/admin/src/pages/Roles/CreatePage/index.js +17 -12
  22. package/admin/src/pages/Roles/EditPage/index.js +17 -13
  23. package/admin/src/pages/Roles/ListPage/components/TableBody.js +2 -6
  24. package/admin/src/pages/Roles/ListPage/index.js +16 -8
  25. package/admin/src/pages/Roles/ListPage/utils/api.js +6 -3
  26. package/admin/src/translations/dk.json +0 -1
  27. package/admin/src/translations/en.json +0 -1
  28. package/admin/src/translations/es.json +0 -1
  29. package/admin/src/translations/ko.json +0 -1
  30. package/admin/src/translations/pl.json +0 -1
  31. package/admin/src/translations/sv.json +0 -1
  32. package/admin/src/translations/tr.json +38 -1
  33. package/admin/src/translations/zh.json +40 -2
  34. package/admin/src/utils/index.js +0 -1
  35. package/package.json +10 -10
  36. package/server/bootstrap/grant-config.js +8 -0
  37. package/server/controllers/validation/email-template.js +32 -8
  38. package/server/services/providers-registry.js +79 -12
  39. package/server/services/providers.js +1 -1
  40. package/server/services/user.js +18 -10
  41. package/server/services/users-permissions.js +15 -2
  42. 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/Stack';
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/ToggleInput';
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/ModalLayout';
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';
@@ -1,5 +1,5 @@
1
1
  import styled, { css } from 'styled-components';
2
- import { Box } from '@strapi/design-system/Box';
2
+ import { Box } from '@strapi/design-system';
3
3
 
4
4
  const activeCheckboxWrapperStyles = css`
5
5
  background: ${(props) => props.theme.colors.primary100};
@@ -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/Box';
6
- import { Checkbox } from '@strapi/design-system/Checkbox';
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/Box';
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/Accordion';
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/Typography';
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/Typography';
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 axiosIntance.get(`/${pluginId}/roles/${roleId}`);
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 axiosInstance.get(endpoint);
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 { useNotifyAT } from '@strapi/design-system/LiveRegions';
17
- import { Main } from '@strapi/design-system/Main';
18
- import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
19
- import { Button } from '@strapi/design-system/Button';
20
- import { Box } from '@strapi/design-system/Box';
21
- import { Stack } from '@strapi/design-system/Stack';
22
- import { Select, Option } from '@strapi/design-system/Select';
23
- import { Typography } from '@strapi/design-system/Typography';
24
- import { Grid, GridItem } from '@strapi/design-system/Grid';
25
- import Check from '@strapi/icons/Check';
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 { axiosInstance, getRequestURL } from '../../../utils';
1
+ import { getFetchClient } from '@strapi/helper-plugin';
2
+ import { getRequestURL } from '../../../utils';
2
3
 
3
4
  const fetchData = async () => {
4
- const { data } = await axiosInstance.get(getRequestURL('advanced'));
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
- return axiosInstance.put(getRequestURL('advanced'), body);
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
- } from '@strapi/design-system/ModalLayout';
12
- import { Grid, GridItem } from '@strapi/design-system/Grid';
13
- import { Button } from '@strapi/design-system/Button';
14
- import { Breadcrumbs, Crumb } from '@strapi/design-system/Breadcrumbs';
15
- import { Textarea } from '@strapi/design-system/Textarea';
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 { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system/Table';
5
- import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
6
- import { Typography } from '@strapi/design-system/Typography';
7
- import { IconButton } from '@strapi/design-system/IconButton';
8
- import { Icon } from '@strapi/design-system/Icon';
9
- import Pencil from '@strapi/icons/Pencil';
10
- import Reload from '@strapi/icons/Refresh';
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
- <Reload
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/LiveRegions';
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 { axiosInstance, getRequestURL } from '../../../utils';
1
+ import { getFetchClient } from '@strapi/helper-plugin';
2
+ import { getRequestURL } from '../../../utils';
2
3
 
3
4
  const fetchData = async () => {
4
- const { data } = await axiosInstance.get(getRequestURL('email-templates'));
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
- return axiosInstance.put(getRequestURL('email-templates'), body);
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 { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
18
- import { HeaderLayout, Layout, ContentLayout } from '@strapi/design-system/Layout';
19
- import { Main } from '@strapi/design-system/Main';
20
- import { useNotifyAT } from '@strapi/design-system/LiveRegions';
21
- import { Table, Thead, Tr, Th, Tbody, Td } from '@strapi/design-system/Table';
22
- import { Typography } from '@strapi/design-system/Typography';
23
- import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
24
- import { IconButton } from '@strapi/design-system/IconButton';
25
- import Pencil from '@strapi/icons/Pencil';
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={4} rowCount={rowCount + 1}>
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 { getRequestURL, axiosInstance } from '../../../utils';
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 { data } = await axiosInstance.get(getRequestURL('providers'));
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
- return axiosInstance.put(getRequestURL('providers'), body);
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 { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
4
- import { Main } from '@strapi/design-system/Main';
5
- import { Button } from '@strapi/design-system/Button';
6
- import { Stack } from '@strapi/design-system/Stack';
7
- import { Box } from '@strapi/design-system/Box';
8
- import { TextInput } from '@strapi/design-system/TextInput';
9
- import { Textarea } from '@strapi/design-system/Textarea';
10
- import { Typography } from '@strapi/design-system/Typography';
11
- import Check from '@strapi/icons/Check';
12
- import { GridItem, Grid } from '@strapi/design-system/Grid';
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 axiosInstance.post(`/${pluginId}/roles`, { ...data, ...permissions, users: [] });
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 { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
14
- import { Main } from '@strapi/design-system/Main';
15
- import { Button } from '@strapi/design-system/Button';
16
- import { Stack } from '@strapi/design-system/Stack';
17
- import { Box } from '@strapi/design-system/Box';
18
- import { TextInput } from '@strapi/design-system/TextInput';
19
- import { Textarea } from '@strapi/design-system/Textarea';
20
- import { Typography } from '@strapi/design-system/Typography';
21
- import ArrowLeft from '@strapi/icons/ArrowLeft';
22
- import Check from '@strapi/icons/Check';
23
- import { GridItem, Grid } from '@strapi/design-system/Grid';
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 axiosInstance.put(`/${pluginId}/roles/${id}`, { ...data, ...permissions, users: [] });
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/IconButton';
4
- import { Typography } from '@strapi/design-system/Typography';
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 { Button } from '@strapi/design-system/Button';
3
- import { HeaderLayout, Layout, ContentLayout, ActionLayout } from '@strapi/design-system/Layout';
4
- import { Main } from '@strapi/design-system/Main';
5
- import { Table, Tr, Thead, Th } from '@strapi/design-system/Table';
6
- import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
7
- import { Typography } from '@strapi/design-system/Typography';
8
- import { useNotifyAT } from '@strapi/design-system/LiveRegions';
9
- import Plus from '@strapi/icons/Plus';
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 { getRequestURL, axiosInstance } from '../../../../utils';
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 { data } = await axiosInstance.get(getRequestURL('roles'));
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
- await axiosInstance.delete(`${getRequestURL('roles')}/${id}`);
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 & İzinler"
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 (Subdomain)",
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
  }
@@ -1,4 +1,3 @@
1
- export { default as axiosInstance } from './axiosInstance';
2
1
  export { default as cleanPermissions } from './cleanPermissions';
3
2
  export { default as getRequestURL } from './getRequestURL';
4
3
  export { default as getTrad } from './getTrad';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-users-permissions",
3
- "version": "0.0.0-de3d4081f5",
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-de3d4081f5",
31
- "@strapi/utils": "0.0.0-de3d4081f5",
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": "^8.1.0",
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": "5.25.1",
42
- "react-redux": "7.2.8",
42
+ "react-intl": "6.2.8",
43
+ "react-redux": "8.0.5",
43
44
  "react-router": "^5.2.0",
44
- "react-router-dom": "5.2.0",
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.42.3",
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": "de3d4081f5b32a6b265f993384f30c5b7b3ffcf7"
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 _ = require('lodash');
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(_.trim(group));
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 matches = matchAll(/<%=([^<>%=]*)%>/, template);
39
- for (const match of matches) {
40
- if (!authorizedKeys.includes(match)) {
41
- return false;
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
- // get the id_token
65
+ async cognito({ query, providers }) {
66
+ const jwksUrl = new URL(providers.cognito.jwksurl);
24
67
  const idToken = query.id_token;
25
- // decode the jwt token
26
- const tokenPayload = jwt.decode(idToken);
27
- if (!tokenPayload) {
28
- throw new Error('unable to decode jwt token');
29
- } else {
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 = () => {
@@ -78,7 +78,7 @@ module.exports = ({ strapi }) => {
78
78
  return user;
79
79
  }
80
80
 
81
- if (users.length > 1 && advancedSettings.unique_email) {
81
+ if (users.length && advancedSettings.unique_email) {
82
82
  throw new Error('Email is already taken.');
83
83
  }
84
84
 
@@ -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
- settings.object = await userPermissionService.template(settings.object, {
121
- USER: sanitizedUserInfo,
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 compiledObject = _.template(layout);
234
- return compiledObject(data);
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;