feat(p9): fence AI + calendar + message by-id leaks (tenant scope)

Task T3.2: AiJobService.getArtifact/accept/reject assertOwns on artifact.job.scopeId;
listArtifacts scoped to caller. CalendarService.getMeeting/setConsent/generateTranscript/
summarize/listActionItems assertOwns on meeting.scopeId (optional principal → asserts
when supplied by a controller, skipped for internal calls). MessageService.getMessageById
gains the same optional fence. Controllers thread the principal through. All 46
ai/calendar/routing/messaging tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 21:51:10 +05:30
parent 063049b724
commit ed0d4dae80
5 changed files with 33 additions and 19 deletions
@@ -217,12 +217,13 @@ export class MessageService {
return { interactionId, actorId: actor.id };
}
async getMessageById(id: string): Promise<MessageDto | null> {
async getMessageById(id: string, principal?: MessagePrincipal): Promise<MessageDto | null> {
const i = await this.prisma.iiosInteraction.findUnique({
where: { id },
include: { parts: { orderBy: { partIndex: 'asc' } } },
});
if (!i || !i.threadId) return null;
if (principal) await this.actors.assertOwns(principal, i.scopeId); // tenant fence (KG-02) when called on behalf of a caller
return this.toDto(i, i.threadId);
}