数据平台权限管理:从 Scoped RBAC 到 ABAC 与 ReBAC
数据平台的权限问题,往往不是“这个人有没有登录”,而是“这个人能否在项目 A 中查看某段视频、导出原始文件、运行高成本任务,以及修改其他人的标注”。按钮能不能显示,只是这套系统的最后一层表现。
本文参考 Data Flywheel 的“设计权限管理系统”讨论,以及 2026-09-06 阅读到的本地代码。当前方案是 Keycloak 负责身份认证,FastAPI 管理应用会话,平台用户/组、角色与作用域负责业务授权,PyCasbin 执行角色权限判断。以下将现有实现与后续扩展建议分开说明。
1. 先分清认证、授权和数据过滤
| 层次 | 回答的问题 | 数据平台中的例子 |
|---|---|---|
| Authentication | 你是谁 | 通过 Keycloak 完成登录 |
| Authorization | 你能做什么 | 是否允许执行 dataset:export |
| Data Scope | 你能操作哪些对象 | 只允许导出项目 A 的授权数据 |
| Audit | 谁在什么情况下做了什么 | 记录导出人、数据版本、范围与结果 |
Keycloak 支持独立账号与身份集成,因此没有企业 SSO 也可以先建设平台登录。选择它管理账号,不意味着需要把每个视频和每条需求的授权都塞进身份系统。参见 Keycloak 官方介绍。
本文建议将身份源与平台内部用户分离,外部身份用 (issuer, subject) 映射到稳定的内部用户 ID。这是扩展建议:当前代码使用 keycloak_subject 字段处理既定身份源,不能直接把它视为已经实现了任意多身份源绑定。
2. RBAC、ABAC、ReBAC 如何选择
这些模型解决的复杂性不同,可以组合使用。下表用于工程决策,不是产品排名。模型概念可参见 OpenFGA 授权概念。
| 模型 | 基本表达 | 适合的业务 | 主要代价 |
|---|---|---|---|
| ACL | 对象列出谁能做什么 | 少量对象的直接共享 | 对象多时维护与继承困难 |
| RBAC | 用户通过角色获得操作权限 | 管理员、工程师、标注员分工 | 粗粒度角色难表达复杂条件 |
| Scoped RBAC | 角色绑定限定生效范围 | 同一人在不同项目担任不同角色 | 需要明确作用域继承与越界规则 |
| ABAC | 根据用户、资源与环境属性判断 | 敏感等级、地区、用途、有效期限制 | 属性可信度、规则冲突与解释成本 |
| ReBAC | 根据对象关系推导授权 | 团队、目录、数据集共享与继承 | 关系模型、查询与一致性更复杂 |
对 Data Flywheel 当前阶段,项目级 Scoped RBAC 加必要的业务条件足够直接。不要为每个项目复制一套角色;应把“编辑者”作为可复用角色,把“项目 A”放到授权绑定中。
如果以后出现复杂目录共享,再评估 ReBAC;如果数据访问大量依赖标签、区域和用途,再评估 ABAC。简单地增加一个 owner_id == user_id 检查,不代表已经需要建设通用属性策略平台。
3. 当前项目如何实现 Scoped RBAC
核心对象可以表达为:
| |
例如:
| 主体 | 角色 | 作用域 |
|---|---|---|
| 算法组 | 数据查看者 | 项目 A |
| 数据工程组 | 数据编辑者 | 项目 A |
| 用户甲 | 数据导出者 | 项目 A |
| 用户甲 | 数据查看者 | 项目 B |
当前 apps/api/app/rbac.py 先从数据库筛选用户直接绑定与用户组绑定,保留全局或匹配当前 scope 的角色,再交给 PyCasbin 判断资源与动作。casbin_model.conf 本身使用的是 sub, obj, act 三元模型,作用域过滤在外层 Python 代码完成,并非直接使用四元 domain 模型。
这与 Casbin 官方提供的 RBAC with Domains 是两种实现路径。文章描述设计时要看实际执行位置,不能因为系统叫 Scoped RBAC,就假定配置文件中一定存在 domain。
目前允许权限取并集,没有匹配授权则拒绝。账号停用、会话限制和资源归属应作为额外约束检查;用户具有某项操作权限,并不应绕过对象范围。
4. 后端必须执行完整的数据边界
推荐的请求处理顺序是:
| |
客户端提交的 project_id 是选择条件,不能作为授权证据。列表、详情、统计、导出和文件访问应遵循同一套范围规则,否则用户可能无法打开详情,却能从统计接口或下载地址拿到信息。
Data Flywheel 的需求模块提供了一个具体例子:在现有业务规则下,拥有管理权限的人可以查看更广范围;普通开发角色只读取自己发起或负责的需求。交付动作还会校验负责人,验收动作对非管理用户校验发起人。这里的“角色操作许可 + 对象关系”共同决定结果。
面向其他数据对象,还应考虑:
- 行级:按租户、项目、数据集或负责人过滤记录。
- 列级:隐藏或脱敏敏感字段,同时限制排序、搜索、聚合等间接访问。
- 对象存储:播放、缩略图、下载都经服务端授权,短期签名链接的有效期与撤权窗口匹配。
- 批量操作:验证所有目标对象,明确全部失败还是返回逐项结果,不能只检查第一条。
- 检索:在可信查询层限定候选范围,并在返回前复核;结果片段、计数和缓存同样受限。
Apache Ranger 提供面向数据服务的集中策略与插件集成能力;是否能实施某种细粒度控制,要看具体引擎与插件支持。它适合评估多种数据引擎统一治理的场景,而非替代所有应用业务规则。参见 Apache Ranger。
5. 策略引擎不等于执行边界
可以把授权系统拆为三个职责:管理策略、做出决策、执行限制。OPA 侧重把策略决策与应用逻辑分离,但应用仍要在正确位置调用并执行结果。参见 OPA 官方文档。
| 选择 | 更适合评估的条件 | 不应误以为 |
|---|---|---|
| PyCasbin | Python 应用内角色规则、嵌入式决策 | 自动过滤所有 SQL 与文件访问 |
| OPA | 多服务共享属性规则与策略测试 | 引擎返回 allow 后就有完整数据隔离 |
| OpenFGA | 对象共享、组织与目录关系复杂 | 接入后不再需要资源查询与撤权设计 |
| Ranger | 数据湖与多引擎访问治理 | 安装管理端后所有存储直连都被覆盖 |
当前代码逐次构造授权判断所需状态,逻辑容易理解。将来优化缓存时,需要一起设计权限变更版本、撤权失效、最长陈旧窗口与失效失败行为。不能只为减少数据库查询而让已撤销权限长时间有效。
6. 受控代登录:保留两种身份
排查用户问题时,“管理员看到的页面”和“普通用户看到的页面”可能不同。当前项目实现了应用层 Impersonation,核心区别是:
| |
授权按有效用户执行,审计保留实际操作者。代操作不能把管理员权限叠加到普通用户身上,否则无法验证真实用户体验。
目前代码包含专用代登录权限、原因、期限、禁止嵌套、CSRF 检查和前端持续提示。后续应继续验证目标停用、权限撤销、会话超时、多标签页与缓存切换等情况。代登录可以帮助排障,但不能代替对真实登录流程的测试。
浏览器使用 HttpOnly Cookie 有助于限制脚本直接读取会话值,修改操作仍需要 CSRF 等配套控制;它也不意味着 XSS 后的操作风险消失。生产配置必须与本地自动开发会话分开。
7. 用反向案例验证权限
以下测试比“管理员能打开页面”更有价值:
| 场景 | 期望结果 |
|---|---|
| 将项目 A 的对象 ID 换成项目 B 的 ID | 后端拒绝或按统一策略隐藏对象存在性 |
| 开发者直接调用他人需求的交付 API | 拒绝 |
| 批量导出混入一个无权限对象 | 按约定拒绝或返回明确的逐项拒绝结果 |
| 移出用户组后继续访问旧缓存 | 在定义的撤权时限内失效 |
| 开始或结束代登录后返回上一页 | 不残留其他身份的业务数据 |
| API 返回无权限 | 前端显示受限状态,不能用演示数据伪装成功 |
数据平台的授权体系应围绕具体对象与操作逐步完善。插件任务如何获取受限能力,继续见 Operator as Plugin 的可行性讨论;界面如何呈现权限与身份状态,见 UI 设计与视觉规范。