@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.
@@ -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 { H3, Text } from '@strapi/design-system/Text';
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
- <H3>
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
  &nbsp;
27
33
  <span>{controller}</span>
28
- <Text style={{ fontSize: 'inherit', fontWeight: 'inherit' }} textColor="primary600">
34
+ <Typography variant="delta" textColor="primary600">
29
35
  .{action}
30
- </Text>
31
- </H3>
32
- <Box hasRadius background="neutral0" borderColor="neutral200">
33
- <Box
34
- color={colors.text}
35
- background={colors.background}
36
- borderColor={colors.border}
37
- padding={2}
38
- hasRadius
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
- <Text key={value} textColor={value.includes(':') ? 'neutral600' : 'neutral900'}>
46
+ <Typography key={value} textColor={value.includes(':') ? 'neutral600' : 'neutral900'}>
51
47
  /{value}
52
- </Text>
48
+ </Typography>
53
49
  ))}
54
50
  </Box>
55
- </Box>
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 { TableLabel } from '@strapi/design-system/Text';
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
- <TableLabel textColor="neutral600">{subCategory.label}</TableLabel>
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 { H3, Text } from '@strapi/design-system/Text';
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
- <H3>
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
- </H3>
44
- <Text as="p" textColor="neutral600">
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
- </Text>
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 { H3, Text } from '@strapi/design-system/Text';
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
- <H3 as="h2">
70
+ <Typography variant="delta" as="h2">
71
71
  {formatMessage({
72
72
  id: getTrad('Plugins.header.title'),
73
73
  defaultMessage: 'Permissions',
74
74
  })}
75
- </H3>
76
- <Text as="p" textColor="neutral600">
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
- </Text>
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 { H3 } from '@strapi/design-system/Text';
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
- <H3>
172
+ <Typography variant="delta" as="h2">
173
173
  {formatMessage({
174
174
  id: getTrad('Form.title.advancedSettings'),
175
175
  defaultMessage: 'Settings',
176
176
  })}
177
- </H3>
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 { Text, TableLabel } from '@strapi/design-system/Text';
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
- <TableLabel textColor="neutral600">
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
- </TableLabel>
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
- <Text>
58
+ <Typography>
59
59
  {formatMessage({
60
60
  id: getTrad('Email.template.reset_password'),
61
61
  defaultMessage: 'Reset password',
62
62
  })}
63
- </Text>
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
- <Text>
87
+ <Typography>
88
88
  {formatMessage({
89
89
  id: getTrad('Email.template.email_confirmation'),
90
90
  defaultMessage: 'Email address confirmation',
91
91
  })}
92
- </Text>
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 { Text, TableLabel } from '@strapi/design-system/Text';
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
- <TableLabel textColor="neutral600">
170
+ <Typography variant="sigma" textColor="neutral600">
171
171
  <VisuallyHidden>
172
172
  {formatMessage({ id: getTrad('Providers.image'), defaultMessage: 'Image' })}
173
173
  </VisuallyHidden>
174
- </TableLabel>
174
+ </Typography>
175
175
  </Th>
176
176
  <Th>
177
- <TableLabel textColor="neutral600">
177
+ <Typography variant="sigma" textColor="neutral600">
178
178
  {formatMessage({ id: getTrad('Providers.name'), defaultMessage: 'Name' })}
179
- </TableLabel>
179
+ </Typography>
180
180
  </Th>
181
181
  <Th>
182
- <TableLabel textColor="neutral600">
182
+ <Typography variant="sigma" textColor="neutral600">
183
183
  {formatMessage({ id: getTrad('Providers.status'), defaultMessage: 'Status' })}
184
- </TableLabel>
184
+ </Typography>
185
185
  </Th>
186
186
  <Th>
187
- <TableLabel>
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
- </TableLabel>
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
- <Text highlighted textColor="neutral800">
211
+ <Typography fontWeight="semiBold" textColor="neutral800">
212
212
  {provider.name}
213
- </Text>
213
+ </Typography>
214
214
  </Td>
215
215
  <Td width="65%">
216
- <Text
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
- </Text>
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 { H3 } from '@strapi/design-system/Text';
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
- <H3 as="h2">
116
+ <Typography variant="delta" as="h2">
117
117
  {formatMessage({
118
118
  id: getTrad('EditPage.form.roles'),
119
119
  defaultMessage: 'Role details',
120
120
  })}
121
- </H3>
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 { H3 } from '@strapi/design-system/Text';
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
- <H3 as="h2">
129
+ <Typography variant="delta" as="h2">
130
130
  {formatMessage({
131
131
  id: getTrad('EditPage.form.roles'),
132
132
  defaultMessage: 'Role details',
133
133
  })}
134
- </H3>
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 { Text } from '@strapi/design-system/Text';
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
- <Text>{role.name}</Text>
37
+ <Typography>{role.name}</Typography>
38
38
  </Td>
39
39
  <Td width="50%">
40
- <Text>{role.description}</Text>
40
+ <Typography>{role.description}</Typography>
41
41
  </Td>
42
42
  <Td width="30%">
43
- <Text>
43
+ <Typography>
44
44
  {`${role.nb_users} ${formatMessage({
45
45
  id: getTrad('Roles.users'),
46
46
  defaultMessage: 'users',
47
47
  }).toLowerCase()}`}
48
- </Text>
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
- Button,
4
- HeaderLayout,
5
- Layout,
6
- Main,
7
- Table,
8
- Tr,
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
- <TableLabel textColor="neutral600">
161
+ <Typography variant="sigma" textColor="neutral600">
170
162
  {formatMessage({ id: getTrad('Roles.name'), defaultMessage: 'Name' })}
171
- </TableLabel>
163
+ </Typography>
172
164
  </Th>
173
165
  <Th>
174
- <TableLabel textColor="neutral600">
166
+ <Typography variant="sigma" textColor="neutral600">
175
167
  {formatMessage({
176
168
  id: getTrad('Roles.description'),
177
169
  defaultMessage: 'Description',
178
170
  })}
179
- </TableLabel>
171
+ </Typography>
180
172
  </Th>
181
173
  <Th>
182
- <TableLabel textColor="neutral600">
174
+ <Typography variant="sigma" textColor="neutral600">
183
175
  {formatMessage({
184
176
  id: getTrad('Roles.users'),
185
177
  defaultMessage: 'Users',
186
178
  })}
187
- </TableLabel>
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.11",
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": "users-permissions.plugin.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.11",
18
- "@strapi/utils": "4.0.0-beta.11",
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": "5b04fa152dc597fc3aa80afb25796217b60403f3"
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: 'User.create', // Use the User plugin's controller.
17
- update: 'User.update',
16
+ create: 'contentManagerUser.create', // Use the User plugin's controller.
17
+ update: 'contentManagerUser.update',
18
18
  },
19
19
  },
20
20
  },
@@ -13,6 +13,9 @@ module.exports = {
13
13
  'content-manager': {
14
14
  visible: false,
15
15
  },
16
+ 'content-type-builder': {
17
+ visible: false,
18
+ },
16
19
  },
17
20
  attributes: {
18
21
  action: {
@@ -13,6 +13,9 @@ module.exports = {
13
13
  'content-manager': {
14
14
  visible: false,
15
15
  },
16
+ 'content-type-builder': {
17
+ visible: false,
18
+ },
16
19
  },
17
20
  attributes: {
18
21
  name: {
@@ -8,8 +8,7 @@ const {
8
8
  NotFoundError,
9
9
  ForbiddenError,
10
10
  } = require('@strapi/utils').errors;
11
- const { getService } = require('../../utils');
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('plugin::users-permissions.user').findOne({ where: { id } });
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
- const roles = _.has(entity, `${CREATED_BY_ATTRIBUTE}.id`)
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
- return { pm, entity };
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 getService('user').add(user);
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 getService('user').edit({ id }, updateData);
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 { sanitize } = require('@strapi/utils');
10
+ const utils = require('@strapi/utils');
11
11
  const { getService } = require('../utils');
12
- const adminUserController = require('./user/admin');
13
- const apiUserController = require('./user/api');
12
+ const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
14
13
 
15
- const sanitizeUser = (user, ctx) => {
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, userSchema, { auth });
21
+ return sanitize.contentAPI.output(user, schema, { auth });
20
22
  };
21
23
 
22
- const resolveController = ctx => {
23
- const {
24
- state: { isAuthenticatedAdmin },
25
- } = ctx;
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
- return isAuthenticatedAdmin ? adminUserController : apiUserController;
28
- };
36
+ const { email, username, role } = ctx.request.body;
29
37
 
30
- const resolveControllerMethod = method => ctx => {
31
- const controller = resolveController(ctx);
32
- const callbackFn = controller[method];
38
+ const userWithSameUsername = await strapi
39
+ .query('plugin::users-permissions.user')
40
+ .findOne({ where: { username } });
33
41
 
34
- if (!_.isFunction(callbackFn)) {
35
- return ctx.notFound();
36
- }
42
+ if (userWithSameUsername) {
43
+ if (!email) throw new ApplicationError('Username already taken');
44
+ }
37
45
 
38
- return callbackFn(ctx);
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
- module.exports = {
42
- create: resolveControllerMethod('create'),
43
- update: resolveControllerMethod('update'),
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 => sanitizeUser(user, ctx)));
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 sanitizeUser(data, ctx);
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 sanitizeUser(data, ctx);
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 sanitizeUser(user, ctx);
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
- .object()
11
- .shape({
12
- email: yup
13
- .string()
14
- .email()
15
- .required(),
16
- username: yup
17
- .string()
18
- .min(1)
19
- .required(),
20
- password: yup
21
- .string()
22
- .min(1)
23
- .required(),
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
@@ -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
- const params = _.assign({}, ctx.request.body, ctx.request.query);
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 === 2) {
23
- const scheme = parts[0];
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
- } else if (params.token) {
34
- token = params.token;
22
+
23
+ token = parts[1];
24
+ } else if (ctx.query.access_token) {
25
+ token = ctx.query.access_token;
35
26
  } else {
36
- throw new ValidationError('No authorization header was found');
27
+ return null;
37
28
  }
38
29
 
39
30
  return this.verify(token);
@@ -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: user,
139
+ USER: sanitizedUserInfo,
136
140
  CODE: confirmationToken,
137
141
  });
138
142
 
139
- settings.object = await userPermissionService.template(settings.object, { USER: user });
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
- if (ctx.request && ctx.request.header && ctx.request.header.authorization) {
14
- try {
15
- const { id } = await getService('jwt').getToken(ctx);
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
- const publicPermissions = await strapi.query('plugin::users-permissions.permission').findMany({
50
- where: {
51
- role: { type: 'public' },
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
- if (publicPermissions.length === 0) {
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
- };