Only allow changing the email if the correct password is provided (#1288)

* fix

* fix overwriting password

* finalize

* BadRequestException

---------

Co-authored-by: Philipinho <16838612+Philipinho@users.noreply.github.com>
This commit is contained in:
Finn Dittmar
2025-06-24 10:02:55 +02:00
committed by GitHub
parent e775e4dd8c
commit d5b84ae0b8
3 changed files with 49 additions and 6 deletions

View File

@ -1,5 +1,13 @@
import { OmitType, PartialType } from '@nestjs/mapped-types';
import { IsBoolean, IsIn, IsOptional, IsString } from 'class-validator';
import {
IsBoolean,
IsIn,
IsNotEmpty,
IsOptional,
IsString,
MaxLength,
MinLength,
} from 'class-validator';
import { CreateUserDto } from '../../auth/dto/create-user.dto';
export class UpdateUserDto extends PartialType(
@ -21,4 +29,10 @@ export class UpdateUserDto extends PartialType(
@IsOptional()
@IsString()
locale: string;
@IsOptional()
@MinLength(8)
@MaxLength(70)
@IsString()
confirmPassword: string;
}

View File

@ -50,6 +50,6 @@ export class UserController {
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
) {
return this.userService.update(updateUserDto, user.id, workspace.id);
return this.userService.update(updateUserDto, user.id, workspace);
}
}

View File

@ -3,8 +3,12 @@ import {
BadRequestException,
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { UpdateUserDto } from './dto/update-user.dto';
import { comparePasswordHash } from 'src/common/helpers/utils';
import { Workspace } from '@docmost/db/types/entity.types';
import { validateSsoEnforcement } from '../auth/auth.util';
@Injectable()
export class UserService {
@ -17,9 +21,14 @@ export class UserService {
async update(
updateUserDto: UpdateUserDto,
userId: string,
workspaceId: string,
workspace: Workspace,
) {
const user = await this.userRepo.findById(userId, workspaceId);
const includePassword =
updateUserDto.email != null && updateUserDto.confirmPassword != null;
const user = await this.userRepo.findById(userId, workspace.id, {
includePassword,
});
if (!user) {
throw new NotFoundException('User not found');
@ -47,9 +56,27 @@ export class UserService {
}
if (updateUserDto.email && user.email != updateUserDto.email) {
if (await this.userRepo.findByEmail(updateUserDto.email, workspaceId)) {
validateSsoEnforcement(workspace);
if (!updateUserDto.confirmPassword) {
throw new BadRequestException(
'You must provide a password to change your email',
);
}
const isPasswordMatch = await comparePasswordHash(
updateUserDto.confirmPassword,
user.password,
);
if (!isPasswordMatch) {
throw new BadRequestException('You must provide the correct password to change your email');
}
if (await this.userRepo.findByEmail(updateUserDto.email, workspace.id)) {
throw new BadRequestException('A user with this email already exists');
}
user.email = updateUserDto.email;
}
@ -61,7 +88,9 @@ export class UserService {
user.locale = updateUserDto.locale;
}
await this.userRepo.updateUser(updateUserDto, userId, workspaceId);
delete updateUserDto.confirmPassword;
await this.userRepo.updateUser(updateUserDto, userId, workspace.id);
return user;
}
}