270 lines
7.9 KiB
TypeScript
270 lines
7.9 KiB
TypeScript
/**
|
|
* 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<string | null>(null);
|
|
const [lastExport, setLastExport] = useState<RbacExport | null>(null);
|
|
const [lastImportResult, setLastImportResult] = useState<RbacImportResult | null>(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;
|