Files
leaudit-platform-backend/docs/权限与地区隔离/角色硬编码与接口影响专项补充分析.md

608 lines
15 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.
# 角色硬编码与接口影响专项补充分析
> 适用范围:`leaudit-platform` 当前角色权限与地区隔离体系
> 文档定位:从主方案中拆出的专项补充稿,专门分析“硬编码角色如何改”和“哪些接口会被联动影响”。
---
## 1. 结论先行
当前项目里的角色硬编码不是零散问题,而是已经渗透到:
- 后端服务层能力派生
- 后端服务层动作放行
- 前端 UI 可见性和可编辑性
- 前端 guard、fallback、role mapping
因此,这类改造不能只改某一个 service,也不能只改权限表配置。
如果只改一部分,会出现 3 类典型问题:
1. 后端 permission 已放行,但服务层仍按角色名拒绝
2. 后端边界已收敛,但前端仍按旧角色逻辑展示按钮或入口
3. 菜单、页面、接口、详情、下载这些边界继续不一致
正确做法是:
1. 先识别所有硬编码角色位置
2. 再区分它们的职责类型
3. 最后按“能力抽象 -> 服务层替换 -> 前端去角色化”的顺序渐进改造
---
## 2. 当前硬编码角色的 4 种形态
## 2.1 服务层上下文派生型
这类代码并不直接判断某个 permission,而是把角色名先转成派生能力:
- `is_global`
- `can_manage`
- `is_super_admin`
- `is_area_admin`
- `bypass_area`
典型位置:
- `documentServiceImpl.py`
- `govdocServiceImpl.py`
- `usageStatsServiceImpl.py`
- `contractTemplateServiceImpl.py`
- `rbacAdminServiceImpl.py`
- `homeServiceImpl.py`
典型模式:
- `role_key IN ('super_admin', 'provincial_admin') => is_global`
- `role_key IN ('super_admin', 'provincial_admin', 'admin') => can_manage`
- `role_key = 'super_admin' => is_super_admin / bypass_area`
本质问题:
- 角色名被直接当成能力模型
- 一旦出现新领域管理员,所有上下文派生逻辑都要重复改
## 2.2 服务层动作白名单型
这类代码直接按角色名决定某个业务动作是否允许。
典型位置:
- `ragDatasetServiceImpl.py`
- `ragChatServiceImpl.py`
典型模式:
- `UserRole not in ("provincial_admin", "admin", "super_admin")`
- `user_role == "provincial_admin"`
- `UserRole == "admin"`
本质问题:
- 控制器层已经做 permission 校验
- 服务层又加了角色白名单
- 结果会出现“有权限但角色名不对仍被拒绝”
这是当前最需要优先清理的硬编码类型。
## 2.3 前端 UI 能力硬编码型
这类代码不是后端鉴权,但会直接影响用户感知边界。
典型位置:
- `components/dify-dataset-manager/index.tsx`
- `components/dify-dataset-manager/area-dataset-config.tsx`
- `hooks/use-area-dataset-config.ts`
典型模式:
- `provincial_admin` 可编辑全部
- `super_admin` 可编辑全部
- `admin` 仅可编辑本地区
本质问题:
- 前端自己在解释“谁能管理”
- 即使后端以后完成去角色化,前端仍可能展示旧权限状态
## 2.4 前端兼容层硬编码型
这类代码主要存在于路由、guard 和迁移兼容层。
典型位置:
- `legal-platform-frontend/lib/auth/user-routes.ts`
- `legal-platform-frontend/lib/api/legacy/auth/user-routes.ts`
- `legal-platform-frontend/lib/auth/guard.ts`
- `legal-platform-frontend/lib/auth/cross-checking-access.ts`
典型模式:
- `provincial_admin -> admin`
- `super_admin -> admin`
- `developer -> admin`
- `provincial_admin` 自动获得“省局 area”候选
本质问题:
- 前端把真实角色体系压缩成少数桶位
- 继续保留会让权限模型越来越难以统一
---
## 3. 为什么不能直接删掉所有角色硬编码
不是所有角色硬编码都应该同一天删除,因为它们承担职责不同。
## 3.1 可优先替换的
优先替换:
- 服务层动作白名单
- RAG 管理动作角色白名单
原因:
- 这些逻辑已经和 permission 决策重复
- 删除后收益最大,副作用相对可控
## 3.2 需要先抽象再替换的
需要先抽象:
- `is_global`
- `can_manage`
- `is_super_admin`
- `is_area_admin`
原因:
- 它们已经被多个模块用于拼接 SQL 过滤条件
- 直接删会导致大量数据边界逻辑断裂
正确做法:
- 先统一沉淀为能力派生层
- 再逐步把“角色名判断”替换为“能力决策”
## 3.3 可以保留为兼容层但必须收缩边界的
允许短期保留:
- route fallback
- role mapping
- 某些旧前端 guard
但必须满足:
1. 只存在于适配层
2. 不再扩散到新业务代码
3. 后续有明确移除计划
---
## 4. 推荐的替换目标
当前很多角色语义,其实应该改写成能力语义。
建议的替换关系如下:
- `super_admin` 全局绕过
替换为:`is_super_admin`
- `provincial_admin => 全省可见`
替换为:`effective_scope == ALL`
- `admin => 本地区管理`
替换为:`effective_scope == DEPT + domain manage permission`
- `common => 自己的数据`
替换为:`effective_scope == SELF`
- `provincial_admin/admin/super_admin` 共用白名单
替换为:显式 `create/update/delete/manage` permission
这一步的本质,是把“角色名”换成“能力决策”。
---
## 5. 建议新增的统一能力层
建议新增统一能力派生对象,例如:
- `is_super_admin`
- `has_global_scope`
- `has_area_scope`
- `can_manage_rbac`
- `can_manage_rag_dataset`
- `can_manage_contract_templates`
- `can_view_usage_stats`
- `can_bypass_home_area`
这些能力不应直接写死在角色名上,而应由以下信息共同决策:
- `roles`
- `permissions`
- `data_scope`
- 可选 `condition_filter`
建议新增两层统一能力:
1. `ScopeContextProvider`
2. `AdminCapabilityResolver`
前者负责“当前用户的通用数据边界”,后者负责“某个业务域是否具备管理能力”。
---
## 6. 当前高风险冲突区
## 6.1 RAG 是最典型的双轨冲突区
当前链路是:
1. 控制器层按 permission 校验
2. 服务层又按 `UserRole` 白名单校验
3. 最终管理能力同时受 permission 和角色名双重控制
风险:
- 未来新增 `rag_manager` 这类角色时,即使给了 `rag:dataset:manage/create/update/delete`,服务层仍会拒绝
优先级:
- 最高
## 6.2 RBAC 管理域存在“角色管理能力”和“权限管理能力”双重耦合
当前链路是:
1. `_assertManagePermission` 依赖 `can_manage`
2. `_assertPermission` 再校验具体权限点
风险:
- 如果未来某用户拥有 `rbac:*` 权限,但主角色不是 `admin/provincial_admin/super_admin`,仍可能被挡在第一层
优先级:
-
## 6.3 文档 / 公文 / 统计存在共性上下文派生复制
这些模块并没有直接角色白名单放行,但都重复实现了:
- `is_global`
- `can_manage`
- `is_super_admin`
风险:
- 改一个模块不改另一个,边界会漂移
优先级:
-
## 6.4 前端知识库管理仍在自行解释角色
风险:
- 后端已经 permission 化后,前端仍会显示不该显示的按钮
- 或相反,后端已允许,前端仍不展示入口
优先级:
-
---
## 7. 受影响接口分析
下面重点回答“其他接口会不会影响到”。
答案是:**会,而且影响面不小。**
## 7.1 文档模块
受影响接口包括:
- `POST /api/upload`
- `GET /api/documents/list`
- `GET /api/documents/status`
- `GET /api/documents/{DocumentId}`
- `GET /api/v3/review-points/{DocumentId}`
- `PATCH /api/v3/review-points/{ReviewPointResultId}/audit`
- `PATCH /api/v3/documents/{DocumentId}/confirm`
- `POST /api/documents/{DocumentId}/attachments`
- `PUT /api/documents/{DocumentId}`
- `DELETE /api/documents/{DocumentId}`
影响原因:
- 这些接口都依赖文档服务里的用户上下文派生和数据范围过滤
- 一旦 `is_global/can_manage` 的计算逻辑变化,全部都会联动
## 7.2 公文模块
受影响接口包括:
- `POST /api/govdoc/documents`
- `GET /api/govdoc/documents`
- `GET /api/govdoc/documents/{documentId}`
- `PATCH /api/govdoc/documents/{documentId}`
- `DELETE /api/govdoc/documents/{documentId}`
- `POST /api/govdoc/runs`
- `GET /api/govdoc/runs/{runId}`
- `GET /api/govdoc/runs/{runId}/result`
- `GET /api/govdoc/runs/{runId}/findings`
- `GET /api/govdoc/runs/{runId}/entities`
- `GET /api/govdoc/runs/{runId}/structure`
- `GET /api/govdoc/runs/{runId}/outline`
- `GET /api/govdoc/runs/{runId}/paragraphs`
- `GET /api/govdoc/runs/{runId}/report/html`
- `GET /api/govdoc/runs/{runId}/report/docx`
- `GET /api/govdoc/documents/{documentId}/original`
影响原因:
- 公文模块也使用了同构的上下文派生
- 尤其结果、报告、下载类接口最容易出现“资源详情边界未同步”的问题
## 7.3 统计模块
受影响接口包括:
- `GET /api/v3/usage-stats/overview`
- `GET /api/v3/usage-stats/trends`
- `GET /api/v3/usage-stats/by-users`
- `GET /api/v3/usage-stats/by-departments`
- `GET /api/v3/usage-stats/by-areas`
- `GET /api/v3/usage-stats/details`
影响原因:
- 当前统计接口对管理员可见性有明显上下文派生依赖
- 去角色化后,这些接口需要统一切到“统计域 permission + scope”模型
## 7.4 RAG 模块
受影响接口包括:
- `GET /api/v3/rag/apps`
- `GET /api/v3/rag/apps/default`
- `GET /api/v3/rag/datasets/my`
- `GET /api/v3/rag/datasets/admin`
- `POST /api/v3/rag/datasets/admin`
- `PUT /api/v3/rag/datasets/admin/{DatasetId}`
- `DELETE /api/v3/rag/datasets/admin/{DatasetId}`
- `GET /api/v3/rag/datasets/{DatasetId}`
- `PATCH /api/v3/rag/datasets/{DatasetId}`
- 各类 `/datasets/{DatasetId}/documents`
- 各类 `/datasets/{DatasetId}/segments`
- 各类检索测试接口
- `POST /api/v3/rag/chat/messages`
- 会话、消息反馈、会话重命名、删除等接口
影响原因:
- 控制器层和服务层当前存在双轨权限逻辑
- 这是最典型的“permission 改了,接口仍会被角色名卡住”的模块
## 7.5 RBAC 管理模块
受影响接口包括:
- `GET /api/v3/rbac/roles`
- `POST /api/v3/rbac/roles`
- `PUT /api/v3/rbac/roles/{RoleId}`
- `DELETE /api/v3/rbac/roles/{RoleId}`
- `GET /api/v3/rbac/users`
- `GET /api/admin/users/organizations/tree`
- `GET /api/v3/rbac/roles/{RoleId}/users`
- `POST /api/v3/rbac/users/{UserId}/roles`
- `DELETE /api/v3/rbac/users/{UserId}/roles/{RoleId}`
- `GET /api/v3/rbac/users/{UserId}/roles`
- `GET /api/v3/routes`
- `GET/PUT /api/rbac/roles/{RoleId}/routes`
- `GET/POST /api/v3/rbac/role-permissions`
- `POST /api/v3/rbac/roles/{RoleId}/access`
- `GET /api/v3/routes/{RouteId}/permissions`
影响原因:
- 当前同时依赖 `_assertManagePermission``_assertPermission`
- 第一层还是角色派生管理能力
## 7.6 首页入口模块
受影响接口:
- `GET /api/home/entry-modules`
影响原因:
- 当前首页入口存在 `super_admin` 的 area bypass 语义
- 这类入口可见性也属于权限边界的一部分
## 7.7 合同模板模块
受影响接口包括:
- `GET /api/v3/contract-templates/categories`
- `GET /api/v3/contract-templates`
- `POST /api/v3/contract-templates`
- `GET /api/v3/contract-templates/search`
- `GET /api/v3/contract-templates/{TemplateId}`
- `DELETE /api/v3/contract-templates/{TemplateId}`
影响原因:
- 当前业务语义明确写着“地区管理员才能上传”
- 如果只去角色化、不补合同模板域显式权限,这类能力会失焦
## 7.8 中影响模块
中影响但必须纳入联调范围的还有:
- 交叉评查模块
- 评查点模块
- 评查点分组模块
- 规则配置模块
原因:
- 它们虽然不一定都直接依赖角色名白名单
- 但仍依赖 permission、route、入口和关系访问逻辑
- 一旦整体权限能力模型调整,也必须回归验证
---
## 8. 前端联动影响
不能只看后端接口,前端也会同步受影响。
## 8.1 菜单与路由
受影响位置:
- `Sidebar.tsx`
- `user-routes.ts`
- `check-route-permission.ts`
- fallback route mapping
风险:
- 菜单可见性与真实接口权限继续分叉
- 某些角色被映射压扁后,真实权限无法完整反映
## 8.2 RAG 管理页面
受影响位置:
- `components/dify-dataset-manager/*`
- `hooks/use-area-dataset-config.ts`
风险:
- 后端能力已收敛,前端仍按旧角色名展示编辑入口
## 8.3 页面 guard
受影响位置:
- `lib/auth/guard.ts`
- `lib/auth/cross-checking-access.ts`
- `lib/auth/session-user.ts`
- `lib/auth/jwt.ts`
风险:
- `user_role` 被继续当成完整权限模型使用
---
## 9. 推荐改造顺序
建议按下面顺序推进,避免同时炸开所有联动面。
## 9.1 第一阶段
先做平台能力层:
- `PermissionDecisionService`
- `ScopeContextProvider`
- `AdminCapabilityResolver`
目标:
- 不先改业务接口行为,只先统一决策能力
## 9.2 第二阶段
优先改 RAG
- 清理服务层角色白名单
- 改为 permission + scope/policy 决策
原因:
- 这是最典型、收益也最大的双轨冲突区
## 9.3 第三阶段
接入共用上下文模块:
- 文档
- 公文
- 统计
原因:
- 它们共享大量上下文派生逻辑
- 最适合沉淀统一 `QueryScopeBuilder`
## 9.4 第四阶段
处理管理域:
- RBAC 管理
- 首页入口
- 合同模板
目标:
- 把“管理能力”从角色名迁移到领域 permission
## 9.5 第五阶段
清理前端角色解释层:
- role mapping
- fallback route
- guard
- UI 编辑能力判断
目标:
- 前端不再自行解释“谁是管理员”
---
## 10. 必做回归清单
建议单独维护一份“角色去硬编码回归清单”,至少覆盖:
1. 用户拥有 permission,但主角色不是 `admin/provincial_admin` 时,接口是否仍能正确访问
2. 用户获得某领域管理权限后,是否无需改代码即可生效
3. 菜单、按钮、接口、详情、下载、导出边界是否一致
4. 前端是否仍存在基于 `user_role` 的旧判断放大或缩小能力
5. RAG 管理接口是否已完全摆脱角色白名单
6. RBAC 管理接口是否已从“角色管理能力”切换到“权限管理能力”
7. 文档、公文、统计是否仍存在模块间边界不一致
---
## 11. 最终建议
这次专项分析的核心结论只有一句话:
**角色硬编码改造,本质上不是“替换几个 if”,而是把整套权限系统从“角色名驱动”升级为“能力决策驱动”。**
如果只做局部替换,问题会更隐蔽。
如果按下面顺序推进,风险最低:
1. 先抽象能力层
2. 先处理 RAG 双轨冲突
3. 再统一文档/公文/统计上下文派生
4. 再处理 RBAC 管理域和首页入口
5. 最后清理前端角色解释和 fallback
这样改完之后,项目才能真正从:
- “角色名决定能力”
走向:
- “权限点 + 数据范围 + 模块 policy 决定能力”
这才是后续权限平台可持续演进的正确方向。