rights system

This commit is contained in:
Matthias Hochmeister
2026-03-23 10:50:52 +01:00
parent 2bb22850f4
commit 515f14956e
24 changed files with 629 additions and 363 deletions

View File

@@ -10,7 +10,7 @@ import BannerManagementTab from '../components/admin/BannerManagementTab';
import ServiceModeTab from '../components/admin/ServiceModeTab';
import FdiskSyncTab from '../components/admin/FdiskSyncTab';
import PermissionMatrixTab from '../components/admin/PermissionMatrixTab';
import { useAuth } from '../contexts/AuthContext';
import { usePermissionContext } from '../contexts/PermissionContext';
interface TabPanelProps {
children: React.ReactNode;
@@ -37,11 +37,9 @@ function AdminDashboard() {
const t = Number(searchParams.get('tab'));
if (t >= 0 && t < ADMIN_TAB_COUNT) setTab(t);
}, [searchParams]);
const { user } = useAuth();
const { hasPermission } = usePermissionContext();
const isAdmin = user?.groups?.includes('dashboard_admin') ?? false;
if (!isAdmin) {
if (!hasPermission('admin:view')) {
return <Navigate to="/dashboard" replace />;
}

View File

@@ -34,7 +34,7 @@ import { WidgetKey } from '../constants/widgets';
function Dashboard() {
const { user } = useAuth();
const { hasPermission, isAdmin } = usePermissionContext();
const { hasPermission } = usePermissionContext();
const [dataLoading, setDataLoading] = useState(true);
const { data: preferences } = useQuery({
@@ -99,7 +99,7 @@ function Dashboard() {
{/* Status Group */}
<WidgetGroup title="Status" gridColumn="1 / -1">
{widgetVisible('vehicles') && (
{hasPermission('fahrzeuge:widget') && widgetVisible('vehicles') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '300ms' }}>
<Box>
<VehicleDashboardCard />
@@ -107,7 +107,7 @@ function Dashboard() {
</Fade>
)}
{widgetVisible('equipment') && (
{hasPermission('ausruestung:widget') && widgetVisible('equipment') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '350ms' }}>
<Box>
<EquipmentDashboardCard />
@@ -123,7 +123,7 @@ function Dashboard() {
</Fade>
)}
{isAdmin && widgetVisible('adminStatus') && (
{hasPermission('admin:view') && widgetVisible('adminStatus') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '440ms' }}>
<Box>
<AdminStatusWidget />
@@ -134,7 +134,7 @@ function Dashboard() {
{/* Kalender Group */}
<WidgetGroup title="Kalender" gridColumn="1 / -1">
{widgetVisible('events') && (
{hasPermission('kalender:widget_events') && widgetVisible('events') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '480ms' }}>
<Box>
<UpcomingEventsWidget />
@@ -142,7 +142,7 @@ function Dashboard() {
</Fade>
)}
{widgetVisible('vehicleBookingList') && (
{hasPermission('kalender:widget_bookings') && widgetVisible('vehicleBookingList') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '520ms' }}>
<Box>
<VehicleBookingListWidget />
@@ -150,7 +150,7 @@ function Dashboard() {
</Fade>
)}
{widgetVisible('vehicleBooking') && (
{hasPermission('kalender:create_bookings') && widgetVisible('vehicleBooking') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '560ms' }}>
<Box>
<VehicleBookingQuickAddWidget />
@@ -169,7 +169,7 @@ function Dashboard() {
{/* Dienste Group */}
<WidgetGroup title="Dienste" gridColumn="1 / -1">
{widgetVisible('bookstackRecent') && (
{hasPermission('wissen:widget_recent') && widgetVisible('bookstackRecent') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '600ms' }}>
<Box>
<BookStackRecentWidget />
@@ -177,7 +177,7 @@ function Dashboard() {
</Fade>
)}
{widgetVisible('bookstackSearch') && (
{hasPermission('wissen:widget_search') && widgetVisible('bookstackSearch') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '640ms' }}>
<Box>
<BookStackSearchWidget />
@@ -185,7 +185,7 @@ function Dashboard() {
</Fade>
)}
{widgetVisible('vikunjaTasks') && (
{hasPermission('vikunja:widget_tasks') && widgetVisible('vikunjaTasks') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '680ms' }}>
<Box>
<VikunjaMyTasksWidget />
@@ -193,7 +193,7 @@ function Dashboard() {
</Fade>
)}
{widgetVisible('vikunjaQuickAdd') && (
{hasPermission('vikunja:widget_quick_add') && widgetVisible('vikunjaQuickAdd') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '720ms' }}>
<Box>
<VikunjaQuickAddWidget />
@@ -204,7 +204,7 @@ function Dashboard() {
{/* Information Group */}
<WidgetGroup title="Information" gridColumn="1 / -1">
{widgetVisible('links') && linkCollections.map((collection, idx) => (
{hasPermission('dashboard:widget_links') && widgetVisible('links') && linkCollections.map((collection, idx) => (
<Fade key={collection.id} in={!dataLoading} timeout={600} style={{ transitionDelay: `${760 + idx * 40}ms` }}>
<Box>
<LinksWidget collection={collection} />
@@ -212,11 +212,13 @@ function Dashboard() {
</Fade>
))}
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: `${760 + linkCollections.length * 40}ms` }}>
<Box>
<BannerWidget />
</Box>
</Fade>
{hasPermission('dashboard:widget_banner') && (
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: `${760 + linkCollections.length * 40}ms` }}>
<Box>
<BannerWidget />
</Box>
</Fade>
)}
</WidgetGroup>
</Box>
</Container>

View File

@@ -96,6 +96,7 @@ function FahrzeugBuchungen() {
const notification = useNotification();
const canCreate = hasPermission('kalender:create_bookings');
const canWrite = hasPermission('kalender:edit_bookings');
const canCancelOwn = hasPermission('kalender:cancel_own_bookings');
const canChangeBuchungsArt = hasPermission('kalender:manage_categories');
// ── Week navigation ────────────────────────────────────────────────────────
@@ -691,7 +692,7 @@ function FahrzeugBuchungen() {
Von: {detailBooking.gebucht_von_name}
</Typography>
)}
{(canWrite || detailBooking.gebucht_von === user?.id) && (
{(canWrite || (canCancelOwn && detailBooking.gebucht_von === user?.id)) && (
<Box sx={{ mt: 1.5, display: 'flex', gap: 1 }}>
{canWrite && (
<Button

View File

@@ -1707,7 +1707,7 @@ export default function Kalender() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const canWriteEvents = hasPermission('kalender:create_events');
const canWriteEvents = hasPermission('kalender:create');
const canWriteBookings = hasPermission('kalender:edit_bookings');
const canCreateBookings = hasPermission('kalender:create_bookings');

View File

@@ -80,7 +80,7 @@ function Mitglieder() {
// --- redirect non-privileged users to their own profile ---
useEffect(() => {
if (!user) return;
if (!hasPermission('mitglieder:edit')) {
if (!hasPermission('mitglieder:view_all')) {
navigate(`/mitglieder/${(user as any).id}`, { replace: true });
}
}, [user, navigate, hasPermission]);

View File

@@ -1074,7 +1074,7 @@ export default function Veranstaltungen() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const canWrite = hasPermission('kalender:create_events');
const canWrite = hasPermission('kalender:create');
const today = new Date();
const [viewMonth, setViewMonth] = useState({ year: today.getFullYear(), month: today.getMonth() });