diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index de320ee95f..c877afc6bb 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -4,7 +4,9 @@ import { AssetOrder, LoginResponseDto, SharedLinkType, + addAssetsToAlbum, deleteUser, + getAlbumInfo, } from '@immich/sdk'; import { createUserDto, uuidDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; @@ -65,7 +67,6 @@ describe('/album', () => { utils.createAlbum(user2.accessToken, { albumName: user2SharedUser, sharedWithUserIds: [user1.userId], - assetIds: [user1Asset1.id], }), utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }), utils.createAlbum(user2.accessToken, { albumName: user2NotShared }), @@ -77,6 +78,13 @@ describe('/album', () => { }), ]); + await addAssetsToAlbum( + { id: albums[3].id, bulkIdsDto: { ids: [user1Asset1.id] } }, + { headers: asBearerAuth(user1.accessToken) }, + ); + + albums[3] = await getAlbumInfo({ id: albums[3].id }, { headers: asBearerAuth(user2.accessToken) }); + user1Albums = albums.slice(0, 3); user2Albums = albums.slice(3, 6); diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 02bb607b52..a8c0a6f647 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -175,6 +175,7 @@ describe(AlbumService.name, () => { it('creates album', async () => { albumMock.create.mockResolvedValue(albumStub.empty); userMock.get.mockResolvedValue(userStub.user1); + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['123'])); await sut.create(authStub.admin, { albumName: 'Empty album', @@ -193,6 +194,7 @@ describe(AlbumService.name, () => { }); expect(userMock.get).toHaveBeenCalledWith('user-id', {}); + expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123'])); }); it('should require valid userIds', async () => { @@ -206,6 +208,31 @@ describe(AlbumService.name, () => { expect(userMock.get).toHaveBeenCalledWith('user-3', {}); expect(albumMock.create).not.toHaveBeenCalled(); }); + + it('should only add assets the user is allowed to access', async () => { + userMock.get.mockResolvedValue(userStub.user1); + albumMock.create.mockResolvedValue(albumStub.oneAsset); + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); + + await sut.create(authStub.admin, { + albumName: 'Test album', + description: '', + assetIds: ['asset-1', 'asset-2'], + }); + + expect(albumMock.create).toHaveBeenCalledWith({ + ownerId: authStub.admin.user.id, + albumName: 'Test album', + description: '', + sharedUsers: [], + assets: [{ id: 'asset-1' }], + albumThumbnailAssetId: 'asset-1', + }); + expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2']), + ); + }); }); describe('update', () => { diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index df6c6b814c..b3b7f6d08a 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -119,13 +119,16 @@ export class AlbumService { } } + const allowedAssetIdsSet = await this.access.checkAccess(auth, Permission.ASSET_SHARE, new Set(dto.assetIds)); + const assets = [...allowedAssetIdsSet].map((id) => ({ id }) as AssetEntity); + const album = await this.albumRepository.create({ ownerId: auth.user.id, albumName: dto.albumName, description: dto.description, sharedUsers: dto.sharedWithUserIds?.map((value) => ({ id: value }) as UserEntity) ?? [], - assets: (dto.assetIds || []).map((id) => ({ id }) as AssetEntity), - albumThumbnailAssetId: dto.assetIds?.[0] || null, + assets, + albumThumbnailAssetId: assets[0]?.id || null, }); return mapAlbumWithAssets(album);