交付出件箱

← 返回出件箱

PC 后台「用户管理 + 角色组 + 权限配置」现状 verify 与方案(2026-09-28)

基线:origin/test @ bc983dba(分支 feature/pc-rbac-plan,只读,未改任何代码)
取证:主会话亲跑 + 两个执行层子代理 tee 落盘原始输出(/tmp/rbac-verify-A.log 1598 行 · /tmp/rbac-verify-B.log),关键项主会话复核
标签:云机环境 · 代码与 test 库只读核查 · 非生产验收证据


一、一句话结论

底子已经有一半,但不能直接拿来给客户用。


二、14 维物理 verify(每条带出处 + 对照组)

D1 用户模型:单角色字段,写死 6 种

D2 Phase 57 现有权限机制:只能配 platform_staff 一个角色

D3 核心后端(app/)接口鉴权:菜单开关基本不生效

主会话亲跑:

require_menu( 在 app/api 使用处:2(全在 monitoring.py)
require_role("admin") 使用处:89 处 / 17 个文件(对照组)

D4 结算后端(settlement/backend,独立服务):菜单开关真生效

D5 JWT:role 单值,没有权限 claim

D6 登录:不按角色拦,也不按角色分流

D7 用户管理:后端全 admin 专属,菜单被藏

D8 前端:只做菜单显隐,没有页面级拦截

D9 test 库实况(sudo db-query test,只读)

D10 数据范围:后台没有「只看自己机构」的能力

D11 审计:改角色不留痕

D12 迁移与种子

D13 仓库已有计划:Phase 58 spec 停在纸面

D14 旁证:发现一个现存安全漏洞(与本需求无关,但必须报)


三、方案

3.1 为什么不照搬 Phase 58 原方案

Phase 58 做法 问题(按上面 verify)
users.role 从 CHECK 改为外键指向 roles 表 role 串被 require_role 89 处 + JWT + 结算 menu_guard + ROLE_PERMISSION_MAP 深度依赖,改语义影响面最大
自定义角色 = 泛化的 platform_staff platform_staff 在结算端自带 13 项操作权限(含收款确认/门店激活),客户账号挂上去等于把这些写操作一起放出去
可授权池只有结算 7 项 跟「把功能开放给客户」冲突,需长官重新定开放清单

3.2 推荐架构:角色组独立成表,账号挂角色组,不动 users.role 语义

数据层(新增,不改存量)
- console_role_groups:id · name · description · data_scope(all / own_org,见 3.4)· is_active · created_by · created_at/updated_at
- console_role_group_menus:(group_id, menu_key) 主键
- users.console_role_group_id:外键,可空
- users.role 新增一个值 console_user(CHECK 6→7,照 phase56_002 先例)。客户账号一律用这个 role:
- 它不在 ROLE_PERMISSION_MAP 里 → 结算端 17 项操作权限默认为空,不会像 platform_staff 那样自带写权限
- 它不在任何 require_role("admin") 里 → 默认什么都进不去,只靠角色组授权放行(默认拒绝)
- 存量 admin / platform_staff / role_menu_permissions 原样保留,零迁移、零行为变化

判定层(两个后端各改一处)
- 核心 app/auth/middleware.py:121 require_menu 与结算 settlement/backend/app/auth/menu_guard.py:25 require_menu 同步改:
admin 直接放行 → 若用户挂了角色组,查 console_role_group_menus → 否则走老逻辑查 role_menu_permissions(platform_staff 兼容)
- 每次请求实时查库(现状就是这样),改了角色组权限立即生效,不用重新登录;停用角色组或账号也立即生效
- 结算服务读核心库要新增两张只读镜像表(core_readonly.py)+ 数据库给结算账号授 SELECT 权限 —— 授权要 root 做,需提前约
- /api/me/menus 返回角色组菜单,前端菜单显隐自动跟上(D8 已是 allowed set 驱动)

接口层(工作量主体,按开放清单逐个做)
- 每个要开放给客户的菜单,把对应后端接口从 require_role("admin") 改成「admin 或 有该菜单权限」
- 结算 9 项已经是 require_menu,但同时挂着 zhaoyang 权限;给 console_user 要按菜单映射出对应的 zhaoyang 权限(Phase 58 §5 已设想「按 allowed_menus 映射细粒度权限」)
- 平台管理组的菜单(机构管理、席位活动等)目前全是 require_role("admin"),开哪个改哪个,不开的不动
- 永不进可授权池:角色组管理本身、用户管理、权限配置、审计日志、系统监控(防客户给自己提权)

前端
- 恢复「用户管理」菜单(8-08 被摘),新建账号时可选「客户账号」+ 选角色组;列表加角色组列、启用/停用
- 新增「角色组管理」页:列表 + 新建/编辑(名称 + 按 平台/结算/设置 分组勾选菜单)+ 停用;Phase 57 的权限配置页合并进来(Phase 58 已拍过这条)
- 加页面级拦截:AdminConsoleShell 按当前路由查 allowed set,无权限显示「无权限」,不再能手输 URL 看页面壳

审计
- 角色组的增删改、账号角色组变更、启停用 → 写审计,记操作人 id(补上 D11 的空洞)

3.3 分批(每批可独立发 test)

批 内容 依赖
A 底座 两张表 + users 加列 + CHECK 加 console_user 迁移;两端 require_menu 改造;角色组 CRUD 接口;审计;contract 测试 结算只读授权需 root
B 前端 角色组管理页 + 用户管理页改造 + 页面级无权限拦截 A
C 开放功能 按长官定的开放清单逐个改接口鉴权(+ 数据范围过滤) A、长官清单
D 验收 test 环境端到端:admin 建角色组「XX 客户」勾 N 个菜单 → 建客户账号挂上 → 登录只见 N 个菜单 → 手输其他 URL 显示无权限 → 调未授权接口 403 → 停用后立即 401/403 → 存量 admin/platform_staff 零回归 A-C

A、B 工作量相对固定;C 的大小完全取决于开放哪些菜单、要不要只看自己的数据。

3.4 最大的坑:功能权限 ≠ 数据权限

角色组只能控制「能进哪个菜单」。D10 已证实后台所有列表都是全平台数据。
例如把「结算列表」开给某个机构客户,他会看到所有机构的结算单。
如果客户只能看自己的数据,每个开放的菜单都要额外做按机构过滤(角色组 data_scope=own_org + 账号 linked_org_id + 接口过滤),这部分是按菜单逐个做的活,不能靠角色组一次解决。


四、需要长官拍板的 4 件事(拍完才能写 tech)

  1. 「客户」具体是谁? 门店机构 / 品牌方 / 渠道 / 其他。决定账号挂在哪(机构还是品牌),也决定数据怎么隔离。
  2. 首批开放哪几个功能? 列出菜单名即可(对照上面 20 项)。直接决定批 C 的工作量。
  3. 客户看全平台数据,还是只看自己的? 如果只看自己的,每个开放菜单都要加数据过滤,工作量翻倍。
  4. Phase 58 的 D1(自定义角色只能授权结算 7 项)作废吗? 本方案的「可授权池」按长官新清单来,并且永远排除角色管理、用户管理、审计、监控。

另:D14 题库审核接口没有角色校验,建议马上单独修(改动约 4 行 + 测试),和本需求分开发。