mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-08 23:33:16 +08:00
Allow admin (and Host plugins) to create workspaces.
super_admin/fallback_admin already could; admin now has canCreateWorkspace, and Host-side creates without browser ALS (IM) are no longer blocked as forbidden. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
050b99426f
commit
89700ef4f9
6 changed files with 26 additions and 15 deletions
|
|
@ -100,7 +100,7 @@ window.__ModuleLoader__.load({
|
||||||
"ui.networkError": "网络错误...",
|
"ui.networkError": "网络错误...",
|
||||||
"ui.qrGenerateFailed": "生成二维码失败",
|
"ui.qrGenerateFailed": "生成二维码失败",
|
||||||
"ui.cronLoginRequired": "登录后才能使用定时任务",
|
"ui.cronLoginRequired": "登录后才能使用定时任务",
|
||||||
"ui.workspaceCreateForbidden": "只有超级管理员可以创建工作区",
|
"ui.workspaceCreateForbidden": "只有管理员可以创建工作区",
|
||||||
"ui.workspaceLoginRequired": "登录后才能使用工作区",
|
"ui.workspaceLoginRequired": "登录后才能使用工作区",
|
||||||
"err.not_logged_in": "未登录",
|
"err.not_logged_in": "未登录",
|
||||||
"err.forbidden_settings": "当前账号无设置权限",
|
"err.forbidden_settings": "当前账号无设置权限",
|
||||||
|
|
@ -138,7 +138,7 @@ window.__ModuleLoader__.load({
|
||||||
"err.session_forbidden": "无权访问该会话",
|
"err.session_forbidden": "无权访问该会话",
|
||||||
"err.session_workspace_only": "只能在自己的工作区创建会话",
|
"err.session_workspace_only": "只能在自己的工作区创建会话",
|
||||||
"err.workspace_path_only": "只能打开自己的工作区路径",
|
"err.workspace_path_only": "只能打开自己的工作区路径",
|
||||||
"err.workspace_create_forbidden": "只有超级管理员可以创建工作区",
|
"err.workspace_create_forbidden": "只有管理员可以创建工作区",
|
||||||
"err.no_skill_credentials": "请先完成 UAC 扫码登录",
|
"err.no_skill_credentials": "请先完成 UAC 扫码登录",
|
||||||
"err.loopback_only_credentials": "agent-credentials 仅允许本机访问",
|
"err.loopback_only_credentials": "agent-credentials 仅允许本机访问",
|
||||||
"err.loopback_only_outbound": "outbound 仅允许本机访问",
|
"err.loopback_only_outbound": "outbound 仅允许本机访问",
|
||||||
|
|
@ -240,7 +240,7 @@ window.__ModuleLoader__.load({
|
||||||
"ui.networkError": "Network error...",
|
"ui.networkError": "Network error...",
|
||||||
"ui.qrGenerateFailed": "Failed to generate QR",
|
"ui.qrGenerateFailed": "Failed to generate QR",
|
||||||
"ui.cronLoginRequired": "Sign in to use scheduled tasks",
|
"ui.cronLoginRequired": "Sign in to use scheduled tasks",
|
||||||
"ui.workspaceCreateForbidden": "Only super admins can create workspaces",
|
"ui.workspaceCreateForbidden": "Only admins can create workspaces",
|
||||||
"ui.workspaceLoginRequired": "Sign in to use workspaces",
|
"ui.workspaceLoginRequired": "Sign in to use workspaces",
|
||||||
"err.not_logged_in": "Not signed in",
|
"err.not_logged_in": "Not signed in",
|
||||||
"err.forbidden_settings": "No settings permission",
|
"err.forbidden_settings": "No settings permission",
|
||||||
|
|
@ -278,7 +278,7 @@ window.__ModuleLoader__.load({
|
||||||
"err.session_forbidden": "No access to this session",
|
"err.session_forbidden": "No access to this session",
|
||||||
"err.session_workspace_only": "Sessions can only be created in your own workspace",
|
"err.session_workspace_only": "Sessions can only be created in your own workspace",
|
||||||
"err.workspace_path_only": "You can only open your own workspace path",
|
"err.workspace_path_only": "You can only open your own workspace path",
|
||||||
"err.workspace_create_forbidden": "Only super admins can create workspaces",
|
"err.workspace_create_forbidden": "Only admins can create workspaces",
|
||||||
"err.no_skill_credentials": "Complete UAC QR sign-in first",
|
"err.no_skill_credentials": "Complete UAC QR sign-in first",
|
||||||
"err.loopback_only_credentials": "agent-credentials is loopback-only",
|
"err.loopback_only_credentials": "agent-credentials is loopback-only",
|
||||||
"err.loopback_only_outbound": "outbound is loopback-only",
|
"err.loopback_only_outbound": "outbound is loopback-only",
|
||||||
|
|
@ -1852,7 +1852,7 @@ function reloadAfterLogin() {
|
||||||
|
|
||||||
|
|
||||||
ctx.effect(() => {
|
ctx.effect(() => {
|
||||||
// Open/choose workspace is super_admin-only (canCreateWorkspace).
|
// Open/choose workspace: admin / super_admin / fallback_admin (canCreateWorkspace).
|
||||||
// Everyone else uses the auto-provisioned per-user workspace and must not open the picker.
|
// Everyone else uses the auto-provisioned per-user workspace and must not open the picker.
|
||||||
const CHOOSER = hostAriaSel('chooseWorkspace')
|
const CHOOSER = hostAriaSel('chooseWorkspace')
|
||||||
// Inert composer: onClick lives on the card (cardWorkspaceTrigger), not the labeled node.
|
// Inert composer: onClick lives on the card (cardWorkspaceTrigger), not the labeled node.
|
||||||
|
|
|
||||||
|
|
@ -929,7 +929,10 @@ ctx.inject(['workspaceController'], (wctx) => {
|
||||||
wc.create = async (request) => {
|
wc.create = async (request) => {
|
||||||
const identity = getUserContext()
|
const identity = getUserContext()
|
||||||
if (identity?._internalProvision) return origCreate(request)
|
if (identity?._internalProvision) return origCreate(request)
|
||||||
if (!identity?.permissions?.canCreateWorkspace) {
|
// No ALS identity: Host-side plugins (IM / cron) create workspaces without a
|
||||||
|
// browser login. Authenticated browser calls always run under user context;
|
||||||
|
// there admin / super_admin / fallback_admin must have canCreateWorkspace.
|
||||||
|
if (identity && !identity.permissions?.canCreateWorkspace) {
|
||||||
throwForbidden('workspace_create_forbidden')
|
throwForbidden('workspace_create_forbidden')
|
||||||
}
|
}
|
||||||
return origCreate(request)
|
return origCreate(request)
|
||||||
|
|
@ -1060,7 +1063,7 @@ ctx.inject(['workspaceController'], (wctx) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// directory picker: only super_admin may pick/create dirs (others get auto workspaces)
|
// directory picker: admin / super_admin / fallback_admin (canCreateWorkspace)
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ export const MESSAGES = {
|
||||||
|
|
||||||
// client gates
|
// client gates
|
||||||
'ui.cronLoginRequired': '登录后才能使用定时任务',
|
'ui.cronLoginRequired': '登录后才能使用定时任务',
|
||||||
'ui.workspaceCreateForbidden': '只有超级管理员可以创建工作区',
|
'ui.workspaceCreateForbidden': '只有管理员可以创建工作区',
|
||||||
'ui.workspaceLoginRequired': '登录后才能使用工作区',
|
'ui.workspaceLoginRequired': '登录后才能使用工作区',
|
||||||
|
|
||||||
// API / ACL errors (stable codes)
|
// API / ACL errors (stable codes)
|
||||||
|
|
@ -137,7 +137,7 @@ export const MESSAGES = {
|
||||||
'err.session_forbidden': '无权访问该会话',
|
'err.session_forbidden': '无权访问该会话',
|
||||||
'err.session_workspace_only': '只能在自己的工作区创建会话',
|
'err.session_workspace_only': '只能在自己的工作区创建会话',
|
||||||
'err.workspace_path_only': '只能打开自己的工作区路径',
|
'err.workspace_path_only': '只能打开自己的工作区路径',
|
||||||
'err.workspace_create_forbidden': '只有超级管理员可以创建工作区',
|
'err.workspace_create_forbidden': '只有管理员可以创建工作区',
|
||||||
'err.no_skill_credentials': '请先完成 UAC 扫码登录',
|
'err.no_skill_credentials': '请先完成 UAC 扫码登录',
|
||||||
'err.loopback_only_credentials': 'agent-credentials 仅允许本机访问',
|
'err.loopback_only_credentials': 'agent-credentials 仅允许本机访问',
|
||||||
'err.loopback_only_outbound': 'outbound 仅允许本机访问',
|
'err.loopback_only_outbound': 'outbound 仅允许本机访问',
|
||||||
|
|
@ -250,7 +250,7 @@ export const MESSAGES = {
|
||||||
'ui.qrGenerateFailed': 'Failed to generate QR',
|
'ui.qrGenerateFailed': 'Failed to generate QR',
|
||||||
|
|
||||||
'ui.cronLoginRequired': 'Sign in to use scheduled tasks',
|
'ui.cronLoginRequired': 'Sign in to use scheduled tasks',
|
||||||
'ui.workspaceCreateForbidden': 'Only super admins can create workspaces',
|
'ui.workspaceCreateForbidden': 'Only admins can create workspaces',
|
||||||
'ui.workspaceLoginRequired': 'Sign in to use workspaces',
|
'ui.workspaceLoginRequired': 'Sign in to use workspaces',
|
||||||
|
|
||||||
'err.not_logged_in': 'Not signed in',
|
'err.not_logged_in': 'Not signed in',
|
||||||
|
|
@ -285,7 +285,7 @@ export const MESSAGES = {
|
||||||
'err.session_forbidden': 'No access to this session',
|
'err.session_forbidden': 'No access to this session',
|
||||||
'err.session_workspace_only': 'Sessions can only be created in your own workspace',
|
'err.session_workspace_only': 'Sessions can only be created in your own workspace',
|
||||||
'err.workspace_path_only': 'You can only open your own workspace path',
|
'err.workspace_path_only': 'You can only open your own workspace path',
|
||||||
'err.workspace_create_forbidden': 'Only super admins can create workspaces',
|
'err.workspace_create_forbidden': 'Only admins can create workspaces',
|
||||||
'err.no_skill_credentials': 'Complete UAC QR sign-in first',
|
'err.no_skill_credentials': 'Complete UAC QR sign-in first',
|
||||||
'err.loopback_only_credentials': 'agent-credentials is loopback-only',
|
'err.loopback_only_credentials': 'agent-credentials is loopback-only',
|
||||||
'err.loopback_only_outbound': 'outbound is loopback-only',
|
'err.loopback_only_outbound': 'outbound is loopback-only',
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
* 角色:
|
* 角色:
|
||||||
* super_admin 所有权限 + 用户管理;默认可见全部会话(可在设置中关闭)
|
* super_admin 所有权限 + 用户管理;默认可见全部会话(可在设置中关闭)
|
||||||
* fallback_admin 等同 super_admin(兜底 administrator)
|
* fallback_admin 等同 super_admin(兜底 administrator)
|
||||||
* admin 无设置齿轮;仅看自己会话(含 @);可见渠道/系统会话
|
* admin 无设置齿轮;仅看自己会话(含 @);可见渠道/系统会话;可创建工作区
|
||||||
* user 仅看自己会话,无设置
|
* user 仅看自己会话,无设置
|
||||||
*
|
*
|
||||||
* 超管/应急默认全览开启;prefs.viewAllSessions === false 时关闭。
|
* 超管/应急默认全览开启;prefs.viewAllSessions === false 时关闭。
|
||||||
* 设置齿轮仅超管/应急(canAccessSettings)。
|
* 设置齿轮仅超管/应急(canAccessSettings)。
|
||||||
|
* 创建工作区:super_admin / fallback_admin / admin(扫码不可用时现场通常只有 admin)。
|
||||||
* 持久化: roles.json (roles + prefs + fallbackPasswordHash)
|
* 持久化: roles.json (roles + prefs + fallbackPasswordHash)
|
||||||
*/
|
*/
|
||||||
import { createHash, randomBytes } from 'node:crypto'
|
import { createHash, randomBytes } from 'node:crypto'
|
||||||
|
|
@ -61,12 +62,12 @@ export function computePermissions(role, opts = {}) {
|
||||||
case ROLES.ADMIN:
|
case ROLES.ADMIN:
|
||||||
return {
|
return {
|
||||||
canManageUsers: false,
|
canManageUsers: false,
|
||||||
// 设置齿轮仅超管/应急;admin 仍可看渠道/系统会话
|
// 设置齿轮仅超管/应急;admin 仍可看渠道/系统会话,并可创建工作区
|
||||||
canAccessSettings: false,
|
canAccessSettings: false,
|
||||||
canToggleViewAllSessions: false,
|
canToggleViewAllSessions: false,
|
||||||
canViewAllSessions: false,
|
canViewAllSessions: false,
|
||||||
canViewSystemSessions: true,
|
canViewSystemSessions: true,
|
||||||
canCreateWorkspace: false,
|
canCreateWorkspace: true,
|
||||||
}
|
}
|
||||||
default: // user / undefined
|
default: // user / undefined
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "uds-auth",
|
"name": "uds-auth",
|
||||||
"version": "0.2.3",
|
"version": "0.2.4",
|
||||||
"description": "UDS authentication plugin for DeepSeek Harness with multi-tenant session isolation",
|
"description": "UDS authentication plugin for DeepSeek Harness with multi-tenant session isolation",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
|
|
||||||
|
|
@ -192,4 +192,11 @@ describe('RolesStore view-all prefs', () => {
|
||||||
assert.throws(() => store.setViewAllSessions('u1', true), (err) => err.code === 'forbidden_view_all_sessions')
|
assert.throws(() => store.setViewAllSessions('u1', true), (err) => err.code === 'forbidden_view_all_sessions')
|
||||||
assert.throws(() => store.setViewAllSessions('a1', true), (err) => err.code === 'forbidden_view_all_sessions')
|
assert.throws(() => store.setViewAllSessions('a1', true), (err) => err.code === 'forbidden_view_all_sessions')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('lets admin, super_admin, and fallback_admin create workspaces', () => {
|
||||||
|
assert.equal(computePermissions(ROLES.SUPER_ADMIN).canCreateWorkspace, true)
|
||||||
|
assert.equal(computePermissions(ROLES.FALLBACK_ADMIN).canCreateWorkspace, true)
|
||||||
|
assert.equal(computePermissions(ROLES.ADMIN).canCreateWorkspace, true)
|
||||||
|
assert.equal(computePermissions(ROLES.USER).canCreateWorkspace, false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue