From 24e793464644f9f831917e35f2265395a6855aa5 Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Thu, 31 Jul 2025 12:04:06 +0100 Subject: [PATCH] dont show os updates page for users who are not global admin or the team admin (#31410) fixes #25367 this doesnt show the os updates page for users who are not global admins or the current team admin. we also redirect users to the os settings page if they try to navigate to the os updates page and dont have permission - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [x] QA'd all new/changed functionality manually --- .../issue-25367-os-updates-page-permissions | 1 + .../ManageControlsPage/ManageControlsPage.tsx | 38 +++++++++++++++---- .../OSUpdates/OSUpdates.tsx | 17 +++++++-- 3 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 changes/issue-25367-os-updates-page-permissions diff --git a/changes/issue-25367-os-updates-page-permissions b/changes/issue-25367-os-updates-page-permissions new file mode 100644 index 0000000000..a4bc53bb30 --- /dev/null +++ b/changes/issue-25367-os-updates-page-permissions @@ -0,0 +1 @@ +- add permissions to os updates page so that only global admins and the team admin can see the page diff --git a/frontend/pages/ManageControlsPage/ManageControlsPage.tsx b/frontend/pages/ManageControlsPage/ManageControlsPage.tsx index 0afe0170e6..4ca17050d4 100644 --- a/frontend/pages/ManageControlsPage/ManageControlsPage.tsx +++ b/frontend/pages/ManageControlsPage/ManageControlsPage.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext } from "react"; +import React, { useCallback, useContext, useMemo } from "react"; import { Tab, Tabs, TabList } from "react-tabs"; import { InjectedRouter } from "react-router"; @@ -54,8 +54,11 @@ interface IManageControlsPageProps { router: InjectedRouter; // v3 } -const getTabIndex = (path: string): number => { - return controlsSubNav.findIndex((navItem) => { +const getTabIndex = ( + permittedControlsSubNav: IControlsSubNavItem[], + path: string +): number => { + return permittedControlsSubNav.findIndex((navItem) => { // tab stays highlighted for paths that start with same pathname return path.startsWith(navItem.pathname); }); @@ -73,7 +76,13 @@ const ManageControlsPage = ({ }: IManageControlsPageProps): JSX.Element => { const page = parseInt(location?.query?.page || "", 10) || 0; - const { config, isOnGlobalTeam, isPremiumTier } = useContext(AppContext); + const { + config, + isOnGlobalTeam, + isPremiumTier, + isGlobalAdmin, + isTeamAdmin, + } = useContext(AppContext); const { currentTeamId, @@ -93,9 +102,19 @@ const ManageControlsPage = ({ }, }); + const permittedControlsSubNav = useMemo(() => { + let renderedSubNav = controlsSubNav; + if (!isGlobalAdmin && !isTeamAdmin) { + renderedSubNav = controlsSubNav.filter((navItem) => { + return navItem.name !== "OS updates"; + }); + } + return renderedSubNav; + }, [isGlobalAdmin, isTeamAdmin]); + const navigateToNav = useCallback( (i: number): void => { - const navPath = controlsSubNav[i].pathname; + const navPath = permittedControlsSubNav[i].pathname; // remove query params related to the prior tab const newParams = new URLSearchParams(location?.search); subNavQueryParams.forEach((p) => newParams.delete(p)); @@ -107,7 +126,7 @@ const ManageControlsPage = ({ .concat(location?.hash || "") ); }, - [location, router] + [location, router, permittedControlsSubNav] ); const renderBody = () => { @@ -115,11 +134,14 @@ const ManageControlsPage = ({
- {controlsSubNav.map((navItem) => { + {permittedControlsSubNav.map((navItem) => { return ( {navItem.name} diff --git a/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx b/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx index 2306b79739..25f7790e70 100644 --- a/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx +++ b/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx @@ -3,6 +3,7 @@ import { InjectedRouter } from "react-router"; import { useQuery } from "react-query"; import { AppContext } from "context/app"; +import PATHS from "router/paths"; import { IConfig } from "interfaces/config"; import { ITeamConfig } from "interfaces/team"; @@ -46,7 +47,13 @@ interface IOSUpdates { } const OSUpdates = ({ router, teamIdForApi, queryParams }: IOSUpdates) => { - const { isPremiumTier, config, setConfig } = useContext(AppContext); + const { + isPremiumTier, + isGlobalAdmin, + isTeamAdmin, + config, + setConfig, + } = useContext(AppContext); const [ selectedPlatformTab, @@ -54,7 +61,6 @@ const OSUpdates = ({ router, teamIdForApi, queryParams }: IOSUpdates) => { ] = useState(null); const { - isError: isErrorConfig, isFetching: isFetchingConfig, isLoading: isLoadingConfig, refetch: refetchAppConfig, @@ -66,7 +72,6 @@ const OSUpdates = ({ router, teamIdForApi, queryParams }: IOSUpdates) => { const { data: teamConfig, - isError: isErrorTeamConfig, isFetching: isFetchingTeamConfig, isLoading: isLoadingTeam, refetch: refetchTeamConfig, @@ -75,11 +80,15 @@ const OSUpdates = ({ router, teamIdForApi, queryParams }: IOSUpdates) => { () => teamsAPI.load(teamIdForApi), { refetchOnWindowFocus: false, - enabled: !!teamIdForApi, + enabled: !!teamIdForApi && (isGlobalAdmin || isTeamAdmin), select: (data) => data.team, } ); + if (!isGlobalAdmin && !isTeamAdmin) { + router.replace(PATHS.CONTROLS_OS_SETTINGS); + } + // Not premium shows premium message if (!isPremiumTier) { return (