@strapi/plugin-users-permissions 4.3.2 → 4.3.5
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/getMethodColor.js +1 -1
- package/admin/src/components/BoundRoute/index.js +1 -1
- package/admin/src/components/FormModal/Input/index.js +1 -1
- package/admin/src/components/FormModal/index.js +7 -9
- package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +3 -3
- package/admin/src/components/Permissions/PermissionRow/SubCategory.js +10 -14
- package/admin/src/components/Permissions/PermissionRow/index.js +1 -1
- package/admin/src/components/Permissions/index.js +2 -2
- package/admin/src/components/Permissions/init.js +1 -1
- package/admin/src/components/Permissions/reducer.js +1 -1
- package/admin/src/components/Policies/index.js +1 -1
- package/admin/src/components/UsersPermissions/index.js +5 -5
- package/admin/src/components/UsersPermissions/reducer.js +1 -1
- package/admin/src/hooks/useFetchRole/index.js +3 -3
- package/admin/src/hooks/useFetchRole/reducer.js +1 -1
- package/admin/src/hooks/useForm/index.js +1 -1
- package/admin/src/hooks/useForm/reducer.js +1 -1
- package/admin/src/hooks/usePlugins/index.js +1 -1
- package/admin/src/hooks/usePlugins/reducer.js +1 -1
- package/admin/src/hooks/useRolesList/reducer.js +1 -1
- package/admin/src/index.js +5 -5
- package/admin/src/pages/AdvancedSettings/index.js +11 -10
- package/admin/src/pages/AdvancedSettings/utils/api.js +1 -1
- package/admin/src/pages/AdvancedSettings/utils/schema.js +2 -4
- package/admin/src/pages/EmailTemplates/index.js +8 -8
- package/admin/src/pages/EmailTemplates/utils/api.js +1 -1
- package/admin/src/pages/EmailTemplates/utils/schema.js +1 -4
- package/admin/src/pages/Providers/index.js +22 -22
- package/admin/src/pages/Providers/reducer.js +1 -1
- package/admin/src/pages/Providers/utils/api.js +2 -2
- package/admin/src/pages/Providers/utils/createProvidersArray.js +1 -1
- package/admin/src/pages/Roles/CreatePage/index.js +1 -1
- package/admin/src/pages/Roles/EditPage/index.js +1 -1
- package/admin/src/pages/Roles/ListPage/components/TableBody.js +5 -4
- package/admin/src/pages/Roles/ListPage/index.js +2 -2
- package/admin/src/utils/axiosInstance.js +4 -4
- package/admin/src/utils/cleanPermissions.js +1 -1
- package/admin/src/utils/formatPolicies.js +1 -1
- package/admin/src/utils/getRequestURL.js +1 -1
- package/admin/src/utils/getTrad.js +1 -1
- package/documentation/content-api.yaml +36 -1
- package/jest.config.front.js +1 -1
- package/package.json +12 -7
- package/server/bootstrap/grant-config.js +1 -1
- package/server/bootstrap/index.js +39 -39
- package/server/controllers/auth.js +33 -5
- package/server/controllers/content-manager-user.js +4 -12
- package/server/controllers/settings.js +1 -1
- package/server/controllers/user.js +2 -2
- package/server/controllers/validation/auth.js +15 -12
- package/server/controllers/validation/email-template.js +3 -2
- package/server/controllers/validation/user.js +4 -16
- package/server/graphql/mutations/auth/change-password.js +38 -0
- package/server/graphql/mutations/auth/forgot-password.js +1 -4
- package/server/graphql/mutations/auth/login.js +1 -4
- package/server/graphql/mutations/auth/register.js +1 -4
- package/server/graphql/mutations/auth/reset-password.js +1 -4
- package/server/graphql/mutations/crud/role/create-role.js +1 -4
- package/server/graphql/mutations/crud/role/delete-role.js +1 -4
- package/server/graphql/mutations/crud/role/update-role.js +1 -4
- package/server/graphql/mutations/crud/user/create-user.js +1 -4
- package/server/graphql/mutations/crud/user/delete-user.js +1 -4
- package/server/graphql/mutations/crud/user/update-user.js +1 -4
- package/server/graphql/mutations/index.js +2 -1
- package/server/graphql/resolvers-configs.js +5 -0
- package/server/graphql/types/index.js +1 -1
- package/server/middlewares/rateLimit.js +22 -25
- package/server/register.js +2 -4
- package/server/routes/content-api/auth.js +9 -0
- package/server/services/jwt.js +11 -9
- package/server/services/providers-registry.js +33 -42
- package/server/services/providers.js +6 -6
- package/server/services/role.js +7 -7
- package/server/services/user.js +1 -8
- package/server/services/users-permissions.js +13 -12
- package/server/strategies/users-permissions.js +3 -3
- package/server/utils/index.js +1 -1
|
@@ -53,26 +53,26 @@ export const ProvidersPage = () => {
|
|
|
53
53
|
allowedActions: { canUpdate },
|
|
54
54
|
} = useRBAC(updatePermissions);
|
|
55
55
|
|
|
56
|
-
const {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
);
|
|
56
|
+
const {
|
|
57
|
+
isLoading: isLoadingForData,
|
|
58
|
+
data: modifiedData,
|
|
59
|
+
isFetching,
|
|
60
|
+
} = useQuery('get-providers', () => fetchData(toggleNotification), {
|
|
61
|
+
onSuccess() {
|
|
62
|
+
notifyStatus(
|
|
63
|
+
formatMessage({
|
|
64
|
+
id: getTrad('Providers.data.loaded'),
|
|
65
|
+
defaultMessage: 'Providers have been loaded',
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
initialData: {},
|
|
70
|
+
});
|
|
71
71
|
|
|
72
72
|
const isLoading = isLoadingForData || isFetching;
|
|
73
73
|
|
|
74
74
|
const submitMutation = useMutation(putProvider, {
|
|
75
|
-
|
|
75
|
+
async onSuccess() {
|
|
76
76
|
await queryClient.invalidateQueries('get-providers');
|
|
77
77
|
toggleNotification({
|
|
78
78
|
type: 'info',
|
|
@@ -84,7 +84,7 @@ export const ProvidersPage = () => {
|
|
|
84
84
|
handleToggleModal();
|
|
85
85
|
unlockApp();
|
|
86
86
|
},
|
|
87
|
-
onError
|
|
87
|
+
onError() {
|
|
88
88
|
toggleNotification({
|
|
89
89
|
type: 'warning',
|
|
90
90
|
message: { id: 'notification.error' },
|
|
@@ -104,7 +104,7 @@ export const ProvidersPage = () => {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
const providerToEdit = providers.find(obj => obj.name === providerToEditName);
|
|
107
|
+
const providerToEdit = providers.find((obj) => obj.name === providerToEditName);
|
|
108
108
|
|
|
109
109
|
return has(providerToEdit, 'subdomain');
|
|
110
110
|
}, [providers, providerToEditName]);
|
|
@@ -127,17 +127,17 @@ export const ProvidersPage = () => {
|
|
|
127
127
|
}, [providerToEditName, isProviderWithSubdomain]);
|
|
128
128
|
|
|
129
129
|
const handleToggleModal = () => {
|
|
130
|
-
setIsOpen(prev => !prev);
|
|
130
|
+
setIsOpen((prev) => !prev);
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
-
const handleClickEdit = provider => {
|
|
133
|
+
const handleClickEdit = (provider) => {
|
|
134
134
|
if (canUpdate) {
|
|
135
135
|
setProviderToEditName(provider.name);
|
|
136
136
|
handleToggleModal();
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
-
const handleSubmit = async values => {
|
|
140
|
+
const handleSubmit = async (values) => {
|
|
141
141
|
setIsSubmiting(true);
|
|
142
142
|
|
|
143
143
|
lockApp();
|
|
@@ -196,7 +196,7 @@ export const ProvidersPage = () => {
|
|
|
196
196
|
</Tr>
|
|
197
197
|
</Thead>
|
|
198
198
|
<Tbody>
|
|
199
|
-
{providers.map(provider => (
|
|
199
|
+
{providers.map((provider) => (
|
|
200
200
|
<Tr
|
|
201
201
|
key={provider.name}
|
|
202
202
|
{...onRowClick({
|
|
@@ -10,7 +10,7 @@ const initialState = {
|
|
|
10
10
|
|
|
11
11
|
const reducer = (state, action) =>
|
|
12
12
|
// eslint-disable-next-line consistent-return
|
|
13
|
-
produce(state, draftState => {
|
|
13
|
+
produce(state, (draftState) => {
|
|
14
14
|
switch (action.type) {
|
|
15
15
|
case 'GET_DATA': {
|
|
16
16
|
draftState.isLoading = true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getRequestURL, axiosInstance } from '../../../utils';
|
|
2
2
|
|
|
3
3
|
// eslint-disable-next-line import/prefer-default-export
|
|
4
|
-
export const fetchData = async toggleNotification => {
|
|
4
|
+
export const fetchData = async (toggleNotification) => {
|
|
5
5
|
try {
|
|
6
6
|
const { data } = await axiosInstance.get(getRequestURL('providers'));
|
|
7
7
|
|
|
@@ -16,6 +16,6 @@ export const fetchData = async toggleNotification => {
|
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export const putProvider = body => {
|
|
19
|
+
export const putProvider = (body) => {
|
|
20
20
|
return axiosInstance.put(getRequestURL('providers'), body);
|
|
21
21
|
};
|
|
@@ -36,7 +36,7 @@ const EditPage = () => {
|
|
|
36
36
|
const { trackUsage } = useTracking();
|
|
37
37
|
const permissionsRef = useRef();
|
|
38
38
|
|
|
39
|
-
const handleCreateRoleSubmit = async data => {
|
|
39
|
+
const handleCreateRoleSubmit = async (data) => {
|
|
40
40
|
// Set loading state
|
|
41
41
|
lockApp();
|
|
42
42
|
setIsSubmitting(true);
|
|
@@ -40,7 +40,7 @@ const EditPage = () => {
|
|
|
40
40
|
const { role, onSubmitSucceeded, isLoading: isLoadingRole } = useFetchRole(id);
|
|
41
41
|
const permissionsRef = useRef();
|
|
42
42
|
|
|
43
|
-
const handleEditRoleSubmit = async data => {
|
|
43
|
+
const handleEditRoleSubmit = async (data) => {
|
|
44
44
|
// Set loading state
|
|
45
45
|
lockApp();
|
|
46
46
|
setIsSubmitting(true);
|
|
@@ -17,20 +17,21 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
|
|
|
17
17
|
const { push } = useHistory();
|
|
18
18
|
const [showConfirmDelete, setShowConfirmDelete] = onDelete;
|
|
19
19
|
|
|
20
|
-
const checkCanDeleteRole = role =>
|
|
20
|
+
const checkCanDeleteRole = (role) =>
|
|
21
|
+
canDelete && !['public', 'authenticated'].includes(role.type);
|
|
21
22
|
|
|
22
|
-
const handleClickDelete = id => {
|
|
23
|
+
const handleClickDelete = (id) => {
|
|
23
24
|
setRoleToDelete(id);
|
|
24
25
|
setShowConfirmDelete(!showConfirmDelete);
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
const handleClickEdit = id => {
|
|
28
|
+
const handleClickEdit = (id) => {
|
|
28
29
|
push(`/settings/${pluginId}/roles/${id}`);
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
return (
|
|
32
33
|
<Tbody>
|
|
33
|
-
{sortedRoles?.map(role => (
|
|
34
|
+
{sortedRoles?.map((role) => (
|
|
34
35
|
<Tr key={role.name} {...onRowClick({ fn: () => handleClickEdit(role.id) })}>
|
|
35
36
|
<Td width="20%">
|
|
36
37
|
<Typography>{role.name}</Typography>
|
|
@@ -97,8 +97,8 @@ const RoleListPage = () => {
|
|
|
97
97
|
defaultMessage: 'Roles',
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
const deleteMutation = useMutation(id => deleteData(id, toggleNotification), {
|
|
101
|
-
|
|
100
|
+
const deleteMutation = useMutation((id) => deleteData(id, toggleNotification), {
|
|
101
|
+
async onSuccess() {
|
|
102
102
|
await queryClient.invalidateQueries('get-roles');
|
|
103
103
|
},
|
|
104
104
|
});
|
|
@@ -6,7 +6,7 @@ const instance = axios.create({
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
instance.interceptors.request.use(
|
|
9
|
-
async config => {
|
|
9
|
+
async (config) => {
|
|
10
10
|
config.headers = {
|
|
11
11
|
Authorization: `Bearer ${auth.getToken()}`,
|
|
12
12
|
Accept: 'application/json',
|
|
@@ -15,14 +15,14 @@ instance.interceptors.request.use(
|
|
|
15
15
|
|
|
16
16
|
return config;
|
|
17
17
|
},
|
|
18
|
-
error => {
|
|
18
|
+
(error) => {
|
|
19
19
|
Promise.reject(error);
|
|
20
20
|
}
|
|
21
21
|
);
|
|
22
22
|
|
|
23
23
|
instance.interceptors.response.use(
|
|
24
|
-
response => response,
|
|
25
|
-
error => {
|
|
24
|
+
(response) => response,
|
|
25
|
+
(error) => {
|
|
26
26
|
// whatever you want to do with the error
|
|
27
27
|
if (error.response?.status === 401) {
|
|
28
28
|
auth.clearAppStorage();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isEmpty } from 'lodash';
|
|
2
2
|
|
|
3
|
-
const cleanPermissions = permissions =>
|
|
3
|
+
const cleanPermissions = (permissions) =>
|
|
4
4
|
Object.keys(permissions).reduce((acc, current) => {
|
|
5
5
|
const currentPermission = permissions[current].controllers;
|
|
6
6
|
const cleanedControllers = Object.keys(currentPermission).reduce((acc2, curr) => {
|
|
@@ -44,7 +44,7 @@ paths:
|
|
|
44
44
|
password:
|
|
45
45
|
type: string
|
|
46
46
|
example:
|
|
47
|
-
|
|
47
|
+
identifier: foobar
|
|
48
48
|
password: Test1234
|
|
49
49
|
required: true
|
|
50
50
|
responses:
|
|
@@ -185,6 +185,41 @@ paths:
|
|
|
185
185
|
schema:
|
|
186
186
|
$ref: '#/components/schemas/Error'
|
|
187
187
|
|
|
188
|
+
/auth/change-password:
|
|
189
|
+
post:
|
|
190
|
+
tags:
|
|
191
|
+
- Users-Permissions - Auth
|
|
192
|
+
summary: Update user's own password
|
|
193
|
+
requestBody:
|
|
194
|
+
required: true
|
|
195
|
+
content:
|
|
196
|
+
application/json:
|
|
197
|
+
schema:
|
|
198
|
+
type: object
|
|
199
|
+
properties:
|
|
200
|
+
password:
|
|
201
|
+
required: true
|
|
202
|
+
type: string
|
|
203
|
+
currentPassword:
|
|
204
|
+
required: true
|
|
205
|
+
type: string
|
|
206
|
+
passwordConfirmation:
|
|
207
|
+
required: true
|
|
208
|
+
type: string
|
|
209
|
+
responses:
|
|
210
|
+
200:
|
|
211
|
+
description: Returns a jwt token and user info
|
|
212
|
+
content:
|
|
213
|
+
application/json:
|
|
214
|
+
schema:
|
|
215
|
+
$ref: '#/components/schemas/Users-Permissions-UserRegistration'
|
|
216
|
+
default:
|
|
217
|
+
description: Error
|
|
218
|
+
content:
|
|
219
|
+
application/json:
|
|
220
|
+
schema:
|
|
221
|
+
$ref: '#/components/schemas/Error'
|
|
222
|
+
|
|
188
223
|
/auth/email-confirmation:
|
|
189
224
|
get:
|
|
190
225
|
tags:
|
package/jest.config.front.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/plugin-users-permissions",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.5",
|
|
4
4
|
"description": "Protect your API with a full-authentication process based on JWT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,26 +27,31 @@
|
|
|
27
27
|
"test:front:watch:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@strapi/helper-plugin": "4.3.
|
|
31
|
-
"@strapi/utils": "4.3.
|
|
30
|
+
"@strapi/helper-plugin": "4.3.5",
|
|
31
|
+
"@strapi/utils": "4.3.5",
|
|
32
32
|
"bcryptjs": "2.4.3",
|
|
33
33
|
"grant-koa": "5.4.8",
|
|
34
34
|
"jsonwebtoken": "^8.1.0",
|
|
35
|
+
"koa": "^2.13.4",
|
|
35
36
|
"koa2-ratelimit": "^1.1.1",
|
|
36
37
|
"lodash": "4.17.21",
|
|
37
38
|
"purest": "4.0.2",
|
|
38
39
|
"react": "^17.0.2",
|
|
39
40
|
"react-dom": "^17.0.2",
|
|
40
|
-
"react-intl": "5.
|
|
41
|
+
"react-intl": "5.25.1",
|
|
41
42
|
"react-redux": "7.2.8",
|
|
42
43
|
"react-router": "^5.2.0",
|
|
43
44
|
"react-router-dom": "5.2.0",
|
|
44
|
-
"redux-saga": "^0.16.0",
|
|
45
45
|
"request": "^2.83.0",
|
|
46
46
|
"url-join": "4.0.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"
|
|
49
|
+
"@testing-library/dom": "8.17.1",
|
|
50
|
+
"@testing-library/react": "12.1.4",
|
|
51
|
+
"@testing-library/react-hooks": "8.0.1",
|
|
52
|
+
"@testing-library/user-event": "14.4.2",
|
|
53
|
+
"msw": "0.42.3",
|
|
54
|
+
"react-test-renderer": "^17.0.2"
|
|
50
55
|
},
|
|
51
56
|
"engines": {
|
|
52
57
|
"node": ">=14.19.1 <=16.x.x",
|
|
@@ -59,5 +64,5 @@
|
|
|
59
64
|
"required": true,
|
|
60
65
|
"kind": "plugin"
|
|
61
66
|
},
|
|
62
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "a52b0535513ff35b4ef46c9daf7be48f2ba71737"
|
|
63
68
|
}
|
|
@@ -15,42 +15,7 @@ const getGrantConfig = require('./grant-config');
|
|
|
15
15
|
|
|
16
16
|
const usersPermissionsActions = require('./users-permissions-actions');
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
const pluginStore = strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
20
|
-
|
|
21
|
-
await initGrant(pluginStore);
|
|
22
|
-
await initEmails(pluginStore);
|
|
23
|
-
await initAdvancedOptions(pluginStore);
|
|
24
|
-
|
|
25
|
-
await strapi.admin.services.permission.actionProvider.registerMany(
|
|
26
|
-
usersPermissionsActions.actions
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
await getService('users-permissions').initialize();
|
|
30
|
-
|
|
31
|
-
if (!strapi.config.get('plugin.users-permissions.jwtSecret')) {
|
|
32
|
-
if (process.env.NODE_ENV !== 'development') {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Missing jwtSecret. Please, set configuration variable "jwtSecret" for the users-permissions plugin in config/plugins.js (ex: you can generate one using Node with \`crypto.randomBytes(16).toString('base64')\`).
|
|
35
|
-
For security reasons, prefer storing the secret in an environment variable and read it in config/plugins.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const jwtSecret = crypto.randomBytes(16).toString('base64');
|
|
40
|
-
|
|
41
|
-
strapi.config.set('plugin.users-permissions.jwtSecret', jwtSecret);
|
|
42
|
-
|
|
43
|
-
if (!process.env.JWT_SECRET) {
|
|
44
|
-
const envPath = process.env.ENV_PATH || '.env';
|
|
45
|
-
strapi.fs.appendFile(envPath, `JWT_SECRET=${jwtSecret}\n`);
|
|
46
|
-
strapi.log.info(
|
|
47
|
-
`The Users & Permissions plugin automatically generated a jwt secret and stored it in ${envPath} under the name JWT_SECRET.`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const initGrant = async pluginStore => {
|
|
18
|
+
const initGrant = async (pluginStore) => {
|
|
54
19
|
const apiPrefix = strapi.config.get('api.rest.prefix');
|
|
55
20
|
const baseURL = urljoin(strapi.config.server.url, apiPrefix, 'auth');
|
|
56
21
|
|
|
@@ -62,7 +27,7 @@ const initGrant = async pluginStore => {
|
|
|
62
27
|
// or we have added/deleted provider here.
|
|
63
28
|
if (!prevGrantConfig || !_.isEqual(_.keys(prevGrantConfig), _.keys(grantConfig))) {
|
|
64
29
|
// merge with the previous provider config.
|
|
65
|
-
_.keys(grantConfig).forEach(key => {
|
|
30
|
+
_.keys(grantConfig).forEach((key) => {
|
|
66
31
|
if (key in prevGrantConfig) {
|
|
67
32
|
grantConfig[key] = _.merge(grantConfig[key], prevGrantConfig[key]);
|
|
68
33
|
}
|
|
@@ -71,7 +36,7 @@ const initGrant = async pluginStore => {
|
|
|
71
36
|
}
|
|
72
37
|
};
|
|
73
38
|
|
|
74
|
-
const initEmails = async pluginStore => {
|
|
39
|
+
const initEmails = async (pluginStore) => {
|
|
75
40
|
if (!(await pluginStore.get({ key: 'email' }))) {
|
|
76
41
|
const value = {
|
|
77
42
|
reset_password: {
|
|
@@ -117,7 +82,7 @@ const initEmails = async pluginStore => {
|
|
|
117
82
|
}
|
|
118
83
|
};
|
|
119
84
|
|
|
120
|
-
const initAdvancedOptions = async pluginStore => {
|
|
85
|
+
const initAdvancedOptions = async (pluginStore) => {
|
|
121
86
|
if (!(await pluginStore.get({ key: 'advanced' }))) {
|
|
122
87
|
const value = {
|
|
123
88
|
unique_email: true,
|
|
@@ -131,3 +96,38 @@ const initAdvancedOptions = async pluginStore => {
|
|
|
131
96
|
await pluginStore.set({ key: 'advanced', value });
|
|
132
97
|
}
|
|
133
98
|
};
|
|
99
|
+
|
|
100
|
+
module.exports = async ({ strapi }) => {
|
|
101
|
+
const pluginStore = strapi.store({ type: 'plugin', name: 'users-permissions' });
|
|
102
|
+
|
|
103
|
+
await initGrant(pluginStore);
|
|
104
|
+
await initEmails(pluginStore);
|
|
105
|
+
await initAdvancedOptions(pluginStore);
|
|
106
|
+
|
|
107
|
+
await strapi.admin.services.permission.actionProvider.registerMany(
|
|
108
|
+
usersPermissionsActions.actions
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
await getService('users-permissions').initialize();
|
|
112
|
+
|
|
113
|
+
if (!strapi.config.get('plugin.users-permissions.jwtSecret')) {
|
|
114
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Missing jwtSecret. Please, set configuration variable "jwtSecret" for the users-permissions plugin in config/plugins.js (ex: you can generate one using Node with \`crypto.randomBytes(16).toString('base64')\`).
|
|
117
|
+
For security reasons, prefer storing the secret in an environment variable and read it in config/plugins.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const jwtSecret = crypto.randomBytes(16).toString('base64');
|
|
122
|
+
|
|
123
|
+
strapi.config.set('plugin.users-permissions.jwtSecret', jwtSecret);
|
|
124
|
+
|
|
125
|
+
if (!process.env.JWT_SECRET) {
|
|
126
|
+
const envPath = process.env.ENV_PATH || '.env';
|
|
127
|
+
strapi.fs.appendFile(envPath, `JWT_SECRET=${jwtSecret}\n`);
|
|
128
|
+
strapi.log.info(
|
|
129
|
+
`The Users & Permissions plugin automatically generated a jwt secret and stored it in ${envPath} under the name JWT_SECRET.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
validateForgotPasswordBody,
|
|
19
19
|
validateResetPasswordBody,
|
|
20
20
|
validateEmailConfirmationBody,
|
|
21
|
+
validateChangePasswordBody,
|
|
21
22
|
} = require('./validation/auth');
|
|
22
23
|
|
|
23
24
|
const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
|
|
@@ -104,6 +105,36 @@ module.exports = {
|
|
|
104
105
|
}
|
|
105
106
|
},
|
|
106
107
|
|
|
108
|
+
async changePassword(ctx) {
|
|
109
|
+
if (!ctx.state.user) {
|
|
110
|
+
throw new ApplicationError('You must be authenticated to reset your password');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { currentPassword, password } = await validateChangePasswordBody(ctx.request.body);
|
|
114
|
+
|
|
115
|
+
const user = await strapi.entityService.findOne(
|
|
116
|
+
'plugin::users-permissions.user',
|
|
117
|
+
ctx.state.user.id
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const validPassword = await getService('user').validatePassword(currentPassword, user.password);
|
|
121
|
+
|
|
122
|
+
if (!validPassword) {
|
|
123
|
+
throw new ValidationError('The provided current password is invalid');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (currentPassword === password) {
|
|
127
|
+
throw new ValidationError('Your new password must be different than your current password');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await getService('user').edit(user.id, { password });
|
|
131
|
+
|
|
132
|
+
ctx.send({
|
|
133
|
+
jwt: getService('jwt').issue({ id: user.id }),
|
|
134
|
+
user: await sanitizeUser(user, ctx),
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
|
|
107
138
|
async resetPassword(ctx) {
|
|
108
139
|
const { password, passwordConfirmation, code } = await validateResetPasswordBody(
|
|
109
140
|
ctx.request.body
|
|
@@ -228,10 +259,7 @@ module.exports = {
|
|
|
228
259
|
await getService('user').edit(user.id, { resetPasswordToken });
|
|
229
260
|
|
|
230
261
|
// Send an email to the user.
|
|
231
|
-
await strapi
|
|
232
|
-
.plugin('email')
|
|
233
|
-
.service('email')
|
|
234
|
-
.send(emailToSend);
|
|
262
|
+
await strapi.plugin('email').service('email').send(emailToSend);
|
|
235
263
|
|
|
236
264
|
ctx.send({ ok: true });
|
|
237
265
|
},
|
|
@@ -295,7 +323,7 @@ module.exports = {
|
|
|
295
323
|
}
|
|
296
324
|
}
|
|
297
325
|
|
|
298
|
-
|
|
326
|
+
const newUser = {
|
|
299
327
|
...params,
|
|
300
328
|
role: role.id,
|
|
301
329
|
email: email.toLowerCase(),
|
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
ValidationError,
|
|
8
|
-
NotFoundError,
|
|
9
|
-
ForbiddenError,
|
|
10
|
-
} = require('@strapi/utils').errors;
|
|
5
|
+
const { ApplicationError, ValidationError, NotFoundError, ForbiddenError } =
|
|
6
|
+
require('@strapi/utils').errors;
|
|
11
7
|
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
12
8
|
|
|
13
9
|
const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
|
|
@@ -132,17 +128,13 @@ module.exports = {
|
|
|
132
128
|
|
|
133
129
|
const { email, username, password } = body;
|
|
134
130
|
|
|
135
|
-
|
|
136
|
-
let user;
|
|
137
|
-
|
|
138
|
-
const { pm: permissionManager, entity } = await findEntityAndCheckPermissions(
|
|
131
|
+
const { pm, entity } = await findEntityAndCheckPermissions(
|
|
139
132
|
userAbility,
|
|
140
133
|
ACTIONS.edit,
|
|
141
134
|
userModel,
|
|
142
135
|
id
|
|
143
136
|
);
|
|
144
|
-
|
|
145
|
-
user = entity;
|
|
137
|
+
const user = entity;
|
|
146
138
|
|
|
147
139
|
await validateUpdateUserBody(ctx.request.body);
|
|
148
140
|
|
|
@@ -17,7 +17,7 @@ module.exports = {
|
|
|
17
17
|
|
|
18
18
|
const emailTemplates = ctx.request.body['email-templates'];
|
|
19
19
|
|
|
20
|
-
for (
|
|
20
|
+
for (const key of Object.keys(emailTemplates)) {
|
|
21
21
|
const template = emailTemplates[key].options.message;
|
|
22
22
|
|
|
23
23
|
if (!isValidEmailTemplate(template)) {
|
|
@@ -121,7 +121,7 @@ module.exports = {
|
|
|
121
121
|
ctx.request.body.email = ctx.request.body.email.toLowerCase();
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
const updateData = {
|
|
125
125
|
...ctx.request.body,
|
|
126
126
|
};
|
|
127
127
|
|
|
@@ -138,7 +138,7 @@ module.exports = {
|
|
|
138
138
|
async find(ctx) {
|
|
139
139
|
const users = await getService('user').fetchAll(ctx.query);
|
|
140
140
|
|
|
141
|
-
ctx.body = await Promise.all(users.map(user => sanitizeOutput(user, ctx)));
|
|
141
|
+
ctx.body = await Promise.all(users.map((user) => sanitizeOutput(user, ctx)));
|
|
142
142
|
},
|
|
143
143
|
|
|
144
144
|
/**
|
|
@@ -8,19 +8,13 @@ const callbackSchema = yup.object({
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
const registerSchema = yup.object({
|
|
11
|
-
email: yup
|
|
12
|
-
.string()
|
|
13
|
-
.email()
|
|
14
|
-
.required(),
|
|
11
|
+
email: yup.string().email().required(),
|
|
15
12
|
username: yup.string().required(),
|
|
16
13
|
password: yup.string().required(),
|
|
17
14
|
});
|
|
18
15
|
|
|
19
16
|
const sendEmailConfirmationSchema = yup.object({
|
|
20
|
-
email: yup
|
|
21
|
-
.string()
|
|
22
|
-
.email()
|
|
23
|
-
.required(),
|
|
17
|
+
email: yup.string().email().required(),
|
|
24
18
|
});
|
|
25
19
|
|
|
26
20
|
const validateEmailConfirmationSchema = yup.object({
|
|
@@ -29,10 +23,7 @@ const validateEmailConfirmationSchema = yup.object({
|
|
|
29
23
|
|
|
30
24
|
const forgotPasswordSchema = yup
|
|
31
25
|
.object({
|
|
32
|
-
email: yup
|
|
33
|
-
.string()
|
|
34
|
-
.email()
|
|
35
|
-
.required(),
|
|
26
|
+
email: yup.string().email().required(),
|
|
36
27
|
})
|
|
37
28
|
.noUnknown();
|
|
38
29
|
|
|
@@ -44,6 +35,17 @@ const resetPasswordSchema = yup
|
|
|
44
35
|
})
|
|
45
36
|
.noUnknown();
|
|
46
37
|
|
|
38
|
+
const changePasswordSchema = yup
|
|
39
|
+
.object({
|
|
40
|
+
password: yup.string().required(),
|
|
41
|
+
passwordConfirmation: yup
|
|
42
|
+
.string()
|
|
43
|
+
.required()
|
|
44
|
+
.oneOf([yup.ref('password')], 'Passwords do not match'),
|
|
45
|
+
currentPassword: yup.string().required(),
|
|
46
|
+
})
|
|
47
|
+
.noUnknown();
|
|
48
|
+
|
|
47
49
|
module.exports = {
|
|
48
50
|
validateCallbackBody: validateYupSchema(callbackSchema),
|
|
49
51
|
validateRegisterBody: validateYupSchema(registerSchema),
|
|
@@ -51,4 +53,5 @@ module.exports = {
|
|
|
51
53
|
validateEmailConfirmationBody: validateYupSchema(validateEmailConfirmationSchema),
|
|
52
54
|
validateForgotPasswordBody: validateYupSchema(forgotPasswordSchema),
|
|
53
55
|
validateResetPasswordBody: validateYupSchema(resetPasswordSchema),
|
|
56
|
+
validateChangePasswordBody: validateYupSchema(changePasswordSchema),
|
|
54
57
|
};
|