1
0
mirror of https://github.com/immich-app/immich.git synced 2025-04-28 13:50:12 +02:00

fix(server): require asset permission when creating an album with them (#8686)

require asset permission when creating an album with them
This commit is contained in:
Daniel Dietzler 2024-04-10 19:41:22 +02:00 committed by GitHub
parent 56079527ef
commit ad5d115abe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 3 deletions

View File

@ -4,7 +4,9 @@ import {
AssetOrder, AssetOrder,
LoginResponseDto, LoginResponseDto,
SharedLinkType, SharedLinkType,
addAssetsToAlbum,
deleteUser, deleteUser,
getAlbumInfo,
} from '@immich/sdk'; } from '@immich/sdk';
import { createUserDto, uuidDto } from 'src/fixtures'; import { createUserDto, uuidDto } from 'src/fixtures';
import { errorDto } from 'src/responses'; import { errorDto } from 'src/responses';
@ -65,7 +67,6 @@ describe('/album', () => {
utils.createAlbum(user2.accessToken, { utils.createAlbum(user2.accessToken, {
albumName: user2SharedUser, albumName: user2SharedUser,
sharedWithUserIds: [user1.userId], sharedWithUserIds: [user1.userId],
assetIds: [user1Asset1.id],
}), }),
utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }), utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }),
utils.createAlbum(user2.accessToken, { albumName: user2NotShared }), 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); user1Albums = albums.slice(0, 3);
user2Albums = albums.slice(3, 6); user2Albums = albums.slice(3, 6);

View File

@ -175,6 +175,7 @@ describe(AlbumService.name, () => {
it('creates album', async () => { it('creates album', async () => {
albumMock.create.mockResolvedValue(albumStub.empty); albumMock.create.mockResolvedValue(albumStub.empty);
userMock.get.mockResolvedValue(userStub.user1); userMock.get.mockResolvedValue(userStub.user1);
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['123']));
await sut.create(authStub.admin, { await sut.create(authStub.admin, {
albumName: 'Empty album', albumName: 'Empty album',
@ -193,6 +194,7 @@ describe(AlbumService.name, () => {
}); });
expect(userMock.get).toHaveBeenCalledWith('user-id', {}); expect(userMock.get).toHaveBeenCalledWith('user-id', {});
expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']));
}); });
it('should require valid userIds', async () => { it('should require valid userIds', async () => {
@ -206,6 +208,31 @@ describe(AlbumService.name, () => {
expect(userMock.get).toHaveBeenCalledWith('user-3', {}); expect(userMock.get).toHaveBeenCalledWith('user-3', {});
expect(albumMock.create).not.toHaveBeenCalled(); 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', () => { describe('update', () => {

View File

@ -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({ const album = await this.albumRepository.create({
ownerId: auth.user.id, ownerId: auth.user.id,
albumName: dto.albumName, albumName: dto.albumName,
description: dto.description, description: dto.description,
sharedUsers: dto.sharedWithUserIds?.map((value) => ({ id: value }) as UserEntity) ?? [], sharedUsers: dto.sharedWithUserIds?.map((value) => ({ id: value }) as UserEntity) ?? [],
assets: (dto.assetIds || []).map((id) => ({ id }) as AssetEntity), assets,
albumThumbnailAssetId: dto.assetIds?.[0] || null, albumThumbnailAssetId: assets[0]?.id || null,
}); });
return mapAlbumWithAssets(album); return mapAlbumWithAssets(album);