@strapi/plugin-users-permissions 4.0.0-beta.2 → 4.0.0-beta.20

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 (46) hide show
  1. package/admin/src/components/BoundRoute/index.js +23 -27
  2. package/admin/src/components/FormModal/Input/index.js +2 -2
  3. package/admin/src/components/FormModal/index.js +10 -5
  4. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +1 -1
  5. package/admin/src/components/Permissions/PermissionRow/SubCategory.js +12 -10
  6. package/admin/src/components/Permissions/PermissionRow/index.js +1 -1
  7. package/admin/src/components/Permissions/index.js +12 -8
  8. package/admin/src/components/Policies/index.js +12 -9
  9. package/admin/src/components/UsersPermissions/index.js +12 -15
  10. package/admin/src/index.js +0 -8
  11. package/admin/src/pages/AdvancedSettings/index.js +13 -13
  12. package/admin/src/pages/EmailTemplates/components/EmailForm.js +10 -5
  13. package/admin/src/pages/EmailTemplates/components/EmailTable.js +16 -16
  14. package/admin/src/pages/EmailTemplates/index.js +3 -3
  15. package/admin/src/pages/Providers/index.js +21 -21
  16. package/admin/src/pages/Providers/utils/api.js +1 -1
  17. package/admin/src/pages/Roles/CreatePage/index.js +13 -13
  18. package/admin/src/pages/Roles/EditPage/index.js +23 -13
  19. package/admin/src/pages/Roles/ListPage/components/TableBody.js +14 -10
  20. package/admin/src/pages/Roles/ListPage/index.js +19 -25
  21. package/documentation/1.0.0/overrides/users-permissions-User.json +7 -7
  22. package/package.json +29 -30
  23. package/server/bootstrap/index.js +17 -17
  24. package/server/config.js +2 -2
  25. package/server/content-types/permission/index.js +3 -0
  26. package/server/content-types/role/index.js +3 -0
  27. package/server/controllers/auth.js +73 -215
  28. package/server/controllers/{user/admin.js → content-manager-user.js} +44 -75
  29. package/server/controllers/index.js +2 -0
  30. package/server/controllers/role.js +7 -7
  31. package/server/controllers/settings.js +5 -4
  32. package/server/controllers/user.js +118 -28
  33. package/server/controllers/validation/auth.js +29 -0
  34. package/server/controllers/validation/user.js +38 -0
  35. package/server/middlewares/rateLimit.js +1 -1
  36. package/server/routes/admin/role.js +5 -5
  37. package/server/routes/admin/settings.js +6 -6
  38. package/server/routes/content-api/auth.js +5 -7
  39. package/server/services/jwt.js +9 -17
  40. package/server/services/providers.js +13 -10
  41. package/server/services/role.js +5 -10
  42. package/server/services/user.js +8 -6
  43. package/server/services/users-permissions.js +56 -45
  44. package/server/strategies/users-permissions.js +23 -22
  45. package/admin/src/assets/images/logo.svg +0 -1
  46. package/server/controllers/user/api.js +0 -158
@@ -9,13 +9,25 @@
9
9
  /* eslint-disable no-useless-escape */
10
10
  const crypto = require('crypto');
11
11
  const _ = require('lodash');
12
- const { sanitizeEntity } = require('@strapi/utils');
12
+ const utils = require('@strapi/utils');
13
13
  const { getService } = require('../utils');
14
+ const {
15
+ validateCallbackBody,
16
+ validateRegisterBody,
17
+ validateSendEmailConfirmationBody,
18
+ } = require('./validation/auth');
19
+
20
+ const { sanitize } = utils;
21
+ const { ApplicationError, ValidationError } = utils.errors;
14
22
 
15
23
  const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
16
- const formatError = error => [
17
- { messages: [{ id: error.id, message: error.message, field: error.field }] },
18
- ];
24
+
25
+ const sanitizeUser = (user, ctx) => {
26
+ const { auth } = ctx.state;
27
+ const userSchema = strapi.getModel('plugin::users-permissions.user');
28
+
29
+ return sanitize.contentAPI.output(user, userSchema, { auth });
30
+ };
19
31
 
20
32
  module.exports = {
21
33
  async callback(ctx) {
@@ -26,30 +38,10 @@ module.exports = {
26
38
 
27
39
  if (provider === 'local') {
28
40
  if (!_.get(await store.get({ key: 'grant' }), 'email.enabled')) {
29
- return ctx.badRequest(null, 'This provider is disabled.');
30
- }
31
-
32
- // The identifier is required.
33
- if (!params.identifier) {
34
- return ctx.badRequest(
35
- null,
36
- formatError({
37
- id: 'Auth.form.error.email.provide',
38
- message: 'Please provide your username or your e-mail.',
39
- })
40
- );
41
+ throw new ApplicationError('This provider is disabled');
41
42
  }
42
43
 
43
- // The password is required.
44
- if (!params.password) {
45
- return ctx.badRequest(
46
- null,
47
- formatError({
48
- id: 'Auth.form.error.password.provide',
49
- message: 'Please provide your password.',
50
- })
51
- );
52
- }
44
+ await validateCallbackBody(params);
53
45
 
54
46
  const query = { provider };
55
47
 
@@ -67,47 +59,24 @@ module.exports = {
67
59
  const user = await strapi.query('plugin::users-permissions.user').findOne({ where: query });
68
60
 
69
61
  if (!user) {
70
- return ctx.badRequest(
71
- null,
72
- formatError({
73
- id: 'Auth.form.error.invalid',
74
- message: 'Identifier or password invalid.',
75
- })
76
- );
62
+ throw new ValidationError('Invalid identifier or password');
77
63
  }
78
64
 
79
65
  if (
80
66
  _.get(await store.get({ key: 'advanced' }), 'email_confirmation') &&
81
67
  user.confirmed !== true
82
68
  ) {
83
- return ctx.badRequest(
84
- null,
85
- formatError({
86
- id: 'Auth.form.error.confirmed',
87
- message: 'Your account email is not confirmed',
88
- })
89
- );
69
+ throw new ApplicationError('Your account email is not confirmed');
90
70
  }
91
71
 
92
72
  if (user.blocked === true) {
93
- return ctx.badRequest(
94
- null,
95
- formatError({
96
- id: 'Auth.form.error.blocked',
97
- message: 'Your account has been blocked by an administrator',
98
- })
99
- );
73
+ throw new ApplicationError('Your account has been blocked by an administrator');
100
74
  }
101
75
 
102
76
  // The user never authenticated with the `local` provider.
103
77
  if (!user.password) {
104
- return ctx.badRequest(
105
- null,
106
- formatError({
107
- id: 'Auth.form.error.password.local',
108
- message:
109
- 'This user never set a local password, please login with the provider used during account creation.',
110
- })
78
+ throw new ApplicationError(
79
+ 'This user never set a local password, please login with the provider used during account creation'
111
80
  );
112
81
  }
113
82
 
@@ -117,32 +86,18 @@ module.exports = {
117
86
  );
118
87
 
119
88
  if (!validPassword) {
120
- return ctx.badRequest(
121
- null,
122
- formatError({
123
- id: 'Auth.form.error.invalid',
124
- message: 'Identifier or password invalid.',
125
- })
126
- );
89
+ throw new ValidationError('Invalid identifier or password');
127
90
  } else {
128
91
  ctx.send({
129
92
  jwt: getService('jwt').issue({
130
93
  id: user.id,
131
94
  }),
132
- user: sanitizeEntity(user, {
133
- model: strapi.getModel('plugin::users-permissions.user'),
134
- }),
95
+ user: await sanitizeUser(user, ctx),
135
96
  });
136
97
  }
137
98
  } else {
138
99
  if (!_.get(await store.get({ key: 'grant' }), [provider, 'enabled'])) {
139
- return ctx.badRequest(
140
- null,
141
- formatError({
142
- id: 'provider.disabled',
143
- message: 'This provider is disabled.',
144
- })
145
- );
100
+ throw new ApplicationError('This provider is disabled');
146
101
  }
147
102
 
148
103
  // Connect the user with the third-party provider.
@@ -151,18 +106,16 @@ module.exports = {
151
106
  try {
152
107
  [user, error] = await getService('providers').connect(provider, ctx.query);
153
108
  } catch ([user, error]) {
154
- return ctx.badRequest(null, error === 'array' ? error[0] : error);
109
+ throw new ApplicationError(error.message);
155
110
  }
156
111
 
157
112
  if (!user) {
158
- return ctx.badRequest(null, error === 'array' ? error[0] : error);
113
+ throw new ApplicationError(error.message);
159
114
  }
160
115
 
161
116
  ctx.send({
162
117
  jwt: getService('jwt').issue({ id: user.id }),
163
- user: sanitizeEntity(user, {
164
- model: strapi.getModel('plugin::users-permissions.user'),
165
- }),
118
+ user: await sanitizeUser(user, ctx),
166
119
  });
167
120
  }
168
121
  },
@@ -181,13 +134,7 @@ module.exports = {
181
134
  .findOne({ where: { resetPasswordToken: `${params.code}` } });
182
135
 
183
136
  if (!user) {
184
- return ctx.badRequest(
185
- null,
186
- formatError({
187
- id: 'Auth.form.error.code.provide',
188
- message: 'Incorrect code provided.',
189
- })
190
- );
137
+ throw new ValidationError('Incorrect code provided');
191
138
  }
192
139
 
193
140
  const password = await getService('user').hashPassword({ password: params.password });
@@ -199,50 +146,44 @@ module.exports = {
199
146
 
200
147
  ctx.send({
201
148
  jwt: getService('jwt').issue({ id: user.id }),
202
- user: sanitizeEntity(user, {
203
- model: strapi.getModel('plugin::users-permissions.user'),
204
- }),
149
+ user: await sanitizeUser(user, ctx),
205
150
  });
206
151
  } else if (
207
152
  params.password &&
208
153
  params.passwordConfirmation &&
209
154
  params.password !== params.passwordConfirmation
210
155
  ) {
211
- return ctx.badRequest(
212
- null,
213
- formatError({
214
- id: 'Auth.form.error.password.matching',
215
- message: 'Passwords do not match.',
216
- })
217
- );
156
+ throw new ValidationError('Passwords do not match');
218
157
  } else {
219
- return ctx.badRequest(
220
- null,
221
- formatError({
222
- id: 'Auth.form.error.params.provide',
223
- message: 'Incorrect params provided.',
224
- })
225
- );
158
+ throw new ValidationError('Incorrect params provided');
226
159
  }
227
160
  },
228
161
 
229
162
  async connect(ctx, next) {
230
163
  const grant = require('grant-koa');
231
164
 
232
- const grantConfig = await strapi
165
+ const providers = await strapi
233
166
  .store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
234
167
  .get();
235
168
 
169
+ const apiPrefix = strapi.config.get('api.rest.prefix');
170
+ const grantConfig = {
171
+ defaults: {
172
+ prefix: `${apiPrefix}/connect`,
173
+ },
174
+ ...providers,
175
+ };
176
+
236
177
  const [requestPath] = ctx.request.url.split('?');
237
178
  const provider = requestPath.split('/connect/')[1].split('/')[0];
238
179
 
239
180
  if (!_.get(grantConfig[provider], 'enabled')) {
240
- return ctx.badRequest(null, 'This provider is disabled.');
181
+ throw new ApplicationError('This provider is disabled');
241
182
  }
242
183
 
243
184
  if (!strapi.config.server.url.startsWith('http')) {
244
185
  strapi.log.warn(
245
- 'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://strapi.io/documentation/developer-docs/latest/development/plugins/users-permissions.html#setting-up-the-server-url'
186
+ 'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url'
246
187
  );
247
188
  }
248
189
 
@@ -262,13 +203,7 @@ module.exports = {
262
203
  if (isEmail) {
263
204
  email = email.toLowerCase();
264
205
  } else {
265
- return ctx.badRequest(
266
- null,
267
- formatError({
268
- id: 'Auth.form.error.email.format',
269
- message: 'Please provide a valid email address.',
270
- })
271
- );
206
+ throw new ValidationError('Please provide a valid email address');
272
207
  }
273
208
 
274
209
  const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
@@ -280,24 +215,12 @@ module.exports = {
280
215
 
281
216
  // User not found.
282
217
  if (!user) {
283
- return ctx.badRequest(
284
- null,
285
- formatError({
286
- id: 'Auth.form.error.user.not-exist',
287
- message: 'This email does not exist.',
288
- })
289
- );
218
+ throw new ApplicationError('This email does not exist');
290
219
  }
291
220
 
292
221
  // User blocked
293
222
  if (user.blocked) {
294
- return ctx.badRequest(
295
- null,
296
- formatError({
297
- id: 'Auth.form.error.user.blocked',
298
- message: 'This user is disabled.',
299
- })
300
- );
223
+ throw new ApplicationError('This user is disabled');
301
224
  }
302
225
 
303
226
  // Generate random token.
@@ -315,9 +238,7 @@ module.exports = {
315
238
  key: 'advanced',
316
239
  });
317
240
 
318
- const userInfo = sanitizeEntity(user, {
319
- model: strapi.getModel('plugin::users-permissions.user'),
320
- });
241
+ const userInfo = await sanitizeUser(user, ctx);
321
242
 
322
243
  settings.message = await getService('users-permissions').template(settings.message, {
323
244
  URL: advanced.email_reset_password,
@@ -346,7 +267,7 @@ module.exports = {
346
267
  html: settings.message,
347
268
  });
348
269
  } catch (err) {
349
- return ctx.badRequest(null, err);
270
+ throw new ApplicationError(err.message);
350
271
  }
351
272
 
352
273
  // Update the user.
@@ -365,13 +286,7 @@ module.exports = {
365
286
  });
366
287
 
367
288
  if (!settings.allow_register) {
368
- return ctx.badRequest(
369
- null,
370
- formatError({
371
- id: 'Auth.advanced.allow_register',
372
- message: 'Register action is currently disabled.',
373
- })
374
- );
289
+ throw new ApplicationError('Register action is currently disabled');
375
290
  }
376
291
 
377
292
  const params = {
@@ -379,37 +294,13 @@ module.exports = {
379
294
  provider: 'local',
380
295
  };
381
296
 
382
- // Password is required.
383
- if (!params.password) {
384
- return ctx.badRequest(
385
- null,
386
- formatError({
387
- id: 'Auth.form.error.password.provide',
388
- message: 'Please provide your password.',
389
- })
390
- );
391
- }
392
-
393
- // Email is required.
394
- if (!params.email) {
395
- return ctx.badRequest(
396
- null,
397
- formatError({
398
- id: 'Auth.form.error.email.provide',
399
- message: 'Please provide your email.',
400
- })
401
- );
402
- }
297
+ await validateRegisterBody(params);
403
298
 
404
299
  // Throw an error if the password selected by the user
405
300
  // contains more than three times the symbol '$'.
406
301
  if (getService('user').isHashed(params.password)) {
407
- return ctx.badRequest(
408
- null,
409
- formatError({
410
- id: 'Auth.form.error.password.format',
411
- message: 'Your password cannot contain more than three times the symbol `$`.',
412
- })
302
+ throw new ValidationError(
303
+ 'Your password cannot contain more than three times the symbol `$`'
413
304
  );
414
305
  }
415
306
 
@@ -418,13 +309,7 @@ module.exports = {
418
309
  .findOne({ where: { type: settings.default_role } });
419
310
 
420
311
  if (!role) {
421
- return ctx.badRequest(
422
- null,
423
- formatError({
424
- id: 'Auth.form.error.role.notFound',
425
- message: 'Impossible to find the default role.',
426
- })
427
- );
312
+ throw new ApplicationError('Impossible to find the default role');
428
313
  }
429
314
 
430
315
  // Check if the provided email is valid or not.
@@ -433,13 +318,7 @@ module.exports = {
433
318
  if (isEmail) {
434
319
  params.email = params.email.toLowerCase();
435
320
  } else {
436
- return ctx.badRequest(
437
- null,
438
- formatError({
439
- id: 'Auth.form.error.email.format',
440
- message: 'Please provide valid email address.',
441
- })
442
- );
321
+ throw new ValidationError('Please provide a valid email address');
443
322
  }
444
323
 
445
324
  params.role = role.id;
@@ -450,23 +329,11 @@ module.exports = {
450
329
  });
451
330
 
452
331
  if (user && user.provider === params.provider) {
453
- return ctx.badRequest(
454
- null,
455
- formatError({
456
- id: 'Auth.form.error.email.taken',
457
- message: 'Email is already taken.',
458
- })
459
- );
332
+ throw new ApplicationError('Email is already taken');
460
333
  }
461
334
 
462
335
  if (user && user.provider !== params.provider && settings.unique_email) {
463
- return ctx.badRequest(
464
- null,
465
- formatError({
466
- id: 'Auth.form.error.email.taken',
467
- message: 'Email is already taken.',
468
- })
469
- );
336
+ throw new ApplicationError('Email is already taken');
470
337
  }
471
338
 
472
339
  try {
@@ -476,15 +343,13 @@ module.exports = {
476
343
 
477
344
  const user = await strapi.query('plugin::users-permissions.user').create({ data: params });
478
345
 
479
- const sanitizedUser = sanitizeEntity(user, {
480
- model: strapi.getModel('plugin::users-permissions.user'),
481
- });
346
+ const sanitizedUser = await sanitizeUser(user, ctx);
482
347
 
483
348
  if (settings.email_confirmation) {
484
349
  try {
485
- await getService('user').sendConfirmationEmail(user);
350
+ await getService('user').sendConfirmationEmail(sanitizedUser);
486
351
  } catch (err) {
487
- return ctx.badRequest(null, err);
352
+ throw new ApplicationError(err.message);
488
353
  }
489
354
 
490
355
  return ctx.send({ user: sanitizedUser });
@@ -497,14 +362,11 @@ module.exports = {
497
362
  user: sanitizedUser,
498
363
  });
499
364
  } catch (err) {
500
- const adminError = _.includes(err.message, 'username')
501
- ? {
502
- id: 'Auth.form.error.username.taken',
503
- message: 'Username already taken',
504
- }
505
- : { id: 'Auth.form.error.email.taken', message: 'Email already taken' };
506
-
507
- ctx.badRequest(null, formatError(adminError));
365
+ if (_.includes(err.message, 'username')) {
366
+ throw new ApplicationError('Username already taken');
367
+ } else {
368
+ throw new ApplicationError('Email already taken');
369
+ }
508
370
  }
509
371
  },
510
372
 
@@ -515,13 +377,13 @@ module.exports = {
515
377
  const jwtService = getService('jwt');
516
378
 
517
379
  if (_.isEmpty(confirmationToken)) {
518
- return ctx.badRequest('token.invalid');
380
+ throw new ValidationError('token.invalid');
519
381
  }
520
382
 
521
383
  const user = await userService.fetch({ confirmationToken }, []);
522
384
 
523
385
  if (!user) {
524
- return ctx.badRequest('token.invalid');
386
+ throw new ValidationError('token.invalid');
525
387
  }
526
388
 
527
389
  await userService.edit({ id: user.id }, { confirmed: true, confirmationToken: null });
@@ -529,9 +391,7 @@ module.exports = {
529
391
  if (returnUser) {
530
392
  ctx.send({
531
393
  jwt: jwtService.issue({ id: user.id }),
532
- user: sanitizeEntity(user, {
533
- model: strapi.getModel('plugin::users-permissions.user'),
534
- }),
394
+ user: await sanitizeUser(user, ctx),
535
395
  });
536
396
  } else {
537
397
  const settings = await strapi
@@ -545,16 +405,14 @@ module.exports = {
545
405
  async sendEmailConfirmation(ctx) {
546
406
  const params = _.assign(ctx.request.body);
547
407
 
548
- if (!params.email) {
549
- return ctx.badRequest('missing.email');
550
- }
408
+ await validateSendEmailConfirmationBody(params);
551
409
 
552
410
  const isEmail = emailRegExp.test(params.email);
553
411
 
554
412
  if (isEmail) {
555
413
  params.email = params.email.toLowerCase();
556
414
  } else {
557
- return ctx.badRequest('wrong.email');
415
+ throw new ValidationError('wrong.email');
558
416
  }
559
417
 
560
418
  const user = await strapi.query('plugin::users-permissions.user').findOne({
@@ -562,11 +420,11 @@ module.exports = {
562
420
  });
563
421
 
564
422
  if (user.confirmed) {
565
- return ctx.badRequest('already.confirmed');
423
+ throw new ApplicationError('already.confirmed');
566
424
  }
567
425
 
568
426
  if (user.blocked) {
569
- return ctx.badRequest('blocked.user');
427
+ throw new ApplicationError('blocked.user');
570
428
  }
571
429
 
572
430
  try {
@@ -576,7 +434,7 @@ module.exports = {
576
434
  sent: true,
577
435
  });
578
436
  } catch (err) {
579
- return ctx.badRequest(null, err);
437
+ throw new ApplicationError(err.message);
580
438
  }
581
439
  },
582
440
  };