diff --git a/server/src/services/timeline.service.ts b/server/src/services/timeline.service.ts index b7ecbad9a9..cb6813cf29 100644 --- a/server/src/services/timeline.service.ts +++ b/server/src/services/timeline.service.ts @@ -60,6 +60,9 @@ export class TimelineService extends BaseService { if (dto.visibility === AssetVisibility.Archive) { await this.requireAccess({ auth, permission: Permission.ArchiveRead, ids: [dto.userId] }); } + if (dto.visibility === AssetVisibility.Locked && dto.userId !== auth.user.id) { + throw new BadRequestException("You may not access another user's locked timeline"); + } } if (dto.tagId) { diff --git a/server/test/medium/specs/services/timeline.service.spec.ts b/server/test/medium/specs/services/timeline.service.spec.ts index d7013b84be..d7d3286733 100644 --- a/server/test/medium/specs/services/timeline.service.spec.ts +++ b/server/test/medium/specs/services/timeline.service.spec.ts @@ -87,6 +87,18 @@ describe(TimelineService.name, () => { ); }); + it('should return error if time bucket is requested with locked visibility for partner', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + const { user: partner } = await ctx.newUser(); + await ctx.newPartner({ sharedById: partner.id, sharedWithId: user.id }); + + const auth = factory.auth({ user, session: { hasElevatedPermission: true } }); + + const response = sut.getTimeBuckets(auth, { userId: partner.id, visibility: AssetVisibility.Locked }); + await expect(response).rejects.toThrow("You may not access another user's locked timeline"); + }); + it('should not allow access for unrelated shared links', async () => { const { sut } = setup(); const auth = factory.auth({ sharedLink: {} });