Check current password on server side
This commit is contained in:
parent
be1206bb93
commit
a890d1e0d3
|
@ -1,14 +1,14 @@
|
||||||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||||
|
|
||||||
<form role="form" (ngSubmit)="checkPassword()" [formGroup]="form">
|
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
|
||||||
|
|
||||||
<label i18n for="new-password">Change password</label>
|
<label i18n for="new-password">Change password</label>
|
||||||
<input
|
<input
|
||||||
type="password" id="old-password" i18n-placeholder placeholder="Old password"
|
type="password" id="current-password" i18n-placeholder placeholder="Current password"
|
||||||
formControlName="old-password" [ngClass]="{ 'input-error': formErrors['old-password'] }"
|
formControlName="current-password" [ngClass]="{ 'input-error': formErrors['current-password'] }"
|
||||||
>
|
>
|
||||||
<div *ngIf="formErrors['old-password']" class="form-error">
|
<div *ngIf="formErrors['current-password']" class="form-error">
|
||||||
{{ formErrors['old-password'] }}
|
{{ formErrors['current-password'] }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.buildForm({
|
this.buildForm({
|
||||||
'old-password': this.userValidatorsService.USER_PASSWORD,
|
'current-password': this.userValidatorsService.USER_PASSWORD,
|
||||||
'new-password': this.userValidatorsService.USER_PASSWORD,
|
'new-password': this.userValidatorsService.USER_PASSWORD,
|
||||||
'new-confirmed-password': this.userValidatorsService.USER_CONFIRM_PASSWORD
|
'new-confirmed-password': this.userValidatorsService.USER_CONFIRM_PASSWORD
|
||||||
})
|
})
|
||||||
|
@ -44,31 +44,26 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
|
||||||
.subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true }))
|
.subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true }))
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPassword () {
|
changePassword () {
|
||||||
this.error = null
|
const currentPassword = this.form.value[ 'current-password' ]
|
||||||
const oldPassword = this.form.value[ 'old-password' ]
|
const newPassword = this.form.value[ 'new-password' ]
|
||||||
|
|
||||||
// compare old password
|
this.userService.changePassword(currentPassword, newPassword).subscribe(
|
||||||
this.authService.login(this.user.account.name, oldPassword)
|
|
||||||
.subscribe(
|
|
||||||
() => this.changePassword(),
|
|
||||||
err => {
|
|
||||||
if (err.message.indexOf('credentials are invalid') !== -1) this.error = this.i18n('Incorrect old password.')
|
|
||||||
else this.error = err.message
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private changePassword () {
|
|
||||||
this.userService.changePassword(this.form.value[ 'new-password' ]).subscribe(
|
|
||||||
() => {
|
() => {
|
||||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.'))
|
this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.'))
|
||||||
|
|
||||||
this.form.reset()
|
this.form.reset()
|
||||||
|
this.error = null
|
||||||
},
|
},
|
||||||
|
|
||||||
err => this.error = err.message
|
err => {
|
||||||
|
if (err.status === 401) {
|
||||||
|
this.error = this.i18n('You current password is invalid.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error = err.message
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ export class UserService {
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
changePassword (newPassword: string) {
|
changePassword (currentPassword: string, newPassword: string) {
|
||||||
const url = UserService.BASE_USERS_URL + 'me'
|
const url = UserService.BASE_USERS_URL + 'me'
|
||||||
const body: UserUpdateMe = {
|
const body: UserUpdateMe = {
|
||||||
|
currentPassword,
|
||||||
password: newPassword
|
password: newPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ meRouter.get('/me/videos/:videoId/rating',
|
||||||
|
|
||||||
meRouter.put('/me',
|
meRouter.put('/me',
|
||||||
authenticate,
|
authenticate,
|
||||||
usersUpdateMeValidator,
|
asyncMiddleware(usersUpdateMeValidator),
|
||||||
asyncRetryTransactionMiddleware(updateMe)
|
asyncRetryTransactionMiddleware(updateMe)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { Redis } from '../../lib/redis'
|
||||||
import { UserModel } from '../../models/account/user'
|
import { UserModel } from '../../models/account/user'
|
||||||
import { areValidationErrors } from './utils'
|
import { areValidationErrors } from './utils'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
|
import { comparePassword } from '../../helpers/peertube-crypto'
|
||||||
|
|
||||||
const usersAddValidator = [
|
const usersAddValidator = [
|
||||||
body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
|
body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
|
||||||
|
@ -137,15 +138,31 @@ const usersUpdateValidator = [
|
||||||
const usersUpdateMeValidator = [
|
const usersUpdateMeValidator = [
|
||||||
body('displayName').optional().custom(isUserDisplayNameValid).withMessage('Should have a valid display name'),
|
body('displayName').optional().custom(isUserDisplayNameValid).withMessage('Should have a valid display name'),
|
||||||
body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
|
body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
|
||||||
|
body('currentPassword').optional().custom(isUserPasswordValid).withMessage('Should have a valid current password'),
|
||||||
body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
|
body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
|
||||||
body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
|
body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
|
||||||
body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'),
|
body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'),
|
||||||
body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
|
body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
// TODO: Add old password verification
|
|
||||||
logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
|
logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
|
||||||
|
|
||||||
|
if (req.body.password) {
|
||||||
|
if (!req.body.currentPassword) {
|
||||||
|
return res.status(400)
|
||||||
|
.send({ error: 'currentPassword parameter is missing.' })
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
const user: UserModel = res.locals.oauth.token.User
|
||||||
|
|
||||||
|
if (await user.isPasswordMatch(req.body.currentPassword) !== true) {
|
||||||
|
return res.status(401)
|
||||||
|
.send({ error: 'currentPassword is invalid.' })
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
|
|
|
@ -254,6 +254,7 @@ describe('Test users API validators', function () {
|
||||||
|
|
||||||
it('Should fail with a too small password', async function () {
|
it('Should fail with a too small password', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
|
currentPassword: 'my super password',
|
||||||
password: 'bla'
|
password: 'bla'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,12 +263,31 @@ describe('Test users API validators', function () {
|
||||||
|
|
||||||
it('Should fail with a too long password', async function () {
|
it('Should fail with a too long password', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
|
currentPassword: 'my super password',
|
||||||
password: 'super'.repeat(61)
|
password: 'super'.repeat(61)
|
||||||
}
|
}
|
||||||
|
|
||||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should fail without the current password', async function () {
|
||||||
|
const fields = {
|
||||||
|
currentPassword: 'my super password',
|
||||||
|
password: 'super'.repeat(61)
|
||||||
|
}
|
||||||
|
|
||||||
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with an invalid current password', async function () {
|
||||||
|
const fields = {
|
||||||
|
currentPassword: 'my super password fail',
|
||||||
|
password: 'super'.repeat(61)
|
||||||
|
}
|
||||||
|
|
||||||
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 401 })
|
||||||
|
})
|
||||||
|
|
||||||
it('Should fail with an invalid NSFW policy attribute', async function () {
|
it('Should fail with an invalid NSFW policy attribute', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
nsfwPolicy: 'hello'
|
nsfwPolicy: 'hello'
|
||||||
|
@ -286,6 +306,7 @@ describe('Test users API validators', function () {
|
||||||
|
|
||||||
it('Should fail with an non authenticated user', async function () {
|
it('Should fail with an non authenticated user', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
|
currentPassword: 'my super password',
|
||||||
password: 'my super password'
|
password: 'my super password'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,8 +321,9 @@ describe('Test users API validators', function () {
|
||||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should succeed with the correct params', async function () {
|
it('Should succeed to change password with the correct params', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
|
currentPassword: 'my super password',
|
||||||
password: 'my super password',
|
password: 'my super password',
|
||||||
nsfwPolicy: 'blur',
|
nsfwPolicy: 'blur',
|
||||||
autoPlayVideo: false,
|
autoPlayVideo: false,
|
||||||
|
@ -310,6 +332,16 @@ describe('Test users API validators', function () {
|
||||||
|
|
||||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should succeed without password change with the correct params', async function () {
|
||||||
|
const fields = {
|
||||||
|
nsfwPolicy: 'blur',
|
||||||
|
autoPlayVideo: false,
|
||||||
|
email: 'super_email@example.com'
|
||||||
|
}
|
||||||
|
|
||||||
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('When updating my avatar', function () {
|
describe('When updating my avatar', function () {
|
||||||
|
|
|
@ -4,10 +4,34 @@ import * as chai from 'chai'
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import { User, UserRole } from '../../../../shared/index'
|
import { User, UserRole } from '../../../../shared/index'
|
||||||
import {
|
import {
|
||||||
createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating,
|
blockUser,
|
||||||
getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo,
|
createUser,
|
||||||
registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin,
|
deleteMe,
|
||||||
deleteMe, blockUser, unblockUser, updateCustomSubConfig
|
flushTests,
|
||||||
|
getBlacklistedVideosList,
|
||||||
|
getMyUserInformation,
|
||||||
|
getMyUserVideoQuotaUsed,
|
||||||
|
getMyUserVideoRating,
|
||||||
|
getUserInformation,
|
||||||
|
getUsersList,
|
||||||
|
getUsersListPaginationAndSort,
|
||||||
|
getVideosList,
|
||||||
|
killallServers,
|
||||||
|
login,
|
||||||
|
makePutBodyRequest,
|
||||||
|
rateVideo,
|
||||||
|
registerUser,
|
||||||
|
removeUser,
|
||||||
|
removeVideo,
|
||||||
|
runServer,
|
||||||
|
ServerInfo,
|
||||||
|
testImage,
|
||||||
|
unblockUser,
|
||||||
|
updateMyAvatar,
|
||||||
|
updateMyUser,
|
||||||
|
updateUser,
|
||||||
|
uploadVideo,
|
||||||
|
userLogin
|
||||||
} from '../../utils/index'
|
} from '../../utils/index'
|
||||||
import { follow } from '../../utils/server/follows'
|
import { follow } from '../../utils/server/follows'
|
||||||
import { setAccessTokensToServers } from '../../utils/users/login'
|
import { setAccessTokensToServers } from '../../utils/users/login'
|
||||||
|
@ -302,6 +326,7 @@ describe('Test users', function () {
|
||||||
await updateMyUser({
|
await updateMyUser({
|
||||||
url: server.url,
|
url: server.url,
|
||||||
accessToken: accessTokenUser,
|
accessToken: accessTokenUser,
|
||||||
|
currentPassword: 'super password',
|
||||||
newPassword: 'new password'
|
newPassword: 'new password'
|
||||||
})
|
})
|
||||||
user.password = 'new password'
|
user.password = 'new password'
|
||||||
|
|
|
@ -162,6 +162,7 @@ function unblockUser (url: string, userId: number | string, accessToken: string,
|
||||||
function updateMyUser (options: {
|
function updateMyUser (options: {
|
||||||
url: string
|
url: string
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
|
currentPassword?: string,
|
||||||
newPassword?: string,
|
newPassword?: string,
|
||||||
nsfwPolicy?: NSFWPolicyType,
|
nsfwPolicy?: NSFWPolicyType,
|
||||||
email?: string,
|
email?: string,
|
||||||
|
@ -172,6 +173,7 @@ function updateMyUser (options: {
|
||||||
const path = '/api/v1/users/me'
|
const path = '/api/v1/users/me'
|
||||||
|
|
||||||
const toSend = {}
|
const toSend = {}
|
||||||
|
if (options.currentPassword !== undefined && options.currentPassword !== null) toSend['currentPassword'] = options.currentPassword
|
||||||
if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
|
if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
|
||||||
if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
|
if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
|
||||||
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
|
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
|
||||||
|
|
Loading…
Reference in New Issue