mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	refactor(server): new password repo method (#8208)
This commit is contained in:
		
							parent
							
								
									604b8ff17c
								
							
						
					
					
						commit
						787eebcf1e
					
				@ -3,6 +3,8 @@ import { readFileSync } from 'node:fs';
 | 
			
		||||
import { join } from 'node:path';
 | 
			
		||||
import { Version } from 'src/utils/version';
 | 
			
		||||
 | 
			
		||||
export const SALT_ROUNDS = 10;
 | 
			
		||||
 | 
			
		||||
const { version } = JSON.parse(readFileSync('./package.json', 'utf8'));
 | 
			
		||||
export const serverVersion = Version.fromString(version);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
 | 
			
		||||
import sanitize from 'sanitize-filename';
 | 
			
		||||
import { SALT_ROUNDS } from 'src/constants';
 | 
			
		||||
import { UserResponseDto } from 'src/dtos/user.dto';
 | 
			
		||||
import { LibraryType } from 'src/entities/library.entity';
 | 
			
		||||
import { UserEntity } from 'src/entities/user.entity';
 | 
			
		||||
@ -7,8 +8,6 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
			
		||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
 | 
			
		||||
import { IUserRepository } from 'src/interfaces/user.interface';
 | 
			
		||||
 | 
			
		||||
const SALT_ROUNDS = 10;
 | 
			
		||||
 | 
			
		||||
let instance: UserCore | null;
 | 
			
		||||
 | 
			
		||||
export class UserCore {
 | 
			
		||||
 | 
			
		||||
@ -8,4 +8,5 @@ export interface ICryptoRepository {
 | 
			
		||||
  hashSha1(data: string | Buffer): Buffer;
 | 
			
		||||
  hashBcrypt(data: string | Buffer, saltOrRounds: string | number): Promise<string>;
 | 
			
		||||
  compareBcrypt(data: string | Buffer, encrypted: string): boolean;
 | 
			
		||||
  newPassword(bytes: number): string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -41,4 +41,8 @@ export class CryptoRepository implements ICryptoRepository {
 | 
			
		||||
      stream.on('end', () => resolve(hash.digest()));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  newPassword(bytes: number) {
 | 
			
		||||
    return randomBytes(bytes).toString('base64').replaceAll(/\W/g, '');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ describe(APIKeyService.name, () => {
 | 
			
		||||
        name: 'Test Key',
 | 
			
		||||
        userId: authStub.admin.user.id,
 | 
			
		||||
      });
 | 
			
		||||
      expect(cryptoMock.randomBytes).toHaveBeenCalled();
 | 
			
		||||
      expect(cryptoMock.newPassword).toHaveBeenCalled();
 | 
			
		||||
      expect(cryptoMock.hashSha256).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@ describe(APIKeyService.name, () => {
 | 
			
		||||
        name: 'API Key',
 | 
			
		||||
        userId: authStub.admin.user.id,
 | 
			
		||||
      });
 | 
			
		||||
      expect(cryptoMock.randomBytes).toHaveBeenCalled();
 | 
			
		||||
      expect(cryptoMock.newPassword).toHaveBeenCalled();
 | 
			
		||||
      expect(cryptoMock.hashSha256).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ export class APIKeyService {
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async create(auth: AuthDto, dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
 | 
			
		||||
    const secret = this.crypto.randomBytes(32).toString('base64').replaceAll(/\W/g, '');
 | 
			
		||||
    const secret = this.crypto.newPassword(32);
 | 
			
		||||
    const entity = await this.repository.create({
 | 
			
		||||
      key: this.crypto.hashSha256(secret),
 | 
			
		||||
      name: dto.name || 'API Key',
 | 
			
		||||
 | 
			
		||||
@ -146,7 +146,6 @@ export class AuthService {
 | 
			
		||||
 | 
			
		||||
  async adminSignUp(dto: SignUpDto): Promise<UserResponseDto> {
 | 
			
		||||
    const adminUser = await this.userRepository.getAdmin();
 | 
			
		||||
 | 
			
		||||
    if (adminUser) {
 | 
			
		||||
      throw new BadRequestException('The server already has an admin');
 | 
			
		||||
    }
 | 
			
		||||
@ -427,7 +426,7 @@ export class AuthService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async createLoginResponse(user: UserEntity, authType: AuthType, loginDetails: LoginDetails) {
 | 
			
		||||
    const key = this.cryptoRepository.randomBytes(32).toString('base64').replaceAll(/\W/g, '');
 | 
			
		||||
    const key = this.cryptoRepository.newPassword(32);
 | 
			
		||||
    const token = this.cryptoRepository.hashSha256(key);
 | 
			
		||||
 | 
			
		||||
    await this.userTokenRepository.create({
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
import { BadRequestException, ForbiddenException, Inject, Injectable, NotFoundException } from '@nestjs/common';
 | 
			
		||||
import { DateTime } from 'luxon';
 | 
			
		||||
import { randomBytes } from 'node:crypto';
 | 
			
		||||
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
 | 
			
		||||
import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
			
		||||
import { UserCore } from 'src/cores/user.core';
 | 
			
		||||
@ -26,7 +25,7 @@ export class UserService {
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
 | 
			
		||||
    @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
 | 
			
		||||
    @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
 | 
			
		||||
    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
			
		||||
    @Inject(ILibraryRepository) libraryRepository: ILibraryRepository,
 | 
			
		||||
    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
			
		||||
@ -132,7 +131,7 @@ export class UserService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const providedPassword = await ask(mapUser(admin));
 | 
			
		||||
    const password = providedPassword || randomBytes(24).toString('base64').replaceAll(/\W/g, '');
 | 
			
		||||
    const password = providedPassword || this.cryptoRepository.newPassword(24);
 | 
			
		||||
 | 
			
		||||
    await this.userCore.updateUser(admin, admin.id, { password });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,5 +9,6 @@ export const newCryptoRepositoryMock = (): jest.Mocked<ICryptoRepository> => {
 | 
			
		||||
    hashSha256: jest.fn().mockImplementation((input) => `${input} (hashed)`),
 | 
			
		||||
    hashSha1: jest.fn().mockImplementation((input) => Buffer.from(`${input.toString()} (hashed)`)),
 | 
			
		||||
    hashFile: jest.fn().mockImplementation((input) => `${input} (file-hashed)`),
 | 
			
		||||
    newPassword: jest.fn().mockReturnValue(Buffer.from('random-bytes').toString('base64')),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user