Files
leaudit-platform-backend/docs/权限与地区隔离/权限、租户能力与数据范围职责边界说明.md

317 lines
8.5 KiB
Markdown

# 权限、租户能力与数据范围职责边界说明
> 适用范围:`leaudit-platform` 当前 `RBAC + 租户主数据 + 多租户业务挂载` 体系
> 更新日期:2026-05-21
> 文档定位:专门解决“角色权限配置”和“租户管理里的功能开关/承载能力”看起来重复的问题,明确三者各自负责什么、不负责什么。
---
## 1. 先看结论
当前系统里确实存在“语义重叠感”,但不应该把这三类配置看成同一层:
1. `角色权限`
- 控制“人能不能操作”
2. `租户功能开关`
- 控制“某租户是否开放某类业务能力”
3. `租户业务承载能力`
- 控制“某租户能不能作为某类业务的数据归属方/挂载方”
4. `数据范围`
- 控制“当前人最终能看到哪些租户、哪些数据”
如果只看页面字段,不看后端职责边界,就会误以为“租户页和角色权限页都在管入口模块、文档上传、知识库”。
这正是当前用户感知混乱的根因。
---
## 2. 三层控制分别管什么
## 2.1 角色权限:管人
角色权限回答的问题是:
- 这个用户能不能进入某页面
- 这个用户能不能新建、编辑、删除、发布、导出
- 这个用户能不能调用某个接口
典型例子:
- `entry_module:create`
- `entry_module:update`
- `documents:upload:create`
- `rag:dataset:manage`
- `rbac:tenants:update`
结论:
- 角色权限只决定“用户有没有操作资格”
- 不决定“数据应该挂在哪个租户下”
- 也不决定“某个租户是否承载某类业务”
---
## 2.2 租户功能开关:管租户是否开放业务能力
租户功能开关回答的问题是:
- 这个租户是否启用了入口模块能力
- 这个租户是否启用了文档上传能力
- 这个租户是否启用了知识库能力
当前主字段:
- `sys_tenant_feature_flags.feature_key`
- 前端对应 `feature_keys`
当前真实语义应理解为:
- “该租户是否开放该业务能力”
- 更偏向租户产品能力配置,不是用户授权配置
结论:
- 功能开关不授予用户权限
- 它只决定“租户是否开放某业务”
---
## 2.3 租户业务承载能力:管数据能不能落到该租户
租户承载能力回答的问题是:
- 新建入口模块时,这个租户能不能被选为适用租户
- 新建文档、模板、知识库时,这个租户能不能作为归属租户
当前主字段:
- `can_host_entry_module`
- `can_host_documents`
- `can_host_rag`
- `can_host_templates`
这类字段和 `feature_keys` 很像,但不是一回事:
- `feature_keys` 更偏“租户是否开放该业务”
- `can_host_*` 更偏“租户能否被选为该业务的数据挂载对象”
结论:
- 承载能力不等于操作权限
- 也不完全等于功能开关
- 它主要服务于“业务归属”和“数据落点”控制
---
## 2.4 数据范围:管最终可见边界
数据范围回答的问题是:
- 这个用户最终能看到哪些租户的数据
- 这个用户能看全部、部门、自身、公共、关系型资源中的哪些部分
它最终会把下面几件事合并起来:
1. 用户身份
2. 角色权限
3. 用户归属租户
4. 目标资源归属租户
5. 模块策略
结论:
- 数据范围是最终访问边界
- 它不是租户主数据页里的布尔开关
- 也不是角色权限页里的单个 permission 就能表达清楚的
## 2.5 三层控制的防护性区别
这三层不是“配置位置不同的同一件事”,而是三道不同的防护闸:
1. `租户功能开关`
- 属于业务准入防护
- 防的是“该租户根本不该开放这类业务,却仍被看见或被使用”
2. `角色权限`
- 属于操作授权防护
- 防的是“租户虽然开放了该业务,但不是任何人都能新增、编辑、删除、发布”
3. `数据范围`
- 属于数据边界防护
- 防的是“有权限的人在操作时串看、串改到不属于自己范围的数据”
可以直接这样理解:
- `租户功能开关 = 模块级总闸`
- `角色权限 = 人的操作闸`
- `数据范围 = 数据行级边界闸`
任意一层缺失,风险都不同:
1. 只有功能开关,没有角色权限
- 结果:租户开了功能后,可能任何角色都能乱用
2. 只有角色权限,没有功能开关
- 结果:本不该承载该业务的租户,仍可能被误开放入口或误创建数据
3. 只有前两层,没有数据范围
- 结果:拥有权限的用户仍可能看到其他租户数据,形成越权或串租户泄漏
所以最终判断某个操作是否允许,不能只看一层,而应按下面顺序串起来判断:
1. 目标租户是否开放该业务
2. 当前用户是否具备该业务动作权限
3. 当前用户的数据范围是否允许访问该目标租户或目标资源
---
## 3. 为什么当前会让人觉得重复
当前用户会觉得重复,原因不是理解错了,而是系统现状确实还没完全收口:
1. 前端租户页把:
- `启用租户`
- `公共租户`
- `功能开关`
- `承载能力`
混在同一个表单区域里
2. 业务名称上和角色权限页高度重合
3. 部分下游模块还没有把 `can_host_*` 做成强执行链路
4. `feature_keys` 已经有使用点,但并不是所有模块都用同一模式接入
因此现在最准确的判断不是“完全重复”,而是:
- 设计上应该分层
- 落地上还处于过渡态
- 页面表达还不够清楚
补一句更贴近当前现状的话:
- 当前系统已经有三层模型雏形
- 但还没有在所有模块做到“租户能力先拦、权限再拦、数据范围最后收口”的稳定统一执行顺序
---
## 4. 当前代码里的真实分工
## 4.1 已经落到真实用途的部分
当前已经能确认的真实用途:
1. `feature_keys`
- 已被入口模块租户选择器消费
- `home.entry_module` 已有真实用途
2. `leaudit_entry_module_tenants`
- 已作为入口模块适用租户关系表使用
3. 用户当前上下文
- 已开始统一输出 `tenant_code / tenant_name`
4. 首页入口模块
- 已按用户租户 + 模块租户关系过滤
## 4.2 仍未完全闭环的部分
以下部分仍需继续补强:
1. `can_host_documents`
2. `can_host_rag`
3. `can_host_templates`
4. `feature_keys` 的统一消费规范
结论:
- 现在租户页里的部分字段已经不是摆设
- 但也还没有做到所有字段都形成“写入即生效”的全链路闭环
---
## 5. 最终建议的收口规则
## 5.1 角色权限页只做“人”的授权
角色权限页只保留:
- 页面访问权限
- 菜单权限
- 按钮权限
- 接口动作权限
- 数据范围策略
不要在角色权限页里表达:
- 某租户是否开放业务
- 某租户是否能承载业务数据
## 5.2 租户管理页只做“租户侧能力配置”
租户管理页应拆成 3 组:
1. 基础状态
- `启用租户`
- `公共租户`
2. 功能开关
- 某租户是否开放某业务能力
3. 业务承载能力
- 某租户能否作为某业务的数据归属方
并且页面必须明确提示:
- 这里不授予用户操作权限
- 用户是否能操作,仍由角色权限决定
## 5.3 数据范围执行器单独收口
数据范围不要继续分散在各业务模块里各写一套:
- 应把用户权限
- 用户租户
- 资源租户
- 公共数据策略
- 关系型特例
统一并入执行器。
---
## 6. 推荐页面文案
### 功能开关
说明:
- 决定该租户是否开放某类业务能力
- 不直接授予用户页面或按钮操作权限
### 业务承载能力
说明:
- 决定该租户能否作为该类业务的数据归属租户
- 会影响新建/编辑时该租户是否可被选中
### 角色权限
说明:
- 控制当前用户能不能访问页面、调用接口、执行动作
- 与租户侧能力配置是两层控制
---
## 7. 后续工程动作建议
优先级建议:
1. 前端租户页分组与文案改清楚
2.`can_host_documents / can_host_rag / can_host_templates` 补下游强校验
3. 梳理 `feature_keys` 的统一消费规范
4. 统一把“租户是否可选”沉到服务层,不让前端自己猜
5. 最后再把数据范围执行器接到更多业务域
---
## 8. 一句话原则
后续所有实现都应遵守下面这个原则:
- `角色权限` 决定“谁能操作”
- `租户功能开关` 决定“租户开不开这类业务”
- `租户承载能力` 决定“数据能不能挂到这个租户”
- `数据范围` 决定“最终能看到哪些数据”
只要这四层不再混写,后面的权限和多租户改造就不会继续失控。