From 8b0e8a8ab4cec32c93537ca083e6ab222ee0b978 Mon Sep 17 00:00:00 2001 From: Debian Date: Sun, 24 May 2026 13:14:51 +0200 Subject: [PATCH] =?UTF-8?q?fix(rbac):=20Viewer-Rolle=20in=20Settings=20+?= =?UTF-8?q?=20Cluster=20=E2=80=94=20operative=20Buttons=20sperren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Settings: HAProxy-Reload, Render-Configs, Backup-Now, Service-Restart, E-Mail-Save, Maintenance-Toggle, Backup-/Audit-Retention, Auto-Update- und IPv6-Toggle disabled für Viewer (Passwort-Änderung bleibt aktiv). Cluster: Join-Token-Generation, Peer-Removal, mTLS-Cert-Renew disabled. Co-Authored-By: Claude Sonnet 4.6 --- VERSION | 2 +- cmd/edgeguard-api/main.go | 2 +- cmd/edgeguard-ctl/main.go | 2 +- cmd/edgeguard-scheduler/main.go | 2 +- management-ui/src/pages/Cluster/index.tsx | 83 ++++++++++++-------- management-ui/src/pages/Settings/index.tsx | 89 +++++++++++++--------- 6 files changed, 109 insertions(+), 71 deletions(-) diff --git a/VERSION b/VERSION index 8731870..69658cd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.89 +1.1.90 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 0da789e..eb689bc 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.89" +var version = "1.1.90" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index 3818852..bfb96bb 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.89" +var version = "1.1.90" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index 3f70e61..f9e0695 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.89" +var version = "1.1.90" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/management-ui/src/pages/Cluster/index.tsx b/management-ui/src/pages/Cluster/index.tsx index 82a0f8e..e3e8607 100644 --- a/management-ui/src/pages/Cluster/index.tsx +++ b/management-ui/src/pages/Cluster/index.tsx @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next' import apiClient, { isEnvelope } from '../../api/client' import PageHeader from '../../components/PageHeader' +import { useAuthStore } from '../../stores/auth' const { Text } = Typography @@ -116,6 +117,7 @@ function useTickingNow(intervalMs = 1000): number { export default function ClusterPage() { const { t } = useTranslation() const now = useTickingNow() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const { data, isLoading } = useQuery({ queryKey: ['cluster', 'status'], @@ -246,22 +248,30 @@ export default function ClusterPage() { { title: t('common.actions'), key: 'actions', width: 110, render: (_, r) => ( - removePeer.mutate(r.id)} - > - + + ) : ( + removePeer.mutate(r.id)} > - {t('cluster.removePeerBtn')} - - + + + ) ), }, ] @@ -274,13 +284,16 @@ export default function ClusterPage() { subtitle={t('cluster.intro', { count: 1 + data.peers.length })} extra={ - + + + {data.mode === 'cluster' ? t('cluster.modeCluster') : t('cluster.modeSingle')} @@ -363,16 +376,22 @@ export default function ClusterPage() { {(certStatus.data?.has_ca || certStatus.data?.has_peer) && ( renewSelf.mutate()} - > - - + isViewer ? ( + + + + ) : ( + renewSelf.mutate()} + > + + + ) )} > diff --git a/management-ui/src/pages/Settings/index.tsx b/management-ui/src/pages/Settings/index.tsx index d0d5277..0a1c47e 100644 --- a/management-ui/src/pages/Settings/index.tsx +++ b/management-ui/src/pages/Settings/index.tsx @@ -1,4 +1,4 @@ -import { Alert, Button, Card, Descriptions, Form, Input, InputNumber, Select, Space, Spin, Switch, Typography, message } from 'antd' +import { Alert, Button, Card, Descriptions, Form, Input, InputNumber, Select, Space, Spin, Switch, Tooltip, Typography, message } from 'antd' import { CloudDownloadOutlined, CloudSyncOutlined, CodeOutlined, DatabaseOutlined, ExclamationCircleOutlined, FileSearchOutlined, GlobalOutlined, LockOutlined, MailOutlined, ReloadOutlined, SettingOutlined, StopOutlined, ToolOutlined } from '@ant-design/icons' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useEffect, useState } from 'react' @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next' import apiClient, { isEnvelope } from '../../api/client' import PageHeader from '../../components/PageHeader' +import { useAuthStore } from '../../stores/auth' interface SetupStatus { completed: boolean @@ -33,6 +34,7 @@ interface ChangePasswordValues { export default function SettingsPage() { const { t } = useTranslation() const qc = useQueryClient() + const isViewer = useAuthStore((s) => s.user?.role) === 'viewer' const [msg, msgCtx] = message.useMessage() const [pwForm] = Form.useForm() @@ -343,27 +345,36 @@ export default function SettingsPage() { size="small" > - - - + + + + + + + + + {t('settings.actionsHint')} @@ -396,14 +407,17 @@ export default function SettingsPage() { )} - + + + ) })} @@ -501,9 +515,11 @@ export default function SettingsPage() { - + + + @@ -532,6 +548,7 @@ export default function SettingsPage() { toggleMaintenance.mutate({ enabled: checked, message: maintMessage || maintenance?.message || '', @@ -574,7 +591,7 @@ export default function SettingsPage() { step={1} value={backupRetention?.keep ?? 0} onChange={(v) => setBackupRetention.mutate((v as number) ?? 0)} - disabled={setBackupRetention.isPending} + disabled={setBackupRetention.isPending || isViewer} addonAfter={t('settings.backupRetentionUnit')} style={{ width: 200 }} /> @@ -603,7 +620,7 @@ export default function SettingsPage() { step={30} value={auditRetention?.days ?? 0} onChange={(v) => setAuditRetention.mutate((v as number) ?? 0)} - disabled={setAuditRetention.isPending} + disabled={setAuditRetention.isPending || isViewer} addonAfter={t('settings.auditRetentionUnit')} style={{ width: 200 }} /> @@ -629,6 +646,7 @@ export default function SettingsPage() { toggleAutoUpdate.mutate(checked)} /> @@ -651,6 +669,7 @@ export default function SettingsPage() { toggleIPv6.mutate(checked)} />