Files
X-Financial/document/development/2026-07-17/feature/agent-asset-tenant-security/CONCEPT.md

189 lines
13 KiB
Markdown
Raw Normal View History

# 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 和风险规则生成链路的租户接线与容器回归。