Revert "feat: implement generalized navigation permission system with PBAC (#23706)" (#23867)

This reverts commit 34b62f7516.
This commit is contained in:
Eunjae Lee
2025-09-16 11:17:08 +01:00
committed by GitHub
parent 5feca13cfb
commit 7fe80dd3dd
8 changed files with 4 additions and 170 deletions
+2 -4
View File
@@ -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 (
<NavigationPermissionsProvider>
<>
<PageWrapper requiresLicense={false} nonce={nonce}>
{children}
{scripts.map((script) => (
@@ -38,6 +36,6 @@ export default async function PageWrapperLayout({ children }: { children: React.
/>
))}
</PageWrapper>
</NavigationPermissionsProvider>
</>
);
}
@@ -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";
@@ -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;
}
@@ -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 (
<NavigationPermissionsContext.Provider value={contextValue}>
{children}
</NavigationPermissionsContext.Provider>
);
}
@@ -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<NavigationItemName, boolean>;
+1 -1
View File
@@ -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";
-32
View File
@@ -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;
}
},
};
@@ -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<NavigationItemName>;
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<NavigationItemName, boolean>;
}),
checkPermission: authedProcedure
.input(
z.object({