feat(rbac): Viewer-Rolle durchsetzen — alle Mutations nur für Admins

- middleware.go: RequireAdminForMutations() blockiert POST/PUT/DELETE
  für Nicht-Admins (GET/HEAD passieren immer durch)
- main.go: Middleware in den authed-Gruppe eingehängt — wirkt für
  alle ~30 Resource-Handler gleichzeitig
- api/client.ts: 403 → AntD-Notification "Nur Lesezugriff" statt
  stiller Fehler
- Header: "Nur lesen"-Badge + Tooltip wenn role=viewer
- i18n de+en: viewerBadge + viewerHint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-23 16:57:17 +02:00
parent 4e01b4569c
commit 386972366b
7 changed files with 66 additions and 23 deletions

View File

@@ -1,5 +1,5 @@
import { Button, Dropdown, Select, Space } from 'antd'
import { GlobalOutlined, LogoutOutlined, MenuOutlined, UserOutlined } from '@ant-design/icons'
import { Button, Dropdown, Select, Space, Tag, Tooltip } from 'antd'
import { EyeOutlined, GlobalOutlined, LogoutOutlined, MenuOutlined, UserOutlined } from '@ant-design/icons'
import { useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
@@ -55,23 +55,32 @@ export default function Header({ pageTitle, onMenuToggle }: HeaderProps) {
popupMatchSelectWidth={false}
/>
{user && (
<Dropdown
menu={{
items: [
{
key: 'logout',
icon: <LogoutOutlined />,
label: t('auth.logout'),
onClick: onLogout,
},
],
}}
placement="bottomRight"
>
<Button type="text" className="header-user">
<Space><UserOutlined />{user.actor}</Space>
</Button>
</Dropdown>
<>
{user.role === 'viewer' && (
<Tooltip title={t('auth.viewerHint')}>
<Tag icon={<EyeOutlined />} color="default" style={{ marginRight: 4 }}>
{t('auth.viewerBadge')}
</Tag>
</Tooltip>
)}
<Dropdown
menu={{
items: [
{
key: 'logout',
icon: <LogoutOutlined />,
label: t('auth.logout'),
onClick: onLogout,
},
],
}}
placement="bottomRight"
>
<Button type="text" className="header-user">
<Space><UserOutlined />{user.actor}</Space>
</Button>
</Dropdown>
</>
)}
</div>
</header>