@strapi/plugin-users-permissions 4.3.1 → 4.3.3

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.
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-users-permissions",
3
- "version": "4.3.1",
3
+ "version": "4.3.3",
4
4
  "description": "Protect your API with a full-authentication process based on JWT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,8 +27,8 @@
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.1",
31
- "@strapi/utils": "4.3.1",
30
+ "@strapi/helper-plugin": "4.3.3",
31
+ "@strapi/utils": "4.3.3",
32
32
  "bcryptjs": "2.4.3",
33
33
  "grant-koa": "5.4.8",
34
34
  "jsonwebtoken": "^8.1.0",
@@ -59,5 +59,5 @@
59
59
  "required": true,
60
60
  "kind": "plugin"
61
61
  },
62
- "gitHead": "1eab2fb08c7a4d3d40a5a7ff3b2f137ce0afcf8a"
62
+ "gitHead": "4d60bfdeee6eb5e9b42b5a614690d7bb86c5f84a"
63
63
  }
@@ -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;
@@ -90,18 +91,36 @@ module.exports = {
90
91
  user: await sanitizeUser(user, ctx),
91
92
  });
92
93
  }
94
+ },
93
95
 
94
- // Connect the user with the third-party provider.
95
- try {
96
- const user = await getService('providers').connect(provider, ctx.query);
96
+ async changePassword(ctx) {
97
+ if (!ctx.state.user) {
98
+ throw new ApplicationError('You must be authenticated to reset your password');
99
+ }
97
100
 
98
- return ctx.send({
99
- jwt: getService('jwt').issue({ id: user.id }),
100
- user: await sanitizeUser(user, ctx),
101
- });
102
- } catch (error) {
103
- throw new ApplicationError(error.message);
101
+ const { currentPassword, password } = await validateChangePasswordBody(ctx.request.body);
102
+
103
+ const user = await strapi.entityService.findOne(
104
+ 'plugin::users-permissions.user',
105
+ ctx.state.user.id
106
+ );
107
+
108
+ const validPassword = await getService('user').validatePassword(currentPassword, user.password);
109
+
110
+ if (!validPassword) {
111
+ throw new ValidationError('The provided current password is invalid');
112
+ }
113
+
114
+ if (currentPassword === password) {
115
+ throw new ValidationError('Your new password must be different than your current password');
104
116
  }
117
+
118
+ await getService('user').edit(user.id, { password });
119
+
120
+ ctx.send({
121
+ jwt: getService('jwt').issue({ id: user.id }),
122
+ user: await sanitizeUser(user, ctx),
123
+ });
105
124
  },
106
125
 
107
126
  async resetPassword(ctx) {
@@ -44,6 +44,17 @@ const resetPasswordSchema = yup
44
44
  })
45
45
  .noUnknown();
46
46
 
47
+ const changePasswordSchema = yup
48
+ .object({
49
+ password: yup.string().required(),
50
+ passwordConfirmation: yup
51
+ .string()
52
+ .required()
53
+ .oneOf([yup.ref('password')], 'Passwords do not match'),
54
+ currentPassword: yup.string().required(),
55
+ })
56
+ .noUnknown();
57
+
47
58
  module.exports = {
48
59
  validateCallbackBody: validateYupSchema(callbackSchema),
49
60
  validateRegisterBody: validateYupSchema(registerSchema),
@@ -51,4 +62,5 @@ module.exports = {
51
62
  validateEmailConfirmationBody: validateYupSchema(validateEmailConfirmationSchema),
52
63
  validateForgotPasswordBody: validateYupSchema(forgotPasswordSchema),
53
64
  validateResetPasswordBody: validateYupSchema(resetPasswordSchema),
65
+ validateChangePasswordBody: validateYupSchema(changePasswordSchema),
54
66
  };
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const { toPlainObject } = require('lodash/fp');
4
+
5
+ const { checkBadRequest } = require('../../utils');
6
+
7
+ module.exports = ({ nexus, strapi }) => {
8
+ const { nonNull } = nexus;
9
+
10
+ return {
11
+ type: 'UsersPermissionsLoginPayload',
12
+
13
+ args: {
14
+ currentPassword: nonNull('String'),
15
+ password: nonNull('String'),
16
+ passwordConfirmation: nonNull('String'),
17
+ },
18
+
19
+ description: 'Change user password. Confirm with the current password.',
20
+
21
+ async resolve(parent, args, context) {
22
+ const { koaContext } = context;
23
+
24
+ koaContext.request.body = toPlainObject(args);
25
+
26
+ await strapi
27
+ .plugin('users-permissions')
28
+ .controller('auth')
29
+ .changePassword(koaContext);
30
+
31
+ const output = koaContext.body;
32
+
33
+ checkBadRequest(output);
34
+
35
+ return {
36
+ user: output.user || output,
37
+ jwt: output.jwt,
38
+ };
39
+ },
40
+ };
41
+ };
@@ -25,6 +25,7 @@ module.exports = context => {
25
25
  register: require('./auth/register'),
26
26
  forgotPassword: require('./auth/forgot-password'),
27
27
  resetPassword: require('./auth/reset-password'),
28
+ changePassword: require('./auth/change-password'),
28
29
  emailConfirmation: require('./auth/email-confirmation'),
29
30
  };
30
31
 
@@ -23,6 +23,11 @@ module.exports = ({ strapi }) => {
23
23
  'Mutation.forgotPassword': { auth: false },
24
24
  'Mutation.resetPassword': { auth: false },
25
25
  'Mutation.emailConfirmation': { auth: false },
26
+ 'Mutation.changePassword': {
27
+ auth: {
28
+ scope: 'plugin::users-permissions.auth.changePassword',
29
+ },
30
+ },
26
31
 
27
32
  // Scoped auth for replaced CRUD operations
28
33
  // Role
@@ -70,4 +70,13 @@ module.exports = [
70
70
  prefix: '',
71
71
  },
72
72
  },
73
+ {
74
+ method: 'POST',
75
+ path: '/auth/change-password',
76
+ handler: 'auth.changePassword',
77
+ config: {
78
+ middlewares: ['plugin::users-permissions.rateLimit'],
79
+ prefix: '',
80
+ },
81
+ },
73
82
  ];
@@ -87,13 +87,6 @@ module.exports = ({ strapi }) => ({
87
87
  async remove(params) {
88
88
  return strapi.query('plugin::users-permissions.user').delete({ where: params });
89
89
  },
90
- isHashed(password) {
91
- if (typeof password !== 'string' || !password) {
92
- return false;
93
- }
94
-
95
- return password.split('$').length === 4;
96
- },
97
90
 
98
91
  validatePassword(password, hash) {
99
92
  return bcrypt.compare(password, hash);
@@ -15,6 +15,7 @@ const DEFAULT_PERMISSIONS = [
15
15
  { action: 'plugin::users-permissions.auth.emailConfirmation', roleType: 'public' },
16
16
  { action: 'plugin::users-permissions.auth.sendEmailConfirmation', roleType: 'public' },
17
17
  { action: 'plugin::users-permissions.user.me', roleType: 'authenticated' },
18
+ { action: 'plugin::users-permissions.auth.changePassword', roleType: 'authenticated' },
18
19
  ];
19
20
 
20
21
  const transformRoutePrefixFor = pluginName => route => {