mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-24 20:14:35 -04:00
Merge pull request #1935 from hedgedoc/feature/verify-password-change
This commit is contained in:
commit
9ecf7ba2be
7 changed files with 44 additions and 9 deletions
|
@ -9,10 +9,10 @@ import {
|
||||||
ConflictException,
|
ConflictException,
|
||||||
Controller,
|
Controller,
|
||||||
Delete,
|
Delete,
|
||||||
NotFoundException,
|
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
Req,
|
Req,
|
||||||
|
UnauthorizedException,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Session } from 'express-session';
|
import { Session } from 'express-session';
|
||||||
|
@ -70,6 +70,10 @@ export class AuthController {
|
||||||
@Body() changePasswordDto: UpdatePasswordDto,
|
@Body() changePasswordDto: UpdatePasswordDto,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
await this.identityService.checkLocalPassword(
|
||||||
|
user,
|
||||||
|
changePasswordDto.currentPassword,
|
||||||
|
);
|
||||||
await this.identityService.updateLocalPassword(
|
await this.identityService.updateLocalPassword(
|
||||||
user,
|
user,
|
||||||
changePasswordDto.newPassword,
|
changePasswordDto.newPassword,
|
||||||
|
@ -77,7 +81,9 @@ export class AuthController {
|
||||||
return;
|
return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
throw new NotFoundException(e.message);
|
throw new UnauthorizedException(
|
||||||
|
'Verifying your identity with the current password did not work.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,9 +102,9 @@ describe('IdentityService', () => {
|
||||||
) as Identity;
|
) as Identity;
|
||||||
identity.passwordHash = await hashPassword(password);
|
identity.passwordHash = await hashPassword(password);
|
||||||
user.identities = Promise.resolve([identity]);
|
user.identities = Promise.resolve([identity]);
|
||||||
await expect(
|
await expect(service.checkLocalPassword(user, password)).resolves.toEqual(
|
||||||
service.loginWithLocalIdentity(user, password),
|
undefined,
|
||||||
).resolves.toEqual(undefined);
|
);
|
||||||
});
|
});
|
||||||
describe('fails', () => {
|
describe('fails', () => {
|
||||||
it('when user has no local identity', async () => {
|
it('when user has no local identity', async () => {
|
||||||
|
|
|
@ -73,20 +73,20 @@ export class IdentityService {
|
||||||
* @param {string} password - the password to use
|
* @param {string} password - the password to use
|
||||||
* @throws {NotInDBError} the specified user can't be logged in
|
* @throws {NotInDBError} the specified user can't be logged in
|
||||||
*/
|
*/
|
||||||
async loginWithLocalIdentity(user: User, password: string): Promise<void> {
|
async checkLocalPassword(user: User, password: string): Promise<void> {
|
||||||
const internalIdentity: Identity | undefined =
|
const internalIdentity: Identity | undefined =
|
||||||
await getFirstIdentityFromUser(user, ProviderType.LOCAL);
|
await getFirstIdentityFromUser(user, ProviderType.LOCAL);
|
||||||
if (internalIdentity === undefined) {
|
if (internalIdentity === undefined) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`The user with the username ${user.username} does not have a internal identity.`,
|
`The user with the username ${user.username} does not have a internal identity.`,
|
||||||
'loginWithLocalIdentity',
|
'checkLocalPassword',
|
||||||
);
|
);
|
||||||
throw new NotInDBError();
|
throw new NotInDBError();
|
||||||
}
|
}
|
||||||
if (!(await checkPassword(password, internalIdentity.passwordHash ?? ''))) {
|
if (!(await checkPassword(password, internalIdentity.passwordHash ?? ''))) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Password check for ${user.username} did not succeed.`,
|
`Password check for ${user.username} did not succeed.`,
|
||||||
'loginWithLocalIdentity',
|
'checkLocalPassword',
|
||||||
);
|
);
|
||||||
throw new NotInDBError();
|
throw new NotInDBError();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
|
||||||
const user = await this.userService.getUserByUsername(username, [
|
const user = await this.userService.getUserByUsername(username, [
|
||||||
UserRelationEnum.IDENTITIES,
|
UserRelationEnum.IDENTITIES,
|
||||||
]);
|
]);
|
||||||
await this.identityService.loginWithLocalIdentity(user, password);
|
await this.identityService.checkLocalPassword(user, password);
|
||||||
return user;
|
return user;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
import { IsString } from 'class-validator';
|
import { IsString } from 'class-validator';
|
||||||
|
|
||||||
export class UpdatePasswordDto {
|
export class UpdatePasswordDto {
|
||||||
|
@IsString()
|
||||||
|
currentPassword: string;
|
||||||
@IsString()
|
@IsString()
|
||||||
newPassword: string;
|
newPassword: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ describe('Auth', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
// Change password
|
// Change password
|
||||||
const changePasswordDto: UpdatePasswordDto = {
|
const changePasswordDto: UpdatePasswordDto = {
|
||||||
|
currentPassword: password,
|
||||||
newPassword: newPassword,
|
newPassword: newPassword,
|
||||||
};
|
};
|
||||||
await request(testSetup.app.getHttpServer())
|
await request(testSetup.app.getHttpServer())
|
||||||
|
@ -133,6 +134,7 @@ describe('Auth', () => {
|
||||||
cookie = response.get('Set-Cookie')[0];
|
cookie = response.get('Set-Cookie')[0];
|
||||||
// Reset password
|
// Reset password
|
||||||
const changePasswordBackDto: UpdatePasswordDto = {
|
const changePasswordBackDto: UpdatePasswordDto = {
|
||||||
|
currentPassword: newPassword,
|
||||||
newPassword: password,
|
newPassword: password,
|
||||||
};
|
};
|
||||||
await request(testSetup.app.getHttpServer())
|
await request(testSetup.app.getHttpServer())
|
||||||
|
@ -146,6 +148,7 @@ describe('Auth', () => {
|
||||||
testSetup.configService.get('authConfig').local.enableLogin = false;
|
testSetup.configService.get('authConfig').local.enableLogin = false;
|
||||||
// Try to change password
|
// Try to change password
|
||||||
const changePasswordDto: UpdatePasswordDto = {
|
const changePasswordDto: UpdatePasswordDto = {
|
||||||
|
currentPassword: password,
|
||||||
newPassword: newPassword,
|
newPassword: newPassword,
|
||||||
};
|
};
|
||||||
await request(testSetup.app.getHttpServer())
|
await request(testSetup.app.getHttpServer())
|
||||||
|
@ -177,6 +180,29 @@ describe('Auth', () => {
|
||||||
.send(JSON.stringify(loginOldPasswordDto))
|
.send(JSON.stringify(loginOldPasswordDto))
|
||||||
.expect(201);
|
.expect(201);
|
||||||
});
|
});
|
||||||
|
it('fails, when old password is wrong', async () => {
|
||||||
|
// Try to change password
|
||||||
|
const changePasswordDto: UpdatePasswordDto = {
|
||||||
|
currentPassword: 'wrong',
|
||||||
|
newPassword: newPassword,
|
||||||
|
};
|
||||||
|
await request(testSetup.app.getHttpServer())
|
||||||
|
.put('/api/private/auth/local')
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.set('Cookie', cookie)
|
||||||
|
.send(JSON.stringify(changePasswordDto))
|
||||||
|
.expect(401);
|
||||||
|
// old password still does work for login
|
||||||
|
const loginOldPasswordDto: LoginDto = {
|
||||||
|
password: password,
|
||||||
|
username: username,
|
||||||
|
};
|
||||||
|
await request(testSetup.app.getHttpServer())
|
||||||
|
.post('/api/private/auth/local/login')
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.send(JSON.stringify(loginOldPasswordDto))
|
||||||
|
.expect(201);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /auth/local/login', () => {
|
describe('POST /auth/local/login', () => {
|
||||||
|
|
|
@ -114,6 +114,7 @@ describe('Register and Login', () => {
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send(
|
.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
currentPassword: PASSWORD,
|
||||||
newPassword: 'newPassword',
|
newPassword: 'newPassword',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue