15 KiB
角色硬编码与接口影响专项补充分析
适用范围:
leaudit-platform当前角色权限与地区隔离体系
文档定位:从主方案中拆出的专项补充稿,专门分析“硬编码角色如何改”和“哪些接口会被联动影响”。
1. 结论先行
当前项目里的角色硬编码不是零散问题,而是已经渗透到:
- 后端服务层能力派生
- 后端服务层动作放行
- 前端 UI 可见性和可编辑性
- 前端 guard、fallback、role mapping
因此,这类改造不能只改某一个 service,也不能只改权限表配置。
如果只改一部分,会出现 3 类典型问题:
- 后端 permission 已放行,但服务层仍按角色名拒绝
- 后端边界已收敛,但前端仍按旧角色逻辑展示按钮或入口
- 菜单、页面、接口、详情、下载这些边界继续不一致
正确做法是:
- 先识别所有硬编码角色位置
- 再区分它们的职责类型
- 最后按“能力抽象 -> 服务层替换 -> 前端去角色化”的顺序渐进改造
2. 当前硬编码角色的 4 种形态
2.1 服务层上下文派生型
这类代码并不直接判断某个 permission,而是把角色名先转成派生能力:
is_globalcan_manageis_super_adminis_area_adminbypass_area
典型位置:
documentServiceImpl.pygovdocServiceImpl.pyusageStatsServiceImpl.pycontractTemplateServiceImpl.pyrbacAdminServiceImpl.pyhomeServiceImpl.py
典型模式:
role_key IN ('super_admin', 'provincial_admin') => is_globalrole_key IN ('super_admin', 'provincial_admin', 'admin') => can_managerole_key = 'super_admin' => is_super_admin / bypass_area
本质问题:
- 角色名被直接当成能力模型
- 一旦出现新领域管理员,所有上下文派生逻辑都要重复改
2.2 服务层动作白名单型
这类代码直接按角色名决定某个业务动作是否允许。
典型位置:
ragDatasetServiceImpl.pyragChatServiceImpl.py
典型模式:
UserRole not in ("provincial_admin", "admin", "super_admin")user_role == "provincial_admin"UserRole == "admin"
本质问题:
- 控制器层已经做 permission 校验
- 服务层又加了角色白名单
- 结果会出现“有权限但角色名不对仍被拒绝”
这是当前最需要优先清理的硬编码类型。
2.3 前端 UI 能力硬编码型
这类代码不是后端鉴权,但会直接影响用户感知边界。
典型位置:
components/dify-dataset-manager/index.tsxcomponents/dify-dataset-manager/area-dataset-config.tsxhooks/use-area-dataset-config.ts
典型模式:
provincial_admin可编辑全部super_admin可编辑全部admin仅可编辑本地区
本质问题:
- 前端自己在解释“谁能管理”
- 即使后端以后完成去角色化,前端仍可能展示旧权限状态
2.4 前端兼容层硬编码型
这类代码主要存在于路由、guard 和迁移兼容层。
典型位置:
legal-platform-frontend/lib/auth/user-routes.tslegal-platform-frontend/lib/api/legacy/auth/user-routes.tslegal-platform-frontend/lib/auth/guard.tslegal-platform-frontend/lib/auth/cross-checking-access.ts
典型模式:
provincial_admin -> adminsuper_admin -> admindeveloper -> adminprovincial_admin自动获得“省局 area”候选
本质问题:
- 前端把真实角色体系压缩成少数桶位
- 继续保留会让权限模型越来越难以统一
3. 为什么不能直接删掉所有角色硬编码
不是所有角色硬编码都应该同一天删除,因为它们承担职责不同。
3.1 可优先替换的
优先替换:
- 服务层动作白名单
- RAG 管理动作角色白名单
原因:
- 这些逻辑已经和 permission 决策重复
- 删除后收益最大,副作用相对可控
3.2 需要先抽象再替换的
需要先抽象:
is_globalcan_manageis_super_adminis_area_admin
原因:
- 它们已经被多个模块用于拼接 SQL 过滤条件
- 直接删会导致大量数据边界逻辑断裂
正确做法:
- 先统一沉淀为能力派生层
- 再逐步把“角色名判断”替换为“能力决策”
3.3 可以保留为兼容层但必须收缩边界的
允许短期保留:
- route fallback
- role mapping
- 某些旧前端 guard
但必须满足:
- 只存在于适配层
- 不再扩散到新业务代码
- 后续有明确移除计划
4. 推荐的替换目标
当前很多角色语义,其实应该改写成能力语义。
建议的替换关系如下:
super_admin全局绕过 替换为:is_super_adminprovincial_admin => 全省可见替换为:effective_scope == ALLadmin => 本地区管理替换为:effective_scope == DEPT + domain manage permissioncommon => 自己的数据替换为:effective_scope == SELFprovincial_admin/admin/super_admin共用白名单 替换为:显式create/update/delete/managepermission
这一步的本质,是把“角色名”换成“能力决策”。
5. 建议新增的统一能力层
建议新增统一能力派生对象,例如:
is_super_adminhas_global_scopehas_area_scopecan_manage_rbaccan_manage_rag_datasetcan_manage_contract_templatescan_view_usage_statscan_bypass_home_area
这些能力不应直接写死在角色名上,而应由以下信息共同决策:
rolespermissionsdata_scope- 可选
condition_filter
建议新增两层统一能力:
ScopeContextProviderAdminCapabilityResolver
前者负责“当前用户的通用数据边界”,后者负责“某个业务域是否具备管理能力”。
6. 当前高风险冲突区
6.1 RAG 是最典型的双轨冲突区
当前链路是:
- 控制器层按 permission 校验
- 服务层又按
UserRole白名单校验 - 最终管理能力同时受 permission 和角色名双重控制
风险:
- 未来新增
rag_manager这类角色时,即使给了rag:dataset:manage/create/update/delete,服务层仍会拒绝
优先级:
- 最高
6.2 RBAC 管理域存在“角色管理能力”和“权限管理能力”双重耦合
当前链路是:
_assertManagePermission依赖can_manage_assertPermission再校验具体权限点
风险:
- 如果未来某用户拥有
rbac:*权限,但主角色不是admin/provincial_admin/super_admin,仍可能被挡在第一层
优先级:
- 高
6.3 文档 / 公文 / 统计存在共性上下文派生复制
这些模块并没有直接角色白名单放行,但都重复实现了:
is_globalcan_manageis_super_admin
风险:
- 改一个模块不改另一个,边界会漂移
优先级:
- 高
6.4 前端知识库管理仍在自行解释角色
风险:
- 后端已经 permission 化后,前端仍会显示不该显示的按钮
- 或相反,后端已允许,前端仍不展示入口
优先级:
- 高
7. 受影响接口分析
下面重点回答“其他接口会不会影响到”。
答案是:会,而且影响面不小。
7.1 文档模块
受影响接口包括:
POST /api/uploadGET /api/documents/listGET /api/documents/statusGET /api/documents/{DocumentId}GET /api/v3/review-points/{DocumentId}PATCH /api/v3/review-points/{ReviewPointResultId}/auditPATCH /api/v3/documents/{DocumentId}/confirmPOST /api/documents/{DocumentId}/attachmentsPUT /api/documents/{DocumentId}DELETE /api/documents/{DocumentId}
影响原因:
- 这些接口都依赖文档服务里的用户上下文派生和数据范围过滤
- 一旦
is_global/can_manage的计算逻辑变化,全部都会联动
7.2 公文模块
受影响接口包括:
POST /api/govdoc/documentsGET /api/govdoc/documentsGET /api/govdoc/documents/{documentId}PATCH /api/govdoc/documents/{documentId}DELETE /api/govdoc/documents/{documentId}POST /api/govdoc/runsGET /api/govdoc/runs/{runId}GET /api/govdoc/runs/{runId}/resultGET /api/govdoc/runs/{runId}/findingsGET /api/govdoc/runs/{runId}/entitiesGET /api/govdoc/runs/{runId}/structureGET /api/govdoc/runs/{runId}/outlineGET /api/govdoc/runs/{runId}/paragraphsGET /api/govdoc/runs/{runId}/report/htmlGET /api/govdoc/runs/{runId}/report/docxGET /api/govdoc/documents/{documentId}/original
影响原因:
- 公文模块也使用了同构的上下文派生
- 尤其结果、报告、下载类接口最容易出现“资源详情边界未同步”的问题
7.3 统计模块
受影响接口包括:
GET /api/v3/usage-stats/overviewGET /api/v3/usage-stats/trendsGET /api/v3/usage-stats/by-usersGET /api/v3/usage-stats/by-departmentsGET /api/v3/usage-stats/by-areasGET /api/v3/usage-stats/details
影响原因:
- 当前统计接口对管理员可见性有明显上下文派生依赖
- 去角色化后,这些接口需要统一切到“统计域 permission + scope”模型
7.4 RAG 模块
受影响接口包括:
GET /api/v3/rag/appsGET /api/v3/rag/apps/defaultGET /api/v3/rag/datasets/myGET /api/v3/rag/datasets/adminPOST /api/v3/rag/datasets/adminPUT /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/rolesPOST /api/v3/rbac/rolesPUT /api/v3/rbac/roles/{RoleId}DELETE /api/v3/rbac/roles/{RoleId}GET /api/v3/rbac/usersGET /api/admin/users/organizations/treeGET /api/v3/rbac/roles/{RoleId}/usersPOST /api/v3/rbac/users/{UserId}/rolesDELETE /api/v3/rbac/users/{UserId}/roles/{RoleId}GET /api/v3/rbac/users/{UserId}/rolesGET /api/v3/routesGET/PUT /api/rbac/roles/{RoleId}/routesGET/POST /api/v3/rbac/role-permissionsPOST /api/v3/rbac/roles/{RoleId}/accessGET /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/categoriesGET /api/v3/contract-templatesPOST /api/v3/contract-templatesGET /api/v3/contract-templates/searchGET /api/v3/contract-templates/{TemplateId}DELETE /api/v3/contract-templates/{TemplateId}
影响原因:
- 当前业务语义明确写着“地区管理员才能上传”
- 如果只去角色化、不补合同模板域显式权限,这类能力会失焦
7.8 中影响模块
中影响但必须纳入联调范围的还有:
- 交叉评查模块
- 评查点模块
- 评查点分组模块
- 规则配置模块
原因:
- 它们虽然不一定都直接依赖角色名白名单
- 但仍依赖 permission、route、入口和关系访问逻辑
- 一旦整体权限能力模型调整,也必须回归验证
8. 前端联动影响
不能只看后端接口,前端也会同步受影响。
8.1 菜单与路由
受影响位置:
Sidebar.tsxuser-routes.tscheck-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.tslib/auth/cross-checking-access.tslib/auth/session-user.tslib/auth/jwt.ts
风险:
user_role被继续当成完整权限模型使用
9. 推荐改造顺序
建议按下面顺序推进,避免同时炸开所有联动面。
9.1 第一阶段
先做平台能力层:
PermissionDecisionServiceScopeContextProviderAdminCapabilityResolver
目标:
- 不先改业务接口行为,只先统一决策能力
9.2 第二阶段
优先改 RAG:
- 清理服务层角色白名单
- 改为 permission + scope/policy 决策
原因:
- 这是最典型、收益也最大的双轨冲突区
9.3 第三阶段
接入共用上下文模块:
- 文档
- 公文
- 统计
原因:
- 它们共享大量上下文派生逻辑
- 最适合沉淀统一
QueryScopeBuilder
9.4 第四阶段
处理管理域:
- RBAC 管理
- 首页入口
- 合同模板
目标:
- 把“管理能力”从角色名迁移到领域 permission
9.5 第五阶段
清理前端角色解释层:
- role mapping
- fallback route
- guard
- UI 编辑能力判断
目标:
- 前端不再自行解释“谁是管理员”
10. 必做回归清单
建议单独维护一份“角色去硬编码回归清单”,至少覆盖:
- 用户拥有 permission,但主角色不是
admin/provincial_admin时,接口是否仍能正确访问 - 用户获得某领域管理权限后,是否无需改代码即可生效
- 菜单、按钮、接口、详情、下载、导出边界是否一致
- 前端是否仍存在基于
user_role的旧判断放大或缩小能力 - RAG 管理接口是否已完全摆脱角色白名单
- RBAC 管理接口是否已从“角色管理能力”切换到“权限管理能力”
- 文档、公文、统计是否仍存在模块间边界不一致
11. 最终建议
这次专项分析的核心结论只有一句话:
角色硬编码改造,本质上不是“替换几个 if”,而是把整套权限系统从“角色名驱动”升级为“能力决策驱动”。
如果只做局部替换,问题会更隐蔽。
如果按下面顺序推进,风险最低:
- 先抽象能力层
- 先处理 RAG 双轨冲突
- 再统一文档/公文/统计上下文派生
- 再处理 RBAC 管理域和首页入口
- 最后清理前端角色解释和 fallback
这样改完之后,项目才能真正从:
- “角色名决定能力”
走向:
- “权限点 + 数据范围 + 模块 policy 决定能力”
这才是后续权限平台可持续演进的正确方向。