diff --git a/VERSION b/VERSION index 69658cd..069199b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.90 +1.1.91 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index eb689bc..cffe9d5 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.90" +var version = "1.1.91" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index bfb96bb..a22ca0e 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -11,7 +11,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) -var version = "1.1.90" +var version = "1.1.91" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index f9e0695..f731b5c 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -35,7 +35,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.1.90" +var version = "1.1.91" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/management-ui/src/pages/Firewall/AddressGroups.tsx b/management-ui/src/pages/Firewall/AddressGroups.tsx index eb2e93a..0148d38 100644 --- a/management-ui/src/pages/Firewall/AddressGroups.tsx +++ b/management-ui/src/pages/Firewall/AddressGroups.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, message } from 'antd' +import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, Tooltip, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' @@ -9,6 +9,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 } from './types' interface FormValues { @@ -31,6 +32,7 @@ async function listObjects(): Promise { export default function AddressGroupsTab() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const { data: groups, isLoading } = useQuery({ queryKey: ['fw', 'addr-grp'], queryFn: listGroups }) const { data: objects } = useQuery({ queryKey: ['fw', 'addr-obj'], queryFn: listObjects }) @@ -75,13 +77,21 @@ export default function AddressGroupsTab() { title: t('common.edit'), key: 'actions', render: (_, row) => ( - - del.mutate(row.id)}> - - + + + + {isViewer ? ( + + + + ) : ( + del.mutate(row.id)}> + + + )} ), }, @@ -94,9 +104,11 @@ export default function AddressGroupsTab() { return ( <> - + + + } title={t('fw.ag.emptyTitle')} description={t('fw.ag.emptyDesc')} - action={} + action={ + + + + } /> } /> diff --git a/management-ui/src/pages/Firewall/AddressObjects.tsx b/management-ui/src/pages/Firewall/AddressObjects.tsx index 8f3bab2..a825581 100644 --- a/management-ui/src/pages/Firewall/AddressObjects.tsx +++ b/management-ui/src/pages/Firewall/AddressObjects.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, message } from 'antd' +import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, Tooltip, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' @@ -9,6 +9,7 @@ import DataTable from '../../components/DataTable' import EmptyState from '../../components/EmptyState' import apiClient, { isEnvelope } from '../../api/client' +import { useAuthStore } from '../../stores/auth' import type { AddressObject } from './types' interface FormValues { @@ -34,6 +35,7 @@ const KIND_PLACEHOLDER: Record = { export default function AddressObjectsTab() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const { data, isLoading } = useQuery({ queryKey: ['fw', 'addr-obj'], queryFn: listAO }) const [editing, setEditing] = useState(null) @@ -68,13 +70,21 @@ export default function AddressObjectsTab() { title: t('common.edit'), key: 'actions', render: (_, row) => ( - - del.mutate(row.id)}> - - + + + + {isViewer ? ( + + + + ) : ( + del.mutate(row.id)}> + + + )} ), }, @@ -87,9 +97,11 @@ export default function AddressObjectsTab() { return ( <> - + + + {t('fw.ao.add')} + + + } /> } diff --git a/management-ui/src/pages/Firewall/NATRules.tsx b/management-ui/src/pages/Firewall/NATRules.tsx index f1aac5b..1b674a9 100644 --- a/management-ui/src/pages/Firewall/NATRules.tsx +++ b/management-ui/src/pages/Firewall/NATRules.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Switch, Tag, message } from 'antd' +import { Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Switch, Tag, Tooltip, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' @@ -9,6 +9,7 @@ import DataTable from '../../components/DataTable' import EmptyState from '../../components/EmptyState' import apiClient, { isEnvelope } from '../../api/client' +import { useAuthStore } from '../../stores/auth' import type { FwZone, NATRule } from './types' interface FormValues { @@ -49,6 +50,7 @@ const KIND_COLORS: Record = { export default function NATRulesTab() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const { data, isLoading } = useQuery({ queryKey: ['fw', 'nat'], queryFn: listNAT }) const { data: zones } = useQuery({ queryKey: ['fw', 'zones'], queryFn: listZones }) @@ -121,6 +123,7 @@ export default function NATRulesTab() { quickToggle.mutate({ id: row.id, row, checked })} /> @@ -130,26 +133,34 @@ export default function NATRulesTab() { title: t('common.edit'), key: 'actions', render: (_, row) => ( - - del.mutate(row.id)}> - - + + + + {isViewer ? ( + + + + ) : ( + del.mutate(row.id)}> + + + )} ), }, @@ -162,9 +173,11 @@ export default function NATRulesTab() { return ( <> - + + + } title={t('fw.nat.emptyTitle')} description={t('fw.nat.emptyDesc')} - action={} + action={ + + + + } /> } /> diff --git a/management-ui/src/pages/Firewall/Rules.tsx b/management-ui/src/pages/Firewall/Rules.tsx index f95b9f8..9faf83e 100644 --- a/management-ui/src/pages/Firewall/Rules.tsx +++ b/management-ui/src/pages/Firewall/Rules.tsx @@ -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() { quickToggle.mutate({ id: row.id, row, checked })} /> @@ -247,10 +250,18 @@ export default function RulesTab() { title: t('common.edit'), key: 'actions', render: (_, row) => ( - - del.mutate(row.id)}> - - + + + + {isViewer ? ( + + + + ) : ( + del.mutate(row.id)}> + + + )} ), }, @@ -267,9 +278,11 @@ export default function RulesTab() { return ( <> - + + + } title={t('fw.rule.emptyTitle')} description={t('fw.rule.emptyDesc')} - action={} + action={ + + + + } /> } /> diff --git a/management-ui/src/pages/Firewall/ServiceGroups.tsx b/management-ui/src/pages/Firewall/ServiceGroups.tsx index 5ecc3b0..1dd0dfa 100644 --- a/management-ui/src/pages/Firewall/ServiceGroups.tsx +++ b/management-ui/src/pages/Firewall/ServiceGroups.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, message } from 'antd' +import { Button, Form, Input, Modal, Popconfirm, Select, Space, Tag, Tooltip, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' @@ -9,6 +9,7 @@ import DataTable from '../../components/DataTable' import EmptyState from '../../components/EmptyState' import apiClient, { isEnvelope } from '../../api/client' +import { useAuthStore } from '../../stores/auth' import type { FwService, ServiceGroup } from './types' interface FormValues { @@ -31,6 +32,7 @@ async function listServices(): Promise { export default function ServiceGroupsTab() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const { data: groups, isLoading } = useQuery({ queryKey: ['fw', 'svc-grp'], queryFn: listGroups }) const { data: services } = useQuery({ queryKey: ['fw', 'svc'], queryFn: listServices }) @@ -75,13 +77,21 @@ export default function ServiceGroupsTab() { title: t('common.edit'), key: 'actions', render: (_, row) => ( - - del.mutate(row.id)}> - - + + + + {isViewer ? ( + + + + ) : ( + del.mutate(row.id)}> + + + )} ), }, @@ -94,9 +104,11 @@ export default function ServiceGroupsTab() { return ( <> - + + + } title={t('fw.sg.emptyTitle')} description={t('fw.sg.emptyDesc')} - action={} + action={ + + + + } /> } /> diff --git a/management-ui/src/pages/Firewall/Services.tsx b/management-ui/src/pages/Firewall/Services.tsx index a1f21dd..4e7f5a2 100644 --- a/management-ui/src/pages/Firewall/Services.tsx +++ b/management-ui/src/pages/Firewall/Services.tsx @@ -9,6 +9,7 @@ import DataTable from '../../components/DataTable' import EmptyState from '../../components/EmptyState' import apiClient, { isEnvelope } from '../../api/client' +import { useAuthStore } from '../../stores/auth' import type { FwService } from './types' interface FormValues { @@ -28,6 +29,7 @@ async function listServices(): Promise { export default function ServicesTab() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const { data, isLoading } = useQuery({ queryKey: ['fw', 'svc'], queryFn: listServices }) const [editing, setEditing] = useState(null) @@ -70,22 +72,30 @@ export default function ServicesTab() { title: t('common.edit'), key: 'actions', render: (_, row) => ( - - del.mutate(row.id)} - disabled={row.builtin} - > - - + + + + {isViewer ? ( + + + + ) : ( + del.mutate(row.id)} + disabled={row.builtin} + > + + + )} ), }, @@ -98,9 +108,11 @@ export default function ServicesTab() { return ( <> - + + + } title={t('fw.svc.emptyTitle')} description={t('fw.svc.emptyDesc')} - action={} + action={ + + + + } /> } /> diff --git a/management-ui/src/pages/Firewall/Zones.tsx b/management-ui/src/pages/Firewall/Zones.tsx index 73b7379..a6e7bb3 100644 --- a/management-ui/src/pages/Firewall/Zones.tsx +++ b/management-ui/src/pages/Firewall/Zones.tsx @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next' import { ApartmentOutlined } from '@ant-design/icons' import apiClient, { isEnvelope } from '../../api/client' +import { useAuthStore } from '../../stores/auth' import DataTable from '../../components/DataTable' import EmptyState from '../../components/EmptyState' import type { FwZone } from './types' @@ -25,6 +26,7 @@ async function listZones(): Promise { export default function ZonesTab() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const [editing, setEditing] = useState(null) const [creating, setCreating] = useState(false) const [form] = Form.useForm() @@ -61,20 +63,22 @@ export default function ZonesTab() { title: t('common.actions'), key: 'actions', render: (_, row) => ( - - {row.builtin - ? - - - : del.mutate(row.id)} - > - - } + + + + {isViewer + ? + : row.builtin + ? + : del.mutate(row.id)} + > + + } ), }, @@ -84,9 +88,11 @@ export default function ZonesTab() { return ( <> - + + + {t('fw.zone.add')} + + + } /> }