aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts33
-rw-r--r--client/src/app/shared/users/user.service.ts3
-rw-r--r--server/controllers/api/users/me.ts2
-rw-r--r--server/middlewares/validators/users.ts21
-rw-r--r--server/tests/api/check-params/users.ts34
-rw-r--r--server/tests/api/users/users.ts33
-rw-r--r--server/tests/utils/users/users.ts2
8 files changed, 105 insertions, 33 deletions
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
index 00b6d20fa..ae797d1bc 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
@@ -1,14 +1,14 @@
1<div *ngIf="error" class="alert alert-danger">{{ error }}</div> 1<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
2 2
3<form role="form" (ngSubmit)="checkPassword()" [formGroup]="form"> 3<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
4 4
5 <label i18n for="new-password">Change password</label> 5 <label i18n for="new-password">Change password</label>
6 <input 6 <input
7 type="password" id="old-password" i18n-placeholder placeholder="Old password" 7 type="password" id="current-password" i18n-placeholder placeholder="Current password"
8 formControlName="old-password" [ngClass]="{ 'input-error': formErrors['old-password'] }" 8 formControlName="current-password" [ngClass]="{ 'input-error': formErrors['current-password'] }"
9 > 9 >
10 <div *ngIf="formErrors['old-password']" class="form-error"> 10 <div *ngIf="formErrors['current-password']" class="form-error">
11 {{ formErrors['old-password'] }} 11 {{ formErrors['current-password'] }}
12 </div> 12 </div>
13 13
14 <input 14 <input
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
index da0021df9..e5343b33d 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
@@ -30,7 +30,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
30 30
31 ngOnInit () { 31 ngOnInit () {
32 this.buildForm({ 32 this.buildForm({
33 'old-password': this.userValidatorsService.USER_PASSWORD, 33 'current-password': this.userValidatorsService.USER_PASSWORD,
34 'new-password': this.userValidatorsService.USER_PASSWORD, 34 'new-password': this.userValidatorsService.USER_PASSWORD,
35 'new-confirmed-password': this.userValidatorsService.USER_CONFIRM_PASSWORD 35 'new-confirmed-password': this.userValidatorsService.USER_CONFIRM_PASSWORD
36 }) 36 })
@@ -44,31 +44,26 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
44 .subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true })) 44 .subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true }))
45 } 45 }
46 46
47 checkPassword () { 47 changePassword () {
48 this.error = null 48 const currentPassword = this.form.value[ 'current-password' ]
49 const oldPassword = this.form.value[ 'old-password' ] 49 const newPassword = this.form.value[ 'new-password' ]
50 50
51 // compare old password 51 this.userService.changePassword(currentPassword, newPassword).subscribe(
52 this.authService.login(this.user.account.name, oldPassword)
53 .subscribe(
54 () => this.changePassword(),
55 err => {
56 if (err.message.indexOf('credentials are invalid') !== -1) this.error = this.i18n('Incorrect old password.')
57 else this.error = err.message
58 }
59 )
60
61 }
62
63 private changePassword () {
64 this.userService.changePassword(this.form.value[ 'new-password' ]).subscribe(
65 () => { 52 () => {
66 this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.')) 53 this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.'))
67 54
68 this.form.reset() 55 this.form.reset()
56 this.error = null
69 }, 57 },
70 58
71 err => this.error = err.message 59 err => {
60 if (err.status === 401) {
61 this.error = this.i18n('You current password is invalid.')
62 return
63 }
64
65 this.error = err.message
66 }
72 ) 67 )
73 } 68 }
74} 69}
diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts
index fad5b0980..bd5cd45d4 100644
--- a/client/src/app/shared/users/user.service.ts
+++ b/client/src/app/shared/users/user.service.ts
@@ -17,9 +17,10 @@ export class UserService {
17 ) { 17 ) {
18 } 18 }
19 19
20 changePassword (newPassword: string) { 20 changePassword (currentPassword: string, newPassword: string) {
21 const url = UserService.BASE_USERS_URL + 'me' 21 const url = UserService.BASE_USERS_URL + 'me'
22 const body: UserUpdateMe = { 22 const body: UserUpdateMe = {
23 currentPassword,
23 password: newPassword 24 password: newPassword
24 } 25 }
25 26
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index ff3a87b7f..591ec6b25 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -87,7 +87,7 @@ meRouter.get('/me/videos/:videoId/rating',
87 87
88meRouter.put('/me', 88meRouter.put('/me',
89 authenticate, 89 authenticate,
90 usersUpdateMeValidator, 90 asyncMiddleware(usersUpdateMeValidator),
91 asyncRetryTransactionMiddleware(updateMe) 91 asyncRetryTransactionMiddleware(updateMe)
92) 92)
93 93
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index d3ba1ae23..61297120a 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -22,6 +22,7 @@ import { Redis } from '../../lib/redis'
22import { UserModel } from '../../models/account/user' 22import { UserModel } from '../../models/account/user'
23import { areValidationErrors } from './utils' 23import { areValidationErrors } from './utils'
24import { ActorModel } from '../../models/activitypub/actor' 24import { ActorModel } from '../../models/activitypub/actor'
25import { comparePassword } from '../../helpers/peertube-crypto'
25 26
26const usersAddValidator = [ 27const usersAddValidator = [
27 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), 28 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
@@ -137,15 +138,31 @@ const usersUpdateValidator = [
137const usersUpdateMeValidator = [ 138const usersUpdateMeValidator = [
138 body('displayName').optional().custom(isUserDisplayNameValid).withMessage('Should have a valid display name'), 139 body('displayName').optional().custom(isUserDisplayNameValid).withMessage('Should have a valid display name'),
139 body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'), 140 body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
141 body('currentPassword').optional().custom(isUserPasswordValid).withMessage('Should have a valid current password'),
140 body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'), 142 body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
141 body('email').optional().isEmail().withMessage('Should have a valid email attribute'), 143 body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
142 body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'), 144 body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'),
143 body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'), 145 body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
144 146
145 (req: express.Request, res: express.Response, next: express.NextFunction) => { 147 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
146 // TODO: Add old password verification
147 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') }) 148 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
148 149
150 if (req.body.password) {
151 if (!req.body.currentPassword) {
152 return res.status(400)
153 .send({ error: 'currentPassword parameter is missing.' })
154 .end()
155 }
156
157 const user: UserModel = res.locals.oauth.token.User
158
159 if (await user.isPasswordMatch(req.body.currentPassword) !== true) {
160 return res.status(401)
161 .send({ error: 'currentPassword is invalid.' })
162 .end()
163 }
164 }
165
149 if (areValidationErrors(req, res)) return 166 if (areValidationErrors(req, res)) return
150 167
151 return next() 168 return next()
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 95903c8a5..cbfa0c137 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -254,6 +254,7 @@ describe('Test users API validators', function () {
254 254
255 it('Should fail with a too small password', async function () { 255 it('Should fail with a too small password', async function () {
256 const fields = { 256 const fields = {
257 currentPassword: 'my super password',
257 password: 'bla' 258 password: 'bla'
258 } 259 }
259 260
@@ -262,12 +263,31 @@ describe('Test users API validators', function () {
262 263
263 it('Should fail with a too long password', async function () { 264 it('Should fail with a too long password', async function () {
264 const fields = { 265 const fields = {
266 currentPassword: 'my super password',
265 password: 'super'.repeat(61) 267 password: 'super'.repeat(61)
266 } 268 }
267 269
268 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 270 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
269 }) 271 })
270 272
273 it('Should fail without the current password', async function () {
274 const fields = {
275 currentPassword: 'my super password',
276 password: 'super'.repeat(61)
277 }
278
279 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
280 })
281
282 it('Should fail with an invalid current password', async function () {
283 const fields = {
284 currentPassword: 'my super password fail',
285 password: 'super'.repeat(61)
286 }
287
288 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 401 })
289 })
290
271 it('Should fail with an invalid NSFW policy attribute', async function () { 291 it('Should fail with an invalid NSFW policy attribute', async function () {
272 const fields = { 292 const fields = {
273 nsfwPolicy: 'hello' 293 nsfwPolicy: 'hello'
@@ -286,6 +306,7 @@ describe('Test users API validators', function () {
286 306
287 it('Should fail with an non authenticated user', async function () { 307 it('Should fail with an non authenticated user', async function () {
288 const fields = { 308 const fields = {
309 currentPassword: 'my super password',
289 password: 'my super password' 310 password: 'my super password'
290 } 311 }
291 312
@@ -300,8 +321,9 @@ describe('Test users API validators', function () {
300 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 321 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
301 }) 322 })
302 323
303 it('Should succeed with the correct params', async function () { 324 it('Should succeed to change password with the correct params', async function () {
304 const fields = { 325 const fields = {
326 currentPassword: 'my super password',
305 password: 'my super password', 327 password: 'my super password',
306 nsfwPolicy: 'blur', 328 nsfwPolicy: 'blur',
307 autoPlayVideo: false, 329 autoPlayVideo: false,
@@ -310,6 +332,16 @@ describe('Test users API validators', function () {
310 332
311 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 }) 333 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
312 }) 334 })
335
336 it('Should succeed without password change with the correct params', async function () {
337 const fields = {
338 nsfwPolicy: 'blur',
339 autoPlayVideo: false,
340 email: 'super_email@example.com'
341 }
342
343 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
344 })
313 }) 345 })
314 346
315 describe('When updating my avatar', function () { 347 describe('When updating my avatar', function () {
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index c0dd587ee..8b9c6b455 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -4,10 +4,34 @@ import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { User, UserRole } from '../../../../shared/index' 5import { User, UserRole } from '../../../../shared/index'
6import { 6import {
7 createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating, 7 blockUser,
8 getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo, 8 createUser,
9 registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin, 9 deleteMe,
10 deleteMe, blockUser, unblockUser, updateCustomSubConfig 10 flushTests,
11 getBlacklistedVideosList,
12 getMyUserInformation,
13 getMyUserVideoQuotaUsed,
14 getMyUserVideoRating,
15 getUserInformation,
16 getUsersList,
17 getUsersListPaginationAndSort,
18 getVideosList,
19 killallServers,
20 login,
21 makePutBodyRequest,
22 rateVideo,
23 registerUser,
24 removeUser,
25 removeVideo,
26 runServer,
27 ServerInfo,
28 testImage,
29 unblockUser,
30 updateMyAvatar,
31 updateMyUser,
32 updateUser,
33 uploadVideo,
34 userLogin
11} from '../../utils/index' 35} from '../../utils/index'
12import { follow } from '../../utils/server/follows' 36import { follow } from '../../utils/server/follows'
13import { setAccessTokensToServers } from '../../utils/users/login' 37import { setAccessTokensToServers } from '../../utils/users/login'
@@ -302,6 +326,7 @@ describe('Test users', function () {
302 await updateMyUser({ 326 await updateMyUser({
303 url: server.url, 327 url: server.url,
304 accessToken: accessTokenUser, 328 accessToken: accessTokenUser,
329 currentPassword: 'super password',
305 newPassword: 'new password' 330 newPassword: 'new password'
306 }) 331 })
307 user.password = 'new password' 332 user.password = 'new password'
diff --git a/server/tests/utils/users/users.ts b/server/tests/utils/users/users.ts
index cd1b07701..41d8ce265 100644
--- a/server/tests/utils/users/users.ts
+++ b/server/tests/utils/users/users.ts
@@ -162,6 +162,7 @@ function unblockUser (url: string, userId: number | string, accessToken: string,
162function updateMyUser (options: { 162function updateMyUser (options: {
163 url: string 163 url: string
164 accessToken: string, 164 accessToken: string,
165 currentPassword?: string,
165 newPassword?: string, 166 newPassword?: string,
166 nsfwPolicy?: NSFWPolicyType, 167 nsfwPolicy?: NSFWPolicyType,
167 email?: string, 168 email?: string,
@@ -172,6 +173,7 @@ function updateMyUser (options: {
172 const path = '/api/v1/users/me' 173 const path = '/api/v1/users/me'
173 174
174 const toSend = {} 175 const toSend = {}
176 if (options.currentPassword !== undefined && options.currentPassword !== null) toSend['currentPassword'] = options.currentPassword
175 if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword 177 if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
176 if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy 178 if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
177 if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo 179 if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo