mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 15:58:18 +00:00
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { ShieldOff } from 'lucide-react';
|
|
import { useAuthStore } from '@/lib/auth-store';
|
|
|
|
interface Props {
|
|
/**
|
|
* A single key, or several of which the user needs **any one**. The any-of form
|
|
* is how a report page accepts either its own key or the `reports:view`
|
|
* umbrella — mirroring the OR semantics of the API's `PassengerPermissionGuard`.
|
|
*/
|
|
permission?: string | string[];
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
/**
|
|
* Wraps a page to enforce auth + optional permission check.
|
|
* - Not logged in → redirect to /login
|
|
* - Missing permission → render an explanation (see below)
|
|
*
|
|
* This used to redirect a user without the permission to /dashboard. That is a
|
|
* dead end for anyone lacking `dashboard:view`, since that page renders nothing
|
|
* either — they got a blank screen with no explanation. Saying what happened is
|
|
* both kinder and easier to support.
|
|
*/
|
|
export function PermissionGuard({ permission, children }: Props) {
|
|
const router = useRouter();
|
|
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
|
const hasPermission = useAuthStore((s) => s.hasPermission);
|
|
|
|
useEffect(() => {
|
|
if (!isAuthenticated) router.replace('/login');
|
|
}, [isAuthenticated, router]);
|
|
|
|
if (!isAuthenticated) return null;
|
|
|
|
const keys = permission === undefined ? [] : Array.isArray(permission) ? permission : [permission];
|
|
const allowed = keys.length === 0 || keys.some((key) => hasPermission(key));
|
|
|
|
if (!allowed) {
|
|
return (
|
|
<div className="flex min-h-[60vh] flex-col items-center justify-center gap-3 px-6 text-center">
|
|
<ShieldOff className="h-10 w-10 text-muted-foreground" />
|
|
<h2 className="text-lg font-semibold text-foreground">You don't have access to this page</h2>
|
|
<p className="max-w-md text-sm text-muted-foreground">
|
|
Your account is missing the permission this page requires. Ask an administrator to grant
|
|
it if you need access.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|