@strapi/plugin-users-permissions 4.0.0-beta.11 → 4.0.0-beta.15
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 +21 -25
- package/admin/src/components/Permissions/PermissionRow/SubCategory.js +4 -2
- package/admin/src/components/Policies/index.js +5 -5
- package/admin/src/components/UsersPermissions/index.js +5 -5
- package/admin/src/pages/AdvancedSettings/index.js +3 -3
- package/admin/src/pages/EmailTemplates/components/EmailTable.js +7 -7
- package/admin/src/pages/Providers/index.js +13 -13
- package/admin/src/pages/Roles/CreatePage/index.js +3 -3
- package/admin/src/pages/Roles/EditPage/index.js +3 -3
- package/admin/src/pages/Roles/ListPage/components/TableBody.js +5 -5
- package/admin/src/pages/Roles/ListPage/index.js +13 -21
- package/package.json +5 -6
- package/server/config.js +2 -2
- package/server/content-types/permission/index.js +3 -0
- package/server/content-types/role/index.js +3 -0
- package/server/controllers/{user/admin.js → content-manager-user.js} +15 -17
- package/server/controllers/index.js +2 -0
- package/server/controllers/user.js +114 -27
- package/server/controllers/validation/user.js +15 -18
- package/server/services/jwt.js +9 -18
- package/server/services/user.js +9 -3
- package/server/strategies/users-permissions.js +19 -17
- package/server/controllers/user/api.js +0 -125
- package/server/controllers/validation/role.js +0 -36
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
2
3
|
import { Stack } from '@strapi/design-system/Stack';
|
|
3
4
|
import { Box } from '@strapi/design-system/Box';
|
|
4
|
-
import {
|
|
5
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
5
6
|
import map from 'lodash/map';
|
|
6
7
|
import tail from 'lodash/tail';
|
|
7
8
|
import { useIntl } from 'react-intl';
|
|
8
9
|
import PropTypes from 'prop-types';
|
|
9
10
|
import getMethodColor from './getMethodColor';
|
|
10
11
|
|
|
12
|
+
const MethodBox = styled(Box)`
|
|
13
|
+
margin: -1px;
|
|
14
|
+
border-radius: ${({ theme }) => theme.spaces[1]} 0 0 ${({ theme }) => theme.spaces[1]};
|
|
15
|
+
`;
|
|
16
|
+
|
|
11
17
|
function BoundRoute({ route }) {
|
|
12
18
|
const { formatMessage } = useIntl();
|
|
13
19
|
|
|
@@ -18,41 +24,31 @@ function BoundRoute({ route }) {
|
|
|
18
24
|
|
|
19
25
|
return (
|
|
20
26
|
<Stack size={2}>
|
|
21
|
-
<
|
|
27
|
+
<Typography variant="delta" as="h3">
|
|
22
28
|
{formatMessage({
|
|
23
29
|
id: 'users-permissions.BoundRoute.title',
|
|
24
30
|
defaultMessage: 'Bound route to',
|
|
25
31
|
})}
|
|
26
32
|
|
|
27
33
|
<span>{controller}</span>
|
|
28
|
-
<
|
|
34
|
+
<Typography variant="delta" textColor="primary600">
|
|
29
35
|
.{action}
|
|
30
|
-
</
|
|
31
|
-
</
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
style={{
|
|
40
|
-
display: 'inline-block',
|
|
41
|
-
margin: '-1px',
|
|
42
|
-
borderTopRightRadius: 0,
|
|
43
|
-
borderBottomRightRadius: 0,
|
|
44
|
-
}}
|
|
45
|
-
>
|
|
46
|
-
<Text bold>{method}</Text>
|
|
47
|
-
</Box>
|
|
48
|
-
<Box style={{ display: 'inline-block' }} paddingLeft={2} paddingRight={2}>
|
|
36
|
+
</Typography>
|
|
37
|
+
</Typography>
|
|
38
|
+
<Stack horizontal hasRadius background="neutral0" borderColor="neutral200" size={0}>
|
|
39
|
+
<MethodBox background={colors.background} borderColor={colors.border} padding={2}>
|
|
40
|
+
<Typography fontWeight="bold" textColor={colors.text}>
|
|
41
|
+
{method}
|
|
42
|
+
</Typography>
|
|
43
|
+
</MethodBox>
|
|
44
|
+
<Box paddingLeft={2} paddingRight={2}>
|
|
49
45
|
{map(formattedRoute, value => (
|
|
50
|
-
<
|
|
46
|
+
<Typography key={value} textColor={value.includes(':') ? 'neutral600' : 'neutral900'}>
|
|
51
47
|
/{value}
|
|
52
|
-
</
|
|
48
|
+
</Typography>
|
|
53
49
|
))}
|
|
54
50
|
</Box>
|
|
55
|
-
</
|
|
51
|
+
</Stack>
|
|
56
52
|
</Stack>
|
|
57
53
|
);
|
|
58
54
|
}
|
|
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
|
|
5
5
|
import { Box } from '@strapi/design-system/Box';
|
|
6
6
|
import { Checkbox } from '@strapi/design-system/Checkbox';
|
|
7
7
|
import { Flex } from '@strapi/design-system/Flex';
|
|
8
|
-
import {
|
|
8
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
9
9
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
10
10
|
import CogIcon from '@strapi/icons/Cog';
|
|
11
11
|
import { useIntl } from 'react-intl';
|
|
@@ -61,7 +61,9 @@ const SubCategory = ({ subCategory }) => {
|
|
|
61
61
|
<Box>
|
|
62
62
|
<Flex justifyContent="space-between" alignItems="center">
|
|
63
63
|
<Box paddingRight={4}>
|
|
64
|
-
<
|
|
64
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
65
|
+
{subCategory.label}
|
|
66
|
+
</Typography>
|
|
65
67
|
</Box>
|
|
66
68
|
<Border />
|
|
67
69
|
<Box paddingLeft={4}>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
|
-
import {
|
|
3
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
4
4
|
import { Stack } from '@strapi/design-system/Stack';
|
|
5
5
|
import { GridItem } from '@strapi/design-system/Grid';
|
|
6
6
|
import { get, isEmpty, takeRight, toLower, without } from 'lodash';
|
|
@@ -35,19 +35,19 @@ const Policies = () => {
|
|
|
35
35
|
</Stack>
|
|
36
36
|
) : (
|
|
37
37
|
<Stack size={2}>
|
|
38
|
-
<
|
|
38
|
+
<Typography variant="delta" as="h3">
|
|
39
39
|
{formatMessage({
|
|
40
40
|
id: 'users-permissions.Policies.header.title',
|
|
41
41
|
defaultMessage: 'Advanced settings',
|
|
42
42
|
})}
|
|
43
|
-
</
|
|
44
|
-
<
|
|
43
|
+
</Typography>
|
|
44
|
+
<Typography as="p" textColor="neutral600">
|
|
45
45
|
{formatMessage({
|
|
46
46
|
id: 'users-permissions.Policies.header.hint',
|
|
47
47
|
defaultMessage:
|
|
48
48
|
"Select the application's actions or the plugin's actions and click on the cog icon to display the bound route",
|
|
49
49
|
})}
|
|
50
|
-
</
|
|
50
|
+
</Typography>
|
|
51
51
|
</Stack>
|
|
52
52
|
)}
|
|
53
53
|
</GridItem>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { memo, useReducer, useCallback, forwardRef, useImperativeHandle } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
3
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
4
4
|
import { Stack } from '@strapi/design-system/Stack';
|
|
5
5
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
6
6
|
import { useIntl } from 'react-intl';
|
|
@@ -67,18 +67,18 @@ const UsersPermissions = forwardRef(({ permissions, routes }, ref) => {
|
|
|
67
67
|
<GridItem col={7} paddingTop={6} paddingBottom={6} paddingLeft={7} paddingRight={7}>
|
|
68
68
|
<Stack size={4}>
|
|
69
69
|
<Stack size={2}>
|
|
70
|
-
<
|
|
70
|
+
<Typography variant="delta" as="h2">
|
|
71
71
|
{formatMessage({
|
|
72
72
|
id: getTrad('Plugins.header.title'),
|
|
73
73
|
defaultMessage: 'Permissions',
|
|
74
74
|
})}
|
|
75
|
-
</
|
|
76
|
-
<
|
|
75
|
+
</Typography>
|
|
76
|
+
<Typography as="p" textColor="neutral600">
|
|
77
77
|
{formatMessage({
|
|
78
78
|
id: getTrad('Plugins.header.description'),
|
|
79
79
|
defaultMessage: 'Only actions bound by a route are listed below.',
|
|
80
80
|
})}
|
|
81
|
-
</
|
|
81
|
+
</Typography>
|
|
82
82
|
</Stack>
|
|
83
83
|
<Permissions />
|
|
84
84
|
</Stack>
|
|
@@ -20,7 +20,7 @@ import { Button } from '@strapi/design-system/Button';
|
|
|
20
20
|
import { Box } from '@strapi/design-system/Box';
|
|
21
21
|
import { Stack } from '@strapi/design-system/Stack';
|
|
22
22
|
import { Select, Option } from '@strapi/design-system/Select';
|
|
23
|
-
import {
|
|
23
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
24
24
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
25
25
|
import Check from '@strapi/icons/Check';
|
|
26
26
|
import pluginPermissions from '../../permissions';
|
|
@@ -169,12 +169,12 @@ const AdvancedSettingsPage = () => {
|
|
|
169
169
|
paddingRight={7}
|
|
170
170
|
>
|
|
171
171
|
<Stack size={4}>
|
|
172
|
-
<
|
|
172
|
+
<Typography variant="delta" as="h2">
|
|
173
173
|
{formatMessage({
|
|
174
174
|
id: getTrad('Form.title.advancedSettings'),
|
|
175
175
|
defaultMessage: 'Settings',
|
|
176
176
|
})}
|
|
177
|
-
</
|
|
177
|
+
</Typography>
|
|
178
178
|
<Grid gap={6}>
|
|
179
179
|
<GridItem col={6} s={12}>
|
|
180
180
|
<Select
|
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system/Table';
|
|
5
5
|
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
|
6
|
-
import {
|
|
6
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
7
7
|
import { IconButton } from '@strapi/design-system/IconButton';
|
|
8
8
|
import Pencil from '@strapi/icons/Pencil';
|
|
9
9
|
import Reload from '@strapi/icons/Refresh';
|
|
@@ -27,12 +27,12 @@ const EmailTable = ({ canUpdate, onEditClick }) => {
|
|
|
27
27
|
</VisuallyHidden>
|
|
28
28
|
</Th>
|
|
29
29
|
<Th>
|
|
30
|
-
<
|
|
30
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
31
31
|
{formatMessage({
|
|
32
32
|
id: getTrad('Email.template.table.name.label'),
|
|
33
33
|
defaultMessage: 'name',
|
|
34
34
|
})}
|
|
35
|
-
</
|
|
35
|
+
</Typography>
|
|
36
36
|
</Th>
|
|
37
37
|
<Th width="1%">
|
|
38
38
|
<VisuallyHidden>
|
|
@@ -55,12 +55,12 @@ const EmailTable = ({ canUpdate, onEditClick }) => {
|
|
|
55
55
|
/>
|
|
56
56
|
</Td>
|
|
57
57
|
<Td>
|
|
58
|
-
<
|
|
58
|
+
<Typography>
|
|
59
59
|
{formatMessage({
|
|
60
60
|
id: getTrad('Email.template.reset_password'),
|
|
61
61
|
defaultMessage: 'Reset password',
|
|
62
62
|
})}
|
|
63
|
-
</
|
|
63
|
+
</Typography>
|
|
64
64
|
</Td>
|
|
65
65
|
<Td {...stopPropagation}>
|
|
66
66
|
<IconButton
|
|
@@ -84,12 +84,12 @@ const EmailTable = ({ canUpdate, onEditClick }) => {
|
|
|
84
84
|
/>
|
|
85
85
|
</Td>
|
|
86
86
|
<Td>
|
|
87
|
-
<
|
|
87
|
+
<Typography>
|
|
88
88
|
{formatMessage({
|
|
89
89
|
id: getTrad('Email.template.email_confirmation'),
|
|
90
90
|
defaultMessage: 'Email address confirmation',
|
|
91
91
|
})}
|
|
92
|
-
</
|
|
92
|
+
</Typography>
|
|
93
93
|
</Td>
|
|
94
94
|
<Td {...stopPropagation}>
|
|
95
95
|
<IconButton
|
|
@@ -19,7 +19,7 @@ import { HeaderLayout, Layout, ContentLayout } from '@strapi/design-system/Layou
|
|
|
19
19
|
import { Main } from '@strapi/design-system/Main';
|
|
20
20
|
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
|
21
21
|
import { Table, Thead, Tr, Th, Tbody, Td } from '@strapi/design-system/Table';
|
|
22
|
-
import {
|
|
22
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
23
23
|
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
|
24
24
|
import { IconButton } from '@strapi/design-system/IconButton';
|
|
25
25
|
import Pencil from '@strapi/icons/Pencil';
|
|
@@ -167,31 +167,31 @@ export const ProvidersPage = () => {
|
|
|
167
167
|
<Thead>
|
|
168
168
|
<Tr>
|
|
169
169
|
<Th>
|
|
170
|
-
<
|
|
170
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
171
171
|
<VisuallyHidden>
|
|
172
172
|
{formatMessage({ id: getTrad('Providers.image'), defaultMessage: 'Image' })}
|
|
173
173
|
</VisuallyHidden>
|
|
174
|
-
</
|
|
174
|
+
</Typography>
|
|
175
175
|
</Th>
|
|
176
176
|
<Th>
|
|
177
|
-
<
|
|
177
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
178
178
|
{formatMessage({ id: getTrad('Providers.name'), defaultMessage: 'Name' })}
|
|
179
|
-
</
|
|
179
|
+
</Typography>
|
|
180
180
|
</Th>
|
|
181
181
|
<Th>
|
|
182
|
-
<
|
|
182
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
183
183
|
{formatMessage({ id: getTrad('Providers.status'), defaultMessage: 'Status' })}
|
|
184
|
-
</
|
|
184
|
+
</Typography>
|
|
185
185
|
</Th>
|
|
186
186
|
<Th>
|
|
187
|
-
<
|
|
187
|
+
<Typography variant="sigma">
|
|
188
188
|
<VisuallyHidden>
|
|
189
189
|
{formatMessage({
|
|
190
190
|
id: getTrad('Providers.settings'),
|
|
191
191
|
defaultMessage: 'Settings',
|
|
192
192
|
})}
|
|
193
193
|
</VisuallyHidden>
|
|
194
|
-
</
|
|
194
|
+
</Typography>
|
|
195
195
|
</Th>
|
|
196
196
|
</Tr>
|
|
197
197
|
</Thead>
|
|
@@ -208,12 +208,12 @@ export const ProvidersPage = () => {
|
|
|
208
208
|
<FontAwesomeIcon icon={provider.icon} />
|
|
209
209
|
</Td>
|
|
210
210
|
<Td width="45%">
|
|
211
|
-
<
|
|
211
|
+
<Typography fontWeight="semiBold" textColor="neutral800">
|
|
212
212
|
{provider.name}
|
|
213
|
-
</
|
|
213
|
+
</Typography>
|
|
214
214
|
</Td>
|
|
215
215
|
<Td width="65%">
|
|
216
|
-
<
|
|
216
|
+
<Typography
|
|
217
217
|
textColor={provider.enabled ? 'success600' : 'danger600'}
|
|
218
218
|
data-testid={`enable-${provider.name}`}
|
|
219
219
|
>
|
|
@@ -226,7 +226,7 @@ export const ProvidersPage = () => {
|
|
|
226
226
|
id: getTrad('Providers.disabled'),
|
|
227
227
|
defaultMessage: 'Disabled',
|
|
228
228
|
})}
|
|
229
|
-
</
|
|
229
|
+
</Typography>
|
|
230
230
|
</Td>
|
|
231
231
|
<Td {...stopPropagation}>
|
|
232
232
|
{canUpdate && (
|
|
@@ -7,7 +7,7 @@ import { Stack } from '@strapi/design-system/Stack';
|
|
|
7
7
|
import { Box } from '@strapi/design-system/Box';
|
|
8
8
|
import { TextInput } from '@strapi/design-system/TextInput';
|
|
9
9
|
import { Textarea } from '@strapi/design-system/Textarea';
|
|
10
|
-
import {
|
|
10
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
11
11
|
import Check from '@strapi/icons/Check';
|
|
12
12
|
import { GridItem, Grid } from '@strapi/design-system/Grid';
|
|
13
13
|
import { Formik } from 'formik';
|
|
@@ -113,12 +113,12 @@ const EditPage = () => {
|
|
|
113
113
|
paddingRight={7}
|
|
114
114
|
>
|
|
115
115
|
<Stack size={4}>
|
|
116
|
-
<
|
|
116
|
+
<Typography variant="delta" as="h2">
|
|
117
117
|
{formatMessage({
|
|
118
118
|
id: getTrad('EditPage.form.roles'),
|
|
119
119
|
defaultMessage: 'Role details',
|
|
120
120
|
})}
|
|
121
|
-
</
|
|
121
|
+
</Typography>
|
|
122
122
|
<Grid gap={4}>
|
|
123
123
|
<GridItem col={6}>
|
|
124
124
|
<TextInput
|
|
@@ -6,7 +6,7 @@ import { Stack } from '@strapi/design-system/Stack';
|
|
|
6
6
|
import { Box } from '@strapi/design-system/Box';
|
|
7
7
|
import { TextInput } from '@strapi/design-system/TextInput';
|
|
8
8
|
import { Textarea } from '@strapi/design-system/Textarea';
|
|
9
|
-
import {
|
|
9
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
10
10
|
import ArrowLeft from '@strapi/icons/ArrowLeft';
|
|
11
11
|
import Check from '@strapi/icons/Check';
|
|
12
12
|
import { Link } from '@strapi/design-system/Link';
|
|
@@ -126,12 +126,12 @@ const EditPage = () => {
|
|
|
126
126
|
paddingRight={7}
|
|
127
127
|
>
|
|
128
128
|
<Stack size={4}>
|
|
129
|
-
<
|
|
129
|
+
<Typography variant="delta" as="h2">
|
|
130
130
|
{formatMessage({
|
|
131
131
|
id: getTrad('EditPage.form.roles'),
|
|
132
132
|
defaultMessage: 'Role details',
|
|
133
133
|
})}
|
|
134
|
-
</
|
|
134
|
+
</Typography>
|
|
135
135
|
<Grid gap={4}>
|
|
136
136
|
<GridItem col={6}>
|
|
137
137
|
<TextInput
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { IconButton } from '@strapi/design-system/IconButton';
|
|
4
|
-
import {
|
|
4
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
5
5
|
import { Flex } from '@strapi/design-system/Flex';
|
|
6
6
|
import { Tbody, Tr, Td } from '@strapi/design-system/Table';
|
|
7
7
|
import Pencil from '@strapi/icons/Pencil';
|
|
@@ -34,18 +34,18 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
|
|
|
34
34
|
{sortedRoles?.map(role => (
|
|
35
35
|
<Tr key={role.name} {...onRowClick({ fn: () => handleClickEdit(role.id) })}>
|
|
36
36
|
<Td width="20%">
|
|
37
|
-
<
|
|
37
|
+
<Typography>{role.name}</Typography>
|
|
38
38
|
</Td>
|
|
39
39
|
<Td width="50%">
|
|
40
|
-
<
|
|
40
|
+
<Typography>{role.description}</Typography>
|
|
41
41
|
</Td>
|
|
42
42
|
<Td width="30%">
|
|
43
|
-
<
|
|
43
|
+
<Typography>
|
|
44
44
|
{`${role.nb_users} ${formatMessage({
|
|
45
45
|
id: getTrad('Roles.users'),
|
|
46
46
|
defaultMessage: 'users',
|
|
47
47
|
}).toLowerCase()}`}
|
|
48
|
-
</
|
|
48
|
+
</Typography>
|
|
49
49
|
</Td>
|
|
50
50
|
<Td>
|
|
51
51
|
<Flex justifyContent="end" {...stopPropagation}>
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Thead,
|
|
10
|
-
Th,
|
|
11
|
-
TableLabel,
|
|
12
|
-
useNotifyAT,
|
|
13
|
-
ContentLayout,
|
|
14
|
-
ActionLayout,
|
|
15
|
-
VisuallyHidden,
|
|
16
|
-
} from '@strapi/design-system';
|
|
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';
|
|
17
9
|
import Plus from '@strapi/icons/Plus';
|
|
18
10
|
import {
|
|
19
11
|
useTracking,
|
|
@@ -166,25 +158,25 @@ const RoleListPage = () => {
|
|
|
166
158
|
<Thead>
|
|
167
159
|
<Tr>
|
|
168
160
|
<Th>
|
|
169
|
-
<
|
|
161
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
170
162
|
{formatMessage({ id: getTrad('Roles.name'), defaultMessage: 'Name' })}
|
|
171
|
-
</
|
|
163
|
+
</Typography>
|
|
172
164
|
</Th>
|
|
173
165
|
<Th>
|
|
174
|
-
<
|
|
166
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
175
167
|
{formatMessage({
|
|
176
168
|
id: getTrad('Roles.description'),
|
|
177
169
|
defaultMessage: 'Description',
|
|
178
170
|
})}
|
|
179
|
-
</
|
|
171
|
+
</Typography>
|
|
180
172
|
</Th>
|
|
181
173
|
<Th>
|
|
182
|
-
<
|
|
174
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
183
175
|
{formatMessage({
|
|
184
176
|
id: getTrad('Roles.users'),
|
|
185
177
|
defaultMessage: 'Users',
|
|
186
178
|
})}
|
|
187
|
-
</
|
|
179
|
+
</Typography>
|
|
188
180
|
</Th>
|
|
189
181
|
<Th>
|
|
190
182
|
<VisuallyHidden>
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/plugin-users-permissions",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.15",
|
|
4
4
|
"description": "Protect your API with a full-authentication process based on JWT",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"displayName": "Roles & Permissions",
|
|
7
7
|
"name": "users-permissions",
|
|
8
|
-
"description": "
|
|
8
|
+
"description": "Protect your API with a full authentication process based on JWT. This plugin comes also with an ACL strategy that allows you to manage the permissions between the groups of users.",
|
|
9
9
|
"required": true,
|
|
10
10
|
"kind": "plugin"
|
|
11
11
|
},
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@purest/providers": "^1.0.2",
|
|
17
|
-
"@strapi/helper-plugin": "4.0.0-beta.
|
|
18
|
-
"@strapi/utils": "4.0.0-beta.
|
|
17
|
+
"@strapi/helper-plugin": "4.0.0-beta.15",
|
|
18
|
+
"@strapi/utils": "4.0.0-beta.15",
|
|
19
19
|
"bcryptjs": "2.4.3",
|
|
20
20
|
"grant-koa": "5.4.8",
|
|
21
21
|
"jsonwebtoken": "^8.1.0",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"react-redux": "7.2.3",
|
|
29
29
|
"react-router": "^5.2.0",
|
|
30
30
|
"react-router-dom": "5.2.0",
|
|
31
|
-
"reactstrap": "8.4.1",
|
|
32
31
|
"redux-saga": "^0.16.0",
|
|
33
32
|
"request": "^2.83.0",
|
|
34
33
|
"uuid": "^3.1.0"
|
|
@@ -57,5 +56,5 @@
|
|
|
57
56
|
"npm": ">=6.0.0"
|
|
58
57
|
},
|
|
59
58
|
"license": "SEE LICENSE IN LICENSE",
|
|
60
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "c69713bf7f437a7cee66ffdeed95227d6246a872"
|
|
61
60
|
}
|
package/server/config.js
CHANGED
|
@@ -13,8 +13,8 @@ module.exports = {
|
|
|
13
13
|
layout: {
|
|
14
14
|
user: {
|
|
15
15
|
actions: {
|
|
16
|
-
create: '
|
|
17
|
-
update: '
|
|
16
|
+
create: 'contentManagerUser.create', // Use the User plugin's controller.
|
|
17
|
+
update: 'contentManagerUser.update',
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
},
|
|
@@ -8,8 +8,7 @@ const {
|
|
|
8
8
|
NotFoundError,
|
|
9
9
|
ForbiddenError,
|
|
10
10
|
} = require('@strapi/utils').errors;
|
|
11
|
-
const {
|
|
12
|
-
const { validateCreateUserBody, validateUpdateUserBody } = require('../validation/user');
|
|
11
|
+
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
13
12
|
|
|
14
13
|
const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
|
|
15
14
|
|
|
@@ -22,7 +21,10 @@ const ACTIONS = {
|
|
|
22
21
|
};
|
|
23
22
|
|
|
24
23
|
const findEntityAndCheckPermissions = async (ability, action, model, id) => {
|
|
25
|
-
const entity = await strapi.query(
|
|
24
|
+
const entity = await strapi.query(userModel).findOne({
|
|
25
|
+
where: { id },
|
|
26
|
+
populate: [`${CREATED_BY_ATTRIBUTE}.roles`],
|
|
27
|
+
});
|
|
26
28
|
|
|
27
29
|
if (_.isNil(entity)) {
|
|
28
30
|
throw new NotFoundError();
|
|
@@ -30,21 +32,13 @@ const findEntityAndCheckPermissions = async (ability, action, model, id) => {
|
|
|
30
32
|
|
|
31
33
|
const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
? await strapi.query('admin::role').findMany({
|
|
35
|
-
where: {
|
|
36
|
-
users: { id: entity[CREATED_BY_ATTRIBUTE].id },
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
: [];
|
|
40
|
-
|
|
41
|
-
const entityWithRoles = _.set(_.cloneDeep(entity), `${CREATED_BY_ATTRIBUTE}.roles`, roles);
|
|
42
|
-
|
|
43
|
-
if (pm.ability.cannot(pm.action, pm.toSubject(entityWithRoles))) {
|
|
35
|
+
if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
|
|
44
36
|
throw new ForbiddenError();
|
|
45
37
|
}
|
|
46
38
|
|
|
47
|
-
|
|
39
|
+
const entityWithoutCreatorRoles = _.omit(entity, `${CREATED_BY_ATTRIBUTE}.roles`);
|
|
40
|
+
|
|
41
|
+
return { pm, entity: entityWithoutCreatorRoles };
|
|
48
42
|
};
|
|
49
43
|
|
|
50
44
|
module.exports = {
|
|
@@ -112,7 +106,9 @@ module.exports = {
|
|
|
112
106
|
}
|
|
113
107
|
|
|
114
108
|
try {
|
|
115
|
-
const data = await
|
|
109
|
+
const data = await strapi
|
|
110
|
+
.service('plugin::content-manager.entity-manager')
|
|
111
|
+
.create(user, userModel);
|
|
116
112
|
const sanitizedData = await pm.sanitizeOutput(data, { action: ACTIONS.read });
|
|
117
113
|
|
|
118
114
|
ctx.created(sanitizedData);
|
|
@@ -178,7 +174,9 @@ module.exports = {
|
|
|
178
174
|
const sanitizedData = await pm.pickPermittedFieldsOf(body, { subject: pm.toSubject(user) });
|
|
179
175
|
const updateData = _.omit({ ...sanitizedData, updatedBy: admin.id }, 'createdBy');
|
|
180
176
|
|
|
181
|
-
const data = await
|
|
177
|
+
const data = await strapi
|
|
178
|
+
.service('plugin::content-manager.entity-manager')
|
|
179
|
+
.update({ id }, updateData, userModel);
|
|
182
180
|
|
|
183
181
|
ctx.body = await pm.sanitizeOutput(data, { action: ACTIONS.read });
|
|
184
182
|
},
|
|
@@ -5,6 +5,7 @@ const user = require('./user');
|
|
|
5
5
|
const role = require('./role');
|
|
6
6
|
const permissions = require('./permissions');
|
|
7
7
|
const settings = require('./settings');
|
|
8
|
+
const contentmanageruser = require('./content-manager-user');
|
|
8
9
|
|
|
9
10
|
module.exports = {
|
|
10
11
|
auth,
|
|
@@ -12,4 +13,5 @@ module.exports = {
|
|
|
12
13
|
role,
|
|
13
14
|
permissions,
|
|
14
15
|
settings,
|
|
16
|
+
contentmanageruser,
|
|
15
17
|
};
|
|
@@ -7,40 +7,127 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const _ = require('lodash');
|
|
10
|
-
const
|
|
10
|
+
const utils = require('@strapi/utils');
|
|
11
11
|
const { getService } = require('../utils');
|
|
12
|
-
const
|
|
13
|
-
const apiUserController = require('./user/api');
|
|
12
|
+
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
14
13
|
|
|
15
|
-
const
|
|
14
|
+
const { sanitize } = utils;
|
|
15
|
+
const { ApplicationError, ValidationError } = utils.errors;
|
|
16
|
+
|
|
17
|
+
const sanitizeOutput = (user, ctx) => {
|
|
18
|
+
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
16
19
|
const { auth } = ctx.state;
|
|
17
|
-
const userSchema = strapi.getModel('plugin::users-permissions.user');
|
|
18
20
|
|
|
19
|
-
return sanitize.contentAPI.output(user,
|
|
21
|
+
return sanitize.contentAPI.output(user, schema, { auth });
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
module.exports = {
|
|
25
|
+
/**
|
|
26
|
+
* Create a/an user record.
|
|
27
|
+
* @return {Object}
|
|
28
|
+
*/
|
|
29
|
+
async create(ctx) {
|
|
30
|
+
const advanced = await strapi
|
|
31
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
32
|
+
.get();
|
|
33
|
+
|
|
34
|
+
await validateCreateUserBody(ctx.request.body);
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
};
|
|
36
|
+
const { email, username, role } = ctx.request.body;
|
|
29
37
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const userWithSameUsername = await strapi
|
|
39
|
+
.query('plugin::users-permissions.user')
|
|
40
|
+
.findOne({ where: { username } });
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
if (userWithSameUsername) {
|
|
43
|
+
if (!email) throw new ApplicationError('Username already taken');
|
|
44
|
+
}
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
if (advanced.unique_email) {
|
|
47
|
+
const userWithSameEmail = await strapi
|
|
48
|
+
.query('plugin::users-permissions.user')
|
|
49
|
+
.findOne({ where: { email: email.toLowerCase() } });
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
if (userWithSameEmail) {
|
|
52
|
+
throw new ApplicationError('Email already taken');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const user = {
|
|
57
|
+
...ctx.request.body,
|
|
58
|
+
provider: 'local',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
user.email = _.toLower(user.email);
|
|
62
|
+
|
|
63
|
+
if (!role) {
|
|
64
|
+
const defaultRole = await strapi
|
|
65
|
+
.query('plugin::users-permissions.role')
|
|
66
|
+
.findOne({ where: { type: advanced.default_role } });
|
|
67
|
+
|
|
68
|
+
user.role = defaultRole.id;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const data = await getService('user').add(user);
|
|
73
|
+
const sanitizedData = await sanitizeOutput(data, ctx);
|
|
74
|
+
|
|
75
|
+
ctx.created(sanitizedData);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw new ApplicationError(error.message);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Update a/an user record.
|
|
83
|
+
* @return {Object}
|
|
84
|
+
*/
|
|
85
|
+
async update(ctx) {
|
|
86
|
+
const advancedConfigs = await strapi
|
|
87
|
+
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
88
|
+
.get();
|
|
89
|
+
|
|
90
|
+
const { id } = ctx.params;
|
|
91
|
+
const { email, username, password } = ctx.request.body;
|
|
92
|
+
|
|
93
|
+
const user = await getService('user').fetch({ id });
|
|
94
|
+
|
|
95
|
+
await validateUpdateUserBody(ctx.request.body);
|
|
96
|
+
|
|
97
|
+
if (user.provider === 'local' && _.has(ctx.request.body, 'password') && !password) {
|
|
98
|
+
throw new ValidationError('password.notNull');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (_.has(ctx.request.body, 'username')) {
|
|
102
|
+
const userWithSameUsername = await strapi
|
|
103
|
+
.query('plugin::users-permissions.user')
|
|
104
|
+
.findOne({ where: { username } });
|
|
105
|
+
|
|
106
|
+
if (userWithSameUsername && userWithSameUsername.id != id) {
|
|
107
|
+
throw new ApplicationError('Username already taken');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
|
|
112
|
+
const userWithSameEmail = await strapi
|
|
113
|
+
.query('plugin::users-permissions.user')
|
|
114
|
+
.findOne({ where: { email: email.toLowerCase() } });
|
|
115
|
+
|
|
116
|
+
if (userWithSameEmail && userWithSameEmail.id != id) {
|
|
117
|
+
throw new ApplicationError('Email already taken');
|
|
118
|
+
}
|
|
119
|
+
ctx.request.body.email = ctx.request.body.email.toLowerCase();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let updateData = {
|
|
123
|
+
...ctx.request.body,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const data = await getService('user').edit({ id }, updateData);
|
|
127
|
+
const sanitizedData = await sanitizeOutput(data, ctx);
|
|
128
|
+
|
|
129
|
+
ctx.send(sanitizedData);
|
|
130
|
+
},
|
|
44
131
|
|
|
45
132
|
/**
|
|
46
133
|
* Retrieve user records.
|
|
@@ -49,7 +136,7 @@ module.exports = {
|
|
|
49
136
|
async find(ctx, next, { populate } = {}) {
|
|
50
137
|
const users = await getService('user').fetchAll(ctx.query, populate);
|
|
51
138
|
|
|
52
|
-
ctx.body = await Promise.all(users.map(user =>
|
|
139
|
+
ctx.body = await Promise.all(users.map(user => sanitizeOutput(user, ctx)));
|
|
53
140
|
},
|
|
54
141
|
|
|
55
142
|
/**
|
|
@@ -61,7 +148,7 @@ module.exports = {
|
|
|
61
148
|
let data = await getService('user').fetch({ id });
|
|
62
149
|
|
|
63
150
|
if (data) {
|
|
64
|
-
data = await
|
|
151
|
+
data = await sanitizeOutput(data, ctx);
|
|
65
152
|
}
|
|
66
153
|
|
|
67
154
|
ctx.body = data;
|
|
@@ -83,7 +170,7 @@ module.exports = {
|
|
|
83
170
|
const { id } = ctx.params;
|
|
84
171
|
|
|
85
172
|
const data = await getService('user').remove({ id });
|
|
86
|
-
const sanitizedUser = await
|
|
173
|
+
const sanitizedUser = await sanitizeOutput(data, ctx);
|
|
87
174
|
|
|
88
175
|
ctx.send(sanitizedUser);
|
|
89
176
|
},
|
|
@@ -99,6 +186,6 @@ module.exports = {
|
|
|
99
186
|
return ctx.unauthorized();
|
|
100
187
|
}
|
|
101
188
|
|
|
102
|
-
ctx.body = await
|
|
189
|
+
ctx.body = await sanitizeOutput(user, ctx);
|
|
103
190
|
},
|
|
104
191
|
};
|
|
@@ -6,24 +6,21 @@ const deleteRoleSchema = yup.object().shape({
|
|
|
6
6
|
role: yup.strapiID().required(),
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
const createUserBodySchema = yup
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
email
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
role: yup.strapiID(),
|
|
25
|
-
})
|
|
26
|
-
.noUnknown();
|
|
9
|
+
const createUserBodySchema = yup.object().shape({
|
|
10
|
+
email: yup
|
|
11
|
+
.string()
|
|
12
|
+
.email()
|
|
13
|
+
.required(),
|
|
14
|
+
username: yup
|
|
15
|
+
.string()
|
|
16
|
+
.min(1)
|
|
17
|
+
.required(),
|
|
18
|
+
password: yup
|
|
19
|
+
.string()
|
|
20
|
+
.min(1)
|
|
21
|
+
.required(),
|
|
22
|
+
role: yup.strapiID(),
|
|
23
|
+
});
|
|
27
24
|
|
|
28
25
|
const updateUserBodySchema = yup.object().shape({
|
|
29
26
|
email: yup
|
package/server/services/jwt.js
CHANGED
|
@@ -8,32 +8,23 @@
|
|
|
8
8
|
|
|
9
9
|
const _ = require('lodash');
|
|
10
10
|
const jwt = require('jsonwebtoken');
|
|
11
|
-
const { ValidationError } = require('@strapi/utils').errors;
|
|
12
11
|
|
|
13
12
|
module.exports = ({ strapi }) => ({
|
|
14
13
|
getToken(ctx) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let token = '';
|
|
14
|
+
let token;
|
|
18
15
|
|
|
19
16
|
if (ctx.request && ctx.request.header && ctx.request.header.authorization) {
|
|
20
|
-
const parts = ctx.request.header.authorization.split(
|
|
17
|
+
const parts = ctx.request.header.authorization.split(/\s+/);
|
|
21
18
|
|
|
22
|
-
if (parts.length
|
|
23
|
-
|
|
24
|
-
const credentials = parts[1];
|
|
25
|
-
if (/^Bearer$/i.test(scheme)) {
|
|
26
|
-
token = credentials;
|
|
27
|
-
}
|
|
28
|
-
} else {
|
|
29
|
-
throw new ValidationError(
|
|
30
|
-
'Invalid authorization header format. Format is Authorization: Bearer [token]'
|
|
31
|
-
);
|
|
19
|
+
if (parts[0].toLowerCase() !== 'bearer' || parts.length !== 2) {
|
|
20
|
+
return null;
|
|
32
21
|
}
|
|
33
|
-
|
|
34
|
-
token =
|
|
22
|
+
|
|
23
|
+
token = parts[1];
|
|
24
|
+
} else if (ctx.query.access_token) {
|
|
25
|
+
token = ctx.query.access_token;
|
|
35
26
|
} else {
|
|
36
|
-
|
|
27
|
+
return null;
|
|
37
28
|
}
|
|
38
29
|
|
|
39
30
|
return this.verify(token);
|
package/server/services/user.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
const crypto = require('crypto');
|
|
10
10
|
const bcrypt = require('bcryptjs');
|
|
11
11
|
|
|
12
|
-
const { getAbsoluteServerUrl } = require('@strapi/utils');
|
|
12
|
+
const { getAbsoluteServerUrl, sanitize } = require('@strapi/utils');
|
|
13
13
|
const { getService } = require('../utils');
|
|
14
14
|
|
|
15
15
|
module.exports = ({ strapi }) => ({
|
|
@@ -121,22 +121,28 @@ module.exports = ({ strapi }) => ({
|
|
|
121
121
|
async sendConfirmationEmail(user) {
|
|
122
122
|
const userPermissionService = getService('users-permissions');
|
|
123
123
|
const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
124
|
+
const userSchema = strapi.getModel('plugin::users-permissions.user');
|
|
124
125
|
|
|
125
126
|
const settings = await pluginStore
|
|
126
127
|
.get({ key: 'email' })
|
|
127
128
|
.then(storeEmail => storeEmail['email_confirmation'].options);
|
|
128
129
|
|
|
130
|
+
// Sanitize the template's user information
|
|
131
|
+
const sanitizedUserInfo = await sanitize.sanitizers.defaultSanitizeOutput(userSchema, user);
|
|
132
|
+
|
|
129
133
|
const confirmationToken = crypto.randomBytes(20).toString('hex');
|
|
130
134
|
|
|
131
135
|
await this.edit({ id: user.id }, { confirmationToken });
|
|
132
136
|
|
|
133
137
|
settings.message = await userPermissionService.template(settings.message, {
|
|
134
138
|
URL: `${getAbsoluteServerUrl(strapi.config)}/auth/email-confirmation`,
|
|
135
|
-
USER:
|
|
139
|
+
USER: sanitizedUserInfo,
|
|
136
140
|
CODE: confirmationToken,
|
|
137
141
|
});
|
|
138
142
|
|
|
139
|
-
settings.object = await userPermissionService.template(settings.object, {
|
|
143
|
+
settings.object = await userPermissionService.template(settings.object, {
|
|
144
|
+
USER: sanitizedUserInfo,
|
|
145
|
+
});
|
|
140
146
|
|
|
141
147
|
// Send an email to the user.
|
|
142
148
|
await strapi
|
|
@@ -10,9 +10,11 @@ const getAdvancedSettings = () => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
const authenticate = async ctx => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
try {
|
|
14
|
+
const token = await getService('jwt').getToken(ctx);
|
|
15
|
+
|
|
16
|
+
if (token) {
|
|
17
|
+
const { id } = token;
|
|
16
18
|
|
|
17
19
|
if (id === undefined) {
|
|
18
20
|
return { authenticated: false };
|
|
@@ -41,25 +43,25 @@ const authenticate = async ctx => {
|
|
|
41
43
|
authenticated: true,
|
|
42
44
|
credentials: user,
|
|
43
45
|
};
|
|
44
|
-
} catch (err) {
|
|
45
|
-
return { authenticated: false };
|
|
46
46
|
}
|
|
47
|
-
}
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
const publicPermissions = await strapi.query('plugin::users-permissions.permission').findMany({
|
|
49
|
+
where: {
|
|
50
|
+
role: { type: 'public' },
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (publicPermissions.length === 0) {
|
|
55
|
+
return { authenticated: false };
|
|
56
|
+
}
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
return {
|
|
59
|
+
authenticated: true,
|
|
60
|
+
credentials: null,
|
|
61
|
+
};
|
|
62
|
+
} catch (err) {
|
|
56
63
|
return { authenticated: false };
|
|
57
64
|
}
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
authenticated: true,
|
|
61
|
-
credentials: null,
|
|
62
|
-
};
|
|
63
65
|
};
|
|
64
66
|
|
|
65
67
|
const verify = async (auth, config) => {
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const utils = require('@strapi/utils');
|
|
5
|
-
const { getService } = require('../../utils');
|
|
6
|
-
const { validateCreateUserBody, validateUpdateUserBody } = require('../validation/user');
|
|
7
|
-
|
|
8
|
-
const { sanitize } = utils;
|
|
9
|
-
const { ApplicationError, ValidationError } = utils.errors;
|
|
10
|
-
|
|
11
|
-
const sanitizeOutput = (user, ctx) => {
|
|
12
|
-
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
13
|
-
const { auth } = ctx.state;
|
|
14
|
-
|
|
15
|
-
return sanitize.contentAPI.output(user, schema, { auth });
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
module.exports = {
|
|
19
|
-
/**
|
|
20
|
-
* Create a/an user record.
|
|
21
|
-
* @return {Object}
|
|
22
|
-
*/
|
|
23
|
-
async create(ctx) {
|
|
24
|
-
const advanced = await strapi
|
|
25
|
-
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
26
|
-
.get();
|
|
27
|
-
|
|
28
|
-
await validateCreateUserBody(ctx.request.body);
|
|
29
|
-
|
|
30
|
-
const { email, username, role } = ctx.request.body;
|
|
31
|
-
|
|
32
|
-
const userWithSameUsername = await strapi
|
|
33
|
-
.query('plugin::users-permissions.user')
|
|
34
|
-
.findOne({ where: { username } });
|
|
35
|
-
|
|
36
|
-
if (userWithSameUsername) {
|
|
37
|
-
if (!email) throw new ApplicationError('Username already taken');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (advanced.unique_email) {
|
|
41
|
-
const userWithSameEmail = await strapi
|
|
42
|
-
.query('plugin::users-permissions.user')
|
|
43
|
-
.findOne({ where: { email: email.toLowerCase() } });
|
|
44
|
-
|
|
45
|
-
if (userWithSameEmail) {
|
|
46
|
-
throw new ApplicationError('Email already taken');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const user = {
|
|
51
|
-
...ctx.request.body,
|
|
52
|
-
provider: 'local',
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
user.email = _.toLower(user.email);
|
|
56
|
-
|
|
57
|
-
if (!role) {
|
|
58
|
-
const defaultRole = await strapi
|
|
59
|
-
.query('plugin::users-permissions.role')
|
|
60
|
-
.findOne({ where: { type: advanced.default_role } });
|
|
61
|
-
|
|
62
|
-
user.role = defaultRole.id;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const data = await getService('user').add(user);
|
|
67
|
-
const sanitizedData = await sanitizeOutput(data, ctx);
|
|
68
|
-
|
|
69
|
-
ctx.created(sanitizedData);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
throw new ApplicationError(error.message);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Update a/an user record.
|
|
77
|
-
* @return {Object}
|
|
78
|
-
*/
|
|
79
|
-
async update(ctx) {
|
|
80
|
-
const advancedConfigs = await strapi
|
|
81
|
-
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
|
|
82
|
-
.get();
|
|
83
|
-
|
|
84
|
-
const { id } = ctx.params;
|
|
85
|
-
const { email, username, password } = ctx.request.body;
|
|
86
|
-
|
|
87
|
-
const user = await getService('user').fetch({ id });
|
|
88
|
-
|
|
89
|
-
await validateUpdateUserBody(ctx.request.body);
|
|
90
|
-
|
|
91
|
-
if (user.provider === 'local' && _.has(ctx.request.body, 'password') && !password) {
|
|
92
|
-
throw new ValidationError('password.notNull');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (_.has(ctx.request.body, 'username')) {
|
|
96
|
-
const userWithSameUsername = await strapi
|
|
97
|
-
.query('plugin::users-permissions.user')
|
|
98
|
-
.findOne({ where: { username } });
|
|
99
|
-
|
|
100
|
-
if (userWithSameUsername && userWithSameUsername.id != id) {
|
|
101
|
-
throw new ApplicationError('Username already taken');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
|
|
106
|
-
const userWithSameEmail = await strapi
|
|
107
|
-
.query('plugin::users-permissions.user')
|
|
108
|
-
.findOne({ where: { email: email.toLowerCase() } });
|
|
109
|
-
|
|
110
|
-
if (userWithSameEmail && userWithSameEmail.id != id) {
|
|
111
|
-
throw new ApplicationError('Email already taken');
|
|
112
|
-
}
|
|
113
|
-
ctx.request.body.email = ctx.request.body.email.toLowerCase();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
let updateData = {
|
|
117
|
-
...ctx.request.body,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const data = await getService('user').edit({ id }, updateData);
|
|
121
|
-
const sanitizedData = await sanitizeOutput(data, ctx);
|
|
122
|
-
|
|
123
|
-
ctx.send(sanitizedData);
|
|
124
|
-
},
|
|
125
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { yup, validateYupSchema } = require('@strapi/utils');
|
|
4
|
-
|
|
5
|
-
const createUserBodySchema = yup
|
|
6
|
-
.object()
|
|
7
|
-
.shape({
|
|
8
|
-
email: yup
|
|
9
|
-
.string()
|
|
10
|
-
.email()
|
|
11
|
-
.required(),
|
|
12
|
-
username: yup
|
|
13
|
-
.string()
|
|
14
|
-
.min(1)
|
|
15
|
-
.required(),
|
|
16
|
-
password: yup
|
|
17
|
-
.string()
|
|
18
|
-
.min(1)
|
|
19
|
-
.required(),
|
|
20
|
-
role: yup.strapiID(),
|
|
21
|
-
})
|
|
22
|
-
.noUnknown();
|
|
23
|
-
|
|
24
|
-
const updateUserBodySchema = yup.object().shape({
|
|
25
|
-
email: yup
|
|
26
|
-
.string()
|
|
27
|
-
.email()
|
|
28
|
-
.min(1),
|
|
29
|
-
username: yup.string().min(1),
|
|
30
|
-
password: yup.string().min(1),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
module.exports = {
|
|
34
|
-
validateCreateUserBody: validateYupSchema(createUserBodySchema),
|
|
35
|
-
validateUpdateUserBody: validateYupSchema(updateUserBodySchema),
|
|
36
|
-
};
|