Block workspace directory picker unless super_admin.

Anonymous and normal users no longer open the OS folder dialog from the empty chat hero; only super_admin can pick/create directories. Host gates directoryPicker.pick/list/createDirectory and the client cancels directoryFlow when canCreateWorkspace is false.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-08 08:30:55 +08:00
parent 2cf253f6aa
commit 2072c10338
2 changed files with 102 additions and 12 deletions

View file

@ -835,11 +835,30 @@ window.__ModuleLoader__.load({
const origCall = rpc.call.bind(rpc) const origCall = rpc.call.bind(rpc)
rpc.call = async function udsAuthRpcCall(channel, endpoint, payload, signal) { rpc.call = async function udsAuthRpcCall(channel, endpoint, payload, signal) {
const result = await origCall(channel, endpoint, payload, signal) const result = await origCall(channel, endpoint, payload, signal)
if (getEmpNo()) return result if (!getEmpNo()) {
if (channel !== '/api') return result if (channel === '/api') {
if (endpoint === 'session/list') return { ok: true, value: { items: [] } } if (endpoint === 'session/list') return { ok: true, value: { items: [] } }
if (endpoint === 'session/search') return { ok: true, value: { items: [], hasMore: false } } if (endpoint === 'session/search') return { ok: true, value: { items: [], hasMore: false } }
return result }
}
if (
channel === '/api'
&& (
endpoint === 'directoryPicker/pick'
|| endpoint === 'directoryPicker/list'
|| endpoint === 'directoryPicker/createDirectory'
)
&& document.documentElement.getAttribute('data-uds-can-create-ws') !== '1'
) {
return {
ok: false,
error: {
code: 'gateway/forbidden',
message: getEmpNo() ? '只有超级管理员可以创建工作区' : '登录后才能使用工作区',
},
}
}
return result
} }
} }
@ -882,6 +901,57 @@ window.__ModuleLoader__.load({
} }
}, 'uds-auth: session-list-gate') }, 'uds-auth: session-list-gate')
ctx.effect(() => {
let disposers = []
const Gate = function UdsAuthDirectoryFlowGate(props) {
React.useEffect(() => {
if (props && props.open) {
try { props.onCancel && props.onCancel() } catch { /* ignore */ }
}
}, [props && props.open])
return null
}
const sync = async () => {
let canCreate = false
try {
const me = await fetchJson('/uds-auth/api/me')
canCreate = !!(me && me.data && me.data.permissions && me.data.permissions.canCreateWorkspace)
} catch { canCreate = false }
for (const d of disposers) {
try { d() } catch { /* ignore */ }
}
disposers = []
if (canCreate) return
for (const slotName of [
'sidebar.workspaces.directoryFlow',
'conversation.hero.workspace.directoryFlow',
]) {
try {
disposers.push(ctx.slots.register({
name: slotName,
id: 'uds-auth-dir-gate',
order: 9999,
}, Gate))
} catch { /* slot may be undeclared briefly */ }
}
}
sync()
const timer = setInterval(sync, 15000)
const onFocus = () => { sync() }
const onAuth = () => { sync() }
window.addEventListener('focus', onFocus)
window.addEventListener('uds-auth-changed', onAuth)
return () => {
clearInterval(timer)
window.removeEventListener('focus', onFocus)
window.removeEventListener('uds-auth-changed', onAuth)
for (const d of disposers) {
try { d() } catch { /* ignore */ }
}
}
}, 'uds-auth: directory-flow-gate')
// sidebar.footer.action; layout effect lays footArea as one row: Settings | UDS login // sidebar.footer.action; layout effect lays footArea as one row: Settings | UDS login
ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({ ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({
name: 'sidebar.footer.action', name: 'sidebar.footer.action',

View file

@ -380,19 +380,39 @@ export function installDshAcl(ctx, {
} }
}) })
// directory picker: block createDirectory for non-super // directory picker: only super_admin may pick/create dirs (others get auto workspaces)
ctx.inject(['directoryPicker'], (dctx) => { ctx.inject(['directoryPicker'], (dctx) => {
const dp = dctx.directoryPicker const dp = dctx.directoryPicker
if (!dp || dp.__udsAcl) return if (!dp || dp.__udsAcl) return
dp.__udsAcl = true dp.__udsAcl = true
const assertCanCreateWorkspace = () => {
const identity = getUserContext()
if (!identity?.empNo) throwForbidden('登录后才能使用工作区')
if (!identity?.permissions?.canCreateWorkspace) {
throwForbidden('只有超级管理员可以创建工作区')
}
}
if (typeof dp.pick === 'function') {
const origPick = dp.pick.bind(dp)
dp.pick = async (...args) => {
assertCanCreateWorkspace()
return origPick(...args)
}
}
if (typeof dp.list === 'function') {
const origList = dp.list.bind(dp)
dp.list = async (...args) => {
assertCanCreateWorkspace()
return origList(...args)
}
}
if (typeof dp.createDirectory === 'function') { if (typeof dp.createDirectory === 'function') {
const orig = dp.createDirectory.bind(dp) const origCreate = dp.createDirectory.bind(dp)
dp.createDirectory = async (...args) => { dp.createDirectory = async (...args) => {
const identity = getUserContext() assertCanCreateWorkspace()
if (!identity?.permissions?.canCreateWorkspace) { return origCreate(...args)
throwForbidden('只有超级管理员可以创建目录/工作区')
}
return orig(...args)
} }
} }
}) })