Files
boss/tests/rbac-tenant-isolation.test.ts

222 lines
6.3 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import type { BossState } from "../src/lib/boss-data";
import { canAccessDevice, canAccessProject } from "../src/lib/boss-permissions";
function baseState(): BossState {
const now = "2026-04-27T17:00:00+08:00";
return {
schemaVersion: 1,
migratedAt: now,
user: {} as BossState["user"],
devices: [
{
id: "device-a",
name: "A 公司 Mac",
avatar: "A",
account: "owner-a@example.com",
companyId: "tenant-a",
source: "production",
status: "online",
projects: ["project-a"],
quota5h: 0,
quota7d: 0,
lastSeenAt: now,
},
{
id: "device-b",
name: "B 公司 Mac",
avatar: "B",
account: "owner-b@example.com",
companyId: "tenant-b",
source: "production",
status: "online",
projects: ["project-b"],
quota5h: 0,
quota7d: 0,
lastSeenAt: now,
},
{
id: "legacy-device",
name: "历史设备",
avatar: "L",
account: "legacy@example.com",
source: "production",
status: "online",
projects: ["legacy-project"],
quota5h: 0,
quota7d: 0,
lastSeenAt: now,
},
],
projects: [
{
id: "project-b",
name: "B 公司项目",
pinned: false,
deviceIds: ["device-b"],
preview: "",
updatedAt: now,
lastMessageAt: now,
isGroup: false,
threadMeta: {
projectId: "project-b",
threadId: "thread-b",
threadDisplayName: "B 线程",
folderName: "b",
activityIconCount: 1,
updatedAt: now,
},
groupMembers: [],
createdByAgent: false,
collaborationMode: "development",
approvalState: "not_required",
unreadCount: 0,
riskLevel: "low",
messages: [],
goals: [],
versions: [],
},
{
id: "legacy-project",
name: "历史项目",
pinned: false,
deviceIds: ["legacy-device"],
preview: "",
updatedAt: now,
lastMessageAt: now,
isGroup: false,
threadMeta: {
projectId: "legacy-project",
threadId: "thread-legacy",
threadDisplayName: "历史线程",
folderName: "legacy",
activityIconCount: 1,
updatedAt: now,
},
groupMembers: [],
createdByAgent: false,
collaborationMode: "development",
approvalState: "not_required",
unreadCount: 0,
riskLevel: "low",
messages: [],
goals: [],
versions: [],
},
],
conversationHistoryClearedAt: undefined,
verificationCodes: [],
verificationDispatches: [],
adminCompanies: [],
authAccounts: [
{
id: "account-a",
account: "worker-a@example.com",
passwordHash: "hash",
displayName: "A 员工",
role: "member",
companyId: "tenant-a",
createdAt: now,
updatedAt: now,
},
{
id: "account-admin",
account: "root@example.com",
passwordHash: "hash",
displayName: "平台管理员",
role: "highest_admin",
createdAt: now,
updatedAt: now,
},
],
authSessions: [],
accountDeviceGrants: [
{
grantId: "cross-device-grant",
account: "worker-a@example.com",
deviceId: "device-b",
permissions: ["device.view", "computer.control"],
grantedBy: "root@example.com",
grantedAt: now,
},
{
grantId: "legacy-device-grant",
account: "worker-a@example.com",
deviceId: "legacy-device",
permissions: ["device.view"],
grantedBy: "root@example.com",
grantedAt: now,
},
],
accountProjectGrants: [
{
grantId: "cross-project-grant",
account: "worker-a@example.com",
projectId: "project-b",
permissions: ["project.view", "thread.chat"],
grantedBy: "root@example.com",
grantedAt: now,
},
],
accountSkillGrants: [],
skillCatalog: [],
skillLifecycleRequests: [],
permissionAuditLogs: [],
aiAccounts: [],
aiAccountSwitchHistory: [],
masterAgentTasks: [],
dispatchPlans: [],
dispatchExecutions: [],
deviceImportDrafts: [],
deviceImportResolutions: [],
threadStatusDocuments: [],
threadProgressEvents: [],
otaUpdates: [],
otaUpdateLogs: [],
deviceSkills: [],
appLogs: [],
userAttachmentStorageConfigs: [],
masterAgentPromptPolicy: null,
userMasterPrompts: [],
masterAgentMemories: [],
userProjectAgentControls: [],
threadContextSnapshots: [],
threadHandoffPackages: [],
threadContextAlerts: [],
deviceEnrollments: [],
opsFaults: [],
opsRepairTickets: [],
opsRepairVerifications: [],
auditRequests: [],
auditResults: [],
capabilities: [],
projectExecutionPolicies: [],
};
}
test("tenant guard blocks accidental cross-company grants for ordinary accounts", () => {
const state = baseState();
const session = { account: "worker-a@example.com", role: "member" as const, displayName: "A 员工" };
assert.equal(canAccessDevice(state, session, "device-b", "device.view"), false);
assert.equal(canAccessProject(state, session, "project-b", "project.view"), false);
assert.equal(canAccessProject(state, session, "project-b", "thread.chat"), false);
});
test("highest admin remains globally visible across tenants", () => {
const state = baseState();
const session = { account: "root@example.com", role: "highest_admin" as const, displayName: "平台管理员" };
assert.equal(canAccessDevice(state, session, "device-b", "device.view"), true);
assert.equal(canAccessProject(state, session, "project-b", "project.view"), true);
});
test("legacy unassigned devices keep explicit grant compatibility", () => {
const state = baseState();
const session = { account: "worker-a@example.com", role: "member" as const, displayName: "A 员工" };
assert.equal(canAccessDevice(state, session, "legacy-device", "device.view"), true);
assert.equal(canAccessProject(state, session, "legacy-project", "project.view"), true);
});