/** * useRbacExportImport Hook * * Hook for exporting and importing RBAC configurations. * Supports mandate-level and global (template) exports. */ import { useState, useCallback } from 'react'; import api from '../api'; // ============================================================================= // TYPES // ============================================================================= export type ImportMode = 'merge' | 'replace' | 'add_only'; export interface RbacExportScope { type: 'global' | 'mandate' | 'instance'; mandateId?: string; mandateName?: string; featureInstanceId?: string; featureCode?: string; instanceLabel?: string; } export interface RbacExportRole { roleLabel: string; description?: string; featureCode?: string; } export interface RbacExportRule { roleLabel: string; context: 'DATA' | 'UI' | 'RESOURCE'; item: string | null; view: boolean; read?: string | null; create?: string | null; update?: string | null; delete?: string | null; } export interface RbacExport { version: string; exportedAt: string; exportedBy?: string; scope: RbacExportScope; roles: RbacExportRole[]; accessRules: RbacExportRule[]; } export interface RbacImportResult { status: 'success' | 'error'; mode: ImportMode; rolesCreated: number; rolesUpdated: number; rulesCreated: number; rulesUpdated: number; errors?: string[]; } // ============================================================================= // HOOK // ============================================================================= export function useRbacExportImport() { const [exporting, setExporting] = useState(false); const [importing, setImporting] = useState(false); const [error, setError] = useState(null); const [lastExport, setLastExport] = useState(null); const [lastImportResult, setLastImportResult] = useState(null); /** * Export RBAC configuration for a mandate */ const exportMandateRbac = useCallback(async ( mandateId: string, featureCode?: string ): Promise<{ success: boolean; data?: RbacExport; error?: string }> => { setExporting(true); setError(null); try { const params = new URLSearchParams(); if (featureCode) params.append('featureCode', featureCode); const url = `/api/mandates/${mandateId}/rbac/export${params.toString() ? '?' + params.toString() : ''}`; const response = await api.get(url); setLastExport(response.data); return { success: true, data: response.data }; } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Failed to export RBAC'; setError(errorMessage); return { success: false, error: errorMessage }; } finally { setExporting(false); } }, []); /** * Export global RBAC templates (SysAdmin only) */ const exportGlobalRbac = useCallback(async ( featureCode?: string ): Promise<{ success: boolean; data?: RbacExport; error?: string }> => { setExporting(true); setError(null); try { const params = new URLSearchParams(); if (featureCode) params.append('featureCode', featureCode); const url = `/api/admin/rbac/global/export${params.toString() ? '?' + params.toString() : ''}`; const response = await api.get(url); setLastExport(response.data); return { success: true, data: response.data }; } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Failed to export global RBAC'; setError(errorMessage); return { success: false, error: errorMessage }; } finally { setExporting(false); } }, []); /** * Export feature instance RBAC */ const exportInstanceRbac = useCallback(async ( instanceId: string ): Promise<{ success: boolean; data?: RbacExport; error?: string }> => { setExporting(true); setError(null); try { const response = await api.get(`/api/features/instances/${instanceId}/rbac/export`); setLastExport(response.data); return { success: true, data: response.data }; } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Failed to export instance RBAC'; setError(errorMessage); return { success: false, error: errorMessage }; } finally { setExporting(false); } }, []); /** * Import RBAC configuration into a mandate */ const importMandateRbac = useCallback(async ( mandateId: string, data: RbacExport, mode: ImportMode = 'merge' ): Promise<{ success: boolean; result?: RbacImportResult; error?: string }> => { setImporting(true); setError(null); try { const response = await api.post( `/api/mandates/${mandateId}/rbac/import?mode=${mode}`, data ); setLastImportResult(response.data); return { success: true, result: response.data }; } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Failed to import RBAC'; setError(errorMessage); return { success: false, error: errorMessage }; } finally { setImporting(false); } }, []); /** * Import global RBAC templates (SysAdmin only) */ const importGlobalRbac = useCallback(async ( data: RbacExport, mode: ImportMode = 'merge' ): Promise<{ success: boolean; result?: RbacImportResult; error?: string }> => { setImporting(true); setError(null); try { const response = await api.post( `/api/admin/rbac/global/import?mode=${mode}`, data ); setLastImportResult(response.data); return { success: true, result: response.data }; } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Failed to import global RBAC'; setError(errorMessage); return { success: false, error: errorMessage }; } finally { setImporting(false); } }, []); /** * Download export as JSON file */ const downloadExport = useCallback((data: RbacExport, filename?: string) => { const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename || `rbac-export-${data.scope.type}-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }, []); /** * Parse uploaded JSON file */ const parseImportFile = useCallback(async (file: File): Promise<{ success: boolean; data?: RbacExport; error?: string }> => { try { const text = await file.text(); const data = JSON.parse(text) as RbacExport; // Basic validation if (!data.version) { return { success: false, error: 'Ungültiges Format: Fehlende Version' }; } if (!data.scope) { return { success: false, error: 'Ungültiges Format: Fehlender Scope' }; } if (!Array.isArray(data.roles)) { return { success: false, error: 'Ungültiges Format: Roles muss ein Array sein' }; } if (!Array.isArray(data.accessRules)) { return { success: false, error: 'Ungültiges Format: AccessRules muss ein Array sein' }; } return { success: true, data }; } catch (err: any) { return { success: false, error: `Fehler beim Parsen: ${err.message}` }; } }, []); /** * Clear state */ const reset = useCallback(() => { setError(null); setLastExport(null); setLastImportResult(null); }, []); return { exporting, importing, error, lastExport, lastImportResult, exportMandateRbac, exportGlobalRbac, exportInstanceRbac, importMandateRbac, importGlobalRbac, downloadExport, parseImportFile, reset, }; } export default useRbacExportImport;