@strapi/plugin-users-permissions 0.0.0-next.d1dda661d262d4773c59ee693c38542d9b0dc54c → 0.0.0-next.d2d15ef227d67cce89c2673764c0555c841cd29c

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 (220) hide show
  1. package/.eslintignore +1 -2
  2. package/.eslintrc +17 -0
  3. package/LICENSE +18 -3
  4. package/admin/src/components/BoundRoute/{index.js → index.jsx} +2 -2
  5. package/admin/src/components/FormModal/Input/{index.js → index.jsx} +32 -31
  6. package/admin/src/components/FormModal/index.jsx +115 -0
  7. package/admin/src/components/Permissions/PermissionRow/{CheckboxWrapper.js → CheckboxWrapper.jsx} +4 -3
  8. package/admin/src/components/Permissions/PermissionRow/{SubCategory.js → SubCategory.jsx} +13 -22
  9. package/admin/src/components/Permissions/index.jsx +47 -0
  10. package/admin/src/components/Permissions/reducer.js +1 -1
  11. package/admin/src/components/Policies/{index.js → index.jsx} +7 -5
  12. package/admin/src/components/UsersPermissions/{index.js → index.jsx} +15 -7
  13. package/admin/src/components/UsersPermissions/reducer.js +1 -1
  14. package/admin/src/index.js +19 -47
  15. package/admin/src/pages/AdvancedSettings/index.jsx +214 -0
  16. package/admin/src/pages/AdvancedSettings/utils/layout.js +20 -35
  17. package/admin/src/pages/AdvancedSettings/utils/schema.js +5 -2
  18. package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +156 -0
  19. package/admin/src/pages/EmailTemplates/components/{EmailTable.js → EmailTable.jsx} +20 -17
  20. package/admin/src/pages/EmailTemplates/index.jsx +148 -0
  21. package/admin/src/pages/EmailTemplates/utils/schema.js +18 -6
  22. package/admin/src/pages/Providers/index.jsx +262 -0
  23. package/admin/src/pages/Providers/utils/forms.js +11 -11
  24. package/admin/src/pages/Roles/constants.js +3 -3
  25. package/admin/src/{hooks → pages/Roles/hooks}/usePlugins.js +19 -12
  26. package/admin/src/pages/Roles/index.jsx +24 -0
  27. package/admin/src/pages/Roles/pages/CreatePage.jsx +194 -0
  28. package/admin/src/pages/Roles/pages/EditPage.jsx +215 -0
  29. package/admin/src/pages/Roles/pages/ListPage/components/TableBody.jsx +119 -0
  30. package/admin/src/pages/Roles/{ListPage/index.js → pages/ListPage/index.jsx} +105 -80
  31. package/admin/src/translations/en.json +1 -1
  32. package/admin/src/translations/zh-Hans.json +80 -80
  33. package/admin/src/utils/index.js +0 -1
  34. package/admin/src/utils/prefixPluginTranslations.js +13 -0
  35. package/dist/_chunks/ar-BguGUqwK.js +44 -0
  36. package/dist/_chunks/ar-BguGUqwK.js.map +1 -0
  37. package/dist/_chunks/ar-CK8BRRXB.mjs +44 -0
  38. package/dist/_chunks/ar-CK8BRRXB.mjs.map +1 -0
  39. package/dist/_chunks/cs-BVigMk0l.mjs +50 -0
  40. package/dist/_chunks/cs-BVigMk0l.mjs.map +1 -0
  41. package/dist/_chunks/cs-BW8-K_GY.js +50 -0
  42. package/dist/_chunks/cs-BW8-K_GY.js.map +1 -0
  43. package/dist/_chunks/de-BKUdRFI4.mjs +62 -0
  44. package/dist/_chunks/de-BKUdRFI4.mjs.map +1 -0
  45. package/dist/_chunks/de-owXpVluI.js +62 -0
  46. package/dist/_chunks/de-owXpVluI.js.map +1 -0
  47. package/dist/_chunks/dk-BQiTK50l.mjs +86 -0
  48. package/dist/_chunks/dk-BQiTK50l.mjs.map +1 -0
  49. package/dist/_chunks/dk-LXAnbuBk.js +86 -0
  50. package/dist/_chunks/dk-LXAnbuBk.js.map +1 -0
  51. package/dist/_chunks/en-DOHtPf-2.mjs +86 -0
  52. package/dist/_chunks/en-DOHtPf-2.mjs.map +1 -0
  53. package/dist/_chunks/en-MHo5mcsU.js +86 -0
  54. package/dist/_chunks/en-MHo5mcsU.js.map +1 -0
  55. package/dist/_chunks/es-BwLCLXAQ.js +86 -0
  56. package/dist/_chunks/es-BwLCLXAQ.js.map +1 -0
  57. package/dist/_chunks/es-DNgOVMjD.mjs +86 -0
  58. package/dist/_chunks/es-DNgOVMjD.mjs.map +1 -0
  59. package/dist/_chunks/fr-DkgRugiU.mjs +50 -0
  60. package/dist/_chunks/fr-DkgRugiU.mjs.map +1 -0
  61. package/dist/_chunks/fr-DkhpSjjm.js +50 -0
  62. package/dist/_chunks/fr-DkhpSjjm.js.map +1 -0
  63. package/dist/_chunks/id-BTemOeTZ.js +62 -0
  64. package/dist/_chunks/id-BTemOeTZ.js.map +1 -0
  65. package/dist/_chunks/id-BdEsvnaF.mjs +62 -0
  66. package/dist/_chunks/id-BdEsvnaF.mjs.map +1 -0
  67. package/dist/_chunks/index-B9cFxoCN.mjs +615 -0
  68. package/dist/_chunks/index-B9cFxoCN.mjs.map +1 -0
  69. package/dist/_chunks/index-BJTnrL31.js +281 -0
  70. package/dist/_chunks/index-BJTnrL31.js.map +1 -0
  71. package/dist/_chunks/index-CAgV-G5k.js +1173 -0
  72. package/dist/_chunks/index-CAgV-G5k.js.map +1 -0
  73. package/dist/_chunks/index-CI_b1cXd.mjs +1143 -0
  74. package/dist/_chunks/index-CI_b1cXd.mjs.map +1 -0
  75. package/dist/_chunks/index-Cb03Ke48.js +366 -0
  76. package/dist/_chunks/index-Cb03Ke48.js.map +1 -0
  77. package/dist/_chunks/index-DDglyd0X.mjs +262 -0
  78. package/dist/_chunks/index-DDglyd0X.mjs.map +1 -0
  79. package/dist/_chunks/index-DJOPnOVo.mjs +344 -0
  80. package/dist/_chunks/index-DJOPnOVo.mjs.map +1 -0
  81. package/dist/_chunks/index-QPUYgtlo-BfJxOAzF.mjs +11845 -0
  82. package/dist/_chunks/index-QPUYgtlo-BfJxOAzF.mjs.map +1 -0
  83. package/dist/_chunks/index-QPUYgtlo-QiBdpWEj.js +11869 -0
  84. package/dist/_chunks/index-QPUYgtlo-QiBdpWEj.js.map +1 -0
  85. package/dist/_chunks/index-_vrdPeYp.js +248 -0
  86. package/dist/_chunks/index-_vrdPeYp.js.map +1 -0
  87. package/dist/_chunks/index-bDCZWhLO.mjs +249 -0
  88. package/dist/_chunks/index-bDCZWhLO.mjs.map +1 -0
  89. package/dist/_chunks/index-cL_YxyjM.js +638 -0
  90. package/dist/_chunks/index-cL_YxyjM.js.map +1 -0
  91. package/dist/_chunks/it-B-rv0E24.mjs +62 -0
  92. package/dist/_chunks/it-B-rv0E24.mjs.map +1 -0
  93. package/dist/_chunks/it-D1rH6V6_.js +62 -0
  94. package/dist/_chunks/it-D1rH6V6_.js.map +1 -0
  95. package/dist/_chunks/ja-C8K-VBPD.mjs +48 -0
  96. package/dist/_chunks/ja-C8K-VBPD.mjs.map +1 -0
  97. package/dist/_chunks/ja-DqShgTMf.js +48 -0
  98. package/dist/_chunks/ja-DqShgTMf.js.map +1 -0
  99. package/dist/_chunks/ko-B9DGEPWH.js +86 -0
  100. package/dist/_chunks/ko-B9DGEPWH.js.map +1 -0
  101. package/dist/_chunks/ko-Busb0wIY.mjs +86 -0
  102. package/dist/_chunks/ko-Busb0wIY.mjs.map +1 -0
  103. package/dist/_chunks/ms-ByvsQjRt.mjs +49 -0
  104. package/dist/_chunks/ms-ByvsQjRt.mjs.map +1 -0
  105. package/dist/_chunks/ms-CPBU3LWf.js +49 -0
  106. package/dist/_chunks/ms-CPBU3LWf.js.map +1 -0
  107. package/dist/_chunks/nl-5qO8Rpcy.mjs +48 -0
  108. package/dist/_chunks/nl-5qO8Rpcy.mjs.map +1 -0
  109. package/dist/_chunks/nl-CwNB6YoO.js +48 -0
  110. package/dist/_chunks/nl-CwNB6YoO.js.map +1 -0
  111. package/dist/_chunks/pl-BdIzifBE.mjs +86 -0
  112. package/dist/_chunks/pl-BdIzifBE.mjs.map +1 -0
  113. package/dist/_chunks/pl-Do9UD69f.js +86 -0
  114. package/dist/_chunks/pl-Do9UD69f.js.map +1 -0
  115. package/dist/_chunks/pt-BIO24ioG.mjs +48 -0
  116. package/dist/_chunks/pt-BIO24ioG.mjs.map +1 -0
  117. package/dist/_chunks/pt-BR-D7dZhxuP.js +44 -0
  118. package/dist/_chunks/pt-BR-D7dZhxuP.js.map +1 -0
  119. package/dist/_chunks/pt-BR-f0p23AQZ.mjs +44 -0
  120. package/dist/_chunks/pt-BR-f0p23AQZ.mjs.map +1 -0
  121. package/dist/_chunks/pt-fdvyOnUp.js +48 -0
  122. package/dist/_chunks/pt-fdvyOnUp.js.map +1 -0
  123. package/dist/_chunks/ru-C94rjPGA.js +86 -0
  124. package/dist/_chunks/ru-C94rjPGA.js.map +1 -0
  125. package/dist/_chunks/ru-VWy-IB7K.mjs +86 -0
  126. package/dist/_chunks/ru-VWy-IB7K.mjs.map +1 -0
  127. package/dist/_chunks/sk-BABEhykl.js +50 -0
  128. package/dist/_chunks/sk-BABEhykl.js.map +1 -0
  129. package/dist/_chunks/sk-B_LIcepm.mjs +50 -0
  130. package/dist/_chunks/sk-B_LIcepm.mjs.map +1 -0
  131. package/dist/_chunks/sv-ABLKOokl.mjs +86 -0
  132. package/dist/_chunks/sv-ABLKOokl.mjs.map +1 -0
  133. package/dist/_chunks/sv-Be43LhA9.js +86 -0
  134. package/dist/_chunks/sv-Be43LhA9.js.map +1 -0
  135. package/dist/_chunks/th-DKyP7ueR.mjs +60 -0
  136. package/dist/_chunks/th-DKyP7ueR.mjs.map +1 -0
  137. package/dist/_chunks/th-DgVhVLhL.js +60 -0
  138. package/dist/_chunks/th-DgVhVLhL.js.map +1 -0
  139. package/dist/_chunks/tr-B_idhkEs.js +85 -0
  140. package/dist/_chunks/tr-B_idhkEs.js.map +1 -0
  141. package/dist/_chunks/tr-qa1Q5UjC.mjs +85 -0
  142. package/dist/_chunks/tr-qa1Q5UjC.mjs.map +1 -0
  143. package/dist/_chunks/uk-BmRqbeQc.mjs +49 -0
  144. package/dist/_chunks/uk-BmRqbeQc.mjs.map +1 -0
  145. package/dist/_chunks/uk-LHOivnhP.js +49 -0
  146. package/dist/_chunks/uk-LHOivnhP.js.map +1 -0
  147. package/dist/_chunks/vi-CdVRdKDw.js +50 -0
  148. package/dist/_chunks/vi-CdVRdKDw.js.map +1 -0
  149. package/dist/_chunks/vi-HW-EdMea.mjs +50 -0
  150. package/dist/_chunks/vi-HW-EdMea.mjs.map +1 -0
  151. package/dist/_chunks/zh-5hKkVPA4.mjs +86 -0
  152. package/dist/_chunks/zh-5hKkVPA4.mjs.map +1 -0
  153. package/dist/_chunks/zh-Cuq8gMnF.js +86 -0
  154. package/dist/_chunks/zh-Cuq8gMnF.js.map +1 -0
  155. package/dist/_chunks/zh-Hans-BHilK-yc.mjs +86 -0
  156. package/dist/_chunks/zh-Hans-BHilK-yc.mjs.map +1 -0
  157. package/dist/_chunks/zh-Hans-GQDMKtY4.js +86 -0
  158. package/dist/_chunks/zh-Hans-GQDMKtY4.js.map +1 -0
  159. package/dist/admin/index.js +4 -0
  160. package/dist/admin/index.js.map +1 -0
  161. package/dist/admin/index.mjs +5 -0
  162. package/dist/admin/index.mjs.map +1 -0
  163. package/documentation/content-api.yaml +1 -1
  164. package/jest.config.front.js +1 -1
  165. package/package.json +47 -28
  166. package/packup.config.ts +22 -0
  167. package/server/bootstrap/index.js +18 -15
  168. package/server/bootstrap/users-permissions-actions.js +6 -0
  169. package/server/config.js +29 -0
  170. package/server/content-types/user/index.js +0 -1
  171. package/server/controllers/auth.js +60 -34
  172. package/server/controllers/content-manager-user.js +28 -30
  173. package/server/controllers/role.js +1 -1
  174. package/server/controllers/user.js +18 -8
  175. package/server/middlewares/rateLimit.js +41 -21
  176. package/server/register.js +1 -1
  177. package/server/services/jwt.js +3 -3
  178. package/server/services/permission.js +3 -7
  179. package/server/services/providers-registry.js +469 -261
  180. package/server/services/providers.js +10 -5
  181. package/server/services/role.js +15 -13
  182. package/server/services/user.js +56 -19
  183. package/server/services/users-permissions.js +15 -13
  184. package/server/utils/index.d.ts +2 -1
  185. package/server/utils/sanitize/sanitizers.js +7 -3
  186. package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +2 -2
  187. package/.eslintrc.js +0 -14
  188. package/admin/src/components/FormModal/index.js +0 -126
  189. package/admin/src/components/Permissions/index.js +0 -57
  190. package/admin/src/hooks/index.js +0 -5
  191. package/admin/src/hooks/useFetchRole/index.js +0 -67
  192. package/admin/src/hooks/useFetchRole/reducer.js +0 -31
  193. package/admin/src/hooks/useForm/index.js +0 -70
  194. package/admin/src/hooks/useForm/reducer.js +0 -40
  195. package/admin/src/hooks/useRolesList/index.js +0 -65
  196. package/admin/src/hooks/useRolesList/init.js +0 -5
  197. package/admin/src/hooks/useRolesList/reducer.js +0 -31
  198. package/admin/src/pages/AdvancedSettings/index.js +0 -243
  199. package/admin/src/pages/AdvancedSettings/utils/api.js +0 -18
  200. package/admin/src/pages/EmailTemplates/components/EmailForm.js +0 -176
  201. package/admin/src/pages/EmailTemplates/index.js +0 -163
  202. package/admin/src/pages/EmailTemplates/utils/api.js +0 -18
  203. package/admin/src/pages/Providers/index.js +0 -275
  204. package/admin/src/pages/Providers/reducer.js +0 -54
  205. package/admin/src/pages/Providers/utils/api.js +0 -26
  206. package/admin/src/pages/Providers/utils/createProvidersArray.js +0 -21
  207. package/admin/src/pages/Roles/CreatePage.js +0 -185
  208. package/admin/src/pages/Roles/EditPage.js +0 -197
  209. package/admin/src/pages/Roles/ListPage/components/TableBody.js +0 -93
  210. package/admin/src/pages/Roles/ListPage/utils/api.js +0 -32
  211. package/admin/src/pages/Roles/ProtectedCreatePage.js +0 -15
  212. package/admin/src/pages/Roles/ProtectedEditPage.js +0 -15
  213. package/admin/src/pages/Roles/ProtectedListPage.js +0 -17
  214. package/admin/src/pages/Roles/index.js +0 -30
  215. package/admin/src/utils/getRequestURL.js +0 -5
  216. package/server/bootstrap/grant-config.js +0 -131
  217. package/strapi-admin.js +0 -3
  218. package/strapi-server.js +0 -3
  219. /package/admin/src/components/Permissions/PermissionRow/{index.js → index.jsx} +0 -0
  220. /package/admin/src/contexts/UsersPermissionsContext/{index.js → index.jsx} +0 -0
@@ -9,6 +9,7 @@
9
9
  /* eslint-disable no-useless-escape */
10
10
  const crypto = require('crypto');
11
11
  const _ = require('lodash');
12
+ const { concat, compact, isArray } = require('lodash/fp');
12
13
  const utils = require('@strapi/utils');
13
14
  const { getService } = require('../utils');
14
15
  const {
@@ -21,14 +22,13 @@ const {
21
22
  validateChangePasswordBody,
22
23
  } = require('./validation/auth');
23
24
 
24
- const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
25
- const { ApplicationError, ValidationError } = utils.errors;
25
+ const { ApplicationError, ValidationError, ForbiddenError } = utils.errors;
26
26
 
27
27
  const sanitizeUser = (user, ctx) => {
28
28
  const { auth } = ctx.state;
29
29
  const userSchema = strapi.getModel('plugin::users-permissions.user');
30
30
 
31
- return sanitize.contentAPI.output(user, userSchema, { auth });
31
+ return strapi.contentAPI.sanitize.output(user, userSchema, { auth });
32
32
  };
33
33
 
34
34
  module.exports = {
@@ -51,7 +51,7 @@ module.exports = {
51
51
  const { identifier } = params;
52
52
 
53
53
  // Check if the user exists.
54
- const user = await strapi.query('plugin::users-permissions.user').findOne({
54
+ const user = await strapi.db.query('plugin::users-permissions.user').findOne({
55
55
  where: {
56
56
  provider,
57
57
  $or: [{ email: identifier.toLowerCase() }, { username: identifier }],
@@ -96,6 +96,10 @@ module.exports = {
96
96
  try {
97
97
  const user = await getService('providers').connect(provider, ctx.query);
98
98
 
99
+ if (user.blocked) {
100
+ throw new ForbiddenError('Your account has been blocked by an administrator');
101
+ }
102
+
99
103
  return ctx.send({
100
104
  jwt: getService('jwt').issue({ id: user.id }),
101
105
  user: await sanitizeUser(user, ctx),
@@ -112,10 +116,9 @@ module.exports = {
112
116
 
113
117
  const { currentPassword, password } = await validateChangePasswordBody(ctx.request.body);
114
118
 
115
- const user = await strapi.entityService.findOne(
116
- 'plugin::users-permissions.user',
117
- ctx.state.user.id
118
- );
119
+ const user = await strapi.db
120
+ .query('plugin::users-permissions.user')
121
+ .findOne({ where: { id: ctx.state.user.id } });
119
122
 
120
123
  const validPassword = await getService('user').validatePassword(currentPassword, user.password);
121
124
 
@@ -144,7 +147,7 @@ module.exports = {
144
147
  throw new ValidationError('Passwords do not match');
145
148
  }
146
149
 
147
- const user = await strapi
150
+ const user = await strapi.db
148
151
  .query('plugin::users-permissions.user')
149
152
  .findOne({ where: { resetPasswordToken: code } });
150
153
 
@@ -193,10 +196,28 @@ module.exports = {
193
196
  }
194
197
 
195
198
  // Ability to pass OAuth callback dynamically
196
- grantConfig[provider].callback =
197
- _.get(ctx, 'query.callback') ||
198
- _.get(ctx, 'session.grant.dynamic.callback') ||
199
- grantConfig[provider].callback;
199
+ const queryCustomCallback = _.get(ctx, 'query.callback');
200
+ const dynamicSessionCallback = _.get(ctx, 'session.grant.dynamic.callback');
201
+
202
+ const customCallback = queryCustomCallback ?? dynamicSessionCallback;
203
+
204
+ // The custom callback is validated to make sure it's not redirecting to an unwanted actor.
205
+ if (customCallback !== undefined) {
206
+ try {
207
+ // We're extracting the callback validator from the plugin config since it can be user-customized
208
+ const { validate: validateCallback } = strapi
209
+ .plugin('users-permissions')
210
+ .config('callback');
211
+
212
+ await validateCallback(customCallback, grantConfig[provider]);
213
+
214
+ grantConfig[provider].callback = customCallback;
215
+ } catch (e) {
216
+ throw new ValidationError('Invalid callback URL provided', { callback: customCallback });
217
+ }
218
+ }
219
+
220
+ // Build a valid redirect URI for the current provider
200
221
  grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);
201
222
 
202
223
  return grant(grantConfig)(ctx, next);
@@ -211,7 +232,7 @@ module.exports = {
211
232
  const advancedSettings = await pluginStore.get({ key: 'advanced' });
212
233
 
213
234
  // Find the user by email.
214
- const user = await strapi
235
+ const user = await strapi.db
215
236
  .query('plugin::users-permissions.user')
216
237
  .findOne({ where: { email: email.toLowerCase() } });
217
238
 
@@ -229,8 +250,8 @@ module.exports = {
229
250
  resetPasswordSettings.message,
230
251
  {
231
252
  URL: advancedSettings.email_reset_password,
232
- SERVER_URL: getAbsoluteServerUrl(strapi.config),
233
- ADMIN_URL: getAbsoluteAdminUrl(strapi.config),
253
+ SERVER_URL: strapi.config.get('server.absoluteUrl'),
254
+ ADMIN_URL: strapi.config.get('admin.absoluteUrl'),
234
255
  USER: userInfo,
235
256
  TOKEN: resetPasswordToken,
236
257
  }
@@ -273,26 +294,30 @@ module.exports = {
273
294
  throw new ApplicationError('Register action is currently disabled');
274
295
  }
275
296
 
297
+ const { register } = strapi.config.get('plugin::users-permissions');
298
+ const alwaysAllowedKeys = ['username', 'password', 'email'];
299
+
300
+ // Note that we intentionally do not filter allowedFields to allow a project to explicitly accept private or other Strapi field on registration
301
+ const allowedKeys = compact(
302
+ concat(alwaysAllowedKeys, isArray(register?.allowedFields) ? register.allowedFields : [])
303
+ );
304
+
305
+ // Check if there are any keys in requestBody that are not in allowedKeys
306
+ const invalidKeys = Object.keys(ctx.request.body).filter((key) => !allowedKeys.includes(key));
307
+
308
+ if (invalidKeys.length > 0) {
309
+ // If there are invalid keys, throw an error
310
+ throw new ValidationError(`Invalid parameters: ${invalidKeys.join(', ')}`);
311
+ }
312
+
276
313
  const params = {
277
- ..._.omit(ctx.request.body, [
278
- 'confirmed',
279
- 'blocked',
280
- 'confirmationToken',
281
- 'resetPasswordToken',
282
- 'provider',
283
- 'id',
284
- 'createdAt',
285
- 'updatedAt',
286
- 'createdBy',
287
- 'updatedBy',
288
- 'role',
289
- ]),
314
+ ..._.pick(ctx.request.body, allowedKeys),
290
315
  provider: 'local',
291
316
  };
292
317
 
293
318
  await validateRegisterBody(params);
294
319
 
295
- const role = await strapi
320
+ const role = await strapi.db
296
321
  .query('plugin::users-permissions.role')
297
322
  .findOne({ where: { type: settings.default_role } });
298
323
 
@@ -311,7 +336,7 @@ module.exports = {
311
336
  ],
312
337
  };
313
338
 
314
- const conflictingUserCount = await strapi.query('plugin::users-permissions.user').count({
339
+ const conflictingUserCount = await strapi.db.query('plugin::users-permissions.user').count({
315
340
  where: { ...identifierFilter, provider },
316
341
  });
317
342
 
@@ -320,7 +345,7 @@ module.exports = {
320
345
  }
321
346
 
322
347
  if (settings.unique_email) {
323
- const conflictingUserCount = await strapi.query('plugin::users-permissions.user').count({
348
+ const conflictingUserCount = await strapi.db.query('plugin::users-permissions.user').count({
324
349
  where: { ...identifierFilter },
325
350
  });
326
351
 
@@ -345,7 +370,8 @@ module.exports = {
345
370
  try {
346
371
  await getService('user').sendConfirmationEmail(sanitizedUser);
347
372
  } catch (err) {
348
- throw new ApplicationError(err.message);
373
+ strapi.log.error(err);
374
+ throw new ApplicationError('Error sending confirmation email');
349
375
  }
350
376
 
351
377
  return ctx.send({ user: sanitizedUser });
@@ -390,7 +416,7 @@ module.exports = {
390
416
  async sendEmailConfirmation(ctx) {
391
417
  const { email } = await validateSendEmailConfirmationBody(ctx.request.body);
392
418
 
393
- const user = await strapi.query('plugin::users-permissions.user').findOne({
419
+ const user = await strapi.db.query('plugin::users-permissions.user').findOne({
394
420
  where: { email: email.toLowerCase() },
395
421
  });
396
422
 
@@ -17,24 +17,25 @@ const ACTIONS = {
17
17
  };
18
18
 
19
19
  const findEntityAndCheckPermissions = async (ability, action, model, id) => {
20
- const entity = await strapi.query(userModel).findOne({
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(entity)) {
24
+ if (_.isNil(doc)) {
26
25
  throw new NotFoundError();
27
26
  }
28
27
 
29
- const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
28
+ const pm = strapi
29
+ .service('admin::permission')
30
+ .createPermissionsManager({ ability, action, model });
30
31
 
31
- if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
32
+ if (pm.ability.cannot(pm.action, pm.toSubject(doc))) {
32
33
  throw new ForbiddenError();
33
34
  }
34
35
 
35
- const entityWithoutCreatorRoles = _.omit(entity, `${CREATED_BY_ATTRIBUTE}.roles`);
36
+ const docWithoutCreatorRoles = _.omit(doc, `${CREATED_BY_ATTRIBUTE}.roles`);
36
37
 
37
- return { pm, entity: entityWithoutCreatorRoles };
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.services.permission.createPermissionsManager({
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.entity-manager')
107
- .create(user, userModel);
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, entity } = await findEntityAndCheckPermissions(
125
+ const { pm, doc } = await findEntityAndCheckPermissions(
132
126
  userAbility,
133
127
  ACTIONS.edit,
134
128
  userModel,
135
- id
129
+ documentId
136
130
  );
137
- const user = entity;
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.entity-manager')
171
- .update({ id }, updateData, userModel);
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
  },
@@ -59,7 +59,7 @@ module.exports = {
59
59
  }
60
60
 
61
61
  // Fetch public role.
62
- const publicRole = await strapi
62
+ const publicRole = await strapi.db
63
63
  .query('plugin::users-permissions.role')
64
64
  .findOne({ where: { type: 'public' } });
65
65
 
@@ -11,21 +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 } = 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 sanitize.contentAPI.output(user, schema, { auth });
20
+ return strapi.contentAPI.sanitize.output(user, schema, { auth });
21
+ };
22
+
23
+ const validateQuery = async (query, ctx) => {
24
+ const schema = strapi.getModel('plugin::users-permissions.user');
25
+ const { auth } = ctx.state;
26
+
27
+ return strapi.contentAPI.validate.query(query, schema, { auth });
22
28
  };
23
29
 
24
30
  const sanitizeQuery = async (query, ctx) => {
25
31
  const schema = strapi.getModel('plugin::users-permissions.user');
26
32
  const { auth } = ctx.state;
27
33
 
28
- return sanitize.contentAPI.query(query, schema, { auth });
34
+ return strapi.contentAPI.sanitize.query(query, schema, { auth });
29
35
  };
30
36
 
31
37
  module.exports = {
@@ -42,7 +48,7 @@ module.exports = {
42
48
 
43
49
  const { email, username, role } = ctx.request.body;
44
50
 
45
- const userWithSameUsername = await strapi
51
+ const userWithSameUsername = await strapi.db
46
52
  .query('plugin::users-permissions.user')
47
53
  .findOne({ where: { username } });
48
54
 
@@ -51,7 +57,7 @@ module.exports = {
51
57
  }
52
58
 
53
59
  if (advanced.unique_email) {
54
- const userWithSameEmail = await strapi
60
+ const userWithSameEmail = await strapi.db
55
61
  .query('plugin::users-permissions.user')
56
62
  .findOne({ where: { email: email.toLowerCase() } });
57
63
 
@@ -67,7 +73,7 @@ module.exports = {
67
73
  };
68
74
 
69
75
  if (!role) {
70
- const defaultRole = await strapi
76
+ const defaultRole = await strapi.db
71
77
  .query('plugin::users-permissions.role')
72
78
  .findOne({ where: { type: advanced.default_role } });
73
79
 
@@ -108,7 +114,7 @@ module.exports = {
108
114
  }
109
115
 
110
116
  if (_.has(ctx.request.body, 'username')) {
111
- const userWithSameUsername = await strapi
117
+ const userWithSameUsername = await strapi.db
112
118
  .query('plugin::users-permissions.user')
113
119
  .findOne({ where: { username } });
114
120
 
@@ -118,7 +124,7 @@ module.exports = {
118
124
  }
119
125
 
120
126
  if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
121
- const userWithSameEmail = await strapi
127
+ const userWithSameEmail = await strapi.db
122
128
  .query('plugin::users-permissions.user')
123
129
  .findOne({ where: { email: email.toLowerCase() } });
124
130
 
@@ -143,6 +149,7 @@ module.exports = {
143
149
  * @return {Object|Array}
144
150
  */
145
151
  async find(ctx) {
152
+ await validateQuery(ctx.query, ctx);
146
153
  const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
147
154
  const users = await getService('user').fetchAll(sanitizedQuery);
148
155
 
@@ -155,6 +162,7 @@ module.exports = {
155
162
  */
156
163
  async findOne(ctx) {
157
164
  const { id } = ctx.params;
165
+ await validateQuery(ctx.query, ctx);
158
166
  const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
159
167
 
160
168
  let data = await getService('user').fetch(id, sanitizedQuery);
@@ -171,6 +179,7 @@ module.exports = {
171
179
  * @return {Number}
172
180
  */
173
181
  async count(ctx) {
182
+ await validateQuery(ctx.query, ctx);
174
183
  const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
175
184
 
176
185
  ctx.body = await getService('user').count(sanitizedQuery);
@@ -201,6 +210,7 @@ module.exports = {
201
210
  return ctx.unauthorized();
202
211
  }
203
212
 
213
+ await validateQuery(query, ctx);
204
214
  const sanitizedQuery = await sanitizeQuery(query, ctx);
205
215
  const user = await getService('user').fetch(authUser.id, sanitizedQuery);
206
216
 
@@ -1,27 +1,47 @@
1
1
  'use strict';
2
2
 
3
+ const path = require('path');
4
+ const utils = require('@strapi/utils');
5
+ const { isString, has, toLower } = require('lodash/fp');
6
+
7
+ const { RateLimitError } = utils.errors;
8
+
3
9
  module.exports =
4
10
  (config, { strapi }) =>
5
11
  async (ctx, next) => {
6
- const ratelimit = require('koa2-ratelimit').RateLimit;
7
-
8
- const message = [
9
- {
10
- messages: [
11
- {
12
- id: 'Auth.form.error.ratelimit',
13
- message: 'Too many attempts, please try again in a minute.',
14
- },
15
- ],
16
- },
17
- ];
18
-
19
- return ratelimit.middleware({
20
- interval: 1 * 60 * 1000,
21
- max: 5,
22
- prefixKey: `${ctx.request.path}:${ctx.request.ip}`,
23
- message,
24
- ...strapi.config.get('plugin.users-permissions.ratelimit'),
25
- ...config,
26
- })(ctx, next);
12
+ let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');
13
+
14
+ if (!rateLimitConfig) {
15
+ rateLimitConfig = {
16
+ enabled: true,
17
+ };
18
+ }
19
+
20
+ if (!has('enabled', rateLimitConfig)) {
21
+ rateLimitConfig.enabled = true;
22
+ }
23
+
24
+ if (rateLimitConfig.enabled === true) {
25
+ const rateLimit = require('koa2-ratelimit').RateLimit;
26
+
27
+ const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';
28
+ const requestPath = isString(ctx.request.path)
29
+ ? toLower(path.normalize(ctx.request.path))
30
+ : 'invalidPath';
31
+
32
+ const loadConfig = {
33
+ interval: { min: 5 },
34
+ max: 5,
35
+ prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,
36
+ handler() {
37
+ throw new RateLimitError();
38
+ },
39
+ ...rateLimitConfig,
40
+ ...config,
41
+ };
42
+
43
+ return rateLimit.middleware(loadConfig)(ctx, next);
44
+ }
45
+
46
+ return next();
27
47
  };
@@ -7,7 +7,7 @@ const authStrategy = require('./strategies/users-permissions');
7
7
  const sanitizers = require('./utils/sanitize/sanitizers');
8
8
 
9
9
  module.exports = ({ strapi }) => {
10
- strapi.container.get('auth').register('content-api', authStrategy);
10
+ strapi.get('auth').register('content-api', authStrategy);
11
11
  strapi.sanitizers.add('content-api.output', sanitizers.defaultSanitizeOutput);
12
12
 
13
13
  if (strapi.plugin('graphql')) {
@@ -29,10 +29,10 @@ module.exports = ({ strapi }) => ({
29
29
  },
30
30
 
31
31
  issue(payload, jwtOptions = {}) {
32
- _.defaults(jwtOptions, strapi.config.get('plugin.users-permissions.jwt'));
32
+ _.defaults(jwtOptions, strapi.config.get('plugin::users-permissions.jwt'));
33
33
  return jwt.sign(
34
34
  _.clone(payload.toJSON ? payload.toJSON() : payload),
35
- strapi.config.get('plugin.users-permissions.jwtSecret'),
35
+ strapi.config.get('plugin::users-permissions.jwtSecret'),
36
36
  jwtOptions
37
37
  );
38
38
  },
@@ -41,7 +41,7 @@ module.exports = ({ strapi }) => ({
41
41
  return new Promise((resolve, reject) => {
42
42
  jwt.verify(
43
43
  token,
44
- strapi.config.get('plugin.users-permissions.jwtSecret'),
44
+ strapi.config.get('plugin::users-permissions.jwtSecret'),
45
45
  {},
46
46
  (err, tokenPayload = {}) => {
47
47
  if (err) {
@@ -11,11 +11,7 @@ module.exports = ({ strapi }) => ({
11
11
  * @return {object[]}
12
12
  */
13
13
  async findRolePermissions(roleID) {
14
- return strapi.entityService.load(
15
- 'plugin::users-permissions.role',
16
- { id: roleID },
17
- 'permissions'
18
- );
14
+ return strapi.db.query('plugin::users-permissions.role').load({ id: roleID }, 'permissions');
19
15
  },
20
16
 
21
17
  /**
@@ -24,8 +20,8 @@ module.exports = ({ strapi }) => ({
24
20
  * @return {object[]}
25
21
  */
26
22
  async findPublicPermissions() {
27
- return strapi.entityService.findMany('plugin::users-permissions.permission', {
28
- filters: PUBLIC_ROLE_FILTER,
23
+ return strapi.db.query('plugin::users-permissions.permission').findMany({
24
+ where: PUBLIC_ROLE_FILTER,
29
25
  });
30
26
  },
31
27