mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	* chore: add typeorm commands to npm and set default database config values * feat: move to server side authentication tokens * fix: websocket should emit error and disconnect on error thrown by the server * refactor: rename cookie-auth-strategy to user-auth-strategy * feat: user tokens and API keys now use SHA256 hash for performance improvements * test: album e2e test remove unneeded module import * infra: truncate api key table as old keys will no longer work with new hash algorithm * fix(server): e2e tests (#1435) * fix: root module paths * chore: linting * chore: rename user-auth to strategy.ts and make validate return AuthUserDto * fix: we should always send HttpOnly for our auth cookies * chore: remove now unused crypto functions and jwt dependencies * fix: return the extra fields for AuthUserDto in auth service validate --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
		
			
				
	
	
		
			171 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common';
 | |
| import {
 | |
|   authStub,
 | |
|   userEntityStub,
 | |
|   newCryptoRepositoryMock,
 | |
|   newSharedLinkRepositoryMock,
 | |
|   newUserRepositoryMock,
 | |
|   sharedLinkResponseStub,
 | |
|   sharedLinkStub,
 | |
| } from '../../test';
 | |
| import { ICryptoRepository } from '../auth';
 | |
| import { IUserRepository } from '../user';
 | |
| import { ShareService } from './share.service';
 | |
| import { ISharedLinkRepository } from './shared-link.repository';
 | |
| 
 | |
| describe(ShareService.name, () => {
 | |
|   let sut: ShareService;
 | |
|   let cryptoMock: jest.Mocked<ICryptoRepository>;
 | |
|   let shareMock: jest.Mocked<ISharedLinkRepository>;
 | |
|   let userMock: jest.Mocked<IUserRepository>;
 | |
| 
 | |
|   beforeEach(async () => {
 | |
|     cryptoMock = newCryptoRepositoryMock();
 | |
|     shareMock = newSharedLinkRepositoryMock();
 | |
|     userMock = newUserRepositoryMock();
 | |
| 
 | |
|     sut = new ShareService(cryptoMock, shareMock, userMock);
 | |
|   });
 | |
| 
 | |
|   it('should work', () => {
 | |
|     expect(sut).toBeDefined();
 | |
|   });
 | |
| 
 | |
|   describe('validate', () => {
 | |
|     it('should not accept a non-existant key', async () => {
 | |
|       shareMock.getByKey.mockResolvedValue(null);
 | |
|       await expect(sut.validate('key')).rejects.toBeInstanceOf(UnauthorizedException);
 | |
|     });
 | |
| 
 | |
|     it('should not accept an expired key', async () => {
 | |
|       shareMock.getByKey.mockResolvedValue(sharedLinkStub.expired);
 | |
|       await expect(sut.validate('key')).rejects.toBeInstanceOf(UnauthorizedException);
 | |
|     });
 | |
| 
 | |
|     it('should not accept a key without a user', async () => {
 | |
|       shareMock.getByKey.mockResolvedValue(sharedLinkStub.expired);
 | |
|       userMock.get.mockResolvedValue(null);
 | |
|       await expect(sut.validate('key')).rejects.toBeInstanceOf(UnauthorizedException);
 | |
|     });
 | |
| 
 | |
|     it('should accept a valid key', async () => {
 | |
|       shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
 | |
|       userMock.get.mockResolvedValue(userEntityStub.admin);
 | |
|       await expect(sut.validate('key')).resolves.toEqual(authStub.adminSharedLink);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('getAll', () => {
 | |
|     it('should return all keys for a user', async () => {
 | |
|       shareMock.getAll.mockResolvedValue([sharedLinkStub.expired, sharedLinkStub.valid]);
 | |
|       await expect(sut.getAll(authStub.user1)).resolves.toEqual([
 | |
|         sharedLinkResponseStub.expired,
 | |
|         sharedLinkResponseStub.valid,
 | |
|       ]);
 | |
|       expect(shareMock.getAll).toHaveBeenCalledWith(authStub.user1.id);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('getMine', () => {
 | |
|     it('should only work for a public user', async () => {
 | |
|       await expect(sut.getMine(authStub.admin)).rejects.toBeInstanceOf(ForbiddenException);
 | |
|       expect(shareMock.get).not.toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
|     it('should return the key for the public user (auth dto)', async () => {
 | |
|       const authDto = authStub.adminSharedLink;
 | |
|       shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | |
|       await expect(sut.getMine(authDto)).resolves.toEqual(sharedLinkResponseStub.valid);
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('get', () => {
 | |
|     it('should not work on a missing key', async () => {
 | |
|       shareMock.get.mockResolvedValue(null);
 | |
|       await expect(sut.getById(authStub.user1, sharedLinkStub.valid.id, true)).rejects.toBeInstanceOf(
 | |
|         BadRequestException,
 | |
|       );
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | |
|       expect(shareMock.remove).not.toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
|     it('should get a key by id', async () => {
 | |
|       shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | |
|       await expect(sut.getById(authStub.user1, sharedLinkStub.valid.id, false)).resolves.toEqual(
 | |
|         sharedLinkResponseStub.valid,
 | |
|       );
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | |
|     });
 | |
| 
 | |
|     it('should include exif', async () => {
 | |
|       shareMock.get.mockResolvedValue(sharedLinkStub.readonly);
 | |
|       await expect(sut.getById(authStub.user1, sharedLinkStub.readonly.id, true)).resolves.toEqual(
 | |
|         sharedLinkResponseStub.readonly,
 | |
|       );
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.readonly.id);
 | |
|     });
 | |
| 
 | |
|     it('should exclude exif', async () => {
 | |
|       shareMock.get.mockResolvedValue(sharedLinkStub.readonly);
 | |
|       await expect(sut.getById(authStub.user1, sharedLinkStub.readonly.id, false)).resolves.toEqual(
 | |
|         sharedLinkResponseStub.readonlyNoExif,
 | |
|       );
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.readonly.id);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('remove', () => {
 | |
|     it('should not work on a missing key', async () => {
 | |
|       shareMock.get.mockResolvedValue(null);
 | |
|       await expect(sut.remove(authStub.user1, sharedLinkStub.valid.id)).rejects.toBeInstanceOf(BadRequestException);
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | |
|       expect(shareMock.remove).not.toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
|     it('should remove a key', async () => {
 | |
|       shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | |
|       await sut.remove(authStub.user1, sharedLinkStub.valid.id);
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | |
|       expect(shareMock.remove).toHaveBeenCalledWith(sharedLinkStub.valid);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('getByKey', () => {
 | |
|     it('should not work on a missing key', async () => {
 | |
|       shareMock.getByKey.mockResolvedValue(null);
 | |
|       await expect(sut.getByKey('secret-key')).rejects.toBeInstanceOf(BadRequestException);
 | |
|       expect(shareMock.getByKey).toHaveBeenCalledWith('secret-key');
 | |
|     });
 | |
| 
 | |
|     it('should find a key', async () => {
 | |
|       shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
 | |
|       await expect(sut.getByKey('secret-key')).resolves.toEqual(sharedLinkResponseStub.valid);
 | |
|       expect(shareMock.getByKey).toHaveBeenCalledWith('secret-key');
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('edit', () => {
 | |
|     it('should not work on a missing key', async () => {
 | |
|       shareMock.get.mockResolvedValue(null);
 | |
|       await expect(sut.edit(authStub.user1, sharedLinkStub.valid.id, {})).rejects.toBeInstanceOf(BadRequestException);
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | |
|       expect(shareMock.save).not.toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
|     it('should edit a key', async () => {
 | |
|       shareMock.get.mockResolvedValue(sharedLinkStub.valid);
 | |
|       shareMock.save.mockResolvedValue(sharedLinkStub.valid);
 | |
|       const dto = { allowDownload: false };
 | |
|       await sut.edit(authStub.user1, sharedLinkStub.valid.id, dto);
 | |
|       // await expect(sut.edit(authStub.user1, sharedLinkStub.valid.id, dto)).rejects.toBeInstanceOf(BadRequestException);
 | |
|       expect(shareMock.get).toHaveBeenCalledWith(authStub.user1.id, sharedLinkStub.valid.id);
 | |
|       expect(shareMock.save).toHaveBeenCalledWith({
 | |
|         id: sharedLinkStub.valid.id,
 | |
|         userId: authStub.user1.id,
 | |
|         allowDownload: false,
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| });
 |