forked from baron/baron-sso
adminfront: TenantListPage에 세부 기능 권한(tenants / manage_tenants) 우회 및 제어 전격 적용하여 접근 제한 버그 해결
This commit is contained in:
@@ -377,6 +377,7 @@ function TenantListPage() {
|
||||
queryFn: fetchMe,
|
||||
});
|
||||
const profileRole = normalizeAdminRole(profile?.role);
|
||||
const isWritable = profileRole === "super_admin" || !!profile?.systemPermissions?.manage_tenants;
|
||||
|
||||
const query = useInfiniteQuery({
|
||||
queryKey: ["tenants", "lazy", debouncedSearch, scopeTenantId],
|
||||
@@ -581,7 +582,7 @@ function TenantListPage() {
|
||||
return () => window.removeEventListener("message", onMessage);
|
||||
}, [allTenants, scopePickerOpen]);
|
||||
|
||||
if (profile && profileRole !== "super_admin") {
|
||||
if (profile && profileRole !== "super_admin" && !profile?.systemPermissions?.tenants) {
|
||||
return (
|
||||
<div className="flex h-[50vh] flex-col items-center justify-center space-y-4">
|
||||
<h3 className="text-lg font-bold">
|
||||
@@ -840,81 +841,83 @@ function TenantListPage() {
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<RoleGuard roles={["super_admin"]}>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
name="tenant-import-file"
|
||||
type="file"
|
||||
accept=".csv,text/csv"
|
||||
className="hidden"
|
||||
data-testid="tenant-import-input"
|
||||
onChange={handleImportFile}
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
data-testid="tenant-data-mgmt-btn"
|
||||
className="gap-2 h-9"
|
||||
>
|
||||
<LayoutDashboard size={16} />
|
||||
{t("ui.admin.tenants.data_mgmt", "데이터 관리")}
|
||||
<ChevronDown size={14} className="opacity-50" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuItem
|
||||
onClick={handleTemplateDownload}
|
||||
data-testid="tenant-template-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<FileSpreadsheet
|
||||
size={16}
|
||||
className="mr-2 opacity-50"
|
||||
/>
|
||||
{t(
|
||||
"ui.admin.tenants.csv_template",
|
||||
"템플릿 다운로드",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={importMutation.isPending}
|
||||
data-testid="tenant-import-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Upload size={16} className="mr-2 opacity-50" />
|
||||
{t("ui.admin.tenants.import", "CSV 가져오기")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => exportMutation.mutate(false)}
|
||||
disabled={exportMutation.isPending}
|
||||
data-testid="tenant-export-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Download size={16} className="mr-2 opacity-50" />
|
||||
{t(
|
||||
"ui.admin.tenants.export_without_ids",
|
||||
"UUID 제외 내보내기",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => exportMutation.mutate(true)}
|
||||
disabled={exportMutation.isPending}
|
||||
data-testid="tenant-export-with-ids-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Download size={16} className="mr-2 opacity-50" />
|
||||
{t(
|
||||
"ui.admin.tenants.export_with_ids",
|
||||
"UUID 포함 내보내기",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</RoleGuard>
|
||||
{isWritable && (
|
||||
<>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
name="tenant-import-file"
|
||||
type="file"
|
||||
accept=".csv,text/csv"
|
||||
className="hidden"
|
||||
data-testid="tenant-import-input"
|
||||
onChange={handleImportFile}
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
data-testid="tenant-data-mgmt-btn"
|
||||
className="gap-2 h-9"
|
||||
>
|
||||
<LayoutDashboard size={16} />
|
||||
{t("ui.admin.tenants.data_mgmt", "데이터 관리")}
|
||||
<ChevronDown size={14} className="opacity-50" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuItem
|
||||
onClick={handleTemplateDownload}
|
||||
data-testid="tenant-template-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<FileSpreadsheet
|
||||
size={16}
|
||||
className="mr-2 opacity-50"
|
||||
/>
|
||||
{t(
|
||||
"ui.admin.tenants.csv_template",
|
||||
"템플릿 다운로드",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={importMutation.isPending}
|
||||
data-testid="tenant-import-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Upload size={16} className="mr-2 opacity-50" />
|
||||
{t("ui.admin.tenants.import", "CSV 가져오기")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => exportMutation.mutate(false)}
|
||||
disabled={exportMutation.isPending}
|
||||
data-testid="tenant-export-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Download size={16} className="mr-2 opacity-50" />
|
||||
{t(
|
||||
"ui.admin.tenants.export_without_ids",
|
||||
"UUID 제외 내보내기",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => exportMutation.mutate(true)}
|
||||
disabled={exportMutation.isPending}
|
||||
data-testid="tenant-export-with-ids-menu-item"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Download size={16} className="mr-2 opacity-50" />
|
||||
{t(
|
||||
"ui.admin.tenants.export_with_ids",
|
||||
"UUID 포함 내보내기",
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -928,14 +931,14 @@ function TenantListPage() {
|
||||
{t("ui.common.refresh", "새로고침")}
|
||||
</span>
|
||||
</Button>
|
||||
<RoleGuard roles={["super_admin"]}>
|
||||
{isWritable && (
|
||||
<Button asChild size="sm" className="h-9">
|
||||
<Link to="/tenants/new">
|
||||
<Plus size={16} />
|
||||
{t("ui.admin.tenants.add", "테넌트 추가")}
|
||||
</Link>
|
||||
</Button>
|
||||
</RoleGuard>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
@@ -1071,7 +1074,7 @@ function TenantListPage() {
|
||||
{t("ui.common.apply", "적용")}
|
||||
</Button>
|
||||
<div className="w-px h-4 bg-background/20 mx-1" />
|
||||
<RoleGuard roles={["super_admin"]}>
|
||||
{isWritable && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -1083,7 +1086,7 @@ function TenantListPage() {
|
||||
<Trash2 size={14} />
|
||||
{t("ui.common.delete", "삭제")}
|
||||
</Button>
|
||||
</RoleGuard>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
Reference in New Issue
Block a user