Files
caoxiaozhu 787bc3a481 feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
2026-07-17 14:14:08 +08:00

189 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent 资产多租户隔离与安全规则编辑 概念文档
更新时间2026-07-17
## 功能一句话
让规则、技能、MCP、任务及其版本、审核、测试和编辑会话都拥有可验证的租户归属并以“企业资产可写、平台资产只读”的双层模型安全贯通规则生成、真实场景验证、审核、发布和 ONLYOFFICE 编辑。
## 背景与问题
原 AgentAsset 数据模型没有结构化 `tenant_id``scope`,部分读取和内部查询可以在没有用户上下文时返回全局资产。版本、审核、测试、反馈和发布链路主要依赖 `asset_id` 或业务约定关联,无法由数据库阻止跨租户子记录注入。同编码资产也不能由不同企业独立维护。
规则场景测试可以在未声明目标企业时抽取费用数据,审核主体还可能使用客户端传入的 actor/reviewer 字段,导致测试证据和盲审身份缺乏稳定、可追溯的企业边界。
规则表的 ONLYOFFICE 内容与回调接口原先缺少持久化的一次性会话。回调下载地址、文档 key、版本、租户和编辑权限之间没有不可变绑定存在匿名读取、跨资产回写、身份伪造、重放和服务端请求伪造风险。
## 目标与非目标
### 目标
- AgentAsset、Version、Review、TestRun、RuleFeedback 和 ONLYOFFICE Session 均保存结构化租户作用域。
- 租户用户只能看到本企业资产和平台资产;同编码时企业资产优先覆盖平台默认资产。
- 跨租户详情、版本、审核、发布、测试和反馈统一表现为不存在,避免泄露资源存在性。
- 平台资产对所有租户只读,仅平台管理员可新增、修改、发布或编辑。
- 所有 HTTP 入口使用认证会话中的 `CurrentUserContext.tenant_id`,不接受客户端覆盖租户。
- 写入、版本和审核操作使用 RuleEditor、RuleReviewer 或平台管理员权限,并以稳定身份写入审计证据。
- 真实风险场景必须显式声明当前企业 `target_tenant_id`,费用样本的第一层 SQL 条件就是该租户。
- ONLYOFFICE 内容和回调使用不同 audience/scope 的签名 token并绑定数据库一次性会话。
- 回调下载拒绝错误 origin、非公网解析、DNS 重绑定、重定向、超限和非安全 OOXML 文件。
### 非目标
- 不允许普通企业用户创建或修改平台资产。
- 不把所有企业资产放在全局结果集中后仅靠前端过滤。
- 不允许平台管理员借普通企业会话修改其他企业的私有资产;跨企业运维需要独立受控流程。
- 不提供允许私网、回环或任意下载地址的 ONLYOFFICE 安全降级开关。
- 不在本切片中重做 Agent 资产管理前端视觉或商业定价页面。
## 用户与场景
- 企业规则编辑者:维护本企业规则资产、上传规则表并创建新版本。
- 企业规则审核者:以稳定登录身份进行盲审、驳回或批准规则版本。
- 企业风控人员:用本企业真实费用申请生成测试样本和质量证据。
- 普通企业用户:读取本企业资产和平台只读资产,但不能写入。
- 平台管理员:维护跨企业可见的平台基础规则和模板。
- 运行时与调度器:在显式租户范围内加载、测试、监控和召回资产,不进行全局扫描。
- ONLYOFFICE 文档服务:使用资源专用 token 读取一次文档,并通过单次 callback session 回写允许编辑的当前版本。
## 功能能力
- 双层可见性:`tenant:{tenant_id}``platform:platform`
- 企业内唯一编码:数据库唯一键为 `(tenant_id, scope, code)`,不同企业可拥有同编码资产。
- 确定性覆盖:按编码加载时先找当前企业资产,再回退平台资产。
- 服务端可信租户HTTP 服务由登录会话构造 `AgentAssetAccessScope`;无用户上下文的内部读取只允许平台作用域。
- 稳定审计主体:优先记录 `employee:{employee_id}`,没有员工 ID 时记录大小写归一的 `username:{username}`
- 真实样本隔离:场景请求必须传 `target_tenant_id`且必须等于登录企业TestRun 记录样本所属企业。
- 平台资产可在企业场景中验证,但测试证据仍归目标企业,不能变成平台或其他企业事实。
- ONLYOFFICE content token 有效期 15 分钟callback session 有效期 4 小时。
- 回调状态机保证同一个 JTI 最多一次进入写入阶段。
## 方案设计
### 前端契约
- AgentAsset DTO 提供 `tenantId``scope`,前端可明确标识企业资产和平台资产。
- 平台资产在非平台管理员会话中必须隐藏或禁用修改、发布、上传和 ONLYOFFICE 编辑动作。
- 场景测试请求必须携带 `targetTenantId`;它是目标企业的显式确认,不是可切换企业的授权参数。
-`X-Actor`/reviewer 头仅为兼容保留,服务端忽略其身份值并使用登录会话主体。
- ONLYOFFICE 配置根据权限返回 `view``edit`;内容和回调 token 只供文档服务使用。
### 后端职责
- `agent_asset_scope`:定义 platform/tenant 常量和合法作用域基础规则。
- `agent_asset_access`:从可信用户构造访问范围、生成稳定主体并提供可见/可写谓词。
- `agent_asset` repository所有列表、详情、版本、审核、测试和反馈查询都注入结构化租户条件。
- `agent_assets`:编排资产 CRUD、版本、审核与序列化不通过未过滤 ORM 关系返回子记录。
- `agent_asset_risk_rule_testing`:校验目标企业、先按租户过滤 ExpenseClaim再生成测试证据。
- 发布、监控、召回、调度、遥测和风险运行时服务:沿资产租户作用域读取和写入,禁止全局 asset id 查询。
- `agent_asset_onlyoffice_security`:签发/验证持久化会话并原子消费 callback JTI。
- `agent_asset_onlyoffice`按会话中的租户、资产、key、版本和指纹定位文档安全下载后创建新版本。
- `knowledge_onlyoffice_security`:复用统一安全下载器,执行 origin、DNS/IP、响应和 OOXML 校验。
### 数据
`20260717_0026_agent_asset_tenant_security.py` 负责以下结构:
- `agent_assets`:新增 `tenant_id``scope`,建立作用域约束、租户外键和企业内 code 唯一键。
- `agent_asset_versions``agent_asset_reviews`:新增租户作用域,并以 `(tenant_id, scope, asset_id)` 复合外键绑定父资产。
- `agent_asset_test_runs``agent_asset_rule_feedback`:保存证据所属企业;平台资产的企业测试/反馈也归企业事实域。
- `agent_asset_onlyoffice_sessions`:保存 JTI、租户、资源 scope、asset、document key/version/fingerprint、audience、权限、actor、过期时间和状态。
- 旧资产默认回填为平台资产;能从父资产确定的历史版本、审核和证据同步回填。
- 无法确认租户的历史数据或不完整旧表结构会 fail-closed不猜测企业归属。
### 权限与信任边界
- 当前企业只来自 `CurrentUserContext.tenant_id`;空值、`platform` 伪企业或请求参数不能构造企业访问范围。
- 租户读取谓词是“当前企业或平台”,写入谓词只允许当前企业;平台写入还要求 `is_admin=true`
- 跨租户资源统一返回 404权限不足的本作用域操作返回受控错误。
- RuleEditor 可维护规则和版本RuleReviewer 执行审核;平台资产的任意写操作额外要求平台管理员。
- 版本 created_by、审核 reviewer、规则表变更 actor 都由稳定登录主体生成。
- 后台 bootstrap/foundation 按平台作用域精确查找种子资产,不能误改同编码企业资产。
- 风险运行时按“企业优先、平台回退”加载发布规则,不扫描其他企业版本。
- ORM 关系不是授权边界;对外响应必须经过带 scope 的 repository/service 查询。
### 资产解析顺序
```text
find_by_code(code, current_tenant):
1. tenant_id = current_tenant AND scope = tenant
2. tenant_id = platform AND scope = platform
3. not found
```
该顺序让企业能够在不修改平台模板的情况下覆盖默认规则,同时保持其他企业和平台资产不受影响。
### 风险场景测试
```text
认证用户 tenant
→ 校验 target_tenant_id == tenant
→ 校验目标资产为 tenant 自有或 platform 只读资产
→ SQL 第一层条件 ExpenseClaim.tenant_id == target_tenant_id
→ 应用时间、费用类型、城市等业务筛选
→ 创建 tenant-scoped TestRun
```
没有目标企业、目标企业不一致或资产属于其他企业时均拒绝,不使用 mock 的默认企业或全局样本补齐。
### ONLYOFFICE 状态机
```text
issue(view) → active ── callback status 2/6 ──拒绝写入
issue(edit) → active ── atomic claim ──→ processing
├─校验/下载/写入成功→ consumed
└─任一步失败────────→ failed
active -- exp 超时 --> 验证拒绝
processing/consumed/failed/revoked -- replay --> 409/拒绝
```
token 同时绑定 issuer、audience、scope、JTI、tenant、resource scope、asset、document key、version、fingerprint、writable、actor、iat/nbf/exp。回调 payload 只能提供状态和下载位置,不能覆盖这些授权事实。
### 降级与回滚策略
- 无可信租户HTTP 请求拒绝;无用户上下文的内部服务只看平台资产,绝不回退全局查询。
- 跨租户资产、版本或测试证据:按不存在处理,不尝试平台管理员越权兼容。
- 场景样本为空:返回空样本测试事实,不改查其他企业数据。
- ONLYOFFICE token、key、版本、指纹、DNS、MIME 或 OOXML 校验失败:拒绝回写并保持原文件不变。
- 生产文档服务必须使用配置白名单中的公网 TLS origin或经满足相同约束的安全代理访问。
- migration downgrade 在存在企业资产/证据或 ONLYOFFICE 会话时拒绝有损回滚;必须先通过受控数据迁移清理事实。
## 测试方案
- 可见性:两企业同编码资产、企业覆盖平台、平台只读、跨企业详情 404、无上下文仅平台。
- 数据完整性scope/tenant check、企业内 code 唯一、Version/Review 复合父子外键、TestRun 证据企业。
- 权限与身份RuleEditor/RuleReviewer、平台管理员、伪造 actor/reviewer 无效、稳定 employee/username 主体。
- 风险场景必须目标企业、目标不一致拒绝、SQL 租户首过滤、平台资产的企业 TestRun。
- 发布链路:发布门禁、监控、召回、运行时、调度和遥测均使用显式租户。
- ONLYOFFICEcontent/callback scope、tenant/asset/key/version/fingerprint 绑定、平台只读、原子 claim、失败终态和重放拒绝。
- SSRF/文件:白名单 origin、全量公网 DNS、固定已校验 IP、拒绝重定向、大小/MIME/ZIP/OOXML 限制。
- 迁移:旧平台数据回填、同编码多企业、非法 scope/跨租户外键拒绝、事实存在时 downgrade 拒绝、清理后可回滚。
- 所有后端验证只在 `local-x-financial-linux` 容器中执行,单条命令限制 60 秒。
## 指标与验收
- 所有 AgentAsset 业务读取都能由 `tenant_id + scope` 确定结果范围。
- 跨企业资产、版本、审核、测试、反馈和回调用例 100% 不可见或拒绝。
- 平台资产对非平台管理员的写入和回调 100% 无副作用。
- 真实费用样本查询 100% 具有服务端校验的企业首过滤条件。
- 审计主体 100% 来自稳定登录身份,客户端 actor/reviewer 不能改变事实。
- 同一 ONLYOFFICE callback JTI 最多一次进入 `processing`
- 相关 Python 文件通过 Ruff、py_compile 和 ORM mapper 配置;定向发布、安全与风险规则回归通过。
## 风险与开放问题
- 旧测试或内部调用若直接构造没有 `tenant_id``CurrentUserContext`,会按新信任边界 fail-closed应由对应调用方补齐真实租户而不是放宽服务契约。
- 两个旧费用风险测试使用尚未持久化、没有 claim tenant 的对象;共享租户作用域已要求先保存并确认归属,需由费用申请切片统一调整测试夹具。
- 两个旧风险发布测试手工注入 aggregate 后直接 promote与当前必须有真实质量 TestRun 的发布门禁不一致;需由发布门禁切片统一口径。
- ONLYOFFICE 实际回写依赖部署环境提供公网可解析、可信 TLS 的文档服务或安全代理;开发网络解析到保留/私网地址时会按设计拒绝。
- 企业间受控复制、平台资产签名发布和跨企业运维审计属于后续独立能力,不应通过放宽本轮隔离实现。
## 本轮实现记录
- 2026-07-17完成 AgentAsset、Version、Review、TestRun、Feedback 的结构化租户作用域、企业覆盖平台读取和跨企业 fail-closed。
- 2026-07-17完成可信目标企业风险场景、真实费用样本 SQL 首过滤、企业 TestRun 证据和稳定盲审身份。
- 2026-07-17完成 DB-backed ONLYOFFICE 一次性会话、平台只读、文档基线绑定与安全下载。
- 2026-07-17完成 `20260717_0026` 迁移及一次性 PostgreSQL `base → head``head → 0025 → head` 验证。
- 2026-07-17完成发布、监控、召回、运行时、调度、遥测、foundation 和风险规则生成链路的租户接线与容器回归。