fix(server): add album-shared assets to albums (#32609)
Co-authored-by: Jason Rasmussen <jason@rasm.me> (cherry picked from commit 37c34343e1b98edf07dc3ff34c5da461de03c746)
This commit is contained in:
@@ -628,25 +628,19 @@ describe('/albums', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be able to remove foreign asset from foreign album', async () => {
|
it('should be able to remove foreign asset from shared album as an editor', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/albums/${user1Albums[0].id}/assets`)
|
.delete(`/albums/${user1Albums[0].id}/assets`)
|
||||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||||
.send({ ids: [user1Asset1.id] });
|
.send({ ids: [user1Asset1.id] });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([expect.objectContaining({ id: user1Asset1.id, success: true })]);
|
||||||
expect.objectContaining({
|
|
||||||
id: user1Asset1.id,
|
|
||||||
success: false,
|
|
||||||
error: 'no_permission',
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to remove own asset from own album', async () => {
|
it('should be able to remove own asset from own album', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/albums/${user1Albums[0].id}/assets`)
|
.delete(`/albums/${user1Albums[2].id}/assets`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
.send({ ids: [user1Asset1.id] });
|
.send({ ids: [user1Asset1.id] });
|
||||||
|
|
||||||
|
|||||||
@@ -888,6 +888,24 @@ describe(AlbumService.name, () => {
|
|||||||
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]));
|
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow adding assets shared via album sharing', async () => {
|
||||||
|
const album = AlbumFactory.create();
|
||||||
|
const { user: owner } = album.albumUsers.find(({ role }) => role === AlbumUserRole.Owner)!;
|
||||||
|
const asset = AssetFactory.create();
|
||||||
|
mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set([album.id]));
|
||||||
|
mocks.access.asset.checkAlbumAccess.mockResolvedValue(new Set([asset.id]));
|
||||||
|
mocks.album.getById.mockResolvedValue(getForAlbum(album));
|
||||||
|
mocks.album.getAssetIds.mockResolvedValueOnce(new Set());
|
||||||
|
|
||||||
|
await expect(sut.addAssets(AuthFactory.create(owner), album.id, { ids: [asset.id] })).resolves.toEqual([
|
||||||
|
{ success: true, id: asset.id },
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(mocks.album.addAssetIds).toHaveBeenCalledWith(album.id, [asset.id]);
|
||||||
|
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]), false);
|
||||||
|
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]));
|
||||||
|
});
|
||||||
|
|
||||||
it('should skip duplicate assets', async () => {
|
it('should skip duplicate assets', async () => {
|
||||||
const asset = AssetFactory.create();
|
const asset = AssetFactory.create();
|
||||||
const album = AlbumFactory.create();
|
const album = AlbumFactory.create();
|
||||||
@@ -917,6 +935,7 @@ describe(AlbumService.name, () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]), false);
|
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]), false);
|
||||||
|
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]));
|
||||||
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]));
|
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(owner.id, new Set([asset.id]));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1185,6 +1204,33 @@ describe(AlbumService.name, () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow adding assets shared via album sharing', async () => {
|
||||||
|
const user = UserFactory.create();
|
||||||
|
const album1 = AlbumFactory.create();
|
||||||
|
const { user: owner } = album1.albumUsers.find(({ role }) => role === AlbumUserRole.Owner)!;
|
||||||
|
const album2 = AlbumFactory.create();
|
||||||
|
const [asset1, asset2] = [AssetFactory.create({ ownerId: user.id }), AssetFactory.create({ ownerId: user.id })];
|
||||||
|
mocks.access.album.checkOwnerAccess.mockResolvedValueOnce(new Set([album1.id, album2.id]));
|
||||||
|
mocks.access.asset.checkAlbumAccess.mockResolvedValue(new Set([asset1.id, asset2.id]));
|
||||||
|
mocks.album.getById.mockResolvedValueOnce(getForAlbum(album1)).mockResolvedValueOnce(getForAlbum(album2));
|
||||||
|
mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set());
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
sut.addAssetsToAlbums(AuthFactory.create(owner), {
|
||||||
|
albumIds: [album1.id, album2.id],
|
||||||
|
assetIds: [asset1.id, asset2.id],
|
||||||
|
}),
|
||||||
|
).resolves.toEqual({ success: true, error: undefined });
|
||||||
|
|
||||||
|
expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([
|
||||||
|
{ albumId: album1.id, assetId: asset1.id },
|
||||||
|
{ albumId: album1.id, assetId: asset2.id },
|
||||||
|
{ albumId: album2.id, assetId: asset1.id },
|
||||||
|
{ albumId: album2.id, assetId: asset2.id },
|
||||||
|
]);
|
||||||
|
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(owner.id, new Set([asset1.id, asset2.id]));
|
||||||
|
});
|
||||||
|
|
||||||
it('should skip some duplicate assets', async () => {
|
it('should skip some duplicate assets', async () => {
|
||||||
const [asset1, asset2, asset3] = [AssetFactory.create(), AssetFactory.create(), AssetFactory.create()];
|
const [asset1, asset2, asset3] = [AssetFactory.create(), AssetFactory.create(), AssetFactory.create()];
|
||||||
const album1 = AlbumFactory.create();
|
const album1 = AlbumFactory.create();
|
||||||
@@ -1282,6 +1328,10 @@ describe(AlbumService.name, () => {
|
|||||||
new Set([asset1.id, asset2.id, asset3.id]),
|
new Set([asset1.id, asset2.id, asset3.id]),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(
|
||||||
|
owner.id,
|
||||||
|
new Set([asset1.id, asset2.id, asset3.id]),
|
||||||
|
);
|
||||||
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(
|
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(
|
||||||
owner.id,
|
owner.id,
|
||||||
new Set([asset1.id, asset2.id, asset3.id]),
|
new Set([asset1.id, asset2.id, asset3.id]),
|
||||||
@@ -1348,6 +1398,23 @@ describe(AlbumService.name, () => {
|
|||||||
expect(mocks.album.removeAssetIds).toHaveBeenCalledWith(album.id, [asset.id]);
|
expect(mocks.album.removeAssetIds).toHaveBeenCalledWith(album.id, [asset.id]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow a shared user to remove assets shared via album sharing', async () => {
|
||||||
|
const user = UserFactory.create();
|
||||||
|
const album = AlbumFactory.from().albumUser({ userId: user.id, role: AlbumUserRole.Editor }).build();
|
||||||
|
const asset = AssetFactory.create();
|
||||||
|
mocks.access.album.checkSharedAlbumAccess.mockResolvedValue(new Set([album.id]));
|
||||||
|
mocks.access.asset.checkAlbumAccess.mockResolvedValue(new Set([asset.id]));
|
||||||
|
mocks.album.getById.mockResolvedValue(getForAlbum(album));
|
||||||
|
mocks.album.getAssetIds.mockResolvedValue(new Set([asset.id]));
|
||||||
|
|
||||||
|
await expect(sut.removeAssets(AuthFactory.create(user), album.id, { ids: [asset.id] })).resolves.toEqual([
|
||||||
|
{ success: true, id: asset.id },
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(mocks.album.removeAssetIds).toHaveBeenCalledWith(album.id, [asset.id]);
|
||||||
|
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(user.id, new Set([asset.id]));
|
||||||
|
});
|
||||||
|
|
||||||
it('should skip assets not in the album', async () => {
|
it('should skip assets not in the album', async () => {
|
||||||
const asset = AssetFactory.create();
|
const asset = AssetFactory.create();
|
||||||
const album = AlbumFactory.create();
|
const album = AlbumFactory.create();
|
||||||
|
|||||||
@@ -173,8 +173,12 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe
|
|||||||
|
|
||||||
case Permission.AssetShare: {
|
case Permission.AssetShare: {
|
||||||
const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids, false);
|
const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids, false);
|
||||||
const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner));
|
const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
return setUnion(isOwner, isPartner);
|
const isPartner = await access.asset.checkPartnerAccess(
|
||||||
|
auth.user.id,
|
||||||
|
setDifference(setDifference(ids, isOwner), isAlbum),
|
||||||
|
);
|
||||||
|
return setUnion(setUnion(isOwner, isAlbum), isPartner);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Permission.AssetFileDownload: {
|
case Permission.AssetFileDownload: {
|
||||||
|
|||||||
Reference in New Issue
Block a user