From 59d3bf0f0047c40475c928a22093a4f35fd76ea3 Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Wed, 3 Jun 2026 16:31:27 +0800 Subject: [PATCH] fix(auth): keep admin out of personal workbench --- web/src/utils/accessControl.js | 40 +++++++++++++++++++------------- web/tests/accessControl.test.mjs | 23 ++++++++++++++++++ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/web/src/utils/accessControl.js b/web/src/utils/accessControl.js index 677394d..b9c1a67 100644 --- a/web/src/utils/accessControl.js +++ b/web/src/utils/accessControl.js @@ -230,18 +230,22 @@ export function isCurrentDirectManagerForRequest(request, user) { return managerNames.length > 0 && identityIntersects(managerNames, currentNames) } -export function canAccessAppView(user, viewId) { - if (!viewId || !user) { - return false - } - - if (!DEFAULT_APP_VIEW_ORDER.includes(viewId)) { - return false - } - - if (viewId === 'budget') { - if (isPlatformAdminUser(user)) { - return true +export function canAccessAppView(user, viewId) { + if (!viewId || !user) { + return false + } + + if (!DEFAULT_APP_VIEW_ORDER.includes(viewId)) { + return false + } + + if (viewId === 'workbench' && isPlatformAdminUser(user)) { + return false + } + + if (viewId === 'budget') { + if (isPlatformAdminUser(user)) { + return true } const roleCodes = normalizedRoleCodes(user) return VIEW_ROLE_RULES.budget.some((roleCode) => roleCodes.includes(roleCode)) @@ -268,7 +272,11 @@ export function filterNavItemsByAccess(navItems, user) { return navItems.filter((item) => canAccessAppView(user, item.id)) } -export function resolveDefaultAuthorizedRoute(user) { - const firstVisibleView = getAccessibleViewIds(user)[0] - return { name: `app-${firstVisibleView || 'workbench'}` } -} +export function resolveDefaultAuthorizedRoute(user) { + if (isPlatformAdminUser(user) && canAccessAppView(user, 'overview')) { + return { name: 'app-overview' } + } + + const firstVisibleView = getAccessibleViewIds(user)[0] + return { name: `app-${firstVisibleView || 'workbench'}` } +} diff --git a/web/tests/accessControl.test.mjs b/web/tests/accessControl.test.mjs index 58e498c..38ff662 100644 --- a/web/tests/accessControl.test.mjs +++ b/web/tests/accessControl.test.mjs @@ -7,10 +7,13 @@ import { canAccessAppView, canDeleteArchivedExpenseClaims, canEditBudgetCenter, + filterNavItemsByAccess, + getAccessibleViewIds, isCurrentDirectManagerForRequest, isCurrentRequestApplicant, canManageExpenseClaims, canReturnExpenseClaims, + resolveDefaultAuthorizedRoute, canSwitchBudgetDepartments } from '../src/utils/accessControl.js' import { canProcessApprovalRequest } from '../src/utils/approvalInbox.js' @@ -71,6 +74,26 @@ test('legacy reimbursement approval and archive centers are no longer accessible assert.equal(canAccessAppView(adminUser, 'documents'), true) }) +test('platform admin users do not enter the personal workbench', () => { + const adminUser = { username: 'admin', isAdmin: true, roleCodes: ['manager', 'finance'] } + const employeeUser = { username: 'employee@example.com', roleCodes: [] } + const navItems = [ + { id: 'workbench', label: '个人工作台' }, + { id: 'documents', label: '单据中心' }, + { id: 'overview', label: '分析看板' }, + { id: 'settings', label: '系统设置' } + ] + + assert.equal(canAccessAppView(adminUser, 'workbench'), false) + assert.equal(canAccessAppView(employeeUser, 'workbench'), true) + assert.equal(getAccessibleViewIds(adminUser).includes('workbench'), false) + assert.deepEqual(resolveDefaultAuthorizedRoute(adminUser), { name: 'app-overview' }) + assert.deepEqual( + filterNavItemsByAccess(navItems, adminUser).map((item) => item.id), + ['documents', 'overview', 'settings'] + ) +}) + test('budget center is visible to platform admin, budget monitor, and executive roles only', () => { assert.equal(canAccessAppView({ isAdmin: true, roleCodes: ['manager'] }, 'budget'), true) assert.equal(canAccessAppView({ username: 'admin', roleCodes: ['manager'] }, 'budget'), true)