Why: The approved role and permission implementation needs a focused, reviewable path through browser acceptance, component delivery, and guarded closure. Evidence: Structural audit confirms DR1-DR21, U1-U9, and KTD1-KTD6; git diff --check passes; six-lens document review findings are incorporated. Impact: Adds the stage 7-10 plan, records the focused specification approval, and indexes the plan without authorizing production migration or deployment. Refs: feature/iam-u4-role-authority
8.7 KiB
title, date, type, status, approved_at, supersedes_scope
| title | date | type | status | approved_at | supersedes_scope |
|---|---|---|---|---|---|
| 聚焦版角色与分层权限系统规格 | 2026-10-15 | feature | approved | 2026-10-15 | 2026-10-13-identity-role-permission-plan.md U4-U7 中超出本文的部分 |
聚焦版角色与分层权限系统规格
目标
实现用户要求的角色与权限系统,不引入账号恢复、多职位、Agent 授权或新的工作流能力。
首期角色等同企业职位。角色与用户分离:同一角色可分配给多个用户,每个用户同时只有一个生效角色。角色包含稳定 roleId、名称、可选描述、启用状态和版本。用户继续使用稳定 userId,姓名、电话、邮箱、工号、部门和备注均为可选资料,不参与授权主键。
权限分为三个配置面,但使用同一服务端动作目录和判定结果:
- 系统权限控制系统设置、账号管理、角色管理、权限上限和项目创建。
- 角色权限描述一个职位默认可以执行的动作及其上限,例如创建项目、查看获准目录信息。
- 项目权限只在一个项目内生效,由该项目创建者维护;A 项目的管理权不能用于 B 项目。
系统管理员负责账号、角色和平台权限上限。系统管理员没有他人项目业务内容的隐式访问权,读取项目正文、原件、草稿、业务审计或导出仍需该项目的明确授权。
范围
角色与用户
Role:roleId、名称、描述、状态、roleVersion、创建者、修改者和时间。UserRoleBinding:bindingId、userId、roleId、状态、版本、任命者和任命时间。- 一个用户最多一条有效 binding;同一角色允许任意多个有效用户 binding。
- 改名、编辑描述和停用角色不改变用户、项目或历史记录的稳定 ID。
- 停用角色时保留 binding 和审计记录,但其角色授权立即失效;不回落到更宽的默认权限。
- 任命停用角色、未知角色或停用账号均拒绝。
权限目录与三层授权
动作目录由受信注册表定义 actionId、中文名、说明、scope、分组、风险级别和是否受保护。请求不能通过动作字符串或请求体自行声明 scope。
首期系统动作至少包括:
| 分组 | 动作 |
|---|---|
| 系统设置 | system.settings.read、system.settings.write |
| 用户管理 | system.users.view、system.users.create、system.users.edit、system.users.disable |
| 角色管理 | system.roles.view、system.roles.manage、system.roles.assign |
| 权限管理 | system.permissions.view、system.permissions.manage |
| 项目入口 | project.create |
受保护的角色、权限和管理员管理动作只能来自明确系统授权,普通角色配置不能授予。project.create 可以来自明确系统授权或当前有效角色的允许项,但仍受平台上限约束。
项目动作至少包括 project.settings、project.members.manage、project.permissions.manage、document.duties.assign、document.read、document.author、document.review、document.approve。项目动作必须同时满足:账号有效、项目关系有效、当前角色有效、角色动作允许且未超过角色上限、平台未禁止、项目未明确拒绝、资源关系和文档状态允许。
项目创建者只获得自己项目的管理默认,且仍受平台和角色上限约束。创建者关系不授予其他项目权限,也不自动授予项目业务内容读取或代签能力。
角色权限
每个角色的动作配置包含 allowed 与 capAllowed:
allowed=true表示该角色默认允许动作。capAllowed=false表示该角色不能通过项目覆盖获得该动作。allowed=true && capAllowed=false是非法配置,服务端拒绝。- 未知动作和未配置动作默认拒绝。
- 角色默认和上限不得写入超出操作者可委派范围的受保护动作。
首期一个用户只有一个有效角色,因此一次判定只使用该 binding,不进行跨角色动作或范围拼接。
项目权限和文档三权
RDM 保留项目成员、项目创建者、项目内覆盖和现有文档三权的领域权威。Backend 负责角色、binding、系统动作和平台/角色上限。
项目覆盖使用 inherit | allow | deny:allow 不能突破平台或角色上限,deny 明确阻断。项目创建者只能修改自己创建项目的成员、权限和文档三权。
文档编制、审核、批准继续使用现有 RDM 三权记录;分配候选必须是该项目有效成员,账号和角色有效,并具有对应动作上限。角色只提供资格,不能代替具体文档职责。
服务端接口
Backend 成为角色和系统权限的唯一写入权威,提供版本化接口:
GET /api/admin/permissions/catalogGET /api/admin/roles,支持名称精确/模糊搜索、状态和动作过滤、分页POST /api/admin/rolesGET /api/admin/roles/{roleId}PATCH /api/admin/roles/{roleId},使用expectedRoleVersionPUT /api/admin/roles/{roleId}/permissions,批量提交固定 actionId 列表和expectedRoleVersionPUT /api/admin/users/{userId}/role,替换该用户唯一有效角色,使用用户状态版本和幂等键GET /api/admin/users/{userId}/roleGET /api/admin/users/{userId}/system-grantsPUT /api/admin/users/{userId}/system-grants,批量提交明确系统动作并使用expectedAuthorityEpoch和幂等键GET /api/authz/explain,仅解释调用者自己或其有权管理的对象
批量权限修改在单库事务内全部成功或全部失败;任一未知动作、越权、越上限或版本冲突不得部分写入。响应返回新版本、实际差异和审计回执标识。
RDM 通过公开、固定版本的 Backend 角色读取/判定契约消费角色数据。RDM 原 rdm.role.* 写命令在迁移期转发 Backend;切换完成后不得继续写 RDM 旧角色表。读取失败、提供者缺失或版本不兼容时拒绝权限动作。
配置界面
系统管理区增加“角色与权限”入口:
- 角色列表显示名称、描述、状态和绑定人数,支持精确搜索、模糊搜索和状态过滤。
- 单角色视图编辑名称、描述、状态及权限。
- 角色矩阵以角色为行、权限为列,按权限分组和分页展示。
- 单角色视图与矩阵使用同一草稿模型和同一批量接口。
- 过滤隐藏的单元格不丢失草稿;全选只作用于当前明确显示的固定 actionId 集合。
- 提交前显示差异;冲突保留草稿并要求刷新,不逐格即时写库。
用户详情显示当前角色和明确系统授权来源,并允许有 system.roles.assign 权限的操作者从有效角色中选择。有 system.permissions.manage 权限的操作者可以编辑目标用户的系统授权,但不能撤销最后一个有效权限管理者。用户可选资料继续用于展示、搜索和辨识,不参与权限计算。
项目设置继续使用现有成员和权限入口,显示角色默认、角色上限、项目覆盖及最终允许/拒绝原因。项目创建者无法切换到其他项目后沿用原项目管理权。
迁移与兼容
- 导出 RDM 当前角色、动作配置和账号单角色绑定,包含来源版本与摘要。
- 以原
roleId导入 Backend;为每个账号生成一个稳定 binding。 - 对角色数量、绑定数量、动作默认/上限和引用逐项比对。
- 在迁移窗口内禁止双主写;RDM 写入口改为转发 Backend。
- 切换消费者后保留旧表只读,确认无回退依赖后再另行批准清理。
迁移失败不得启用部分角色数据;恢复时保留已撤销、停用和拒绝事实,不能复活旧权限。
验收条件
- 创建“软件工程师”角色并分配给两个用户,两人保留各自稳定 userId。
- 同一用户第二次任命替换原生效角色,不产生两个同时有效的 binding。
- 角色描述可编辑,用户可选资料全空仍合法。
- 未知动作、默认超过上限、停用角色任命、无授权角色管理和最后系统权限管理者撤销均拒绝。
- 普通角色可被授予
project.create,但不能因此修改系统设置、用户、角色或权限上限。 - A 项目创建者管理 A 的成员、项目权限和文档三权成功;操作 B 全部拒绝。
- 系统管理员未获项目授权时不能读取 A 或 B 的业务内容。
- 单角色配置和角色矩阵产生等价的固定 actionId 批量 patch;过滤和分页不丢草稿。
- SQLite 与 PostgreSQL 的版本冲突、事务回滚和迁移结果一致。
明确排除
本增量不实现一人多职、账号恢复、SSO/组织树、Agent 权限、工作流重建、新的文档审签流程、生产迁移或部署。现有文档三权只接入新的角色资格和项目权限判定。