const base = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787"; const scopeHeaders = { "x-organization-id": "org-studio-lab", "x-workspace-id": "ws-local-aidrama", "x-project-id": "thunder-mouth" }; async function request(path, headers = ownerHeaders, init = {}) { const response = await fetch(`${base}${path}`, { ...init, headers: { ...headers, ...(init.headers || {}) } }); const payload = await response.json(); return { response, payload }; } function assert(condition, message) { if (!condition) throw new Error(message); } async function login(email) { const result = await request("/api/auth/login", {}, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email, password: "Demo@123456" }) }); assert(result.response.ok, `${email} 登录失败`); assert(result.payload.user?.id && result.payload.user.id !== result.payload.session.id, `${email} 登录响应不能把 session id 当作 user id`); assert(result.payload.user.id === result.payload.context.currentUser.id, `${email} 登录响应 user 与 context.currentUser 不一致`); return { authorization: `Bearer ${result.payload.session.token}` }; } const ownerAuth = await login("producer@local.test"); const writerAuth = await login("writer@local.test"); const ownerHeaders = { ...ownerAuth, ...scopeHeaders }; const sessionTestAuth = await login("producer2@local.test"); const sessionTestHeaders = { ...sessionTestAuth }; const ownerSessions = await request("/api/auth/sessions", sessionTestHeaders); assert(ownerSessions.response.ok && ownerSessions.payload.sessions.some((session) => session.current), "当前用户必须能看到当前登录会话"); const revokedOthers = await request("/api/auth/sessions/revoke-others", sessionTestHeaders, { method: "POST", body: "{}", headers: { "content-type": "application/json" } }); assert(revokedOthers.response.ok && revokedOthers.payload.sessions.some((session) => session.current), "撤销其他会话后当前会话必须保持有效"); const context = await request("/api/context", {}); assert(context.response.status === 401, "未登录请求不应访问 context"); const authenticatedContext = await request("/api/context", ownerHeaders); assert(authenticatedContext.response.ok, "登录后的 context 不可访问"); assert(authenticatedContext.payload.platform.organizations.length >= 2, "没有多组织数据"); assert(authenticatedContext.payload.platform.workspaces.length >= 2, "没有多工作区数据"); assert(authenticatedContext.payload.context.permissions.includes("job:create"), "所有者缺少生成任务权限"); assert(authenticatedContext.payload.context.systemAdmin === true, "系统管理员身份未生效"); assert(authenticatedContext.payload.rolePermissions.some((role) => role.name === "组织所有者" && role.permissions.includes("organization:manage")), "管理员必须能读取服务端角色权限矩阵"); const ownerRolePolicies = await request("/api/organizations/org-studio-lab/role-policies", ownerHeaders); assert(ownerRolePolicies.response.ok, "组织管理员无法读取角色权限策略"); assert(ownerRolePolicies.payload.roles.some((role) => role.key === "writer"), "角色策略目录缺少编剧角色"); assert(ownerRolePolicies.payload.permissions.some((permission) => permission.key === "voice:approve"), "角色策略目录缺少声音审批权限"); const writerRolePolicy = ownerRolePolicies.payload.roles.find((role) => role.key === "writer"); const policyPermissionKey = "voice:approve"; const originalWriterPermission = writerRolePolicy.permissions.includes(policyPermissionKey); const writerRolePolicies = await request("/api/organizations/org-studio-lab/role-policies", { ...writerAuth, ...scopeHeaders }); assert(writerRolePolicies.response.status === 403, "普通用户不应读取组织角色权限策略"); try { const grantedWriterPolicy = await request("/api/organizations/org-studio-lab/role-policies/writer", ownerHeaders, { method: "PATCH", body: JSON.stringify({ permissionKey: policyPermissionKey, enabled: true }), headers: { "content-type": "application/json" } }); assert(grantedWriterPolicy.response.ok && grantedWriterPolicy.payload.roles.find((role) => role.key === "writer")?.permissions.includes(policyPermissionKey), "管理员授予组织角色权限失败"); const writerAfterGrant = await request("/api/context", { ...writerAuth, ...scopeHeaders }); assert(writerAfterGrant.response.ok && writerAfterGrant.payload.context.permissions.includes(policyPermissionKey), "组织角色授权没有进入普通用户有效权限"); const revokedWriterPolicy = await request("/api/organizations/org-studio-lab/role-policies/writer", ownerHeaders, { method: "PATCH", body: JSON.stringify({ permissionKey: policyPermissionKey, enabled: false }), headers: { "content-type": "application/json" } }); assert(revokedWriterPolicy.response.ok && !revokedWriterPolicy.payload.roles.find((role) => role.key === "writer")?.permissions.includes(policyPermissionKey), "管理员撤销组织角色权限失败"); const writerAfterRevoke = await request("/api/context", { ...writerAuth, ...scopeHeaders }); assert(writerAfterRevoke.response.ok && !writerAfterRevoke.payload.context.permissions.includes(policyPermissionKey), "组织角色撤销没有从普通用户有效权限移除"); } finally { await request("/api/organizations/org-studio-lab/role-policies/writer", ownerHeaders, { method: "PATCH", body: JSON.stringify({ permissionKey: policyPermissionKey, enabled: originalWriterPermission }), headers: { "content-type": "application/json" } }); } const lockedOwnerPolicy = await request("/api/organizations/org-studio-lab/role-policies/org_owner", ownerHeaders, { method: "PATCH", body: JSON.stringify({ permissionKey: policyPermissionKey, enabled: false }), headers: { "content-type": "application/json" } }); assert(lockedOwnerPolicy.response.status === 400 && lockedOwnerPolicy.payload.error === "owner_policy_locked", "组织所有者策略必须保持锁定"); const systemConfig = await request("/api/system/config", ownerHeaders); assert(systemConfig.response.ok, "系统管理员无法访问系统配置"); const ownerModels = await request("/api/platform/models", ownerHeaders); assert(ownerModels.response.ok, "系统管理员无法访问模型中台"); const ownerCosts = await request("/api/platform/costs", ownerHeaders); assert(ownerCosts.response.ok, "系统管理员无法访问成本中心"); const ownerCompliance = await request("/api/platform/compliance", ownerHeaders); assert(ownerCompliance.response.ok, "系统管理员无法访问合规中心"); const ownerOrganizationPath = await request("/api/organizations/org-northstar", ownerHeaders); assert(ownerOrganizationPath.response.ok && ownerOrganizationPath.payload.organization.id === "org-northstar", "组织路径没有优先于旧上下文头"); const writerContext = await request("/api/context", { ...writerAuth, ...scopeHeaders }); assert(writerContext.response.ok, "普通用户 context 不可访问"); assert(!writerContext.payload.context.permissions.includes("system:settings:view"), "普通用户不应拥有系统配置权限"); assert(writerContext.payload.platform.members.length === 0, "普通用户不应收到组织成员明细"); assert(writerContext.payload.platform.workspaceMembers.length === 0, "普通用户不应收到工作区成员明细"); assert(writerContext.payload.platform.projectMembers.length === 0, "普通用户不应收到项目成员明细"); assert(writerContext.payload.platform.invitations.length === 0, "普通用户不应收到组织邀请明细"); assert(writerContext.payload.platform.billing === null, "普通用户不应收到计费账户"); assert(writerContext.payload.platform.usage === null, "普通用户不应收到用量明细"); assert(writerContext.payload.platform.auditLog.length === 0, "普通用户不应收到审计日志"); assert(writerContext.payload.platform.modelRegistry.length === 0, "普通用户不应收到模型连接器明细"); assert(writerContext.payload.platform.adapterCatalog.some((model) => model.id === "owned-i2v"), "有生成权限的普通用户必须收到可用适配器目录"); assert(writerContext.payload.platform.adapterCatalog.find((model) => model.id === "newapi-audio-production")?.approvalRequired === true, "混合音频连接器必须携带审批标记"); assert(writerContext.payload.platform.adapterCatalog.every((model) => !Object.prototype.hasOwnProperty.call(model, "endpoint")), "普通用户的适配器目录不应暴露连接器 Endpoint"); assert(writerContext.payload.platform.runnerHealth?.length === 0, "普通用户不应收到 Runner 健康明细"); const writerSystem = await request("/api/system/config", { ...writerAuth, ...scopeHeaders }); assert(writerSystem.response.status === 403, "普通用户不应访问系统配置"); const writerProjectPayload = await request("/api/project", { ...writerAuth, ...scopeHeaders }); assert(writerProjectPayload.response.ok, "普通用户应能读取当前项目生产数据"); assert((writerProjectPayload.payload.adapters || []).every((adapter) => !Object.prototype.hasOwnProperty.call(adapter, "endpoint") && !Object.prototype.hasOwnProperty.call(adapter, "baseUrl")), "项目接口不应向普通用户泄露连接器地址"); const writerModels = await request("/api/platform/models", { ...writerAuth, ...scopeHeaders }); assert(writerModels.response.status === 403, "普通用户不应访问模型中台"); const writerCosts = await request("/api/platform/costs", { ...writerAuth, ...scopeHeaders }); assert(writerCosts.response.status === 403, "普通用户不应访问成本中心"); const writerCompliance = await request("/api/platform/compliance", { ...writerAuth, ...scopeHeaders }); assert(writerCompliance.response.status === 403, "普通用户不应访问合规中心"); const writerMembers = await request("/api/organizations/org-studio-lab/members", { ...writerAuth, ...scopeHeaders }); assert(writerMembers.response.status === 403, "普通用户不应访问组织成员接口"); const writerQa = await request("/api/qa", { ...writerAuth, ...scopeHeaders }); assert(writerQa.response.status === 403, "普通用户不应执行审片接口"); const writerCreateOrganization = await request("/api/organizations", { ...writerAuth, ...scopeHeaders }, { method: "POST", body: JSON.stringify({ name: "blocked-org" }), headers: { "content-type": "application/json" } }); assert(writerCreateOrganization.response.status === 403, "普通用户不应创建组织"); const apiClientCreated = await request("/api/system/api-clients", ownerHeaders, { method: "POST", body: JSON.stringify({ name: `smoke-runner-${Date.now()}`, scopes: ["jobs:read", "jobs:write"] }), headers: { "content-type": "application/json" } }); assert(apiClientCreated.response.status === 201 && apiClientCreated.payload.clientKey, "系统 API 客户端必须返回一次性 client key"); const apiClientHeaders = { authorization: `Bearer ${apiClientCreated.payload.clientKey}`, ...scopeHeaders }; const apiClientContext = await request("/api/context", apiClientHeaders); assert(apiClientContext.response.ok && apiClientContext.payload.context.systemAdmin === false, "API 客户端不应继承系统管理员身份"); assert(apiClientContext.payload.context.permissions.includes("job:create"), "API 客户端 jobs scope 未映射为生成权限"); const apiClientSystem = await request("/api/system/config", apiClientHeaders); assert(apiClientSystem.response.status === 403, "API 客户端不应访问系统配置"); const revokeApiClient = await request(`/api/system/api-clients/${apiClientCreated.payload.client.id}`, ownerHeaders, { method: "PATCH", body: JSON.stringify({ status: "revoked" }), headers: { "content-type": "application/json" } }); assert(revokeApiClient.response.ok, "API 客户端撤销失败"); const revokedApiClient = await request("/api/context", apiClientHeaders); assert(revokedApiClient.response.status === 401, "撤销 API 客户端后必须返回 401"); const externalJob = await request("/api/jobs", { ...writerAuth, ...scopeHeaders }, { method: "POST", body: JSON.stringify({ adapter: "newapi-audio-production", kind: "smoke external gate", shotId: "shot-01", output: "qa/smoke/external-gate.json" }), headers: { "content-type": "application/json" } }); assert(externalJob.response.status === 403 && externalJob.payload.error === "external_connector_requires_approval", "混合连接器未批准时必须由后端阻断"); const orgAdminAuth = await login("producer2@local.test"); const orgAdminHeaders = { ...orgAdminAuth, "x-organization-id": "org-northstar", "x-workspace-id": "ws-northstar-main", "x-project-id": "northstar-pilot" }; const orgAdminContext = await request("/api/context", orgAdminHeaders); assert(orgAdminContext.response.ok, "组织管理员 context 不可访问"); assert(orgAdminContext.payload.context.systemAdmin === false, "组织管理员不应被识别为系统管理员"); assert(orgAdminContext.payload.context.permissions.includes("model:manage"), "组织管理员缺少模型管理权限"); const orgAdminModels = await request("/api/platform/models", orgAdminHeaders); assert(orgAdminModels.response.ok, "组织管理员无法访问本组织模型中台"); const orgAdminQueue = await request("/api/admin/queue", orgAdminHeaders); assert(orgAdminQueue.response.ok, "组织管理员无法访问本组织任务队列"); const orgAdminSystem = await request("/api/system/config", orgAdminHeaders); assert(orgAdminSystem.response.status === 403, "组织管理员不应访问系统配置"); const northstar = await request("/api/context", { ...ownerAuth, "x-organization-id": "org-northstar", "x-workspace-id": "ws-northstar-main", "x-project-id": "northstar-pilot" }); assert(northstar.response.ok, "组织切换失败"); assert(northstar.payload.context.currentOrganization.id === "org-northstar", "组织 scope 没有切换"); assert(northstar.payload.platform.modelRegistry.every((model) => model.id === "northstar-image"), "模型没有按组织隔离"); const writerInvite = await request("/api/organizations/org-studio-lab/invitations", { ...writerAuth, ...scopeHeaders }, { method: "POST", body: JSON.stringify({ email: "blocked-smoke@local.test", roleKey: "writer" }), headers: { "content-type": "application/json" } }); assert(writerInvite.response.status === 403 && writerInvite.payload.error === "permission_denied", "编剧越权邀请未被拒绝"); const crossOrg = await request("/api/context", { ...writerAuth, "x-organization-id": "org-northstar", "x-workspace-id": "ws-northstar-main" }); assert(crossOrg.response.status === 403 && crossOrg.payload.error === "organization_forbidden", "跨组织访问未被拒绝"); console.log(`tenant smoke passed: ${base}`);