feat(rbac): Viewer-Rolle auf alle Firewall-CRUD-Seiten durchsetzen — v1.1.91

AddressObjects, AddressGroups, Services, ServiceGroups, Rules, NATRules, Zones:
Add/Edit-Buttons disabled + Tooltip, Delete bedingt gerendert (viewer vs. Popconfirm).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-24 13:20:54 +02:00
parent 8b0e8a8ab4
commit d4267e2003
11 changed files with 215 additions and 111 deletions

View File

@@ -11,6 +11,7 @@ import DataTable from '../../components/DataTable'
import EmptyState from '../../components/EmptyState'
import apiClient, { isEnvelope } from '../../api/client'
import { useAuthStore } from '../../stores/auth'
import type { AddressGroup, AddressObject, FwRule, FwService, FwZone, ServiceGroup, Zone } from './types'
import { ZONES_FALLBACK } from './types'
@@ -112,6 +113,7 @@ function buildPayload(v: FormValues) {
export default function RulesTab() {
const { t } = useTranslation()
const qc = useQueryClient()
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
const { data: rules, isLoading } = useQuery({ queryKey: ['fw', 'rules'], queryFn: listRules })
const { data: aos } = useQuery({ queryKey: ['fw', 'addr-obj'], queryFn: listAO })
const { data: ags } = useQuery({ queryKey: ['fw', 'addr-grp'], queryFn: listAG })
@@ -225,6 +227,7 @@ export default function RulesTab() {
<Switch
size="small"
checked={v}
disabled={isViewer}
loading={quickToggle.isPending && quickToggle.variables?.id === row.id}
onChange={(checked) => quickToggle.mutate({ id: row.id, row, checked })}
/>
@@ -247,10 +250,18 @@ export default function RulesTab() {
title: t('common.edit'), key: 'actions',
render: (_, row) => (
<Space>
<Button size="small" onClick={() => editFromRow(row)}>{t('common.edit')}</Button>
<Popconfirm title={t('fw.rule.deleteConfirm')} onConfirm={() => del.mutate(row.id)}>
<Button size="small" danger>{t('common.delete')}</Button>
</Popconfirm>
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
<Button size="small" disabled={isViewer} onClick={() => editFromRow(row)}>{t('common.edit')}</Button>
</Tooltip>
{isViewer ? (
<Tooltip title={t('auth.viewerBadge')}>
<Button size="small" danger disabled>{t('common.delete')}</Button>
</Tooltip>
) : (
<Popconfirm title={t('fw.rule.deleteConfirm')} onConfirm={() => del.mutate(row.id)}>
<Button size="small" danger>{t('common.delete')}</Button>
</Popconfirm>
)}
</Space>
),
},
@@ -267,9 +278,11 @@ export default function RulesTab() {
return (
<>
<Button type="primary" className="mb-16" onClick={openCreate}>
{t('fw.rule.add')}
</Button>
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
<Button type="primary" className="mb-16" disabled={isViewer} onClick={openCreate}>
{t('fw.rule.add')}
</Button>
</Tooltip>
<DataTable
rowKey="id"
loading={isLoading}
@@ -280,7 +293,11 @@ export default function RulesTab() {
icon={<FireOutlined />}
title={t('fw.rule.emptyTitle')}
description={t('fw.rule.emptyDesc')}
action={<Button type="primary" onClick={openCreate}>{t('fw.rule.add')}</Button>}
action={
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
<Button type="primary" disabled={isViewer} onClick={openCreate}>{t('fw.rule.add')}</Button>
</Tooltip>
}
/>
}
/>