agent-exec transport 73562c7c2cf83a3efd62efd0baae1583/1e09e148c1d0
This commit is contained in:
@@ -51,6 +51,8 @@ export function AccountUsersPanel() {
|
||||
const [copyValues, setCopyValues] = useState<Record<string, RolePermissionValue>>({})
|
||||
const [selected, setSelected] = useState<ManagedAccountRecord | null>(null)
|
||||
const [roleView, setRoleView] = useState<UserRoleView | null>(null)
|
||||
const [fullSourceRole, setFullSourceRole] = useState<RoleRecord | null>(null)
|
||||
const [sourceRoleError, setSourceRoleError] = useState('')
|
||||
const [roles, setRoles] = useState<RoleRecord[]>([])
|
||||
const [roleQuery, setRoleQuery] = useState('')
|
||||
const [roleCursor, setRoleCursor] = useState('')
|
||||
@@ -108,7 +110,7 @@ export function AccountUsersPanel() {
|
||||
roleSequence.current++
|
||||
setRoleQuery(''); setRoleCursor(''); setRoleTrail([]); setNextRoleCursor(''); setRoleLoading(false)
|
||||
setSelected(null); setRoleView(null); setErr(''); setNotice('')
|
||||
setCopyName(''); setCopyDescription(''); setCopyValues({})
|
||||
setCopyName(''); setCopyDescription(''); setCopyValues({}); setFullSourceRole(null); setSourceRoleError('')
|
||||
const detailResult = await Promise.allSettled([api.adminGetUser(account.userId)])
|
||||
if (request !== sequence.current) return
|
||||
if (detailResult[0].status === 'rejected') { setErr(String(detailResult[0].reason)); return }
|
||||
@@ -123,6 +125,13 @@ export function AccountUsersPanel() {
|
||||
if (roleResult.status === 'fulfilled' && roleResult.value) {
|
||||
const currentRole = roleResult.value as UserRoleView
|
||||
setRoleView(currentRole); setSelectedRoleId(currentRole.role?.roleId ?? '')
|
||||
if (canCopyRole && currentRole.role) {
|
||||
void api.getRole(currentRole.role.roleId).then(role => {
|
||||
if (request !== sequence.current) return
|
||||
if (Array.isArray(role?.permissions)) setFullSourceRole(role)
|
||||
else setSourceRoleError('源角色权限读取不完整,暂不能复制,请重新打开用户')
|
||||
}).catch(error => { if (request === sequence.current) setSourceRoleError(`读取源角色权限失败:${String(error)}`) })
|
||||
}
|
||||
} else { setRoleView(null); setSelectedRoleId('') }
|
||||
if (catalogResult.status === 'fulfilled' && catalogResult.value) setCatalog(catalogResult.value)
|
||||
else setCatalog([])
|
||||
@@ -268,7 +277,7 @@ export function AccountUsersPanel() {
|
||||
}
|
||||
|
||||
const copyAndAssign = async () => {
|
||||
const source = roleView?.role
|
||||
const source = fullSourceRole
|
||||
if (!selected || !source) return
|
||||
setErr(''); setNotice('')
|
||||
let request
|
||||
@@ -387,7 +396,9 @@ export function AccountUsersPanel() {
|
||||
</div>
|
||||
{canAssignRoles && roleSearch()}
|
||||
{canCopyRole && roleView?.role && catalog.length > 0 && <Collapsible id={`copy-role-${selected.userId}`} trigger="复制角色并分配…">
|
||||
{copyForm(roleView.role)}
|
||||
{fullSourceRole ? copyForm(fullSourceRole)
|
||||
: sourceRoleError ? <p className="lg-err" role="alert"><Icon name="alert" size={14} />{sourceRoleError}</p>
|
||||
: <p className="ad-hint">正在读取源角色权限…</p>}
|
||||
</Collapsible>}
|
||||
{canViewGrants && <Collapsible id={`system-grants-${selected.userId}`} trigger="明确系统权限">
|
||||
<div className="rp-grants">{catalog.filter(item => item.scope === 'system').map(item => <label className="ad-check" key={item.actionId}>
|
||||
|
||||
@@ -110,7 +110,9 @@ export function previewAllowsAssign(previews: readonly UserRoleAssignmentPreview
|
||||
})
|
||||
}
|
||||
|
||||
/** 用户角色投影(GET users/{id}/role)不含 permissions;只有完整角色记录才能作为复制起点。 */
|
||||
export function roleSourceValues(role: RoleRecord, catalog: readonly PermissionDescriptor[]): Record<string, RolePermissionValue> {
|
||||
if (!Array.isArray(role.permissions)) return {}
|
||||
const existing = new Map(role.permissions.map(item => [item.actionId, item]))
|
||||
return Object.fromEntries(catalog.filter(item => !item.protected).map(item => {
|
||||
const value = existing.get(item.actionId)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
import type { PermissionDescriptor, RoleRecord, UserRoleAssignmentPreview } from '../src/api'
|
||||
import {
|
||||
assignedToRole, clearSelection, copyAssignRequest, createSelection, memberAssignments, pageSelectionState, previewAllowsAssign,
|
||||
previewTransition, refreshStateVersions, selectPage, selectionCounts, selectionSummary, staleUserIds, toggleOne,
|
||||
previewTransition, refreshStateVersions, roleSourceValues, selectPage, selectionCounts, selectionSummary, staleUserIds, toggleOne,
|
||||
} from '../src/roleMembers'
|
||||
import { stateToValue } from '../src/rolePermissions'
|
||||
|
||||
@@ -125,4 +125,9 @@ describe('copy role and assign request', () => {
|
||||
expect(request).not.toHaveProperty('overrides')
|
||||
expect(request.assignments[0]).not.toHaveProperty('roleId')
|
||||
})
|
||||
|
||||
it('treats a role projection without permissions as an empty baseline instead of throwing', () => {
|
||||
expect(() => roleSourceValues({ permissions: undefined } as any, catalog)).not.toThrow()
|
||||
expect(roleSourceValues({ permissions: undefined } as any, catalog)).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user