From 870871ebee8699b4cd65d8f961e06603cddc3ab9 Mon Sep 17 00:00:00 2001 From: Cannon Lock Date: Wed, 4 Dec 2024 12:42:03 -0600 Subject: [PATCH] Redirect Director Metrics if Not Admin - Redirect director metrics if not admin - Hide navigation options if not admin - Update AuthenicatedContent to consume static lists to enable easier SSR --- web_ui/frontend/app/director/layout.tsx | 19 +- web_ui/frontend/app/director/metrics/page.tsx | 167 +++++++++--------- .../app/origin/globus/callback/page.tsx | 2 +- web_ui/frontend/app/origin/globus/page.tsx | 5 +- web_ui/frontend/app/origin/page.tsx | 5 +- web_ui/frontend/app/registry/layout.tsx | 9 +- .../layout/AuthenticatedContent.tsx | 24 ++- 7 files changed, 126 insertions(+), 105 deletions(-) diff --git a/web_ui/frontend/app/director/layout.tsx b/web_ui/frontend/app/director/layout.tsx index 5d9325d30..55e01cd65 100644 --- a/web_ui/frontend/app/director/layout.tsx +++ b/web_ui/frontend/app/director/layout.tsx @@ -20,7 +20,8 @@ import { Box } from '@mui/material'; import { ButtonLink, Sidebar } from '@/components/layout/Sidebar'; import BuildIcon from '@mui/icons-material/Build'; import Main from '@/components/layout/Main'; -import { Dashboard, Equalizer, MapOutlined } from '@mui/icons-material'; +import { Block, Dashboard, Equalizer, MapOutlined } from '@mui/icons-material'; +import AuthenticatedContent from '@/components/layout/AuthenticatedContent'; export const metadata = { title: 'Pelican Director', @@ -38,15 +39,19 @@ export default function RootLayout({ - - - - - - + + + + + + + + + +
{children}
diff --git a/web_ui/frontend/app/director/metrics/page.tsx b/web_ui/frontend/app/director/metrics/page.tsx index 2d47410f7..52590a59a 100644 --- a/web_ui/frontend/app/director/metrics/page.tsx +++ b/web_ui/frontend/app/director/metrics/page.tsx @@ -9,96 +9,103 @@ import { } from '@/app/director/metrics/components/MetricBoxPlot'; import { StorageTable } from '@/app/director/metrics/components/StorageTable'; import { TransferBarGraph } from '@/app/director/metrics/components/TransferBarGraph'; +import AuthenticatedContent from '@/components/layout/AuthenticatedContent'; const Page = () => { return ( - - - - {[ - , - , - ].map((component, index) => ( - - {component} - - ))} - - - - - - - - - - - - - - - + + + + + {[ + , + , + ].map((component, index) => ( + + {component} - - - - - - - - - - - - - - - - + ))} - - - {[ - , - , - ].map((component, index) => ( - - {component} + + + + + + + + + + + + + + + + + + + + + + + + - ))} + + + + + + + + + + {[ + , + , + ].map((component, index) => ( + + {component} + + ))} + - + ); }; diff --git a/web_ui/frontend/app/origin/globus/callback/page.tsx b/web_ui/frontend/app/origin/globus/callback/page.tsx index 2bcad0b8c..19b5c2ea0 100644 --- a/web_ui/frontend/app/origin/globus/callback/page.tsx +++ b/web_ui/frontend/app/origin/globus/callback/page.tsx @@ -96,7 +96,7 @@ export default function Home() { u?.role == 'admin'} + allowedRoles={['admin']} > u?.role == 'admin'} - > + Globus Exports diff --git a/web_ui/frontend/app/origin/page.tsx b/web_ui/frontend/app/origin/page.tsx index 98e5fbcd6..bcc00c005 100644 --- a/web_ui/frontend/app/origin/page.tsx +++ b/web_ui/frontend/app/origin/page.tsx @@ -51,10 +51,7 @@ export default function Home() { }; return ( - u?.role == 'admin'} - > + diff --git a/web_ui/frontend/app/registry/layout.tsx b/web_ui/frontend/app/registry/layout.tsx index 36e2b69cd..2c0febf4b 100644 --- a/web_ui/frontend/app/registry/layout.tsx +++ b/web_ui/frontend/app/registry/layout.tsx @@ -35,6 +35,7 @@ import SpeedDial, { } from '@/components/layout/SidebarSpeedDial'; import AuthenticatedContent from '@/components/layout/AuthenticatedContent'; import { PaddedContent } from '@/components/layout'; +import BuildIcon from '@mui/icons-material/Build'; export const metadata = { title: 'Pelican Registry', @@ -81,9 +82,11 @@ export default function RootLayout({ - - - + + + + +
{children} diff --git a/web_ui/frontend/components/layout/AuthenticatedContent.tsx b/web_ui/frontend/components/layout/AuthenticatedContent.tsx index f1382b828..0da52c668 100644 --- a/web_ui/frontend/components/layout/AuthenticatedContent.tsx +++ b/web_ui/frontend/components/layout/AuthenticatedContent.tsx @@ -41,18 +41,30 @@ interface AuthenticatedContentProps { promptLogin?: boolean; redirect?: boolean; trustThenValidate?: boolean; - children: React.ReactNode; boxProps?: BoxProps; - checkAuthentication?: (user: User) => boolean; + allowedRoles?: User['role'][]; + replace?: boolean; + children: React.ReactNode; } +/** + * AuthenticatedContent is a component that will show the children if the user is authenticated. + * @param promptLogin If true then the user will be prompted to login if they are not authenticated + * @param redirect If true then the user will be redirected to the login page if they are not authenticated + * @param trustThenValidate If true then the user will be shown the content if they are not authenticated but will be validated after + * @param boxProps The props to pass to the Box component + * @param allowedRoles The roles that are allowed to see the content + * @param replace If true then the + * @param children The content to show if the user is authenticated + * @constructor + */ const AuthenticatedContent = ({ promptLogin = false, redirect = false, trustThenValidate = false, children, boxProps, - checkAuthentication, + allowedRoles, }: AuthenticatedContentProps) => { if (redirect && promptLogin) { throw new Error('redirect XOR promptLogin must be true'); @@ -66,12 +78,12 @@ const AuthenticatedContent = ({ const [pageUrl, setPageUrl] = useState(''); const authenticated = useMemo(() => { - if (data && checkAuthentication) { - return checkAuthentication(data); + if (data && allowedRoles) { + return data?.role && allowedRoles.includes(data?.role); } else { return !!data?.authenticated; } - }, [data, checkAuthentication]); + }, [data, allowedRoles]); useEffect(() => { // Keep pathname as is since backend handles the redirect after logging in and needs the full path