From 7fe80dd3ddb5a1e10a86286c5944b23e15dd3ab3 Mon Sep 17 00:00:00 2001 From: Eunjae Lee Date: Tue, 16 Sep 2025 12:17:08 +0200 Subject: [PATCH] Revert "feat: implement generalized navigation permission system with PBAC (#23706)" (#23867) This reverts commit 34b62f7516b04293ac6e09ab8ecf896f8d750d47. --- apps/web/app/(use-page-wrapper)/layout.tsx | 6 +- .../shell/navigation/NavigationItem.tsx | 1 - .../useShouldDisplayNavigationItem.ts | 19 +----- .../NavigationPermissionsProvider.tsx | 65 ------------------- packages/features/shell/permissions/types.ts | 19 ------ packages/lib/server/repository/membership.ts | 2 +- packages/lib/webstorage.ts | 32 --------- .../server/routers/viewer/pbac/_router.tsx | 30 --------- 8 files changed, 4 insertions(+), 170 deletions(-) delete mode 100644 packages/features/shell/permissions/NavigationPermissionsProvider.tsx delete mode 100644 packages/features/shell/permissions/types.ts diff --git a/apps/web/app/(use-page-wrapper)/layout.tsx b/apps/web/app/(use-page-wrapper)/layout.tsx index cecdb5b371..878635c237 100644 --- a/apps/web/app/(use-page-wrapper)/layout.tsx +++ b/apps/web/app/(use-page-wrapper)/layout.tsx @@ -1,8 +1,6 @@ import { headers } from "next/headers"; import Script from "next/script"; -import { NavigationPermissionsProvider } from "@calcom/features/shell/permissions/NavigationPermissionsProvider"; - import PageWrapper from "@components/PageWrapperAppDir"; export default async function PageWrapperLayout({ children }: { children: React.ReactNode }) { @@ -23,7 +21,7 @@ export default async function PageWrapperLayout({ children }: { children: React. ].filter((script): script is { id: string; script: string } => !!script.script); return ( - + <> {children} {scripts.map((script) => ( @@ -38,6 +36,6 @@ export default async function PageWrapperLayout({ children }: { children: React. /> ))} - + ); } diff --git a/packages/features/shell/navigation/NavigationItem.tsx b/packages/features/shell/navigation/NavigationItem.tsx index 72affdd75b..7b59c7ed0f 100644 --- a/packages/features/shell/navigation/NavigationItem.tsx +++ b/packages/features/shell/navigation/NavigationItem.tsx @@ -4,7 +4,6 @@ import React, { Fragment, useState, useEffect } from "react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; -import { sessionStorage } from "@calcom/lib/webstorage"; import classNames from "@calcom/ui/classNames"; import { Badge } from "@calcom/ui/components/badge"; import { Icon } from "@calcom/ui/components/icon"; diff --git a/packages/features/shell/navigation/useShouldDisplayNavigationItem.ts b/packages/features/shell/navigation/useShouldDisplayNavigationItem.ts index 75ed5ee181..ffba4b37c8 100644 --- a/packages/features/shell/navigation/useShouldDisplayNavigationItem.ts +++ b/packages/features/shell/navigation/useShouldDisplayNavigationItem.ts @@ -1,27 +1,10 @@ import { useFlagMap } from "@calcom/features/flags/context/provider"; import { isKeyInObject } from "@calcom/lib/isKeyInObject"; -import { - useNavigationPermissions, - type NavigationItemName, -} from "../permissions/NavigationPermissionsProvider"; import type { NavigationItemType } from "./NavigationItem"; export function useShouldDisplayNavigationItem(item: NavigationItemType) { const flags = useFlagMap(); - const { permissions: navigationPermissions, isLoading } = useNavigationPermissions(); - - if (isKeyInObject(item.name, flags) && flags[item.name] === false) { - return false; - } - - if (isLoading) { - return true; - } - - if (item.name in navigationPermissions) { - return navigationPermissions[item.name as NavigationItemName]; - } - + if (isKeyInObject(item.name, flags)) return flags[item.name]; return true; } diff --git a/packages/features/shell/permissions/NavigationPermissionsProvider.tsx b/packages/features/shell/permissions/NavigationPermissionsProvider.tsx deleted file mode 100644 index d452fc8fcf..0000000000 --- a/packages/features/shell/permissions/NavigationPermissionsProvider.tsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client"; - -import React, { createContext, useContext } from "react"; - -import { trpc } from "@calcom/trpc/react"; - -import type { NavigationItemName, NavigationPermissions } from "./types"; -import { DEFAULT_PERMISSIONS } from "./types"; - -export type { NavigationItemName, NavigationPermissions }; - -/** - * Context for navigation permissions - */ -const NavigationPermissionsContext = createContext<{ - permissions: NavigationPermissions; - isLoading: boolean; -} | null>(null); - -/** - * Hook to access navigation permissions from context - * @returns NavigationPermissions object with boolean flags for each navigation item - */ -export function useNavigationPermissions(): { permissions: NavigationPermissions; isLoading: boolean } { - const context = useContext(NavigationPermissionsContext); - if (context === null) { - return { - permissions: DEFAULT_PERMISSIONS, - isLoading: false, - }; - } - return context; -} - -/** - * Hook to check if a specific navigation item should be displayed - * @param itemName The navigation item name to check - * @returns boolean indicating if the item should be displayed - */ -export function useNavigationPermission(itemName: NavigationItemName): boolean { - const { permissions } = useNavigationPermissions(); - return permissions[itemName]; -} - -/** - * Provider component for navigation permissions - * Fetches permissions client-side using tRPC - */ -export function NavigationPermissionsProvider({ children }: { children: React.ReactNode }) { - const { data: permissions, isLoading } = trpc.viewer.pbac.getNavigationPermissions.useQuery(undefined, { - staleTime: 5 * 60 * 1000, - refetchOnWindowFocus: false, - }); - - const contextValue = { - permissions: permissions || DEFAULT_PERMISSIONS, - isLoading, - }; - - return ( - - {children} - - ); -} diff --git a/packages/features/shell/permissions/types.ts b/packages/features/shell/permissions/types.ts deleted file mode 100644 index a24ea8dec8..0000000000 --- a/packages/features/shell/permissions/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Navigation permission mapping for menu items - */ -export const NAVIGATION_PERMISSION_MAP = { - members: "organization.listMembers", - teams: "team.read", - insights: "insights.read", -} as const; - -export const DEFAULT_PERMISSIONS = Object.fromEntries( - Object.keys(NAVIGATION_PERMISSION_MAP).map((key) => [key, true]) -) as NavigationPermissions; - -export type NavigationItemName = keyof typeof NAVIGATION_PERMISSION_MAP; - -/** - * Navigation permissions object containing boolean flags for each navigation item - */ -export type NavigationPermissions = Record; diff --git a/packages/lib/server/repository/membership.ts b/packages/lib/server/repository/membership.ts index 9850ddccb1..6063f14136 100644 --- a/packages/lib/server/repository/membership.ts +++ b/packages/lib/server/repository/membership.ts @@ -1,7 +1,7 @@ import { availabilityUserSelect, prisma, type PrismaTransaction } from "@calcom/prisma"; -import type { Prisma, Membership, PrismaClient } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; +import type { Prisma, Membership, PrismaClient } from "@calcom/prisma/client"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import logger from "../../logger"; diff --git a/packages/lib/webstorage.ts b/packages/lib/webstorage.ts index 9522799179..c703bfda05 100644 --- a/packages/lib/webstorage.ts +++ b/packages/lib/webstorage.ts @@ -34,35 +34,3 @@ export const localStorage = { } }, }; - -export const sessionStorage = { - getItem(key: string) { - try { - // eslint-disable-next-line @calcom/eslint/avoid-web-storage - return window.sessionStorage.getItem(key); - } catch (e) { - // In case storage is restricted. Possible reasons - // 1. Third Party Context in Chrome Incognito mode. - return null; - } - }, - setItem(key: string, value: string) { - try { - // eslint-disable-next-line @calcom/eslint/avoid-web-storage - window.sessionStorage.setItem(key, value); - } catch (e) { - // In case storage is restricted. Possible reasons - // 1. Third Party Context in Chrome Incognito mode. - // 2. Storage limit reached - return; - } - }, - removeItem: (key: string) => { - try { - // eslint-disable-next-line @calcom/eslint/avoid-web-storage - window.sessionStorage.removeItem(key); - } catch (e) { - return; - } - }, -}; diff --git a/packages/trpc/server/routers/viewer/pbac/_router.tsx b/packages/trpc/server/routers/viewer/pbac/_router.tsx index b314d39263..c3c07b8e09 100644 --- a/packages/trpc/server/routers/viewer/pbac/_router.tsx +++ b/packages/trpc/server/routers/viewer/pbac/_router.tsx @@ -5,12 +5,6 @@ import { isValidPermissionString } from "@calcom/features/pbac/domain/types/perm import type { PermissionString } from "@calcom/features/pbac/domain/types/permission-registry"; import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service"; import { RoleService } from "@calcom/features/pbac/services/role.service"; -import { - NAVIGATION_PERMISSION_MAP, - DEFAULT_PERMISSIONS, - type NavigationItemName, -} from "@calcom/features/shell/permissions/types"; -import { MembershipRepository } from "@calcom/lib/server/repository/membership"; import prisma from "@calcom/prisma"; import { RoleType, MembershipRole } from "@calcom/prisma/enums"; @@ -39,30 +33,6 @@ export const permissionsRouter = router({ return await permissionCheckService.getUserPermissions(ctx.user.id); }), - getNavigationPermissions: authedProcedure.query(async ({ ctx }) => { - if (!ctx.user?.id) { - return DEFAULT_PERMISSIONS; - } - - const teamIds = await MembershipRepository.findUserTeamIds({ userId: ctx.user.id }); - if (teamIds.length === 0) { - return DEFAULT_PERMISSIONS; - } - - const permissionService = new PermissionCheckService(); - const navigationItems = Object.keys(NAVIGATION_PERMISSION_MAP) as Array; - - const permissionChecks = await Promise.all( - navigationItems.map((itemName) => - permissionService.getTeamIdsWithPermission(ctx.user.id, NAVIGATION_PERMISSION_MAP[itemName]) - ) - ); - - return Object.fromEntries( - navigationItems.map((itemName, index) => [itemName, permissionChecks[index].length > 0]) - ) as Record; - }), - checkPermission: authedProcedure .input( z.object({