@strapi/plugin-users-permissions 4.3.2-alpha.0 → 4.3.4

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.2-alpha.0",
3
+ "version": "4.3.4",
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.2-alpha.0",
31
- "@strapi/utils": "4.3.2-alpha.0",
30
+ "@strapi/helper-plugin": "4.3.4",
31
+ "@strapi/utils": "4.3.4",
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": "8ab2db1497c3a5a805173b2d92248648b40a6f65"
62
+ "gitHead": "28a2a00db8234ffcf644661c4c8092aa82f8c119"
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;
@@ -104,6 +105,36 @@ module.exports = {
104
105
  }
105
106
  },
106
107
 
108
+ async changePassword(ctx) {
109
+ if (!ctx.state.user) {
110
+ throw new ApplicationError('You must be authenticated to reset your password');
111
+ }
112
+
113
+ const { currentPassword, password } = await validateChangePasswordBody(ctx.request.body);
114
+
115
+ const user = await strapi.entityService.findOne(
116
+ 'plugin::users-permissions.user',
117
+ ctx.state.user.id
118
+ );
119
+
120
+ const validPassword = await getService('user').validatePassword(currentPassword, user.password);
121
+
122
+ if (!validPassword) {
123
+ throw new ValidationError('The provided current password is invalid');
124
+ }
125
+
126
+ if (currentPassword === password) {
127
+ throw new ValidationError('Your new password must be different than your current password');
128
+ }
129
+
130
+ await getService('user').edit(user.id, { password });
131
+
132
+ ctx.send({
133
+ jwt: getService('jwt').issue({ id: user.id }),
134
+ user: await sanitizeUser(user, ctx),
135
+ });
136
+ },
137
+
107
138
  async resetPassword(ctx) {
108
139
  const { password, passwordConfirmation, code } = await validateResetPasswordBody(
109
140
  ctx.request.body
@@ -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 => {