@strapi/plugin-users-permissions 0.0.0-next.fab38a080023b0850fc7a8ec8605d1ee6970e3c9 → 0.0.0-next.fc1775f7731f8999840e56e298a216b9a6c5c4ad
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/.eslintignore +1 -2
- package/.eslintrc +17 -0
- package/LICENSE +18 -3
- package/admin/src/components/BoundRoute/{index.js → index.jsx} +2 -2
- package/admin/src/components/FormModal/Input/{index.js → index.jsx} +32 -31
- package/admin/src/components/FormModal/index.jsx +115 -0
- package/admin/src/components/Permissions/PermissionRow/{CheckboxWrapper.js → CheckboxWrapper.jsx} +4 -3
- package/admin/src/components/Permissions/PermissionRow/{SubCategory.js → SubCategory.jsx} +13 -22
- package/admin/src/components/Permissions/index.jsx +47 -0
- package/admin/src/components/Permissions/reducer.js +1 -1
- package/admin/src/components/Policies/{index.js → index.jsx} +7 -5
- package/admin/src/components/UsersPermissions/{index.js → index.jsx} +15 -7
- package/admin/src/components/UsersPermissions/reducer.js +1 -1
- package/admin/src/index.js +16 -43
- package/admin/src/pages/AdvancedSettings/index.jsx +214 -0
- package/admin/src/pages/AdvancedSettings/utils/layout.js +20 -35
- package/admin/src/pages/AdvancedSettings/utils/schema.js +5 -2
- package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +156 -0
- package/admin/src/pages/EmailTemplates/components/{EmailTable.js → EmailTable.jsx} +20 -17
- package/admin/src/pages/EmailTemplates/{index.js → index.jsx} +36 -62
- package/admin/src/pages/EmailTemplates/utils/schema.js +18 -6
- package/admin/src/pages/Providers/{index.js → index.jsx} +98 -113
- package/admin/src/pages/Providers/utils/forms.js +23 -11
- package/admin/src/pages/Roles/constants.js +3 -3
- package/admin/src/pages/Roles/hooks/usePlugins.js +4 -4
- package/admin/src/pages/Roles/index.jsx +24 -0
- package/admin/src/pages/Roles/pages/{CreatePage.js → CreatePage.jsx} +53 -58
- package/admin/src/pages/Roles/pages/{EditPage.js → EditPage.jsx} +63 -68
- package/admin/src/pages/Roles/pages/ListPage/components/{TableBody.js → TableBody.jsx} +27 -31
- package/admin/src/pages/Roles/pages/ListPage/{index.js → index.jsx} +79 -55
- package/admin/src/translations/en.json +1 -1
- package/admin/src/utils/prefixPluginTranslations.js +13 -0
- package/dist/_chunks/ar-BguGUqwK.js +44 -0
- package/dist/_chunks/ar-BguGUqwK.js.map +1 -0
- package/dist/_chunks/ar-CK8BRRXB.mjs +44 -0
- package/dist/_chunks/ar-CK8BRRXB.mjs.map +1 -0
- package/dist/_chunks/cs-BVigMk0l.mjs +50 -0
- package/dist/_chunks/cs-BVigMk0l.mjs.map +1 -0
- package/dist/_chunks/cs-BW8-K_GY.js +50 -0
- package/dist/_chunks/cs-BW8-K_GY.js.map +1 -0
- package/dist/_chunks/de-BKUdRFI4.mjs +62 -0
- package/dist/_chunks/de-BKUdRFI4.mjs.map +1 -0
- package/dist/_chunks/de-owXpVluI.js +62 -0
- package/dist/_chunks/de-owXpVluI.js.map +1 -0
- package/dist/_chunks/dk-BQiTK50l.mjs +86 -0
- package/dist/_chunks/dk-BQiTK50l.mjs.map +1 -0
- package/dist/_chunks/dk-LXAnbuBk.js +86 -0
- package/dist/_chunks/dk-LXAnbuBk.js.map +1 -0
- package/dist/_chunks/en-DOHtPf-2.mjs +86 -0
- package/dist/_chunks/en-DOHtPf-2.mjs.map +1 -0
- package/dist/_chunks/en-MHo5mcsU.js +86 -0
- package/dist/_chunks/en-MHo5mcsU.js.map +1 -0
- package/dist/_chunks/es-BwLCLXAQ.js +86 -0
- package/dist/_chunks/es-BwLCLXAQ.js.map +1 -0
- package/dist/_chunks/es-DNgOVMjD.mjs +86 -0
- package/dist/_chunks/es-DNgOVMjD.mjs.map +1 -0
- package/dist/_chunks/fr-DkgRugiU.mjs +50 -0
- package/dist/_chunks/fr-DkgRugiU.mjs.map +1 -0
- package/dist/_chunks/fr-DkhpSjjm.js +50 -0
- package/dist/_chunks/fr-DkhpSjjm.js.map +1 -0
- package/dist/_chunks/id-BTemOeTZ.js +62 -0
- package/dist/_chunks/id-BTemOeTZ.js.map +1 -0
- package/dist/_chunks/id-BdEsvnaF.mjs +62 -0
- package/dist/_chunks/id-BdEsvnaF.mjs.map +1 -0
- package/dist/_chunks/index-6IfauKkm.js +365 -0
- package/dist/_chunks/index-6IfauKkm.js.map +1 -0
- package/dist/_chunks/index-BH2Kbto3.mjs +344 -0
- package/dist/_chunks/index-BH2Kbto3.mjs.map +1 -0
- package/dist/_chunks/index-BPLSKK9l.mjs +1142 -0
- package/dist/_chunks/index-BPLSKK9l.mjs.map +1 -0
- package/dist/_chunks/index-BteVBZ5H.js +252 -0
- package/dist/_chunks/index-BteVBZ5H.js.map +1 -0
- package/dist/_chunks/index-CItjLvCp.mjs +617 -0
- package/dist/_chunks/index-CItjLvCp.mjs.map +1 -0
- package/dist/_chunks/index-CU5dmnhc.js +280 -0
- package/dist/_chunks/index-CU5dmnhc.js.map +1 -0
- package/dist/_chunks/index-D4QvcqXm-BWNRataA.mjs +11090 -0
- package/dist/_chunks/index-D4QvcqXm-BWNRataA.mjs.map +1 -0
- package/dist/_chunks/index-D4QvcqXm-HpyyuCo7.js +11113 -0
- package/dist/_chunks/index-D4QvcqXm-HpyyuCo7.js.map +1 -0
- package/dist/_chunks/index-DDd0fVR6.js +1171 -0
- package/dist/_chunks/index-DDd0fVR6.js.map +1 -0
- package/dist/_chunks/index-DMkiPQOl.mjs +262 -0
- package/dist/_chunks/index-DMkiPQOl.mjs.map +1 -0
- package/dist/_chunks/index-DZnDAnLg.mjs +253 -0
- package/dist/_chunks/index-DZnDAnLg.mjs.map +1 -0
- package/dist/_chunks/index-Dd0JjBda.js +639 -0
- package/dist/_chunks/index-Dd0JjBda.js.map +1 -0
- package/dist/_chunks/it-B-rv0E24.mjs +62 -0
- package/dist/_chunks/it-B-rv0E24.mjs.map +1 -0
- package/dist/_chunks/it-D1rH6V6_.js +62 -0
- package/dist/_chunks/it-D1rH6V6_.js.map +1 -0
- package/dist/_chunks/ja-C8K-VBPD.mjs +48 -0
- package/dist/_chunks/ja-C8K-VBPD.mjs.map +1 -0
- package/dist/_chunks/ja-DqShgTMf.js +48 -0
- package/dist/_chunks/ja-DqShgTMf.js.map +1 -0
- package/dist/_chunks/ko-B9DGEPWH.js +86 -0
- package/dist/_chunks/ko-B9DGEPWH.js.map +1 -0
- package/dist/_chunks/ko-Busb0wIY.mjs +86 -0
- package/dist/_chunks/ko-Busb0wIY.mjs.map +1 -0
- package/dist/_chunks/ms-ByvsQjRt.mjs +49 -0
- package/dist/_chunks/ms-ByvsQjRt.mjs.map +1 -0
- package/dist/_chunks/ms-CPBU3LWf.js +49 -0
- package/dist/_chunks/ms-CPBU3LWf.js.map +1 -0
- package/dist/_chunks/nl-5qO8Rpcy.mjs +48 -0
- package/dist/_chunks/nl-5qO8Rpcy.mjs.map +1 -0
- package/dist/_chunks/nl-CwNB6YoO.js +48 -0
- package/dist/_chunks/nl-CwNB6YoO.js.map +1 -0
- package/dist/_chunks/pl-BdIzifBE.mjs +86 -0
- package/dist/_chunks/pl-BdIzifBE.mjs.map +1 -0
- package/dist/_chunks/pl-Do9UD69f.js +86 -0
- package/dist/_chunks/pl-Do9UD69f.js.map +1 -0
- package/dist/_chunks/pt-BIO24ioG.mjs +48 -0
- package/dist/_chunks/pt-BIO24ioG.mjs.map +1 -0
- package/dist/_chunks/pt-BR-D7dZhxuP.js +44 -0
- package/dist/_chunks/pt-BR-D7dZhxuP.js.map +1 -0
- package/dist/_chunks/pt-BR-f0p23AQZ.mjs +44 -0
- package/dist/_chunks/pt-BR-f0p23AQZ.mjs.map +1 -0
- package/dist/_chunks/pt-fdvyOnUp.js +48 -0
- package/dist/_chunks/pt-fdvyOnUp.js.map +1 -0
- package/dist/_chunks/ru-C94rjPGA.js +86 -0
- package/dist/_chunks/ru-C94rjPGA.js.map +1 -0
- package/dist/_chunks/ru-VWy-IB7K.mjs +86 -0
- package/dist/_chunks/ru-VWy-IB7K.mjs.map +1 -0
- package/dist/_chunks/sk-BABEhykl.js +50 -0
- package/dist/_chunks/sk-BABEhykl.js.map +1 -0
- package/dist/_chunks/sk-B_LIcepm.mjs +50 -0
- package/dist/_chunks/sk-B_LIcepm.mjs.map +1 -0
- package/dist/_chunks/sv-ABLKOokl.mjs +86 -0
- package/dist/_chunks/sv-ABLKOokl.mjs.map +1 -0
- package/dist/_chunks/sv-Be43LhA9.js +86 -0
- package/dist/_chunks/sv-Be43LhA9.js.map +1 -0
- package/dist/_chunks/th-DKyP7ueR.mjs +60 -0
- package/dist/_chunks/th-DKyP7ueR.mjs.map +1 -0
- package/dist/_chunks/th-DgVhVLhL.js +60 -0
- package/dist/_chunks/th-DgVhVLhL.js.map +1 -0
- package/dist/_chunks/tr-B_idhkEs.js +85 -0
- package/dist/_chunks/tr-B_idhkEs.js.map +1 -0
- package/dist/_chunks/tr-qa1Q5UjC.mjs +85 -0
- package/dist/_chunks/tr-qa1Q5UjC.mjs.map +1 -0
- package/dist/_chunks/uk-BmRqbeQc.mjs +49 -0
- package/dist/_chunks/uk-BmRqbeQc.mjs.map +1 -0
- package/dist/_chunks/uk-LHOivnhP.js +49 -0
- package/dist/_chunks/uk-LHOivnhP.js.map +1 -0
- package/dist/_chunks/vi-CdVRdKDw.js +50 -0
- package/dist/_chunks/vi-CdVRdKDw.js.map +1 -0
- package/dist/_chunks/vi-HW-EdMea.mjs +50 -0
- package/dist/_chunks/vi-HW-EdMea.mjs.map +1 -0
- package/dist/_chunks/zh-5hKkVPA4.mjs +86 -0
- package/dist/_chunks/zh-5hKkVPA4.mjs.map +1 -0
- package/dist/_chunks/zh-Cuq8gMnF.js +86 -0
- package/dist/_chunks/zh-Cuq8gMnF.js.map +1 -0
- package/dist/_chunks/zh-Hans-BHilK-yc.mjs +86 -0
- package/dist/_chunks/zh-Hans-BHilK-yc.mjs.map +1 -0
- package/dist/_chunks/zh-Hans-GQDMKtY4.js +86 -0
- package/dist/_chunks/zh-Hans-GQDMKtY4.js.map +1 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/index.mjs +5 -0
- package/dist/admin/index.mjs.map +1 -0
- package/package.json +45 -27
- package/packup.config.ts +22 -0
- package/server/bootstrap/index.js +18 -51
- package/server/bootstrap/users-permissions-actions.js +6 -0
- package/server/config.js +29 -0
- package/server/content-types/user/index.js +0 -1
- package/server/controllers/auth.js +62 -63
- package/server/controllers/content-manager-user.js +28 -30
- package/server/controllers/role.js +17 -4
- package/server/controllers/user.js +8 -9
- package/server/controllers/validation/auth.js +81 -25
- package/server/graphql/types/index.js +1 -0
- package/server/graphql/types/me.js +1 -0
- package/server/graphql/types/user-input.js +20 -0
- package/server/middlewares/rateLimit.js +1 -1
- package/server/register.js +1 -1
- package/server/services/jwt.js +3 -3
- package/server/services/permission.js +3 -7
- package/server/services/providers-registry.js +469 -261
- package/server/services/providers.js +10 -5
- package/server/services/role.js +15 -13
- package/server/services/user.js +56 -19
- package/server/services/users-permissions.js +15 -13
- package/server/utils/index.d.ts +2 -1
- package/server/utils/sanitize/sanitizers.js +7 -3
- package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +2 -2
- package/.eslintrc.js +0 -14
- package/admin/src/components/FormModal/index.js +0 -126
- package/admin/src/components/Permissions/index.js +0 -55
- package/admin/src/pages/AdvancedSettings/index.js +0 -259
- package/admin/src/pages/EmailTemplates/components/EmailForm.js +0 -176
- package/admin/src/pages/Roles/index.js +0 -33
- package/admin/src/pages/Roles/pages/ListPage/utils/api.js +0 -30
- package/server/bootstrap/grant-config.js +0 -131
- package/strapi-admin.js +0 -3
- package/strapi-server.js +0 -3
- /package/admin/src/components/Permissions/PermissionRow/{index.js → index.jsx} +0 -0
- /package/admin/src/contexts/UsersPermissionsContext/{index.js → index.jsx} +0 -0
|
@@ -11,9 +11,6 @@ const crypto = require('crypto');
|
|
|
11
11
|
const _ = require('lodash');
|
|
12
12
|
const { concat, compact, isArray } = require('lodash/fp');
|
|
13
13
|
const utils = require('@strapi/utils');
|
|
14
|
-
const {
|
|
15
|
-
contentTypes: { getNonWritableAttributes },
|
|
16
|
-
} = require('@strapi/utils');
|
|
17
14
|
const { getService } = require('../utils');
|
|
18
15
|
const {
|
|
19
16
|
validateCallbackBody,
|
|
@@ -25,17 +22,16 @@ const {
|
|
|
25
22
|
validateChangePasswordBody,
|
|
26
23
|
} = require('./validation/auth');
|
|
27
24
|
|
|
28
|
-
const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
|
|
29
25
|
const { ApplicationError, ValidationError, ForbiddenError } = utils.errors;
|
|
30
26
|
|
|
31
27
|
const sanitizeUser = (user, ctx) => {
|
|
32
28
|
const { auth } = ctx.state;
|
|
33
29
|
const userSchema = strapi.getModel('plugin::users-permissions.user');
|
|
34
30
|
|
|
35
|
-
return
|
|
31
|
+
return strapi.contentAPI.sanitize.output(user, userSchema, { auth });
|
|
36
32
|
};
|
|
37
33
|
|
|
38
|
-
module.exports = {
|
|
34
|
+
module.exports = ({ strapi }) => ({
|
|
39
35
|
async callback(ctx) {
|
|
40
36
|
const provider = ctx.params.provider || 'local';
|
|
41
37
|
const params = ctx.request.body;
|
|
@@ -55,7 +51,7 @@ module.exports = {
|
|
|
55
51
|
const { identifier } = params;
|
|
56
52
|
|
|
57
53
|
// Check if the user exists.
|
|
58
|
-
const user = await strapi.query('plugin::users-permissions.user').findOne({
|
|
54
|
+
const user = await strapi.db.query('plugin::users-permissions.user').findOne({
|
|
59
55
|
where: {
|
|
60
56
|
provider,
|
|
61
57
|
$or: [{ email: identifier.toLowerCase() }, { username: identifier }],
|
|
@@ -118,13 +114,17 @@ module.exports = {
|
|
|
118
114
|
throw new ApplicationError('You must be authenticated to reset your password');
|
|
119
115
|
}
|
|
120
116
|
|
|
121
|
-
const
|
|
117
|
+
const validations = strapi.config.get('plugin::users-permissions.validationRules');
|
|
122
118
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
const { currentPassword, password } = await validateChangePasswordBody(
|
|
120
|
+
ctx.request.body,
|
|
121
|
+
validations
|
|
126
122
|
);
|
|
127
123
|
|
|
124
|
+
const user = await strapi.db
|
|
125
|
+
.query('plugin::users-permissions.user')
|
|
126
|
+
.findOne({ where: { id: ctx.state.user.id } });
|
|
127
|
+
|
|
128
128
|
const validPassword = await getService('user').validatePassword(currentPassword, user.password);
|
|
129
129
|
|
|
130
130
|
if (!validPassword) {
|
|
@@ -144,15 +144,18 @@ module.exports = {
|
|
|
144
144
|
},
|
|
145
145
|
|
|
146
146
|
async resetPassword(ctx) {
|
|
147
|
+
const validations = strapi.config.get('plugin::users-permissions.validationRules');
|
|
148
|
+
|
|
147
149
|
const { password, passwordConfirmation, code } = await validateResetPasswordBody(
|
|
148
|
-
ctx.request.body
|
|
150
|
+
ctx.request.body,
|
|
151
|
+
validations
|
|
149
152
|
);
|
|
150
153
|
|
|
151
154
|
if (password !== passwordConfirmation) {
|
|
152
155
|
throw new ValidationError('Passwords do not match');
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
const user = await strapi
|
|
158
|
+
const user = await strapi.db
|
|
156
159
|
.query('plugin::users-permissions.user')
|
|
157
160
|
.findOne({ where: { resetPasswordToken: code } });
|
|
158
161
|
|
|
@@ -201,10 +204,28 @@ module.exports = {
|
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
// Ability to pass OAuth callback dynamically
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
const queryCustomCallback = _.get(ctx, 'query.callback');
|
|
208
|
+
const dynamicSessionCallback = _.get(ctx, 'session.grant.dynamic.callback');
|
|
209
|
+
|
|
210
|
+
const customCallback = queryCustomCallback ?? dynamicSessionCallback;
|
|
211
|
+
|
|
212
|
+
// The custom callback is validated to make sure it's not redirecting to an unwanted actor.
|
|
213
|
+
if (customCallback !== undefined) {
|
|
214
|
+
try {
|
|
215
|
+
// We're extracting the callback validator from the plugin config since it can be user-customized
|
|
216
|
+
const { validate: validateCallback } = strapi
|
|
217
|
+
.plugin('users-permissions')
|
|
218
|
+
.config('callback');
|
|
219
|
+
|
|
220
|
+
await validateCallback(customCallback, grantConfig[provider]);
|
|
221
|
+
|
|
222
|
+
grantConfig[provider].callback = customCallback;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
throw new ValidationError('Invalid callback URL provided', { callback: customCallback });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Build a valid redirect URI for the current provider
|
|
208
229
|
grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);
|
|
209
230
|
|
|
210
231
|
return grant(grantConfig)(ctx, next);
|
|
@@ -219,7 +240,7 @@ module.exports = {
|
|
|
219
240
|
const advancedSettings = await pluginStore.get({ key: 'advanced' });
|
|
220
241
|
|
|
221
242
|
// Find the user by email.
|
|
222
|
-
const user = await strapi
|
|
243
|
+
const user = await strapi.db
|
|
223
244
|
.query('plugin::users-permissions.user')
|
|
224
245
|
.findOne({ where: { email: email.toLowerCase() } });
|
|
225
246
|
|
|
@@ -237,8 +258,8 @@ module.exports = {
|
|
|
237
258
|
resetPasswordSettings.message,
|
|
238
259
|
{
|
|
239
260
|
URL: advancedSettings.email_reset_password,
|
|
240
|
-
SERVER_URL:
|
|
241
|
-
ADMIN_URL:
|
|
261
|
+
SERVER_URL: strapi.config.get('server.absoluteUrl'),
|
|
262
|
+
ADMIN_URL: strapi.config.get('admin.absoluteUrl'),
|
|
242
263
|
USER: userInfo,
|
|
243
264
|
TOKEN: resetPasswordToken,
|
|
244
265
|
}
|
|
@@ -281,55 +302,32 @@ module.exports = {
|
|
|
281
302
|
throw new ApplicationError('Register action is currently disabled');
|
|
282
303
|
}
|
|
283
304
|
|
|
284
|
-
const { register } = strapi.config.get('plugin
|
|
305
|
+
const { register } = strapi.config.get('plugin::users-permissions');
|
|
285
306
|
const alwaysAllowedKeys = ['username', 'password', 'email'];
|
|
286
|
-
const userModel = strapi.contentTypes['plugin::users-permissions.user'];
|
|
287
|
-
const { attributes } = userModel;
|
|
288
|
-
|
|
289
|
-
const nonWritable = getNonWritableAttributes(userModel);
|
|
290
307
|
|
|
308
|
+
// Note that we intentionally do not filter allowedFields to allow a project to explicitly accept private or other Strapi field on registration
|
|
291
309
|
const allowedKeys = compact(
|
|
292
|
-
concat(
|
|
293
|
-
alwaysAllowedKeys,
|
|
294
|
-
isArray(register?.allowedFields)
|
|
295
|
-
? // Note that we do not filter allowedFields in case a user explicitly chooses to allow a private or otherwise omitted field on registration
|
|
296
|
-
register.allowedFields // if null or undefined, compact will remove it
|
|
297
|
-
: // to prevent breaking changes, if allowedFields is not set in config, we only remove private and known dangerous user schema fields
|
|
298
|
-
// TODO V5: allowedFields defaults to [] when undefined and remove this case
|
|
299
|
-
Object.keys(attributes).filter(
|
|
300
|
-
(key) =>
|
|
301
|
-
!nonWritable.includes(key) &&
|
|
302
|
-
!attributes[key].private &&
|
|
303
|
-
![
|
|
304
|
-
// many of these are included in nonWritable, but we'll list them again to be safe and since we're removing this code in v5 anyway
|
|
305
|
-
// Strapi user schema fields
|
|
306
|
-
'confirmed',
|
|
307
|
-
'blocked',
|
|
308
|
-
'confirmationToken',
|
|
309
|
-
'resetPasswordToken',
|
|
310
|
-
'provider',
|
|
311
|
-
'id',
|
|
312
|
-
'role',
|
|
313
|
-
// other Strapi fields that might be added
|
|
314
|
-
'createdAt',
|
|
315
|
-
'updatedAt',
|
|
316
|
-
'createdBy',
|
|
317
|
-
'updatedBy',
|
|
318
|
-
'publishedAt', // d&p
|
|
319
|
-
'strapi_reviewWorkflows_stage', // review workflows
|
|
320
|
-
].includes(key)
|
|
321
|
-
)
|
|
322
|
-
)
|
|
310
|
+
concat(alwaysAllowedKeys, isArray(register?.allowedFields) ? register.allowedFields : [])
|
|
323
311
|
);
|
|
324
312
|
|
|
313
|
+
// Check if there are any keys in requestBody that are not in allowedKeys
|
|
314
|
+
const invalidKeys = Object.keys(ctx.request.body).filter((key) => !allowedKeys.includes(key));
|
|
315
|
+
|
|
316
|
+
if (invalidKeys.length > 0) {
|
|
317
|
+
// If there are invalid keys, throw an error
|
|
318
|
+
throw new ValidationError(`Invalid parameters: ${invalidKeys.join(', ')}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
325
321
|
const params = {
|
|
326
322
|
..._.pick(ctx.request.body, allowedKeys),
|
|
327
323
|
provider: 'local',
|
|
328
324
|
};
|
|
329
325
|
|
|
330
|
-
|
|
326
|
+
const validations = strapi.config.get('plugin::users-permissions.validationRules');
|
|
331
327
|
|
|
332
|
-
|
|
328
|
+
await validateRegisterBody(params, validations);
|
|
329
|
+
|
|
330
|
+
const role = await strapi.db
|
|
333
331
|
.query('plugin::users-permissions.role')
|
|
334
332
|
.findOne({ where: { type: settings.default_role } });
|
|
335
333
|
|
|
@@ -348,7 +346,7 @@ module.exports = {
|
|
|
348
346
|
],
|
|
349
347
|
};
|
|
350
348
|
|
|
351
|
-
const conflictingUserCount = await strapi.query('plugin::users-permissions.user').count({
|
|
349
|
+
const conflictingUserCount = await strapi.db.query('plugin::users-permissions.user').count({
|
|
352
350
|
where: { ...identifierFilter, provider },
|
|
353
351
|
});
|
|
354
352
|
|
|
@@ -357,7 +355,7 @@ module.exports = {
|
|
|
357
355
|
}
|
|
358
356
|
|
|
359
357
|
if (settings.unique_email) {
|
|
360
|
-
const conflictingUserCount = await strapi.query('plugin::users-permissions.user').count({
|
|
358
|
+
const conflictingUserCount = await strapi.db.query('plugin::users-permissions.user').count({
|
|
361
359
|
where: { ...identifierFilter },
|
|
362
360
|
});
|
|
363
361
|
|
|
@@ -382,7 +380,8 @@ module.exports = {
|
|
|
382
380
|
try {
|
|
383
381
|
await getService('user').sendConfirmationEmail(sanitizedUser);
|
|
384
382
|
} catch (err) {
|
|
385
|
-
|
|
383
|
+
strapi.log.error(err);
|
|
384
|
+
throw new ApplicationError('Error sending confirmation email');
|
|
386
385
|
}
|
|
387
386
|
|
|
388
387
|
return ctx.send({ user: sanitizedUser });
|
|
@@ -427,7 +426,7 @@ module.exports = {
|
|
|
427
426
|
async sendEmailConfirmation(ctx) {
|
|
428
427
|
const { email } = await validateSendEmailConfirmationBody(ctx.request.body);
|
|
429
428
|
|
|
430
|
-
const user = await strapi.query('plugin::users-permissions.user').findOne({
|
|
429
|
+
const user = await strapi.db.query('plugin::users-permissions.user').findOne({
|
|
431
430
|
where: { email: email.toLowerCase() },
|
|
432
431
|
});
|
|
433
432
|
|
|
@@ -450,4 +449,4 @@ module.exports = {
|
|
|
450
449
|
sent: true,
|
|
451
450
|
});
|
|
452
451
|
},
|
|
453
|
-
};
|
|
452
|
+
});
|
|
@@ -17,24 +17,25 @@ const ACTIONS = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const findEntityAndCheckPermissions = async (ability, action, model, id) => {
|
|
20
|
-
const
|
|
21
|
-
where: { id },
|
|
20
|
+
const doc = await strapi.service('plugin::content-manager.document-manager').findOne(id, model, {
|
|
22
21
|
populate: [`${CREATED_BY_ATTRIBUTE}.roles`],
|
|
23
22
|
});
|
|
24
23
|
|
|
25
|
-
if (_.isNil(
|
|
24
|
+
if (_.isNil(doc)) {
|
|
26
25
|
throw new NotFoundError();
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
const pm = strapi
|
|
28
|
+
const pm = strapi
|
|
29
|
+
.service('admin::permission')
|
|
30
|
+
.createPermissionsManager({ ability, action, model });
|
|
30
31
|
|
|
31
|
-
if (pm.ability.cannot(pm.action, pm.toSubject(
|
|
32
|
+
if (pm.ability.cannot(pm.action, pm.toSubject(doc))) {
|
|
32
33
|
throw new ForbiddenError();
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const
|
|
36
|
+
const docWithoutCreatorRoles = _.omit(doc, `${CREATED_BY_ATTRIBUTE}.roles`);
|
|
36
37
|
|
|
37
|
-
return { pm,
|
|
38
|
+
return { pm, doc: docWithoutCreatorRoles };
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
module.exports = {
|
|
@@ -48,7 +49,7 @@ module.exports = {
|
|
|
48
49
|
|
|
49
50
|
const { email, username } = body;
|
|
50
51
|
|
|
51
|
-
const pm = strapi.admin
|
|
52
|
+
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
52
53
|
ability: userAbility,
|
|
53
54
|
action: ACTIONS.create,
|
|
54
55
|
model: userModel,
|
|
@@ -66,7 +67,7 @@ module.exports = {
|
|
|
66
67
|
|
|
67
68
|
await validateCreateUserBody(ctx.request.body);
|
|
68
69
|
|
|
69
|
-
const userWithSameUsername = await strapi
|
|
70
|
+
const userWithSameUsername = await strapi.db
|
|
70
71
|
.query('plugin::users-permissions.user')
|
|
71
72
|
.findOne({ where: { username } });
|
|
72
73
|
|
|
@@ -75,7 +76,7 @@ module.exports = {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
if (advanced.unique_email) {
|
|
78
|
-
const userWithSameEmail = await strapi
|
|
79
|
+
const userWithSameEmail = await strapi.db
|
|
79
80
|
.query('plugin::users-permissions.user')
|
|
80
81
|
.findOne({ where: { email: email.toLowerCase() } });
|
|
81
82
|
|
|
@@ -93,18 +94,11 @@ module.exports = {
|
|
|
93
94
|
|
|
94
95
|
user.email = _.toLower(user.email);
|
|
95
96
|
|
|
96
|
-
if (!user.role) {
|
|
97
|
-
const defaultRole = await strapi
|
|
98
|
-
.query('plugin::users-permissions.role')
|
|
99
|
-
.findOne({ where: { type: advanced.default_role } });
|
|
100
|
-
|
|
101
|
-
user.role = defaultRole.id;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
97
|
try {
|
|
105
98
|
const data = await strapi
|
|
106
|
-
.service('plugin::content-manager.
|
|
107
|
-
.create(
|
|
99
|
+
.service('plugin::content-manager.document-manager')
|
|
100
|
+
.create(userModel, { data: user });
|
|
101
|
+
|
|
108
102
|
const sanitizedData = await pm.sanitizeOutput(data, { action: ACTIONS.read });
|
|
109
103
|
|
|
110
104
|
ctx.created(sanitizedData);
|
|
@@ -118,7 +112,7 @@ module.exports = {
|
|
|
118
112
|
*/
|
|
119
113
|
|
|
120
114
|
async update(ctx) {
|
|
121
|
-
const { id } = ctx.params;
|
|
115
|
+
const { id: documentId } = ctx.params;
|
|
122
116
|
const { body } = ctx.request;
|
|
123
117
|
const { user: admin, userAbility } = ctx.state;
|
|
124
118
|
|
|
@@ -128,13 +122,14 @@ module.exports = {
|
|
|
128
122
|
|
|
129
123
|
const { email, username, password } = body;
|
|
130
124
|
|
|
131
|
-
const { pm,
|
|
125
|
+
const { pm, doc } = await findEntityAndCheckPermissions(
|
|
132
126
|
userAbility,
|
|
133
127
|
ACTIONS.edit,
|
|
134
128
|
userModel,
|
|
135
|
-
|
|
129
|
+
documentId
|
|
136
130
|
);
|
|
137
|
-
|
|
131
|
+
|
|
132
|
+
const user = doc;
|
|
138
133
|
|
|
139
134
|
await validateUpdateUserBody(ctx.request.body);
|
|
140
135
|
|
|
@@ -143,23 +138,24 @@ module.exports = {
|
|
|
143
138
|
}
|
|
144
139
|
|
|
145
140
|
if (_.has(body, 'username')) {
|
|
146
|
-
const userWithSameUsername = await strapi
|
|
141
|
+
const userWithSameUsername = await strapi.db
|
|
147
142
|
.query('plugin::users-permissions.user')
|
|
148
143
|
.findOne({ where: { username } });
|
|
149
144
|
|
|
150
|
-
if (userWithSameUsername && _.toString(userWithSameUsername.id) !== _.toString(id)) {
|
|
145
|
+
if (userWithSameUsername && _.toString(userWithSameUsername.id) !== _.toString(user.id)) {
|
|
151
146
|
throw new ApplicationError('Username already taken');
|
|
152
147
|
}
|
|
153
148
|
}
|
|
154
149
|
|
|
155
150
|
if (_.has(body, 'email') && advancedConfigs.unique_email) {
|
|
156
|
-
const userWithSameEmail = await strapi
|
|
151
|
+
const userWithSameEmail = await strapi.db
|
|
157
152
|
.query('plugin::users-permissions.user')
|
|
158
153
|
.findOne({ where: { email: _.toLower(email) } });
|
|
159
154
|
|
|
160
|
-
if (userWithSameEmail && _.toString(userWithSameEmail.id) !== _.toString(id)) {
|
|
155
|
+
if (userWithSameEmail && _.toString(userWithSameEmail.id) !== _.toString(user.id)) {
|
|
161
156
|
throw new ApplicationError('Email already taken');
|
|
162
157
|
}
|
|
158
|
+
|
|
163
159
|
body.email = _.toLower(body.email);
|
|
164
160
|
}
|
|
165
161
|
|
|
@@ -167,8 +163,10 @@ module.exports = {
|
|
|
167
163
|
const updateData = _.omit({ ...sanitizedData, updatedBy: admin.id }, 'createdBy');
|
|
168
164
|
|
|
169
165
|
const data = await strapi
|
|
170
|
-
.service('plugin::content-manager.
|
|
171
|
-
.update(
|
|
166
|
+
.service('plugin::content-manager.document-manager')
|
|
167
|
+
.update(documentId, userModel, {
|
|
168
|
+
data: updateData,
|
|
169
|
+
});
|
|
172
170
|
|
|
173
171
|
ctx.body = await pm.sanitizeOutput(data, { action: ACTIONS.read });
|
|
174
172
|
},
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
-
const {
|
|
4
|
+
const { async, errors } = require('@strapi/utils');
|
|
5
5
|
const { getService } = require('../utils');
|
|
6
6
|
const { validateDeleteRoleBody } = require('./validation/user');
|
|
7
7
|
|
|
8
|
+
const { ApplicationError, ValidationError } = errors;
|
|
9
|
+
|
|
10
|
+
const sanitizeOutput = async (role) => {
|
|
11
|
+
const { sanitizeLocalizationFields } = strapi.plugin('i18n').service('sanitize');
|
|
12
|
+
const schema = strapi.getModel('plugin::users-permissions.role');
|
|
13
|
+
|
|
14
|
+
return async.pipe(sanitizeLocalizationFields(schema))(role);
|
|
15
|
+
};
|
|
16
|
+
|
|
8
17
|
module.exports = {
|
|
9
18
|
/**
|
|
10
19
|
* Default action.
|
|
@@ -30,13 +39,17 @@ module.exports = {
|
|
|
30
39
|
return ctx.notFound();
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
|
|
42
|
+
const safeRole = await sanitizeOutput(role);
|
|
43
|
+
|
|
44
|
+
ctx.send({ role: safeRole });
|
|
34
45
|
},
|
|
35
46
|
|
|
36
47
|
async find(ctx) {
|
|
37
48
|
const roles = await getService('role').find();
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
const safeRoles = await Promise.all(roles.map(sanitizeOutput));
|
|
51
|
+
|
|
52
|
+
ctx.send({ roles: safeRoles });
|
|
40
53
|
},
|
|
41
54
|
|
|
42
55
|
async updateRole(ctx) {
|
|
@@ -59,7 +72,7 @@ module.exports = {
|
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
// Fetch public role.
|
|
62
|
-
const publicRole = await strapi
|
|
75
|
+
const publicRole = await strapi.db
|
|
63
76
|
.query('plugin::users-permissions.role')
|
|
64
77
|
.findOne({ where: { type: 'public' } });
|
|
65
78
|
|
|
@@ -11,28 +11,27 @@ const utils = require('@strapi/utils');
|
|
|
11
11
|
const { getService } = require('../utils');
|
|
12
12
|
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
13
13
|
|
|
14
|
-
const { sanitize, validate } = utils;
|
|
15
14
|
const { ApplicationError, ValidationError, NotFoundError } = utils.errors;
|
|
16
15
|
|
|
17
16
|
const sanitizeOutput = async (user, ctx) => {
|
|
18
17
|
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
19
18
|
const { auth } = ctx.state;
|
|
20
19
|
|
|
21
|
-
return
|
|
20
|
+
return strapi.contentAPI.sanitize.output(user, schema, { auth });
|
|
22
21
|
};
|
|
23
22
|
|
|
24
23
|
const validateQuery = async (query, ctx) => {
|
|
25
24
|
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
26
25
|
const { auth } = ctx.state;
|
|
27
26
|
|
|
28
|
-
return
|
|
27
|
+
return strapi.contentAPI.validate.query(query, schema, { auth });
|
|
29
28
|
};
|
|
30
29
|
|
|
31
30
|
const sanitizeQuery = async (query, ctx) => {
|
|
32
31
|
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
33
32
|
const { auth } = ctx.state;
|
|
34
33
|
|
|
35
|
-
return
|
|
34
|
+
return strapi.contentAPI.sanitize.query(query, schema, { auth });
|
|
36
35
|
};
|
|
37
36
|
|
|
38
37
|
module.exports = {
|
|
@@ -49,7 +48,7 @@ module.exports = {
|
|
|
49
48
|
|
|
50
49
|
const { email, username, role } = ctx.request.body;
|
|
51
50
|
|
|
52
|
-
const userWithSameUsername = await strapi
|
|
51
|
+
const userWithSameUsername = await strapi.db
|
|
53
52
|
.query('plugin::users-permissions.user')
|
|
54
53
|
.findOne({ where: { username } });
|
|
55
54
|
|
|
@@ -58,7 +57,7 @@ module.exports = {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
if (advanced.unique_email) {
|
|
61
|
-
const userWithSameEmail = await strapi
|
|
60
|
+
const userWithSameEmail = await strapi.db
|
|
62
61
|
.query('plugin::users-permissions.user')
|
|
63
62
|
.findOne({ where: { email: email.toLowerCase() } });
|
|
64
63
|
|
|
@@ -74,7 +73,7 @@ module.exports = {
|
|
|
74
73
|
};
|
|
75
74
|
|
|
76
75
|
if (!role) {
|
|
77
|
-
const defaultRole = await strapi
|
|
76
|
+
const defaultRole = await strapi.db
|
|
78
77
|
.query('plugin::users-permissions.role')
|
|
79
78
|
.findOne({ where: { type: advanced.default_role } });
|
|
80
79
|
|
|
@@ -115,7 +114,7 @@ module.exports = {
|
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
if (_.has(ctx.request.body, 'username')) {
|
|
118
|
-
const userWithSameUsername = await strapi
|
|
117
|
+
const userWithSameUsername = await strapi.db
|
|
119
118
|
.query('plugin::users-permissions.user')
|
|
120
119
|
.findOne({ where: { username } });
|
|
121
120
|
|
|
@@ -125,7 +124,7 @@ module.exports = {
|
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
|
|
128
|
-
const userWithSameEmail = await strapi
|
|
127
|
+
const userWithSameEmail = await strapi.db
|
|
129
128
|
.query('plugin::users-permissions.user')
|
|
130
129
|
.findOne({ where: { email: email.toLowerCase() } });
|
|
131
130
|
|
|
@@ -7,11 +7,27 @@ const callbackSchema = yup.object({
|
|
|
7
7
|
password: yup.string().required(),
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const createRegisterSchema = (config) =>
|
|
11
|
+
yup.object({
|
|
12
|
+
email: yup.string().email().required(),
|
|
13
|
+
username: yup.string().required(),
|
|
14
|
+
password: yup
|
|
15
|
+
.string()
|
|
16
|
+
.required()
|
|
17
|
+
.test(async function (value) {
|
|
18
|
+
if (typeof config?.validatePassword === 'function') {
|
|
19
|
+
try {
|
|
20
|
+
const isValid = await config.validatePassword(value);
|
|
21
|
+
if (!isValid) {
|
|
22
|
+
return this.createError({ message: 'Password validation failed.' });
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return this.createError({ message: error.message || 'An error occurred.' });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
15
31
|
|
|
16
32
|
const sendEmailConfirmationSchema = yup.object({
|
|
17
33
|
email: yup.string().email().required(),
|
|
@@ -27,31 +43,71 @@ const forgotPasswordSchema = yup
|
|
|
27
43
|
})
|
|
28
44
|
.noUnknown();
|
|
29
45
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
const createResetPasswordSchema = (config) =>
|
|
47
|
+
yup
|
|
48
|
+
.object({
|
|
49
|
+
password: yup
|
|
50
|
+
.string()
|
|
51
|
+
.required()
|
|
52
|
+
.test(async function (value) {
|
|
53
|
+
if (typeof config?.validatePassword === 'function') {
|
|
54
|
+
try {
|
|
55
|
+
const isValid = await config.validatePassword(value);
|
|
56
|
+
if (!isValid) {
|
|
57
|
+
return this.createError({ message: 'Password validation failed.' });
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return this.createError({ message: error.message || 'An error occurred.' });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}),
|
|
37
65
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.required()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
passwordConfirmation: yup
|
|
67
|
+
.string()
|
|
68
|
+
.required()
|
|
69
|
+
.oneOf([yup.ref('password')], 'Passwords do not match'),
|
|
70
|
+
|
|
71
|
+
code: yup.string().required(),
|
|
72
|
+
})
|
|
73
|
+
.noUnknown();
|
|
74
|
+
|
|
75
|
+
const createChangePasswordSchema = (config) =>
|
|
76
|
+
yup
|
|
77
|
+
.object({
|
|
78
|
+
password: yup
|
|
79
|
+
.string()
|
|
80
|
+
.required()
|
|
81
|
+
.test(async function (value) {
|
|
82
|
+
if (typeof config?.validatePassword === 'function') {
|
|
83
|
+
try {
|
|
84
|
+
const isValid = await config.validatePassword(value);
|
|
85
|
+
if (!isValid) {
|
|
86
|
+
return this.createError({ message: 'Password validation failed.' });
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return this.createError({ message: error.message || 'An error occurred.' });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}),
|
|
94
|
+
passwordConfirmation: yup
|
|
95
|
+
.string()
|
|
96
|
+
.required()
|
|
97
|
+
.oneOf([yup.ref('password')], 'Passwords do not match'),
|
|
98
|
+
currentPassword: yup.string().required(),
|
|
99
|
+
})
|
|
100
|
+
.noUnknown();
|
|
48
101
|
|
|
49
102
|
module.exports = {
|
|
50
103
|
validateCallbackBody: validateYupSchema(callbackSchema),
|
|
51
|
-
validateRegisterBody:
|
|
104
|
+
validateRegisterBody: (payload, config) =>
|
|
105
|
+
validateYupSchema(createRegisterSchema(config))(payload),
|
|
52
106
|
validateSendEmailConfirmationBody: validateYupSchema(sendEmailConfirmationSchema),
|
|
53
107
|
validateEmailConfirmationBody: validateYupSchema(validateEmailConfirmationSchema),
|
|
54
108
|
validateForgotPasswordBody: validateYupSchema(forgotPasswordSchema),
|
|
55
|
-
validateResetPasswordBody:
|
|
56
|
-
|
|
109
|
+
validateResetPasswordBody: (payload, config) =>
|
|
110
|
+
validateYupSchema(createResetPasswordSchema(config))(payload),
|
|
111
|
+
validateChangePasswordBody: (payload, config) =>
|
|
112
|
+
validateYupSchema(createChangePasswordSchema(config))(payload),
|
|
57
113
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const usersPermissionsUserUID = 'plugin::users-permissions.user';
|
|
4
|
+
|
|
5
|
+
module.exports = ({ nexus, strapi }) => {
|
|
6
|
+
const { getContentTypeInputName } = strapi.plugin('graphql').service('utils').naming;
|
|
7
|
+
|
|
8
|
+
const userContentType = strapi.getModel(usersPermissionsUserUID);
|
|
9
|
+
const userInputName = getContentTypeInputName(userContentType);
|
|
10
|
+
|
|
11
|
+
return nexus.extendInputType({
|
|
12
|
+
type: userInputName,
|
|
13
|
+
|
|
14
|
+
definition(t) {
|
|
15
|
+
// Manually add the private password field back to the data
|
|
16
|
+
// input type as it is used for CRUD operations on users
|
|
17
|
+
t.string('password');
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|