mirror of
https://github.com/immich-app/immich.git
synced 2024-12-26 10:50:29 +02:00
refactor(server): shared link asset access check (#2680)
This commit is contained in:
parent
d1b0b64d59
commit
284edd97d6
@ -225,7 +225,7 @@ describe('AssetService', () => {
|
|||||||
|
|
||||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||||
sharedLinkRepositoryMock.get.mockResolvedValue(null);
|
sharedLinkRepositoryMock.get.mockResolvedValue(null);
|
||||||
sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
|
accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true);
|
||||||
|
|
||||||
await expect(sut.addAssetsToSharedLink(authDto, dto)).rejects.toBeInstanceOf(BadRequestException);
|
await expect(sut.addAssetsToSharedLink(authDto, dto)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ describe('AssetService', () => {
|
|||||||
|
|
||||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||||
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||||
sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
|
accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true);
|
||||||
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
||||||
|
|
||||||
await expect(sut.addAssetsToSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
await expect(sut.addAssetsToSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||||
@ -260,7 +260,7 @@ describe('AssetService', () => {
|
|||||||
|
|
||||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||||
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||||
sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
|
accessMock.hasSharedLinkAssetAccess.mockResolvedValue(true);
|
||||||
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
||||||
|
|
||||||
await expect(sut.removeAssetsFromSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
await expect(sut.removeAssetsFromSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||||
|
@ -564,10 +564,12 @@ export class AssetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async checkAssetsAccess(authUser: AuthUserDto, assetIds: string[], mustBeOwner = false) {
|
private async checkAssetsAccess(authUser: AuthUserDto, assetIds: string[], mustBeOwner = false) {
|
||||||
|
const sharedLinkId = authUser.sharedLinkId;
|
||||||
|
|
||||||
for (const assetId of assetIds) {
|
for (const assetId of assetIds) {
|
||||||
// Step 1: Check if asset is part of a public shared
|
// Step 1: Check if asset is part of a public shared
|
||||||
if (authUser.sharedLinkId) {
|
if (sharedLinkId) {
|
||||||
const canAccess = await this.shareCore.hasAssetAccess(authUser.sharedLinkId, assetId);
|
const canAccess = await this.accessRepository.hasSharedLinkAssetAccess(sharedLinkId, assetId);
|
||||||
if (canAccess) {
|
if (canAccess) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,5 @@ export const IAccessRepository = 'IAccessRepository';
|
|||||||
export interface IAccessRepository {
|
export interface IAccessRepository {
|
||||||
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>;
|
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>;
|
||||||
hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||||
|
hasSharedLinkAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,6 @@ export class SharedLinkCore {
|
|||||||
return this.repository.update({ ...link, assets: newAssets });
|
return this.repository.update({ ...link, assets: newAssets });
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasAssetAccess(id: string, assetId: string): Promise<boolean> {
|
|
||||||
return this.repository.hasAssetAccess(id, assetId);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkDownloadAccess(user: AuthUserDto) {
|
checkDownloadAccess(user: AuthUserDto) {
|
||||||
if (user.isPublicUser && !user.isAllowDownload) {
|
if (user.isPublicUser && !user.isAllowDownload) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
|
@ -9,5 +9,4 @@ export interface ISharedLinkRepository {
|
|||||||
create(entity: Omit<SharedLinkEntity, 'id' | 'user'>): Promise<SharedLinkEntity>;
|
create(entity: Omit<SharedLinkEntity, 'id' | 'user'>): Promise<SharedLinkEntity>;
|
||||||
update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
|
update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity>;
|
||||||
remove(entity: SharedLinkEntity): Promise<void>;
|
remove(entity: SharedLinkEntity): Promise<void>;
|
||||||
hasAssetAccess(id: string, assetId: string): Promise<boolean>;
|
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,6 @@ export const newAccessRepositoryMock = (): jest.Mocked<IAccessRepository> => {
|
|||||||
return {
|
return {
|
||||||
hasPartnerAccess: jest.fn(),
|
hasPartnerAccess: jest.fn(),
|
||||||
hasPartnerAssetAccess: jest.fn(),
|
hasPartnerAssetAccess: jest.fn(),
|
||||||
|
hasSharedLinkAssetAccess: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,5 @@ export const newSharedLinkRepositoryMock = (): jest.Mocked<ISharedLinkRepository
|
|||||||
create: jest.fn(),
|
create: jest.fn(),
|
||||||
remove: jest.fn(),
|
remove: jest.fn(),
|
||||||
update: jest.fn(),
|
update: jest.fn(),
|
||||||
hasAssetAccess: jest.fn(),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { IAccessRepository } from '@app/domain';
|
import { IAccessRepository } from '@app/domain';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { PartnerEntity } from '../entities';
|
import { PartnerEntity, SharedLinkEntity } from '../entities';
|
||||||
|
|
||||||
export class AccessRepository implements IAccessRepository {
|
export class AccessRepository implements IAccessRepository {
|
||||||
constructor(@InjectRepository(PartnerEntity) private partnerRepository: Repository<PartnerEntity>) {}
|
constructor(
|
||||||
|
@InjectRepository(PartnerEntity) private partnerRepository: Repository<PartnerEntity>,
|
||||||
|
@InjectRepository(SharedLinkEntity) private sharedLinkRepository: Repository<SharedLinkEntity>,
|
||||||
|
) {}
|
||||||
|
|
||||||
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean> {
|
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean> {
|
||||||
return this.partnerRepository.exist({
|
return this.partnerRepository.exist({
|
||||||
@ -35,4 +38,29 @@ export class AccessRepository implements IAccessRepository {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async hasSharedLinkAssetAccess(sharedLinkId: string, assetId: string): Promise<boolean> {
|
||||||
|
return (
|
||||||
|
// album asset
|
||||||
|
(await this.sharedLinkRepository.exist({
|
||||||
|
where: {
|
||||||
|
id: sharedLinkId,
|
||||||
|
album: {
|
||||||
|
assets: {
|
||||||
|
id: assetId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})) ||
|
||||||
|
// individual asset
|
||||||
|
(await this.sharedLinkRepository.exist({
|
||||||
|
where: {
|
||||||
|
id: sharedLinkId,
|
||||||
|
assets: {
|
||||||
|
id: assetId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,31 +86,6 @@ export class SharedLinkRepository implements ISharedLinkRepository {
|
|||||||
await this.repository.remove(entity);
|
await this.repository.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasAssetAccess(id: string, assetId: string): Promise<boolean> {
|
|
||||||
return (
|
|
||||||
// album asset
|
|
||||||
(await this.repository.exist({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
album: {
|
|
||||||
assets: {
|
|
||||||
id: assetId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) ||
|
|
||||||
// individual asset
|
|
||||||
(await this.repository.exist({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
assets: {
|
|
||||||
id: assetId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async save(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
|
private async save(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
|
||||||
await this.repository.save(entity);
|
await this.repository.save(entity);
|
||||||
return this.repository.findOneOrFail({ where: { id: entity.id } });
|
return this.repository.findOneOrFail({ where: { id: entity.id } });
|
||||||
|
Loading…
Reference in New Issue
Block a user