Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logger from "@calcom/lib/logger";
import db from "@calcom/prisma";
import type { PrismaClient as PrismaClientWithExtensions } from "@calcom/prisma";
import type { MembershipRole } from "@calcom/prisma/enums";
Expand All @@ -15,6 +16,7 @@ import {
export class PermissionRepository implements IPermissionRepository {
private readonly PBAC_FEATURE_FLAG = "pbac" as const;
private client: PrismaClientWithExtensions;
private readonly logger = logger.getSubLogger({ prefix: ["PermissionRepository"] });

constructor(client: PrismaClientWithExtensions = db) {
this.client = client;
Expand Down Expand Up @@ -237,7 +239,16 @@ export class PermissionRepository implements IPermissionRepository {
return { resource, action };
});

const permissionPairsJson = JSON.stringify(permissionPairs);

// Teams with PBAC permissions (direct memberships + child teams via org membership)
const teamsWithPermissionPromise = this.client.$queryRaw<{ teamId: number }[]>`
WITH required_permissions AS (
SELECT
required_perm->>'resource' as resource,
required_perm->>'action' as action
FROM jsonb_array_elements(${permissionPairsJson}::jsonb) AS required_perm
)
SELECT DISTINCT m."teamId"
FROM "Membership" m
INNER JOIN "Role" r ON m."customRoleId" = r.id
Expand All @@ -246,26 +257,61 @@ export class PermissionRepository implements IPermissionRepository {
AND m."customRoleId" IS NOT NULL
AND (
SELECT COUNT(*)
FROM jsonb_array_elements(${JSON.stringify(permissionPairs)}::jsonb) AS required_perm(perm)
FROM required_permissions rp_req
WHERE EXISTS (
SELECT 1
FROM "RolePermission" rp
WHERE rp."roleId" = r.id
AND (
(rp."resource" = '*' AND rp."action" = '*') OR
(rp."resource" = '*' AND rp."action" = rp_req.action) OR
(rp."resource" = rp_req.resource AND rp."action" = '*') OR
(rp."resource" = rp_req.resource AND rp."action" = rp_req.action)
)
)
) = ${permissions.length}
UNION
SELECT DISTINCT child."id"
FROM "Membership" m
INNER JOIN "Role" r ON m."customRoleId" = r.id
INNER JOIN "Team" org ON m."teamId" = org.id
INNER JOIN "Team" child ON child."parentId" = org.id
WHERE m."userId" = ${userId}
AND m."accepted" = true
AND m."customRoleId" IS NOT NULL
AND (
SELECT COUNT(*)
FROM required_permissions rp_req
WHERE EXISTS (
SELECT 1
FROM "RolePermission" rp
WHERE rp."roleId" = r.id
AND (
(rp."resource" = '*' AND rp."action" = '*') OR
(rp."resource" = '*' AND rp."action" = required_perm.perm->>'action') OR
(rp."resource" = required_perm.perm->>'resource' AND rp."action" = '*') OR
(rp."resource" = required_perm.perm->>'resource' AND rp."action" = required_perm.perm->>'action')
(rp."resource" = '*' AND rp."action" = rp_req.action) OR
(rp."resource" = rp_req.resource AND rp."action" = '*') OR
(rp."resource" = rp_req.resource AND rp."action" = rp_req.action)
)
)
) = ${permissions.length}
`;

// Teams with fallback roles (direct memberships + child teams via org membership, PBAC disabled)
const teamsWithFallbackRolesPromise = this.client.$queryRaw<{ teamId: number }[]>`
SELECT DISTINCT m."teamId"
FROM "Membership" m
INNER JOIN "Team" t ON m."teamId" = t.id
LEFT JOIN "TeamFeatures" f ON t.id = f."teamId" AND f."featureId" = ${this.PBAC_FEATURE_FLAG}
LEFT JOIN "TeamFeatures" f ON f."teamId" = t.id AND f."featureId" = ${this.PBAC_FEATURE_FLAG}
WHERE m."userId" = ${userId}
AND m."accepted" = true
AND m."role"::text = ANY(${fallbackRoles})
AND f."teamId" IS NULL
UNION
SELECT DISTINCT child."id"
FROM "Membership" m
INNER JOIN "Team" org ON m."teamId" = org.id
INNER JOIN "Team" child ON child."parentId" = org.id
LEFT JOIN "TeamFeatures" f ON f."teamId" = org.id AND f."featureId" = ${this.PBAC_FEATURE_FLAG}
WHERE m."userId" = ${userId}
AND m."accepted" = true
AND m."role"::text = ANY(${fallbackRoles})
Expand Down
Loading
Loading