Files
dashboard/frontend/src/contexts/PermissionContext.tsx
Matthias Hochmeister 2bb22850f4 rights system
2026-03-23 10:07:53 +01:00

99 lines
2.8 KiB
TypeScript

import React, { createContext, useContext, useMemo, useCallback, ReactNode } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useAuth } from './AuthContext';
import { permissionsApi } from '../services/permissions';
interface PermissionContextType {
permissions: Set<string>;
maintenance: Record<string, boolean>;
isAdmin: boolean;
isLoading: boolean;
hasPermission: (perm: string) => boolean;
hasAnyPermission: (...perms: string[]) => boolean;
isFeatureEnabled: (featureGroup: string) => boolean;
refetch: () => void;
}
const PermissionContext = createContext<PermissionContextType | undefined>(undefined);
interface PermissionProviderProps {
children: ReactNode;
}
export const PermissionProvider: React.FC<PermissionProviderProps> = ({ children }) => {
const { isAuthenticated } = useAuth();
const queryClient = useQueryClient();
const { data, isLoading } = useQuery({
queryKey: ['my-permissions'],
queryFn: permissionsApi.getMyPermissions,
enabled: isAuthenticated,
staleTime: 5 * 60 * 1000,
});
const permissions = useMemo(
() => new Set(data?.permissions ?? []),
[data?.permissions]
);
const maintenance = data?.maintenance ?? {};
const isAdmin = data?.isAdmin ?? false;
const isFeatureEnabled = useCallback(
(featureGroup: string): boolean => {
if (isAdmin) return true;
return !maintenance[featureGroup];
},
[isAdmin, maintenance]
);
const hasPermission = useCallback(
(perm: string): boolean => {
if (isAdmin) return true;
const featureGroup = perm.split(':')[0];
if (!isFeatureEnabled(featureGroup)) return false;
return permissions.has(perm);
},
[isAdmin, permissions, isFeatureEnabled]
);
const hasAnyPermission = useCallback(
(...perms: string[]): boolean => {
return perms.some(p => hasPermission(p));
},
[hasPermission]
);
const refetch = useCallback(() => {
queryClient.invalidateQueries({ queryKey: ['my-permissions'] });
}, [queryClient]);
const value = useMemo(
(): PermissionContextType => ({
permissions,
maintenance,
isAdmin,
isLoading: isAuthenticated ? isLoading : false,
hasPermission,
hasAnyPermission,
isFeatureEnabled,
refetch,
}),
[permissions, maintenance, isAdmin, isAuthenticated, isLoading, hasPermission, hasAnyPermission, isFeatureEnabled, refetch]
);
return (
<PermissionContext.Provider value={value}>
{children}
</PermissionContext.Provider>
);
};
export const usePermissionContext = (): PermissionContextType => {
const context = useContext(PermissionContext);
if (context === undefined) {
throw new Error('usePermissionContext must be used within a PermissionProvider');
}
return context;
};