From 43d18ccc36e4b5eb840c5de087d9cbbfa5c871eb Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 24 Oct 2024 17:24:37 -0400 Subject: [PATCH] refactor(server): user create logic (#13728) --- server/src/services/auth.service.ts | 35 +++++++++-------------- server/src/services/base.service.ts | 29 ++++++++++++++++++- server/src/services/user-admin.service.ts | 10 +++---- server/src/utils/user.ts | 35 ----------------------- 4 files changed, 47 insertions(+), 62 deletions(-) delete mode 100644 server/src/utils/user.ts diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 00324c909c5df..b0094ae9edba9 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -23,7 +23,6 @@ import { OAuthProfile } from 'src/interfaces/oauth.interface'; import { BaseService } from 'src/services/base.service'; import { isGranted } from 'src/utils/access'; import { HumanReadableSize } from 'src/utils/bytes'; -import { createUser } from 'src/utils/user'; export interface LoginDetails { isSecure: boolean; @@ -115,16 +114,13 @@ export class AuthService extends BaseService { throw new BadRequestException('The server already has an admin'); } - const admin = await createUser( - { userRepo: this.userRepository, cryptoRepo: this.cryptoRepository }, - { - isAdmin: true, - email: dto.email, - name: dto.name, - password: dto.password, - storageLabel: 'admin', - }, - ); + const admin = await this.createUser({ + isAdmin: true, + email: dto.email, + name: dto.name, + password: dto.password, + storageLabel: 'admin', + }); return mapUserAdmin(admin); } @@ -234,16 +230,13 @@ export class AuthService extends BaseService { }); const userName = profile.name ?? `${profile.given_name || ''} ${profile.family_name || ''}`; - user = await createUser( - { userRepo: this.userRepository, cryptoRepo: this.cryptoRepository }, - { - name: userName, - email: profile.email, - oauthId: profile.sub, - quotaSizeInBytes: storageQuota * HumanReadableSize.GiB || null, - storageLabel: storageLabel || null, - }, - ); + user = await this.createUser({ + name: userName, + email: profile.email, + oauthId: profile.sub, + quotaSizeInBytes: storageQuota * HumanReadableSize.GiB || null, + storageLabel: storageLabel || null, + }); } return this.createLoginResponse(user, loginDetails); diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 441a81cf91031..5ee4014e9108b 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -1,6 +1,9 @@ -import { Inject } from '@nestjs/common'; +import { BadRequestException, Inject } from '@nestjs/common'; +import sanitize from 'sanitize-filename'; import { SystemConfig } from 'src/config'; +import { SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; +import { UserEntity } from 'src/entities/user.entity'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IActivityRepository } from 'src/interfaces/activity.interface'; import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; @@ -119,4 +122,28 @@ export class BaseService { checkAccess(request: AccessRequest) { return checkAccess(this.accessRepository, request); } + + async createUser(dto: Partial & { email: string }): Promise { + const user = await this.userRepository.getByEmail(dto.email); + if (user) { + throw new BadRequestException('User exists'); + } + + if (!dto.isAdmin) { + const localAdmin = await this.userRepository.getAdmin(); + if (!localAdmin) { + throw new BadRequestException('The first registered account must the administrator.'); + } + } + + const payload: Partial = { ...dto }; + if (payload.password) { + payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS); + } + if (payload.storageLabel) { + payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', '')); + } + + return this.userRepository.create(payload); + } } diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index 84a5b5842d9b9..a4be671c2237c 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -15,7 +15,6 @@ import { JobName } from 'src/interfaces/job.interface'; import { UserFindOptions } from 'src/interfaces/user.interface'; import { BaseService } from 'src/services/base.service'; import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences'; -import { createUser } from 'src/utils/user'; @Injectable() export class UserAdminService extends BaseService { @@ -25,17 +24,18 @@ export class UserAdminService extends BaseService { } async create(dto: UserAdminCreateDto): Promise { - const { notify, ...rest } = dto; + const { notify, ...userDto } = dto; const config = await this.getConfig({ withCache: false }); - if (!config.oauth.enabled && !rest.password) { + if (!config.oauth.enabled && !userDto.password) { throw new BadRequestException('password is required'); } - const user = await createUser({ userRepo: this.userRepository, cryptoRepo: this.cryptoRepository }, rest); + + const user = await this.createUser(userDto); await this.eventRepository.emit('user.signup', { notify: !!notify, id: user.id, - tempPassword: user.shouldChangePassword ? rest.password : undefined, + tempPassword: user.shouldChangePassword ? userDto.password : undefined, }); return mapUserAdmin(user); diff --git a/server/src/utils/user.ts b/server/src/utils/user.ts deleted file mode 100644 index c7029a1ecae4b..0000000000000 --- a/server/src/utils/user.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BadRequestException } from '@nestjs/common'; -import sanitize from 'sanitize-filename'; -import { SALT_ROUNDS } from 'src/constants'; -import { UserEntity } from 'src/entities/user.entity'; -import { ICryptoRepository } from 'src/interfaces/crypto.interface'; -import { IUserRepository } from 'src/interfaces/user.interface'; - -type RepoDeps = { userRepo: IUserRepository; cryptoRepo: ICryptoRepository }; - -export const createUser = async ( - { userRepo, cryptoRepo }: RepoDeps, - dto: Partial & { email: string }, -): Promise => { - const user = await userRepo.getByEmail(dto.email); - if (user) { - throw new BadRequestException('User exists'); - } - - if (!dto.isAdmin) { - const localAdmin = await userRepo.getAdmin(); - if (!localAdmin) { - throw new BadRequestException('The first registered account must the administrator.'); - } - } - - const payload: Partial = { ...dto }; - if (payload.password) { - payload.password = await cryptoRepo.hashBcrypt(payload.password, SALT_ROUNDS); - } - if (payload.storageLabel) { - payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', '')); - } - - return userRepo.create(payload); -};