feat: isolate administrators from task data plane

This commit is contained in:
inman committed 2026-09-07 20:25:24 +08:00
1 parent 03d01315a9
commit fd39347603
13 files changed
+644 -112

No files matched your search

@@ -71,6 +71,30 @@ test('AgentBus account-worker migration adds fail-closed channel, task, browser,
assert.match(sql, /owner_user_id IS NULL[\s\S]+enabled = true/);
});
test('administrator task-isolation migration removes legacy bindings and rejects future data-plane principals', async () => {
const sql = await source('../migrations/021_admin_task_data_plane_isolation.sql');
assert.match(sql, /UPDATE tasks task[\s\S]+assigned_user_id = NULL[\s\S]+account\.role = 'admin'/);
assert.match(sql, /UPDATE user_channels channel[\s\S]+owner_user_id = NULL[\s\S]+account\.role = 'admin'/);
assert.match(sql, /UPDATE browser_connections connection[\s\S]+status = 'superseded'[\s\S]+account\.role = 'admin'/);
assert.match(sql, /DELETE FROM user_business_route_authorizations[\s\S]+account\.role = 'admin'/);
assert.match(sql, /UPDATE leader_task_summary_subscriptions subscription[\s\S]+enabled = false[\s\S]+account\.role = 'admin'/);
assert.match(sql, /UPDATE leader_task_summary_deliveries delivery[\s\S]+delivery_status = 'cancelled'[\s\S]+account\.role = 'admin'/);
assert.match(sql, /CREATE OR REPLACE FUNCTION reject_admin_task_principal/);
assert.match(sql, /FOR SHARE/);
for (const constraint of [
'tasks_admin_assignee_forbidden',
'tasks_admin_creator_forbidden',
'user_channels_admin_owner_forbidden',
'browser_connections_admin_worker_forbidden',
'user_business_routes_admin_grantee_forbidden',
'leader_task_summary_admin_subscriber_forbidden',
'leader_task_summary_admin_recipient_forbidden'
]) assert.match(sql, new RegExp(constraint));
assert.match(sql, /CREATE OR REPLACE FUNCTION isolate_administrator_from_task_runtime/);
assert.match(sql, /AFTER UPDATE OF role ON users/);
assert.doesNotMatch(sql, /DELETE FROM tasks/);
});
test('AgentBus channel keys and owners are unique so one inbound identity cannot fan out to multiple employees', async () => {
const channels = await source('../src/agentbus-channels.ts');
assert.match(channels, /requireAssignableOwner/);
@@ -109,7 +133,7 @@ test('account lifecycle is administrator-gated and protects passwords, sessions,
assert.doesNotMatch(publicUser, /organization/);
});
test('administrators manage task-type grants and manual intake enforces them before parsing or ERP dispatch', async () => {
test('administrators manage employee task-type grants while remaining outside manual intake', async () => {
const [auth, tasks, server] = await Promise.all([
source('../src/auth.ts'),
source('../src/task-service.ts'),
@@ -120,6 +144,7 @@ test('administrators manage task-type grants and manual intake enforces them bef
assert.match(auth, /business_authorization_revision_conflict/);
assert.match(auth, /account\.business_authorizations_updated/);
assert.match(auth, /admin_business_authorization_fixed/);
assert.match(auth, /authorized_business_route_ids: role === 'admin' \? \[\] : storedRouteIds/);
assert.match(server, /task_types: BUSINESS_ROUTES\.map/);
assert.match(server, /app\.put\('\/api\/accounts\/:userId\/business-authorizations'[\s\S]+requireAdminMutationSession\(request\)/);
assert.match(tasks, /export function canExecuteBusinessRoute/);
@@ -155,7 +180,7 @@ test('operations dashboard is leadership-gated, cross-source, business-facing, a
source('../src/task-service.ts'),
source('../src/server.ts')
]);
assert.match(tasks, /canViewOperationsDashboard[\s\S]+role === 'admin' \|\| role === 'team_lead'/);
assert.match(tasks, /canViewOperationsDashboard[\s\S]+return role === 'team_lead'/);
assert.match(tasks, /isTaskOwnerRestricted[\s\S]+role === 'team_lead' \|\| role === 'user'/);
assert.match(tasks, /async listOperationsDashboard[\s\S]+t\.assigned_user_id IS NOT NULL[\s\S]+t\.source IN \('manual', 'agentbus'\)/);
assert.match(tasks, /actorUserId[\s\S]+t\.assigned_user_id = \$\$\{params\.length\}/);
@@ -243,9 +268,11 @@ test('ordinary task access is enforced across reads, mutations, artifacts, event
assert.match(server, /tasks\.listTasksPage[\s\S]+access: contextFor\(session, request\)/);
assert.match(server, /tasks\.getTaskArtifact[\s\S]+contextFor\(session, request\)/);
assert.match(server, /tasks\.eventsSince\(session\.user\.organizationId, session\.user\.id, since\)/);
assert.match(server, /app\.addHook\('preHandler'[\s\S]+isTaskDataPlaneRoute\(request\.routeOptions\.url\)[\s\S]+requireTaskDataPlane\(await getSession\(request\)\)/);
assert.match(server, /task_access_forbidden/);
});
test('administrator visibility is isolated from executable events and plugin result routing', async () => {
test('administrators are excluded from task events and plugin result routing', async () => {
const [tasks, server, app] = await Promise.all([
source('../src/task-service.ts'),
source('../src/server.ts'),
@@ -264,6 +291,7 @@ test('administrator visibility is isolated from executable events and plugin res
assert.match(eventRoute, /command\.assigned_user_id !== session\.user\.id/);
assert.match(eventRoute, /event: browser-command/);
assert.match(eventRoute, /tasks\.eventsSince\(session\.user\.organizationId, session\.user\.id, since\)/);
assert.match(eventRoute, /requireTaskSession\(request\)/);
const eventStream = app.slice(
app.indexOf('function startRemoteEventStream()'),
@@ -377,6 +405,12 @@ test('operator UI exposes role-aware accounts, executive drill-through, archive,
assert.match(app, /\/business-authorizations/);
assert.match(app, /当前默认不能执行任何业务/);
assert.match(app, /function canViewOperationsDashboard/);
assert.match(app, /function canUseTaskDataPlane/);
assert.match(app, /isAdministrator\(\) && \(IS_TASK_PAGE \|\| IS_OPERATIONS_DASHBOARD_PAGE\)[\s\S]+window\.location\.replace\('\/accounts'\)/);
assert.match(app, /browserConnectionId = canUseTaskDataPlane\(\) \? connectionIdForUser\(user\) : ''/);
assert.match(app, /if \(bridgeState\) bridgeState\.hidden = !canUseTaskDataPlane\(\)/);
assert.match(app, /if \(!canUseTaskDataPlane\(\)\) return false;[\s\S]+sendToExtension\('PING'/);
assert.match(app, /account\.role === 'admin' \? '不参与任务' : '任务权限'/);
assert.match(app, /\/api\/operations-dashboard\?/);
assert.match(app, /\/api\/operations-dashboard\/tasks\/\$\{encodeURIComponent\(taskId\)\}/);
assert.match(app, /business_route_id/);
@@ -457,6 +491,6 @@ test('account authorization editor uses a scroll-safe open layout without overri
assert.match(openLayoutSource, /overflow:\s*visible/);
assert.doesNotMatch(styles, /\.account-panel\s*\{\s*grid-template-rows:/);
assert.match(index, /styles\.css\?v=20260907-leader-summary-2/);
assert.match(index, /app\.js\?v=20260907-leader-summary-2/);
assert.match(index, /styles\.css\?v=20260907-admin-task-isolation-1/);
assert.match(index, /app\.js\?v=20260907-admin-task-isolation-1/);
});