diff --git a/packages/backend/src/core/members/members.module.ts b/packages/backend/src/core/members/members.module.ts index ce1ed5ca6..9cfdbabab 100644 --- a/packages/backend/src/core/members/members.module.ts +++ b/packages/backend/src/core/members/members.module.ts @@ -7,6 +7,8 @@ import { DeleteCoreMembersResolver } from './delete/delete.resolver'; import { DeleteCoreMembersService } from './delete/delete.service'; import { CreateKeyResetPasswordCoreMembersService } from './reset_password/create_key/create_key.service'; import { CreateKeyResetPasswordCoreMembersResolver } from './reset_password/create_key/create_key.resolver'; +import { ChangePasswordCoreMembersResolver } from './reset_password/change_password/change_password.resolver'; +import { ChangePasswordCoreMembersService } from './reset_password/change_password/change_password.service'; @Module({ providers: [ @@ -16,6 +18,8 @@ import { CreateKeyResetPasswordCoreMembersResolver } from './reset_password/crea DeleteCoreMembersService, CreateKeyResetPasswordCoreMembersService, CreateKeyResetPasswordCoreMembersResolver, + ChangePasswordCoreMembersResolver, + ChangePasswordCoreMembersService, ], imports: [AvatarCoreMembers], }) diff --git a/packages/backend/src/core/members/reset_password/change_password/change_password.resolver.ts b/packages/backend/src/core/members/reset_password/change_password/change_password.resolver.ts new file mode 100644 index 000000000..c88b9cc77 --- /dev/null +++ b/packages/backend/src/core/members/reset_password/change_password/change_password.resolver.ts @@ -0,0 +1,18 @@ +import { Args, Mutation, Resolver } from '@nestjs/graphql'; + +import { ChangePasswordCoreMembersArgs } from './dto/change_password.args'; +import { ChangePasswordCoreMembersService } from './change_password.service'; + +import { User } from '@/decorators/user.decorator'; + +@Resolver() +export class ChangePasswordCoreMembersResolver { + constructor(private readonly service: ChangePasswordCoreMembersService) {} + + @Mutation(() => User) + async core_members__change_password( + @Args() args: ChangePasswordCoreMembersArgs, + ): Promise { + return this.service.change_password(args); + } +} diff --git a/packages/backend/src/core/members/reset_password/change_password/change_password.service.ts b/packages/backend/src/core/members/reset_password/change_password/change_password.service.ts new file mode 100644 index 000000000..d63001e55 --- /dev/null +++ b/packages/backend/src/core/members/reset_password/change_password/change_password.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { eq } from 'drizzle-orm'; + +import { ChangePasswordCoreMembersArgs } from './dto/change_password.args'; + +import { DatabaseService } from '@/database'; +import { User } from '@/decorators'; +import { encryptPassword } from '@/core/sessions/encrypt_password'; +import { + core_users, + core_users_pass_reset, +} from '@/templates/core/admin/database/schema/users'; + +@Injectable() +export class ChangePasswordCoreMembersService { + constructor( + private readonly databaseService: DatabaseService, + private readonly configService: ConfigService, + ) {} + + async change_password({ + hashKey, + password, + }: ChangePasswordCoreMembersArgs): Promise { + const keyData = + await this.databaseService.db.query.core_users_pass_reset.findFirst({ + where: eq(core_users_pass_reset.key, hashKey), + }); + + const id = keyData.user_id; + const hashPassword = await encryptPassword(this.configService, password); + + const update = await this.databaseService.db + .update(core_users) + .set({ + password: hashPassword, + }) + .where(eq(core_users.id, id)) + .returning(); + + return { + ...update[0], + avatar: null, + group: null, + }; + } +} diff --git a/packages/backend/src/core/members/reset_password/change_password/dto/change_password.args.ts b/packages/backend/src/core/members/reset_password/change_password/dto/change_password.args.ts new file mode 100644 index 000000000..6b7f93df8 --- /dev/null +++ b/packages/backend/src/core/members/reset_password/change_password/dto/change_password.args.ts @@ -0,0 +1,18 @@ +import { IsStrongPassword } from 'class-validator'; +import { ArgsType, Field } from '@nestjs/graphql'; + +@ArgsType() +export class ChangePasswordCoreMembersArgs { + @Field(() => String) + hashKey: string; + + @IsStrongPassword({ + minLength: 8, + minLowercase: 1, + minUppercase: 1, + minNumbers: 1, + minSymbols: 1, + }) + @Field(() => String) + password: string; +} diff --git a/packages/backend/src/core/sessions/encrypt_password.ts b/packages/backend/src/core/sessions/encrypt_password.ts new file mode 100644 index 000000000..e0b8c472d --- /dev/null +++ b/packages/backend/src/core/sessions/encrypt_password.ts @@ -0,0 +1,14 @@ +import { ConfigService } from '@nestjs/config'; +import { genSalt, hash } from 'bcrypt'; + +async function encryptPassword( + configService: ConfigService, + password: string, +): Promise { + const passwordSalt = await genSalt(configService.getOrThrow('password_salt')); + const hashPassword = await hash(password, passwordSalt); + + return hashPassword; +} + +export { encryptPassword }; diff --git a/packages/backend/src/core/sessions/sign_up/sign_up.service.ts b/packages/backend/src/core/sessions/sign_up/sign_up.service.ts index 9818adbbe..71b48ea8f 100644 --- a/packages/backend/src/core/sessions/sign_up/sign_up.service.ts +++ b/packages/backend/src/core/sessions/sign_up/sign_up.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@nestjs/common'; -import { genSalt, hash } from 'bcrypt'; import { count } from 'drizzle-orm'; import { ConfigService } from '@nestjs/config'; import { removeSpecialCharacters } from 'vitnode-shared'; @@ -7,6 +6,7 @@ import { removeSpecialCharacters } from 'vitnode-shared'; import { SignUpCoreSessionsArgs } from './dto/sign_up.args'; import { SignUpCoreSessionsObj } from './dto/sign_up.obj'; import { AvatarColorService } from './helpers/avatar-color.service'; +import { encryptPassword } from '../encrypt_password'; import { DatabaseService } from '../../../database'; import { core_users } from '../../../templates/core/admin/database/schema/users'; @@ -79,10 +79,7 @@ export class SignUpCoreSessionsService extends AvatarColorService { }); } - const passwordSalt = await genSalt( - this.configService.getOrThrow('password_salt'), - ); - const hashPassword = await hash(password, passwordSalt); + const hashPassword = await encryptPassword(this.configService, password); const user = await this.databaseService.db .insert(core_users)