fix: Inconsistent permissions for /oauth2: Access restricted to Global Manager only #196

This commit is contained in:
rustmailer
2026-04-16 23:50:56 +08:00
parent 18a0d52c57
commit d90943bbfe
10 changed files with 45 additions and 75 deletions
@@ -65,8 +65,7 @@ export function useSidebarData(): SidebarData {
{
title: t('navigation.oauth2'),
url: '/oauth2',
icon: IdCard,
visible: require_any_permission(['system:root', 'account:create']),
icon: IdCard
}
]
},
@@ -33,9 +33,9 @@ export const getColumns = (t: (key: string) => string): ColumnDef<OAuth2Entity>[
<DataTableColumnHeader column={column} title={t('settings.id')} />
),
cell: ({ row }) => {
return <LongText className='max-w-[100px]'>{row.original.id}</LongText>
return <LongText className='max-w-[140px]'>{row.original.id}</LongText>
},
meta: { className: 'max-w-[100px]' },
meta: { className: 'max-w-[140px]' },
enableHiding: false,
enableSorting: false,
},
@@ -92,9 +92,9 @@ export const getColumns = (t: (key: string) => string): ColumnDef<OAuth2Entity>[
<DataTableColumnHeader column={column} title={t('settings.description')} />
),
cell: ({ row }) => (
<LongText className='max-w-[200px]'>{row.original.description}</LongText>
<LongText className='max-w-[180px]'>{row.original.description}</LongText>
),
meta: { className: 'max-w-[200px]' },
meta: { className: 'max-w-[180px]' },
enableHiding: true,
enableSorting: false
},
@@ -33,6 +33,7 @@ import { useOAuth2Context } from '../context'
import { OAuth2Entity } from '../data/schema'
import { WorkflowIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useCurrentUser } from '@/hooks/use-current-user'
interface DataTableRowActionsProps {
@@ -42,6 +43,9 @@ interface DataTableRowActionsProps {
export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { t } = useTranslation()
const { setOpen, setCurrentRow } = useOAuth2Context()
const { require_any_permission } = useCurrentUser()
return (
<>
<DropdownMenu modal={false}>
@@ -56,6 +60,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuTrigger>
<DropdownMenuContent align='end' className='w-[160px]'>
<DropdownMenuItem
disabled={!require_any_permission(['system:root'])}
onClick={() => {
setCurrentRow(row.original)
setOpen('edit')
@@ -80,6 +85,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
disabled={!require_any_permission(['system:root'])}
onClick={() => {
setCurrentRow(row.original)
setOpen('delete')
+5 -8
View File
@@ -37,12 +37,13 @@ import { TableSkeleton } from '@/components/table-skeleton'
import { AuthorizeDialog } from './components/authorize-dialog'
import { FixedHeader } from '@/components/layout/fixed-header'
import { useTranslation } from 'react-i18next'
import { useCurrentUser } from '@/hooks/use-current-user'
export default function OAuth2() {
const { t } = useTranslation()
const [currentRow, setCurrentRow] = useState<OAuth2Entity | null>(null)
const [open, setOpen] = useDialogState<OAuth2DialogType>(null)
const { require_any_permission } = useCurrentUser()
const { data: oauth2List, isLoading } = useQuery({
queryKey: ['oauth2-list'],
@@ -53,12 +54,9 @@ export default function OAuth2() {
return (
<OAuth2Provider value={{ open, setOpen, currentRow, setCurrentRow }}>
{/* ===== Top Heading ===== */}
<FixedHeader />
<Main>
<div className="mx-auto w-full max-w-6xl px-4">
{/* Header */}
<div className="mx-auto max-w-[88rem] px-4">
<div className="mb-2 flex items-start flex-wrap gap-x-4 gap-y-2">
<div className="flex-1 min-w-[300px]">
<h2 className="text-2xl font-bold tracking-tight">{t('oauth2.title')}</h2>
@@ -67,13 +65,12 @@ export default function OAuth2() {
</p>
</div>
<div className="flex gap-2 ml-auto">
<Button className="space-x-1" onClick={() => setOpen("add")}>
<Button className="space-x-1" disabled={!require_any_permission(['system:root'])} onClick={() => setOpen("add")}>
<span>{t('common.add')}</span>
<Plus size={18} />
</Button>
</div>
</div>
{/* Table / Empty State */}
<div className="flex-1 overflow-auto py-1 flex-row lg:space-x-12 space-y-0">
{isLoading ? (
<TableSkeleton columns={columns.length} rows={10} />
@@ -91,7 +88,7 @@ export default function OAuth2() {
<p className="mb-4 mt-2 text-sm text-muted-foreground">
{t('oauth2.noConfigurationsDesc')}
</p>
<Button onClick={() => setOpen("add")}>{t('oauth2.addConfiguration')}</Button>
<Button disabled={!require_any_permission(['system:root'])} onClick={() => setOpen("add")}>{t('oauth2.addConfiguration')}</Button>
</div>
</div>
)}
+1 -2
View File
@@ -27,7 +27,7 @@ import { useTranslation } from 'react-i18next'
export default function Settings() {
const { t } = useTranslation()
const { require_any_permission, canGlobal } = useCurrentUser()
const { canGlobal } = useCurrentUser()
const sidebarNavItems = [
@@ -50,7 +50,6 @@ export default function Settings() {
title: t('settings.sidebar.proxy'),
icon: <Waypoints size={18} />,
href: '/settings/proxy',
visible: require_any_permission(['system:root', 'account:create']),
},
{
title: t('settings.sidebar.configurations'),
@@ -32,6 +32,7 @@ import {
import { useProxyContext } from '../context'
import { useTranslation } from 'react-i18next'
import { Proxy } from '@/api/system/api'
import { useCurrentUser } from '@/hooks/use-current-user'
interface DataTableRowActionsProps {
@@ -40,6 +41,7 @@ interface DataTableRowActionsProps {
export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { setOpen, setCurrentRow } = useProxyContext()
const { require_any_permission } = useCurrentUser()
const { t } = useTranslation()
return (
<>
@@ -55,6 +57,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuTrigger>
<DropdownMenuContent align='end' className='w-[160px]'>
<DropdownMenuItem
disabled={!require_any_permission(['system:root'])}
onClick={() => {
setCurrentRow(row.original)
setOpen('edit')
@@ -67,6 +70,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
disabled={!require_any_permission(['system:root'])}
onClick={() => {
setCurrentRow(row.original)
setOpen('delete')
+3 -2
View File
@@ -33,13 +33,14 @@ import Logo from '@/assets/logo.svg'
import useProxyList from '@/hooks/use-proxy'
import { useTranslation } from 'react-i18next'
import { Proxy } from '@/api/system/api'
import { useCurrentUser } from '@/hooks/use-current-user'
export default function ProxyManagerPage() {
const { t } = useTranslation()
const [currentRow, setCurrentRow] = useState<Proxy | null>(null)
const [open, setOpen] = useDialogState<ProxyDialogType>(null)
const { require_any_permission } = useCurrentUser()
const { proxyList, isLoading } = useProxyList()
const columns = getColumns(t)
@@ -49,7 +50,7 @@ export default function ProxyManagerPage() {
<div>
<div className="mb-4 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2">
<div className="flex gap-2">
<Button className="space-x-1" onClick={() => setOpen('add')}>
<Button className="space-x-1" disabled={!require_any_permission(['system:root'])} onClick={() => setOpen('add')}>
<span>{t('settings.add')}</span> <Plus size={18} />
</Button>
</div>