hotfix: ProtectedRoute allows access for system_admin + empty permissions + /kein-zugriff route + NoAccessPage
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Navigate } from 'react-router-dom';
|
import { Navigate } from 'react-router-dom';
|
||||||
import { usePermission } from '@/hooks/usePermission';
|
import { usePermission } from '@/hooks/usePermission';
|
||||||
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
permission: string;
|
permission: string;
|
||||||
@@ -9,6 +10,19 @@ interface ProtectedRouteProps {
|
|||||||
|
|
||||||
export function ProtectedRoute({ permission, children }: ProtectedRouteProps) {
|
export function ProtectedRoute({ permission, children }: ProtectedRouteProps) {
|
||||||
const { hasPermission } = usePermission();
|
const { hasPermission } = usePermission();
|
||||||
|
const user = useAuthStore((state) => state.user);
|
||||||
|
|
||||||
|
// If user is system admin, always allow
|
||||||
|
if (user?.is_system_admin) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If permissions array is empty or not loaded yet, allow access
|
||||||
|
// (better to show page and let backend 403 handle it than block everything)
|
||||||
|
const perms = user?.permissions || [];
|
||||||
|
if (perms.length === 0) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasPermission(permission)) {
|
if (!hasPermission(permission)) {
|
||||||
return <Navigate to="/kein-zugriff" replace />;
|
return <Navigate to="/kein-zugriff" replace />;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { ShieldX } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function NoAccessPage() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-screen bg-secondary-50 p-4">
|
||||||
|
<ShieldX className="w-16 h-16 text-secondary-400 mb-4" strokeWidth={1.5} />
|
||||||
|
<h1 className="text-2xl font-bold text-secondary-700 mb-2">Kein Zugriff</h1>
|
||||||
|
<p className="text-secondary-500 mb-6 text-center max-w-md">
|
||||||
|
Sie haben keine Berechtigung, auf diese Seite zuzugreifen.
|
||||||
|
Bitte wenden Sie sich an einen Administrator, falls Sie Zugriff benötigen.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
to="/dashboard"
|
||||||
|
className="px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 transition-colors"
|
||||||
|
>
|
||||||
|
Zum Dashboard
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -64,6 +64,7 @@ const ActivityTimelinePage = React.lazy(() => import('@/pages/ActivityTimeline')
|
|||||||
const SettingsWebhooksPage = React.lazy(() => import('@/pages/SettingsWebhooks').then(m => ({ default: m.SettingsWebhooksPage })));
|
const SettingsWebhooksPage = React.lazy(() => import('@/pages/SettingsWebhooks').then(m => ({ default: m.SettingsWebhooksPage })));
|
||||||
const SettingsBackupPage = React.lazy(() => import('@/pages/SettingsBackup').then(m => ({ default: m.SettingsBackupPage })));
|
const SettingsBackupPage = React.lazy(() => import('@/pages/SettingsBackup').then(m => ({ default: m.SettingsBackupPage })));
|
||||||
const SettingsRechtePage = React.lazy(() => import('@/pages/SettingsRechte').then(m => ({ default: m.SettingsRechtePage })));
|
const SettingsRechtePage = React.lazy(() => import('@/pages/SettingsRechte').then(m => ({ default: m.SettingsRechtePage })));
|
||||||
|
const NoAccessPage = React.lazy(() => import('@/pages/NoAccessPage').then(m => ({ default: m.NoAccessPage })));
|
||||||
|
|
||||||
/** Centered spinner fallback for lazy-loaded routes */
|
/** Centered spinner fallback for lazy-loaded routes */
|
||||||
function PageLoader() {
|
function PageLoader() {
|
||||||
@@ -120,6 +121,10 @@ const router = createBrowserRouter([
|
|||||||
path: '/guest/contacts',
|
path: '/guest/contacts',
|
||||||
element: <GuestContactsPage />,
|
element: <GuestContactsPage />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/kein-zugriff',
|
||||||
|
element: <ErrorBoundary>{withSuspense(<NoAccessPage />)}</ErrorBoundary>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
element: (
|
element: (
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
|
|||||||
Reference in New Issue
Block a user