diff --git a/server/apps/immich/src/api-v1/album/album-repository.ts b/server/apps/immich/src/api-v1/album/album-repository.ts index b19729c35c..cef78e0b2f 100644 --- a/server/apps/immich/src/api-v1/album/album-repository.ts +++ b/server/apps/immich/src/api-v1/album/album-repository.ts @@ -25,6 +25,7 @@ export interface IAlbumRepository { updateAlbum(album: AlbumEntity, updateAlbumDto: UpdateAlbumDto): Promise; getListByAssetId(userId: string, assetId: string): Promise; getCountByUserId(userId: string): Promise; + getSharedAlbumCount(userId: string, assetId: string): Promise; } export const ALBUM_REPOSITORY = 'ALBUM_REPOSITORY'; @@ -283,4 +284,17 @@ export class AlbumRepository implements IAlbumRepository { return this.albumRepository.save(album); } + + async getSharedAlbumCount(userId: string, assetId: string): Promise { + const result = await this + .userAlbumRepository + .createQueryBuilder('usa') + .select('count(aa)', 'count') + .innerJoin('asset_album', 'aa', 'aa.albumId = usa.albumId') + .where('aa.assetId = :assetId', { assetId }) + .andWhere('usa.sharedUserId = :userId', { userId }) + .getRawOne(); + + return result.count; + } } diff --git a/server/apps/immich/src/api-v1/asset/asset.module.ts b/server/apps/immich/src/api-v1/asset/asset.module.ts index f33d7a415b..698fc46ba3 100644 --- a/server/apps/immich/src/api-v1/asset/asset.module.ts +++ b/server/apps/immich/src/api-v1/asset/asset.module.ts @@ -10,13 +10,18 @@ import { CommunicationModule } from '../communication/communication.module'; import { QueueNameEnum } from '@app/job/constants/queue-name.constant'; import { AssetRepository, ASSET_REPOSITORY } from './asset-repository'; import { DownloadModule } from '../../modules/download/download.module'; +import {ALBUM_REPOSITORY, AlbumRepository} from "../album/album-repository"; +import {AlbumEntity} from "@app/database/entities/album.entity"; +import {UserAlbumEntity} from "@app/database/entities/user-album.entity"; +import {UserEntity} from "@app/database/entities/user.entity"; +import {AssetAlbumEntity} from "@app/database/entities/asset-album.entity"; @Module({ imports: [ CommunicationModule, BackgroundTaskModule, DownloadModule, - TypeOrmModule.forFeature([AssetEntity]), + TypeOrmModule.forFeature([AssetEntity, AlbumEntity, UserAlbumEntity, UserEntity, AssetAlbumEntity]), BullModule.registerQueue({ name: QueueNameEnum.ASSET_UPLOADED, defaultJobOptions: { @@ -42,6 +47,10 @@ import { DownloadModule } from '../../modules/download/download.module'; provide: ASSET_REPOSITORY, useClass: AssetRepository, }, + { + provide: ALBUM_REPOSITORY, + useClass: AlbumRepository, + }, ], exports: [AssetService], }) diff --git a/server/apps/immich/src/api-v1/asset/asset.service.ts b/server/apps/immich/src/api-v1/asset/asset.service.ts index 9f69bf45b6..50a8548d6f 100644 --- a/server/apps/immich/src/api-v1/asset/asset.service.ts +++ b/server/apps/immich/src/api-v1/asset/asset.service.ts @@ -54,6 +54,7 @@ import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; import { DownloadService } from '../../modules/download/download.service'; import { DownloadDto } from './dto/download-library.dto'; +import { ALBUM_REPOSITORY, IAlbumRepository } from "../album/album-repository"; const fileInfo = promisify(stat); @@ -63,6 +64,9 @@ export class AssetService { @Inject(ASSET_REPOSITORY) private _assetRepository: IAssetRepository, + @Inject(ALBUM_REPOSITORY) + private _albumRepository: IAlbumRepository, + @InjectRepository(AssetEntity) private assetRepository: Repository, @@ -627,8 +631,8 @@ export class AssetService { return this._assetRepository.getAssetCountByUserId(authUser.id); } - async checkAssetsAccess(authUser: AuthUserDto, assetIds: string[], mustBeOwner: boolean = false) { - for (let assetId of assetIds) { + async checkAssetsAccess(authUser: AuthUserDto, assetIds: string[], mustBeOwner = false) { + for (const assetId of assetIds) { // Step 1: Check if user owns asset if (await this._assetRepository.countByIdAndUser(assetId, authUser.id) == 1) { continue; @@ -636,9 +640,13 @@ export class AssetService { // Avoid additional checks if ownership is required if (!mustBeOwner) { + // Step 2: Check if asset is part of an album shared with me + if (await this._albumRepository.getSharedAlbumCount(authUser.id, assetId) > 0) { + continue; + } + //TODO: Step 3: Check if asset is part of a public album } - throw new ForbiddenException(); } }