fix(server): follow symlinks when zipping assets (#11685)

* follow symlinks when zipping assets

fixes #9335

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
This commit is contained in:
Carsten Otto
2024-08-13 17:39:24 +02:00
committed by GitHub
parent 81c813a882
commit df45ef0e35
5 changed files with 45 additions and 4 deletions

View File

@@ -2,12 +2,14 @@ import { BadRequestException } from '@nestjs/common';
import { DownloadResponseDto } from 'src/dtos/download.dto';
import { AssetEntity } from 'src/entities/asset.entity';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IStorageRepository } from 'src/interfaces/storage.interface';
import { DownloadService } from 'src/services/download.service';
import { assetStub } from 'test/fixtures/asset.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { Readable } from 'typeorm/platform/PlatformTools.js';
import { Mocked, vitest } from 'vitest';
@@ -26,6 +28,7 @@ describe(DownloadService.name, () => {
let sut: DownloadService;
let accessMock: IAccessRepositoryMock;
let assetMock: Mocked<IAssetRepository>;
let loggerMock: Mocked<ILoggerRepository>;
let storageMock: Mocked<IStorageRepository>;
it('should work', () => {
@@ -35,9 +38,10 @@ describe(DownloadService.name, () => {
beforeEach(() => {
accessMock = newAccessRepositoryMock();
assetMock = newAssetRepositoryMock();
loggerMock = newLoggerRepositoryMock();
storageMock = newStorageRepositoryMock();
sut = new DownloadService(accessMock, assetMock, storageMock);
sut = new DownloadService(accessMock, assetMock, loggerMock, storageMock);
});
describe('downloadArchive', () => {
@@ -109,6 +113,27 @@ describe(DownloadService.name, () => {
expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg');
expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg');
});
it('should resolve symlinks', async () => {
const archiveMock = {
addFile: vitest.fn(),
finalize: vitest.fn(),
stream: new Readable(),
};
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
assetMock.getByIds.mockResolvedValue([
{ ...assetStub.noResizePath, id: 'asset-1', originalPath: '/path/to/symlink.jpg' },
]);
storageMock.realpath.mockResolvedValue('/path/to/realpath.jpg');
storageMock.createZipStream.mockReturnValue(archiveMock);
await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1'] })).resolves.toEqual({
stream: archiveMock.stream,
});
expect(archiveMock.addFile).toHaveBeenCalledWith('/path/to/realpath.jpg', 'IMG_123.jpg');
});
});
describe('getDownloadInfo', () => {