From 1d2f4657bae4d924a7fe6cbb2de80672f89d2a3f Mon Sep 17 00:00:00 2001 From: Hong Jiarong Date: Tue, 14 Jul 2026 23:55:20 +0800 Subject: [PATCH] fix(org): match listMyProjects grants by principal pair Filtering permission grants with separate principalType/principalId IN lists matched cross-product rows (e.g. TEAM + user id). Use OR of exact (type, id) pairs so members only see projects their principals hold. --- hub/src/org/explorer.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hub/src/org/explorer.ts b/hub/src/org/explorer.ts index 784c479..5018a58 100644 --- a/hub/src/org/explorer.ts +++ b/hub/src/org/explorer.ts @@ -350,16 +350,17 @@ export async function listMyProjects( { feishuOpenId: input.actorFeishuOpenId }, { organizationId: input.organizationId }, ); - const principalKeys = resolution.principals.map((p) => `${p.type}:${p.id}`); - if (principalKeys.length === 0) return []; - const principalTypes = resolution.principals.map((p) => p.type); - const principalIds = resolution.principals.map((p) => p.id); + // Match exact (type, id) pairs — independent IN filters form a cartesian + // product and can attribute another principal's grants to this actor. + if (resolution.principals.length === 0) return []; const grants = await prisma.permissionGrant.findMany({ where: { resourceType: "PROJECT", revokedAt: null, - principalType: { in: principalTypes }, - principalId: { in: principalIds }, + OR: resolution.principals.map((p) => ({ + principalType: p.type, + principalId: p.id, + })), }, select: { resourceId: true }, distinct: ["resourceId"],