mirror of
https://github.com/immich-app/immich.git
synced 2026-07-10 06:03:14 -07:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c432a592a | |||
| fbecc16e2d |
@@ -0,0 +1,16 @@
|
||||
import { Kysely, sql } from 'kysely';
|
||||
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
// Delete cross-owner memory assets
|
||||
await sql`
|
||||
DELETE FROM memory_asset
|
||||
USING memory, asset
|
||||
WHERE memory_asset."memoriesId" = memory.id
|
||||
AND memory_asset."assetId" = asset.id
|
||||
AND memory."ownerId" != asset."ownerId"
|
||||
`.execute(db);
|
||||
}
|
||||
|
||||
export async function down(): Promise<void> {
|
||||
// Not implemented: the deleted rows were cross-owner entries
|
||||
}
|
||||
@@ -175,7 +175,7 @@ export class AlbumService extends BaseService {
|
||||
const results = await addAssets(
|
||||
auth,
|
||||
{ access: this.accessRepository, bulk: this.albumRepository },
|
||||
{ parentId: id, assetIds: dto.ids },
|
||||
{ parentId: id, assetIds: dto.ids, permission: Permission.AssetShare },
|
||||
);
|
||||
|
||||
const { id: firstNewAssetId } = results.find(({ success }) => success) || {};
|
||||
|
||||
@@ -93,7 +93,7 @@ export class MemoryService extends BaseService {
|
||||
const assetIds = dto.assetIds || [];
|
||||
const allowedAssetIds = await this.checkAccess({
|
||||
auth,
|
||||
permission: Permission.AssetShare,
|
||||
permission: Permission.AssetUpdate,
|
||||
ids: assetIds,
|
||||
});
|
||||
const memory = await this.memoryRepository.create(
|
||||
@@ -134,7 +134,11 @@ export class MemoryService extends BaseService {
|
||||
await this.requireAccess({ auth, permission: Permission.MemoryRead, ids: [id] });
|
||||
|
||||
const repos = { access: this.accessRepository, bulk: this.memoryRepository };
|
||||
const results = await addAssets(auth, repos, { parentId: id, assetIds: dto.ids });
|
||||
const results = await addAssets(auth, repos, {
|
||||
parentId: id,
|
||||
assetIds: dto.ids,
|
||||
permission: Permission.AssetUpdate,
|
||||
});
|
||||
|
||||
const hasSuccess = results.find(({ success }) => success);
|
||||
if (hasSuccess) {
|
||||
|
||||
@@ -104,7 +104,7 @@ export class TagService extends BaseService {
|
||||
const results = await addAssets(
|
||||
auth,
|
||||
{ access: this.accessRepository, bulk: this.tagRepository },
|
||||
{ parentId: id, assetIds: dto.ids },
|
||||
{ parentId: id, assetIds: dto.ids, permission: Permission.AssetUpdate },
|
||||
);
|
||||
|
||||
for (const { id: assetId, success } of results) {
|
||||
|
||||
@@ -33,14 +33,14 @@ export const getAssetFiles = (files: AssetFile[]) => ({
|
||||
export const addAssets = async (
|
||||
auth: AuthDto,
|
||||
repositories: { access: AccessRepository; bulk: IBulkAsset },
|
||||
dto: { parentId: string; assetIds: string[] },
|
||||
dto: { parentId: string; assetIds: string[]; permission: Permission },
|
||||
) => {
|
||||
const { access, bulk } = repositories;
|
||||
const existingAssetIds = await bulk.getAssetIds(dto.parentId, dto.assetIds);
|
||||
const notPresentAssetIds = dto.assetIds.filter((id) => !existingAssetIds.has(id));
|
||||
const allowedAssetIds = await checkAccess(access, {
|
||||
auth,
|
||||
permission: Permission.AssetShare,
|
||||
permission: dto.permission,
|
||||
ids: notPresentAssetIds,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { DateTime } from 'luxon';
|
||||
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
|
||||
import { AssetFileType, MemoryType } from 'src/enum';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||
@@ -105,6 +106,43 @@ describe(MemoryService.name, () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not link a partner asset', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user: owner } = await ctx.newUser();
|
||||
const { user: partner } = await ctx.newUser();
|
||||
await ctx.newPartner({ sharedById: partner.id, sharedWithId: owner.id, inTimeline: true });
|
||||
const { asset } = await ctx.newAsset({ ownerId: partner.id });
|
||||
const auth = factory.auth({ user: owner });
|
||||
const dto = {
|
||||
type: MemoryType.OnThisDay,
|
||||
data: { year: 2021 },
|
||||
memoryAt: new Date(2021),
|
||||
assetIds: [asset.id],
|
||||
};
|
||||
|
||||
await expect(sut.create(auth, dto)).resolves.toEqual(expect.objectContaining({ assets: [] }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('addAssets', () => {
|
||||
it('should not link a partner asset', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user: owner } = await ctx.newUser();
|
||||
const { user: partner } = await ctx.newUser();
|
||||
await ctx.newPartner({ sharedById: partner.id, sharedWithId: owner.id, inTimeline: true });
|
||||
const { asset } = await ctx.newAsset({ ownerId: partner.id });
|
||||
const auth = factory.auth({ user: owner });
|
||||
const memory = await sut.create(auth, {
|
||||
type: MemoryType.OnThisDay,
|
||||
data: { year: 2021 },
|
||||
memoryAt: new Date(2021),
|
||||
});
|
||||
|
||||
await expect(sut.addAssets(auth, memory.id, { ids: [asset.id] })).resolves.toEqual([
|
||||
{ id: asset.id, success: false, error: BulkIdErrorReason.NO_PERMISSION },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMemoryCreate', () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
|
||||
import { JobStatus } from 'src/enum';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||
@@ -52,6 +53,21 @@ describe(TagService.name, () => {
|
||||
);
|
||||
await expect(ctx.get(TagRepository).getAssetIds(tag.id, [asset.id])).resolves.toContain(asset.id);
|
||||
});
|
||||
|
||||
it('should not tag a partner asset', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||
const { user: owner } = await ctx.newUser();
|
||||
const { user: partner } = await ctx.newUser();
|
||||
await ctx.newPartner({ sharedById: partner.id, sharedWithId: owner.id, inTimeline: true });
|
||||
const { asset } = await ctx.newAsset({ ownerId: partner.id });
|
||||
const [tag] = await upsertTags(ctx.get(TagRepository), { userId: owner.id, tags: ['tag-1'] });
|
||||
const authDto = factory.auth({ user: owner });
|
||||
|
||||
await expect(sut.addAssets(authDto, tag.id, { ids: [asset.id] })).resolves.toEqual([
|
||||
{ id: asset.id, success: false, error: BulkIdErrorReason.NO_PERMISSION },
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('deleteEmptyTags', () => {
|
||||
it('single tag exists, not connected to any assets, and is deleted', async () => {
|
||||
|
||||
Reference in New Issue
Block a user