mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	test(server): change password (#1177)
This commit is contained in:
		
							parent
							
								
									4f8bc641bd
								
							
						
					
					
						commit
						0c896d9e59
					
				@ -1,11 +1,10 @@
 | 
				
			|||||||
import { UserEntity } from '@app/database/entities/user.entity';
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
import { BadRequestException } from '@nestjs/common';
 | 
					import { BadRequestException, UnauthorizedException } from '@nestjs/common';
 | 
				
			||||||
import { Test } from '@nestjs/testing';
 | 
					 | 
				
			||||||
import * as bcrypt from 'bcrypt';
 | 
					import * as bcrypt from 'bcrypt';
 | 
				
			||||||
import { AuthType } from '../../constants/jwt.constant';
 | 
					import { AuthType } from '../../constants/jwt.constant';
 | 
				
			||||||
import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 | 
					import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 | 
				
			||||||
import { OAuthService } from '../oauth/oauth.service';
 | 
					import { OAuthService } from '../oauth/oauth.service';
 | 
				
			||||||
import { IUserRepository, USER_REPOSITORY } from '../user/user-repository';
 | 
					import { IUserRepository } from '../user/user-repository';
 | 
				
			||||||
import { AuthService } from './auth.service';
 | 
					import { AuthService } from './auth.service';
 | 
				
			||||||
import { SignUpDto } from './dto/sign-up.dto';
 | 
					import { SignUpDto } from './dto/sign-up.dto';
 | 
				
			||||||
import { LoginResponseDto } from './response-dto/login-response.dto';
 | 
					import { LoginResponseDto } from './response-dto/login-response.dto';
 | 
				
			||||||
@ -20,6 +19,17 @@ const fixtures = {
 | 
				
			|||||||
const CLIENT_IP = '127.0.0.1';
 | 
					const CLIENT_IP = '127.0.0.1';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock('bcrypt');
 | 
					jest.mock('bcrypt');
 | 
				
			||||||
 | 
					jest.mock('@nestjs/common', () => ({
 | 
				
			||||||
 | 
					  ...jest.requireActual('@nestjs/common'),
 | 
				
			||||||
 | 
					  Logger: jest.fn().mockReturnValue({
 | 
				
			||||||
 | 
					    verbose: jest.fn(),
 | 
				
			||||||
 | 
					    debug: jest.fn(),
 | 
				
			||||||
 | 
					    log: jest.fn(),
 | 
				
			||||||
 | 
					    info: jest.fn(),
 | 
				
			||||||
 | 
					    warn: jest.fn(),
 | 
				
			||||||
 | 
					    error: jest.fn(),
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('AuthService', () => {
 | 
					describe('AuthService', () => {
 | 
				
			||||||
  let sut: AuthService;
 | 
					  let sut: AuthService;
 | 
				
			||||||
@ -61,19 +71,7 @@ describe('AuthService', () => {
 | 
				
			|||||||
      getLogoutEndpoint: jest.fn(),
 | 
					      getLogoutEndpoint: jest.fn(),
 | 
				
			||||||
    } as unknown as jest.Mocked<OAuthService>;
 | 
					    } as unknown as jest.Mocked<OAuthService>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const moduleRef = await Test.createTestingModule({
 | 
					    sut = new AuthService(oauthServiceMock, immichJwtServiceMock, userRepositoryMock);
 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        AuthService,
 | 
					 | 
				
			||||||
        { provide: ImmichJwtService, useValue: immichJwtServiceMock },
 | 
					 | 
				
			||||||
        { provide: OAuthService, useValue: oauthServiceMock },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          provide: USER_REPOSITORY,
 | 
					 | 
				
			||||||
          useValue: userRepositoryMock,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
    }).compile();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sut = moduleRef.get(AuthService);
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should be defined', () => {
 | 
					  it('should be defined', () => {
 | 
				
			||||||
@ -104,6 +102,62 @@ describe('AuthService', () => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('changePassword', () => {
 | 
				
			||||||
 | 
					    it('should change the password', async () => {
 | 
				
			||||||
 | 
					      const authUser = { email: 'test@imimch.com' } as UserEntity;
 | 
				
			||||||
 | 
					      const dto = { password: 'old-password', newPassword: 'new-password' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      compare.mockResolvedValue(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      userRepositoryMock.getByEmail.mockResolvedValue({
 | 
				
			||||||
 | 
					        email: 'test@immich.com',
 | 
				
			||||||
 | 
					        password: 'hash-password',
 | 
				
			||||||
 | 
					      } as UserEntity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await sut.changePassword(authUser, dto);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userRepositoryMock.getByEmail).toHaveBeenCalledWith(authUser.email, true);
 | 
				
			||||||
 | 
					      expect(compare).toHaveBeenCalledWith('old-password', 'hash-password');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should throw when auth user email is not found', async () => {
 | 
				
			||||||
 | 
					      const authUser = { email: 'test@imimch.com' } as UserEntity;
 | 
				
			||||||
 | 
					      const dto = { password: 'old-password', newPassword: 'new-password' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      userRepositoryMock.getByEmail.mockResolvedValue(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.changePassword(authUser, dto)).rejects.toBeInstanceOf(UnauthorizedException);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should throw when password does not match existing password', async () => {
 | 
				
			||||||
 | 
					      const authUser = { email: 'test@imimch.com' } as UserEntity;
 | 
				
			||||||
 | 
					      const dto = { password: 'old-password', newPassword: 'new-password' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      compare.mockResolvedValue(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      userRepositoryMock.getByEmail.mockResolvedValue({
 | 
				
			||||||
 | 
					        email: 'test@immich.com',
 | 
				
			||||||
 | 
					        password: 'hash-password',
 | 
				
			||||||
 | 
					      } as UserEntity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.changePassword(authUser, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should throw when user does not have a password', async () => {
 | 
				
			||||||
 | 
					      const authUser = { email: 'test@imimch.com' } as UserEntity;
 | 
				
			||||||
 | 
					      const dto = { password: 'old-password', newPassword: 'new-password' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      compare.mockResolvedValue(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      userRepositoryMock.getByEmail.mockResolvedValue({
 | 
				
			||||||
 | 
					        email: 'test@immich.com',
 | 
				
			||||||
 | 
					        password: '',
 | 
				
			||||||
 | 
					      } as UserEntity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.changePassword(authUser, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('logout', () => {
 | 
					  describe('logout', () => {
 | 
				
			||||||
    it('should return the end session endpoint', async () => {
 | 
					    it('should return the end session endpoint', async () => {
 | 
				
			||||||
      oauthServiceMock.getLogoutEndpoint.mockResolvedValue('end-session-endpoint');
 | 
					      oauthServiceMock.getLogoutEndpoint.mockResolvedValue('end-session-endpoint');
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ import { UserCore } from '../user/user.core';
 | 
				
			|||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class AuthService {
 | 
					export class AuthService {
 | 
				
			||||||
  private userCore: UserCore;
 | 
					  private userCore: UserCore;
 | 
				
			||||||
 | 
					  private logger = new Logger(AuthService.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private oauthService: OAuthService,
 | 
					    private oauthService: OAuthService,
 | 
				
			||||||
@ -44,7 +45,7 @@ export class AuthService {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!user) {
 | 
					    if (!user) {
 | 
				
			||||||
      Logger.warn(`Failed login attempt for user ${loginCredential.email} from ip address ${clientIp}`);
 | 
					      this.logger.warn(`Failed login attempt for user ${loginCredential.email} from ip address ${clientIp}`);
 | 
				
			||||||
      throw new BadRequestException('Incorrect email or password');
 | 
					      throw new BadRequestException('Incorrect email or password');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -94,8 +95,8 @@ export class AuthService {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return mapAdminSignupResponse(admin);
 | 
					      return mapAdminSignupResponse(admin);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (error) {
 | 
				
			||||||
      Logger.error('e', 'signUp');
 | 
					      this.logger.error(`Unable to register admin user: ${error}`, (error as Error).stack);
 | 
				
			||||||
      throw new InternalServerErrorException('Failed to register new admin user');
 | 
					      throw new InternalServerErrorException('Failed to register new admin user');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { SystemConfig } from '@app/database/entities/system-config.entity';
 | 
					import { SystemConfig } from '@app/database/entities/system-config.entity';
 | 
				
			||||||
import { UserEntity } from '@app/database/entities/user.entity';
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
import { ImmichConfigService } from '@app/immich-config';
 | 
					import { ImmichConfigService } from '@app/immich-config';
 | 
				
			||||||
import { BadRequestException, Logger } from '@nestjs/common';
 | 
					import { BadRequestException } from '@nestjs/common';
 | 
				
			||||||
import { generators, Issuer } from 'openid-client';
 | 
					import { generators, Issuer } from 'openid-client';
 | 
				
			||||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
 | 
					import { AuthUserDto } from '../../decorators/auth-user.decorator';
 | 
				
			||||||
import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 | 
					import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 | 
				
			||||||
@ -32,20 +32,17 @@ const loginResponse = {
 | 
				
			|||||||
  userEmail: 'user@immich.com,',
 | 
					  userEmail: 'user@immich.com,',
 | 
				
			||||||
} as LoginResponseDto;
 | 
					} as LoginResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock('@nestjs/common', () => {
 | 
					jest.mock('@nestjs/common', () => ({
 | 
				
			||||||
  return {
 | 
					  ...jest.requireActual('@nestjs/common'),
 | 
				
			||||||
    ...jest.requireActual('@nestjs/common'),
 | 
					  Logger: jest.fn().mockReturnValue({
 | 
				
			||||||
    Logger: function MockLogger() {
 | 
					    verbose: jest.fn(),
 | 
				
			||||||
      Object.assign(this as Logger, {
 | 
					    debug: jest.fn(),
 | 
				
			||||||
        verbose: jest.fn(),
 | 
					    log: jest.fn(),
 | 
				
			||||||
        debug: jest.fn(),
 | 
					    info: jest.fn(),
 | 
				
			||||||
        log: jest.fn(),
 | 
					    warn: jest.fn(),
 | 
				
			||||||
        warn: jest.fn(),
 | 
					    error: jest.fn(),
 | 
				
			||||||
        error: jest.fn(),
 | 
					  }),
 | 
				
			||||||
      });
 | 
					}));
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('OAuthService', () => {
 | 
					describe('OAuthService', () => {
 | 
				
			||||||
  let sut: OAuthService;
 | 
					  let sut: OAuthService;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user