@strapi/plugin-users-permissions 4.13.0-beta.0 → 4.13.0

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 (35) hide show
  1. package/admin/src/components/Permissions/index.js +2 -4
  2. package/admin/src/index.js +7 -8
  3. package/admin/src/pages/AdvancedSettings/index.js +41 -24
  4. package/admin/src/pages/EmailTemplates/index.js +62 -47
  5. package/admin/src/pages/Providers/index.js +64 -58
  6. package/admin/src/{hooks → pages/Roles/hooks}/usePlugins.js +15 -8
  7. package/admin/src/pages/Roles/index.js +10 -7
  8. package/admin/src/pages/Roles/pages/CreatePage.js +190 -0
  9. package/admin/src/pages/Roles/pages/EditPage.js +211 -0
  10. package/admin/src/pages/Roles/{ListPage → pages/ListPage}/components/TableBody.js +37 -10
  11. package/admin/src/pages/Roles/{ListPage → pages/ListPage}/index.js +19 -15
  12. package/jest.config.front.js +1 -1
  13. package/package.json +4 -4
  14. package/server/bootstrap/index.js +36 -0
  15. package/server/controllers/auth.js +46 -13
  16. package/server/controllers/user.js +12 -1
  17. package/admin/src/hooks/index.js +0 -5
  18. package/admin/src/hooks/useFetchRole/index.js +0 -67
  19. package/admin/src/hooks/useFetchRole/reducer.js +0 -31
  20. package/admin/src/hooks/useForm/index.js +0 -68
  21. package/admin/src/hooks/useForm/reducer.js +0 -40
  22. package/admin/src/hooks/useRolesList/index.js +0 -65
  23. package/admin/src/hooks/useRolesList/init.js +0 -5
  24. package/admin/src/hooks/useRolesList/reducer.js +0 -31
  25. package/admin/src/pages/AdvancedSettings/utils/api.js +0 -16
  26. package/admin/src/pages/EmailTemplates/utils/api.js +0 -16
  27. package/admin/src/pages/Providers/reducer.js +0 -54
  28. package/admin/src/pages/Providers/utils/api.js +0 -24
  29. package/admin/src/pages/Providers/utils/createProvidersArray.js +0 -21
  30. package/admin/src/pages/Roles/CreatePage.js +0 -185
  31. package/admin/src/pages/Roles/EditPage.js +0 -197
  32. package/admin/src/pages/Roles/ProtectedCreatePage.js +0 -15
  33. package/admin/src/pages/Roles/ProtectedEditPage.js +0 -15
  34. package/admin/src/pages/Roles/ProtectedListPage.js +0 -17
  35. /package/admin/src/pages/Roles/{ListPage → pages/ListPage}/utils/api.js +0 -0
@@ -0,0 +1,190 @@
1
+ import * as React from 'react';
2
+
3
+ import {
4
+ Button,
5
+ ContentLayout,
6
+ Flex,
7
+ Grid,
8
+ GridItem,
9
+ HeaderLayout,
10
+ Main,
11
+ Textarea,
12
+ TextInput,
13
+ Typography,
14
+ } from '@strapi/design-system';
15
+ import {
16
+ CheckPagePermissions,
17
+ Form,
18
+ SettingsPageTitle,
19
+ useFetchClient,
20
+ useNotification,
21
+ useOverlayBlocker,
22
+ useTracking,
23
+ } from '@strapi/helper-plugin';
24
+ import { Check } from '@strapi/icons';
25
+ import { Formik } from 'formik';
26
+ import { useIntl } from 'react-intl';
27
+ import { useMutation } from 'react-query';
28
+ import { useHistory } from 'react-router-dom';
29
+
30
+ import UsersPermissions from '../../../components/UsersPermissions';
31
+ import { PERMISSIONS } from '../../../constants';
32
+ import getTrad from '../../../utils/getTrad';
33
+ import { createRoleSchema } from '../constants';
34
+ import { usePlugins } from '../hooks/usePlugins';
35
+
36
+ export const CreatePage = () => {
37
+ const { formatMessage } = useIntl();
38
+ const toggleNotification = useNotification();
39
+ const { goBack } = useHistory();
40
+ const { lockApp, unlockApp } = useOverlayBlocker();
41
+ const { isLoading: isLoadingPlugins, permissions, routes } = usePlugins();
42
+ const { trackUsage } = useTracking();
43
+ const permissionsRef = React.useRef();
44
+ const { post } = useFetchClient();
45
+ const mutation = useMutation((body) => post(`/users-permissions/roles`, body), {
46
+ onError() {
47
+ toggleNotification({
48
+ type: 'warning',
49
+ message: {
50
+ id: 'notification.error',
51
+ defaultMessage: 'An error occurred',
52
+ },
53
+ });
54
+ },
55
+
56
+ onSuccess() {
57
+ trackUsage('didCreateRole');
58
+
59
+ toggleNotification({
60
+ type: 'success',
61
+ message: {
62
+ id: getTrad('Settings.roles.created'),
63
+ defaultMessage: 'Role created',
64
+ },
65
+ });
66
+
67
+ // Forcing redirecting since we don't have the id in the response
68
+ goBack();
69
+ },
70
+ });
71
+
72
+ const handleCreateRoleSubmit = async (data) => {
73
+ lockApp();
74
+
75
+ // TODO: refactor. Child -> parent component communication is evil;
76
+ // We should either move the provider one level up or move the state
77
+ // straight into redux.
78
+ const permissions = permissionsRef.current.getPermissions();
79
+
80
+ await mutation.mutate({ ...data, ...permissions, users: [] });
81
+
82
+ unlockApp();
83
+ };
84
+
85
+ return (
86
+ <Main>
87
+ {/* TODO: This needs to be translated */}
88
+ <SettingsPageTitle name="Roles" />
89
+ <Formik
90
+ enableReinitialize
91
+ initialValues={{ name: '', description: '' }}
92
+ onSubmit={handleCreateRoleSubmit}
93
+ validationSchema={createRoleSchema}
94
+ >
95
+ {({ handleSubmit, values, handleChange, errors }) => (
96
+ <Form noValidate onSubmit={handleSubmit}>
97
+ <HeaderLayout
98
+ primaryAction={
99
+ !isLoadingPlugins && (
100
+ <Button type="submit" loading={mutation.isLoading} startIcon={<Check />}>
101
+ {formatMessage({
102
+ id: 'global.save',
103
+ defaultMessage: 'Save',
104
+ })}
105
+ </Button>
106
+ )
107
+ }
108
+ title={formatMessage({
109
+ id: 'Settings.roles.create.title',
110
+ defaultMessage: 'Create a role',
111
+ })}
112
+ subtitle={formatMessage({
113
+ id: 'Settings.roles.create.description',
114
+ defaultMessage: 'Define the rights given to the role',
115
+ })}
116
+ />
117
+ <ContentLayout>
118
+ <Flex
119
+ background="neutral0"
120
+ direction="column"
121
+ alignItems="stretch"
122
+ gap={7}
123
+ hasRadius
124
+ paddingTop={6}
125
+ paddingBottom={6}
126
+ paddingLeft={7}
127
+ paddingRight={7}
128
+ shadow="filterShadow"
129
+ >
130
+ <Flex direction="column" alignItems="stretch">
131
+ <Typography variant="delta" as="h2">
132
+ {formatMessage({
133
+ id: getTrad('EditPage.form.roles'),
134
+ defaultMessage: 'Role details',
135
+ })}
136
+ </Typography>
137
+
138
+ <Grid gap={4}>
139
+ <GridItem col={6}>
140
+ <TextInput
141
+ name="name"
142
+ value={values.name || ''}
143
+ onChange={handleChange}
144
+ label={formatMessage({
145
+ id: 'global.name',
146
+ defaultMessage: 'Name',
147
+ })}
148
+ error={errors?.name ? formatMessage({ id: errors.name }) : false}
149
+ required
150
+ />
151
+ </GridItem>
152
+ <GridItem col={6}>
153
+ <Textarea
154
+ id="description"
155
+ value={values.description || ''}
156
+ onChange={handleChange}
157
+ label={formatMessage({
158
+ id: 'global.description',
159
+ defaultMessage: 'Description',
160
+ })}
161
+ error={
162
+ errors?.description ? formatMessage({ id: errors.description }) : false
163
+ }
164
+ required
165
+ />
166
+ </GridItem>
167
+ </Grid>
168
+ </Flex>
169
+
170
+ {!isLoadingPlugins && (
171
+ <UsersPermissions
172
+ ref={permissionsRef}
173
+ permissions={permissions}
174
+ routes={routes}
175
+ />
176
+ )}
177
+ </Flex>
178
+ </ContentLayout>
179
+ </Form>
180
+ )}
181
+ </Formik>
182
+ </Main>
183
+ );
184
+ };
185
+
186
+ export const ProtectedRolesCreatePage = () => (
187
+ <CheckPagePermissions permissions={PERMISSIONS.createRole}>
188
+ <CreatePage />
189
+ </CheckPagePermissions>
190
+ );
@@ -0,0 +1,211 @@
1
+ import * as React from 'react';
2
+
3
+ import {
4
+ ContentLayout,
5
+ HeaderLayout,
6
+ Main,
7
+ Button,
8
+ Flex,
9
+ TextInput,
10
+ Textarea,
11
+ Typography,
12
+ GridItem,
13
+ Grid,
14
+ } from '@strapi/design-system';
15
+ import {
16
+ CheckPagePermissions,
17
+ useOverlayBlocker,
18
+ SettingsPageTitle,
19
+ LoadingIndicatorPage,
20
+ Form,
21
+ useAPIErrorHandler,
22
+ useFetchClient,
23
+ useNotification,
24
+ Link,
25
+ } from '@strapi/helper-plugin';
26
+ import { ArrowLeft, Check } from '@strapi/icons';
27
+ import { Formik } from 'formik';
28
+ import { useIntl } from 'react-intl';
29
+ import { useQuery, useMutation } from 'react-query';
30
+ import { useRouteMatch } from 'react-router-dom';
31
+
32
+ import UsersPermissions from '../../../components/UsersPermissions';
33
+ import { PERMISSIONS } from '../../../constants';
34
+ import getTrad from '../../../utils/getTrad';
35
+ import { createRoleSchema } from '../constants';
36
+ import { usePlugins } from '../hooks/usePlugins';
37
+
38
+ export const EditPage = () => {
39
+ const { formatMessage } = useIntl();
40
+ const toggleNotification = useNotification();
41
+ const { lockApp, unlockApp } = useOverlayBlocker();
42
+ const {
43
+ params: { id },
44
+ } = useRouteMatch(`/settings/users-permissions/roles/:id`);
45
+ const { get } = useFetchClient();
46
+ const { isLoading: isLoadingPlugins, routes } = usePlugins();
47
+ const {
48
+ data: role,
49
+ isLoading: isLoadingRole,
50
+ refetch: refetchRole,
51
+ } = useQuery(['users-permissions', 'role', id], async () => {
52
+ // TODO: why doesn't this endpoint follow the admin API conventions?
53
+ const {
54
+ data: { role },
55
+ } = await get(`/users-permissions/roles/${id}`);
56
+
57
+ return role;
58
+ });
59
+
60
+ const permissionsRef = React.useRef();
61
+ const { put } = useFetchClient();
62
+ const { formatAPIError } = useAPIErrorHandler();
63
+ const mutation = useMutation((body) => put(`/users-permissions/roles/${id}`, body), {
64
+ onError(error) {
65
+ toggleNotification({
66
+ type: 'warning',
67
+ message: formatAPIError(error),
68
+ });
69
+ },
70
+
71
+ async onSuccess() {
72
+ toggleNotification({
73
+ type: 'success',
74
+ message: {
75
+ id: getTrad('Settings.roles.created'),
76
+ defaultMessage: 'Role edited',
77
+ },
78
+ });
79
+
80
+ await refetchRole();
81
+ },
82
+ });
83
+
84
+ const handleEditRoleSubmit = async (data) => {
85
+ // Set loading state
86
+ lockApp();
87
+
88
+ const permissions = permissionsRef.current.getPermissions();
89
+
90
+ await mutation.mutate({ ...data, ...permissions, users: [] });
91
+
92
+ unlockApp();
93
+ };
94
+
95
+ if (isLoadingRole) {
96
+ return <LoadingIndicatorPage />;
97
+ }
98
+
99
+ return (
100
+ <Main>
101
+ {/* TODO: this needs to be translated */}
102
+ <SettingsPageTitle name="Roles" />
103
+ <Formik
104
+ enableReinitialize
105
+ initialValues={{ name: role.name, description: role.description }}
106
+ onSubmit={handleEditRoleSubmit}
107
+ validationSchema={createRoleSchema}
108
+ >
109
+ {({ handleSubmit, values, handleChange, errors }) => (
110
+ <Form noValidate onSubmit={handleSubmit}>
111
+ <HeaderLayout
112
+ primaryAction={
113
+ !isLoadingPlugins && (
114
+ <Button
115
+ disabled={role.code === 'strapi-super-admin'}
116
+ type="submit"
117
+ loading={mutation.isLoading}
118
+ startIcon={<Check />}
119
+ >
120
+ {formatMessage({
121
+ id: 'global.save',
122
+ defaultMessage: 'Save',
123
+ })}
124
+ </Button>
125
+ )
126
+ }
127
+ title={role.name}
128
+ subtitle={role.description}
129
+ navigationAction={
130
+ <Link startIcon={<ArrowLeft />} to="/settings/users-permissions/roles">
131
+ {formatMessage({
132
+ id: 'global.back',
133
+ defaultMessage: 'Back',
134
+ })}
135
+ </Link>
136
+ }
137
+ />
138
+ <ContentLayout>
139
+ <Flex
140
+ background="neutral0"
141
+ direction="column"
142
+ alignItems="stretch"
143
+ gap={7}
144
+ hasRadius
145
+ paddingTop={6}
146
+ paddingBottom={6}
147
+ paddingLeft={7}
148
+ paddingRight={7}
149
+ shadow="filterShadow"
150
+ >
151
+ <Flex direction="column" alignItems="stretch" gap={4}>
152
+ <Typography variant="delta" as="h2">
153
+ {formatMessage({
154
+ id: getTrad('EditPage.form.roles'),
155
+ defaultMessage: 'Role details',
156
+ })}
157
+ </Typography>
158
+
159
+ <Grid gap={4}>
160
+ <GridItem col={6}>
161
+ <TextInput
162
+ name="name"
163
+ value={values.name || ''}
164
+ onChange={handleChange}
165
+ label={formatMessage({
166
+ id: 'global.name',
167
+ defaultMessage: 'Name',
168
+ })}
169
+ error={errors?.name ? formatMessage({ id: errors.name }) : false}
170
+ required
171
+ />
172
+ </GridItem>
173
+ <GridItem col={6}>
174
+ <Textarea
175
+ id="description"
176
+ value={values.description || ''}
177
+ onChange={handleChange}
178
+ label={formatMessage({
179
+ id: 'global.description',
180
+ defaultMessage: 'Description',
181
+ })}
182
+ error={
183
+ errors?.description ? formatMessage({ id: errors.description }) : false
184
+ }
185
+ required
186
+ />
187
+ </GridItem>
188
+ </Grid>
189
+ </Flex>
190
+
191
+ {!isLoadingPlugins && (
192
+ <UsersPermissions
193
+ ref={permissionsRef}
194
+ permissions={role.permissions}
195
+ routes={routes}
196
+ />
197
+ )}
198
+ </Flex>
199
+ </ContentLayout>
200
+ </Form>
201
+ )}
202
+ </Formik>
203
+ </Main>
204
+ );
205
+ };
206
+
207
+ export const ProtectedRolesEditPage = () => (
208
+ <CheckPagePermissions permissions={PERMISSIONS.updateRole}>
209
+ <EditPage />
210
+ </CheckPagePermissions>
211
+ );
@@ -1,13 +1,39 @@
1
1
  import React from 'react';
2
2
 
3
- import { Flex, IconButton, Tbody, Td, Tr, Typography } from '@strapi/design-system';
4
- import { CheckPermissions, onRowClick, stopPropagation } from '@strapi/helper-plugin';
3
+ import { Flex, IconButton, Link, Tbody, Td, Tr, Typography } from '@strapi/design-system';
4
+ import { CheckPermissions, onRowClick, pxToRem, stopPropagation } from '@strapi/helper-plugin';
5
5
  import { Pencil, Trash } from '@strapi/icons';
6
6
  import PropTypes from 'prop-types';
7
7
  import { useIntl } from 'react-intl';
8
8
  import { useHistory } from 'react-router-dom';
9
+ import styled from 'styled-components';
9
10
 
10
- import pluginId from '../../../../pluginId';
11
+ const EditLink = styled(Link)`
12
+ align-items: center;
13
+ height: ${pxToRem(32)};
14
+ display: flex;
15
+ justify-content: center;
16
+ padding: ${({ theme }) => `${theme.spaces[2]}}`};
17
+ width: ${pxToRem(32)};
18
+
19
+ svg {
20
+ height: ${pxToRem(12)};
21
+ width: ${pxToRem(12)};
22
+
23
+ path {
24
+ fill: ${({ theme }) => theme.colors.neutral500};
25
+ }
26
+ }
27
+
28
+ &:hover,
29
+ &:focus {
30
+ svg {
31
+ path {
32
+ fill: ${({ theme }) => theme.colors.neutral800};
33
+ }
34
+ }
35
+ }
36
+ `;
11
37
 
12
38
  const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDelete }) => {
13
39
  const { formatMessage } = useIntl();
@@ -23,7 +49,7 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
23
49
  };
24
50
 
25
51
  const handleClickEdit = (id) => {
26
- push(`/settings/${pluginId}/roles/${id}`);
52
+ push(`/settings/users-permissions/roles/${id}`);
27
53
  };
28
54
 
29
55
  return (
@@ -50,16 +76,17 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
50
76
  <Td>
51
77
  <Flex justifyContent="end" {...stopPropagation}>
52
78
  <CheckPermissions permissions={permissions.updateRole}>
53
- <IconButton
54
- onClick={() => handleClickEdit(role.id)}
55
- noBorder
56
- icon={<Pencil />}
57
- label={formatMessage(
79
+ <EditLink
80
+ to={`/settings/users-permissions/roles/${role.id}`}
81
+ aria-label={formatMessage(
58
82
  { id: 'app.component.table.edit', defaultMessage: 'Edit {target}' },
59
83
  { target: `${role.name}` }
60
84
  )}
61
- />
85
+ >
86
+ <Pencil />
87
+ </EditLink>
62
88
  </CheckPermissions>
89
+
63
90
  {checkCanDeleteRole(role) && (
64
91
  <CheckPermissions permissions={permissions.deleteRole}>
65
92
  <IconButton
@@ -2,7 +2,6 @@ import React, { useState } from 'react';
2
2
 
3
3
  import {
4
4
  ActionLayout,
5
- Button,
6
5
  ContentLayout,
7
6
  HeaderLayout,
8
7
  Layout,
@@ -16,9 +15,11 @@ import {
16
15
  VisuallyHidden,
17
16
  } from '@strapi/design-system';
18
17
  import {
18
+ CheckPagePermissions,
19
19
  CheckPermissions,
20
20
  ConfirmDialog,
21
21
  EmptyStateLayout,
22
+ LinkButton,
22
23
  LoadingIndicatorPage,
23
24
  NoPermissions,
24
25
  SearchURLQuery,
@@ -34,19 +35,16 @@ import {
34
35
  import { Plus } from '@strapi/icons';
35
36
  import { useIntl } from 'react-intl';
36
37
  import { useMutation, useQuery } from 'react-query';
37
- import { useHistory } from 'react-router-dom';
38
38
 
39
- import { PERMISSIONS } from '../../../constants';
40
- import pluginId from '../../../pluginId';
41
- import { getTrad } from '../../../utils';
39
+ import { PERMISSIONS } from '../../../../constants';
40
+ import { getTrad } from '../../../../utils';
42
41
 
43
42
  import TableBody from './components/TableBody';
44
43
  import { deleteData, fetchData } from './utils/api';
45
44
 
46
- const RoleListPage = () => {
45
+ export const RolesListPage = () => {
47
46
  const { trackUsage } = useTracking();
48
47
  const { formatMessage, locale } = useIntl();
49
- const { push } = useHistory();
50
48
  const toggleNotification = useNotification();
51
49
  const { notifyStatus } = useNotifyAT();
52
50
  const [{ query }] = useQueryParams();
@@ -89,11 +87,6 @@ const RoleListPage = () => {
89
87
 
90
88
  const isLoading = isLoadingForData || isFetching;
91
89
 
92
- const handleNewRoleClick = () => {
93
- trackUsage('willCreateRole');
94
- push(`/settings/${pluginId}/roles/new`);
95
- };
96
-
97
90
  const handleShowConfirmDelete = () => {
98
91
  setShowConfirmDelete(!showConfirmDelete);
99
92
  };
@@ -153,12 +146,17 @@ const RoleListPage = () => {
153
146
  })}
154
147
  primaryAction={
155
148
  <CheckPermissions permissions={PERMISSIONS.createRole}>
156
- <Button onClick={handleNewRoleClick} startIcon={<Plus />} size="S">
149
+ <LinkButton
150
+ to="/settings/users-permissions/roles/new"
151
+ onClick={() => trackUsage('willCreateRole')}
152
+ startIcon={<Plus />}
153
+ size="S"
154
+ >
157
155
  {formatMessage({
158
156
  id: getTrad('List.button.roles'),
159
157
  defaultMessage: 'Add new role',
160
158
  })}
161
- </Button>
159
+ </LinkButton>
162
160
  </CheckPermissions>
163
161
  }
164
162
  />
@@ -235,4 +233,10 @@ const RoleListPage = () => {
235
233
  );
236
234
  };
237
235
 
238
- export default RoleListPage;
236
+ export const ProtectedRolesListPage = () => {
237
+ return (
238
+ <CheckPagePermissions permissions={PERMISSIONS.accessRoles}>
239
+ <RolesListPage />
240
+ </CheckPagePermissions>
241
+ );
242
+ };
@@ -3,5 +3,5 @@
3
3
  module.exports = {
4
4
  preset: '../../../jest-preset.front.js',
5
5
  displayName: 'Users & Permissions plugin',
6
- setupFilesAfterEnv: ['./tests/setup.js'],
6
+ setupFilesAfterEnv: ['./admin/src/tests/setup.js'],
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-users-permissions",
3
- "version": "4.13.0-beta.0",
3
+ "version": "4.13.0",
4
4
  "description": "Protect your API with a full-authentication process based on JWT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,9 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@strapi/design-system": "1.9.0",
33
- "@strapi/helper-plugin": "4.13.0-beta.0",
33
+ "@strapi/helper-plugin": "4.13.0",
34
34
  "@strapi/icons": "1.9.0",
35
- "@strapi/utils": "4.13.0-beta.0",
35
+ "@strapi/utils": "4.13.0",
36
36
  "bcryptjs": "2.4.3",
37
37
  "formik": "2.4.0",
38
38
  "grant-koa": "5.4.8",
@@ -77,5 +77,5 @@
77
77
  "required": true,
78
78
  "kind": "plugin"
79
79
  },
80
- "gitHead": "f1b8431a6a0b7f9bd9a8444adb56217bba91ec07"
80
+ "gitHead": "cc7c99b8c74d829e98d6d4bc5a2ba364587a3be9"
81
81
  }
@@ -10,10 +10,12 @@
10
10
  const crypto = require('crypto');
11
11
  const _ = require('lodash');
12
12
  const urljoin = require('url-join');
13
+ const { isArray } = require('lodash/fp');
13
14
  const { getService } = require('../utils');
14
15
  const getGrantConfig = require('./grant-config');
15
16
 
16
17
  const usersPermissionsActions = require('./users-permissions-actions');
18
+ const userSchema = require('../content-types/user');
17
19
 
18
20
  const initGrant = async (pluginStore) => {
19
21
  const apiPrefix = strapi.config.get('api.rest.prefix');
@@ -97,6 +99,27 @@ const initAdvancedOptions = async (pluginStore) => {
97
99
  }
98
100
  };
99
101
 
102
+ const userSchemaAdditions = () => {
103
+ const defaultSchema = Object.keys(userSchema.attributes);
104
+ const currentSchema = Object.keys(
105
+ strapi.contentTypes['plugin::users-permissions.user'].attributes
106
+ );
107
+
108
+ // Some dynamic fields may not have been initialized yet, so we need to ignore them
109
+ // TODO: we should have a global method for finding these
110
+ const ignoreDiffs = [
111
+ 'createdBy',
112
+ 'createdAt',
113
+ 'updatedBy',
114
+ 'updatedAt',
115
+ 'publishedAt',
116
+ 'strapi_stage',
117
+ 'strapi_assignee',
118
+ ];
119
+
120
+ return currentSchema.filter((key) => !(ignoreDiffs.includes(key) || defaultSchema.includes(key)));
121
+ };
122
+
100
123
  module.exports = async ({ strapi }) => {
101
124
  const pluginStore = strapi.store({ type: 'plugin', name: 'users-permissions' });
102
125
 
@@ -130,4 +153,17 @@ For security reasons, prefer storing the secret in an environment variable and r
130
153
  );
131
154
  }
132
155
  }
156
+
157
+ // TODO v5: Remove this block of code and default allowedFields to empty array
158
+ if (!isArray(strapi.config.get('plugin.users-permissions.register.allowedFields'))) {
159
+ const modifications = userSchemaAdditions();
160
+ if (modifications.length > 0) {
161
+ // if there is a potential vulnerability, show a warning
162
+ strapi.log.warn(
163
+ `Users-permissions registration has defaulted to accepting the following additional user fields during registration: ${modifications.join(
164
+ ','
165
+ )}`
166
+ );
167
+ }
168
+ }
133
169
  };