Co-authored-by: Codex <codex@noreply.local>
23 KiB
SPEC: PJ2026-01050105 YAML-first users/nav access
修改历史
| 版本 | 对应 commit id | 更新日期 | 变更说明 |
|---|
当前正文仍在规格治理草稿中;未定稿前不新增版本号,不为单次编辑追加 待提交 版本。
正文
PJ2026-01050105 用户导航ACL需求规格
1. 文档控制
| 字段 | 内容 |
|---|---|
| 编号 | PJ2026-01050105 |
| 短名 | 用户导航ACL |
| 层级 | L3 子课题 |
| 状态 | 已生效 |
| 实现引用版本 | draft-2026-06-26-users-nav-acl |
| 需求规格模板 | ISO/IEC/IEEE 29148 需求规格模板 |
| 上级规格 | PJ2026-010501 账号会话 |
| 关联规格 | PJ2026-010503 权限配额、PJ2026-010401 Web工作台、PJ2026-010403 API契约、PJ2026-010603 YAML运维、PJ2026-010601 发布流水、PJ2026-010404 项目管理 |
| 规格治理索引 | 规格治理 |
本文采用 ISO/IEC/IEEE 29148 需求规格模板的项目裁剪版:正文只保留 D601 v0.3 YAML-first 预置用户、根导航访问控制、前后端 enforcement 和发布验收的稳定边界。
本规格范围内新增或修改的源码文件必须在文件头部标注 SPEC: PJ2026-01050105 用户导航ACL draft-2026-06-26-users-nav-acl,并用一句话说明文件职责。纯 YAML/config、锁文件、生成产物和无法承载注释头的二进制产物不要求加头部,但对应解析器、渲染器或 CLI 入口必须能追溯到本规格。
2. 目的和范围
2.1 目的
用户导航ACL负责把 D601 HWLAB v0.3 的预置用户、密码来源、角色、状态、根导航可见性和后端访问兜底拒绝收敛到 YAML-first source of truth,使 admin 与运营预置普通用户可以通过同一 bootstrap/sync 流程进入本地用户表、OpenFGA 或应用 ACL,并让 Cloud Web 根导航、router guard 和后端 API guard 使用同一份访问摘要。
本规格的目标状态是:admin 和 huiggao@163.com 都由 UniDesk/HWLAB owning YAML 声明;密码明文只存在于 gitignored owner-only .env/... sourceRef 和运行面 Secret;CLI plan/status 只输出 sourceRef、key、presence、fingerprint 和 mutation 摘要;Cloud API 在 /v1/auth/session 与 /v1/users/me 返回非敏感访问摘要;Cloud Web 只显示当前用户被授权的根导航;未授权用户直接访问前端路由或后端 API 时被稳定拒绝。
2.2 范围内
- D601 v0.3 预置用户 YAML、nav profile YAML 和 node/lane
accessControlconfigRef。 admin全量导航访问与huiggao@163.com仅Code、MDTODO根导航访问。- 密码强密码生成、gitignored plaintext sourceRef、password hash/bootstrap Secret 和 redacted plan/status。
- 多用户 bootstrap/sync 幂等落库,包括
id、username/email、displayName、role、status、password hash 和 nav profile binding。 - OpenFGA system/admin/tool 关系与应用层 nav claim 的映射边界。
/v1/auth/session与/v1/users/me的非敏感access.nav.allowedIds摘要。- Cloud Web 根导航
navId模型、AppShell过滤、router meta 校验和 403/redirect 用户体验。 - Code/workbench、MDTODO/project-management、admin 等后端 API capability guard。
- D601 v0.3 env-reuse、git-mirror、GitOps、Tekton、Argo 和 web-probe 登录导航验收。
2.3 范围外
- 不引入 Keycloak、外部 IAM 或第三方身份源。
- 不把 Sub2API 的 DB settings/custom menu 机制复制为 HWLAB source of truth。
- 不只做前端隐藏;router 和后端 API 必须兜底拒绝。
- 不把明文密码、password hash、session token、API key、DB DSN 或 Secret value 输出到 issue、日志、Git 提交、PR 描述、CI 日志或普通 CLI 输出。
- 不在运行面 Secret、pod env、日志或数据库中反推、解码或回填本地 owner-only sourceRef。
- 不把 D601 legacy、G14 v0.2、旧 direct port、旧
dev/prod或手工 SQL/kubectl patch作为完成态路径。
3. 术语表
| 术语 | 定义 |
|---|---|
| 预置用户 | 由 YAML 声明并通过受控 bootstrap/sync 幂等进入 HWLAB 本地用户表的账号。 |
| nav profile | YAML 中声明的根导航访问集合,包含稳定 profile id 与 allowedNavIds。 |
| navId | Cloud Web 每个根导航入口和对应 router/API capability 的稳定标识,例如 workbench.code。 |
| 根导航ACL | 用户对登录后左侧根导航项的可见性和可访问性约束。 |
| access claim | 后端根据用户、角色、状态和 nav profile 生成的非敏感访问摘要。 |
| app-local ACL | Cloud API 应用层对 navId/capability 的访问判断;它可以消费 OpenFGA 结果,但不把前端导航当作唯一防线。 |
| sourceRef | YAML 中指向 owner-only 明文密码来源的声明,输出时只能显示 path/key/presence/fingerprint,不显示值。 |
| password hash | Cloud API 用户表或 bootstrap Secret 中保存的密码摘要;不能输出到普通 CLI、issue 或日志。 |
nav_access_denied |
用户已认证但缺少目标 nav/capability 时返回的稳定错误码。 |
4. 系统边界和接口
本规格把用户导航ACL作为账号会话下的登录后访问切片看待;本章只描述输入、输出和责任边界。
| 边界项 | 内容 |
|---|---|
| 外部使用者 | 普通用户、平台管理员、Cloud Web、Cloud API、HWLAB CLI、D601 发布操作员和 web-probe。 |
| 外部输入 | D601/v03 node/lane 选择、users/navProfiles YAML、password sourceRef、登录凭据、Web session cookie、直接访问的前端路由和后端 API path。 |
| 受控资源 | 本地用户表、password hash、user/nav profile binding、OpenFGA tuple、应用层 capability map、Cloud Web 根导航和 route meta。 |
| 外部输出 | 登录/session 结果、当前用户摘要、access.nav.allowedIds、导航可见性、403/redirect、redacted plan/status、发布与验收摘要。 |
| 用户接口 | Cloud Web 登录后页面、/v1/auth/session、/v1/users/me、受保护业务 API、UniDesk/HWLAB 受控 CLI、D601 v0.3 web-probe。 |
| 系统边界 | 用户导航ACL负责预置身份、导航 capability 和兜底授权;不拥有 Workbench、Project Management、Admin、billing 或发布流水的业务事实,只消费它们的 API 和发布入口。 |
4.1 目标架构图
flowchart LR
subgraph YAML[UniDesk/HWLAB YAML source of truth]
Users[users.d601-v03.yaml]
Profiles[nav-profiles.yaml]
Lane[hwlab-node-lanes accessControl refs]
end
subgraph CLI[受控 CLI]
Plan[plan/status]
Generate[generate-password]
Sync[bootstrap/sync]
end
subgraph Runtime[HWLAB v0.3 runtime]
Secret[Kubernetes Secret password hashes]
DB[(local users DB)]
FGA[OpenFGA tuples]
Claims[AccessClaims service]
end
subgraph Client[Cloud Web]
Session[/v1/auth/session]
Shell[AppShell nav filter]
Router[router nav guard]
API[protected API guard]
end
Lane --> Users
Lane --> Profiles
Users --> Plan
Profiles --> Plan
Plan --> Generate
Plan --> Sync
Generate --> Users
Sync --> Secret
Sync --> DB
Sync --> FGA
DB --> Claims
FGA --> Claims
Claims --> Session
Session --> Shell
Session --> Router
Router --> API
4.2 数据流图
flowchart TD
Source[gitignored plaintext env sourceRef] --> Hash[CLI/local transform to password hash]
Hash --> Secret[hwlab-v03 bootstrap/user Secret]
UsersYaml[users.d601-v03.yaml] --> Sync[bootstrap user sync]
ProfilesYaml[nav-profiles.yaml] --> Sync
Secret --> Sync
Sync --> UserTable[local user table]
Sync --> Binding[user_nav_profile binding]
Sync --> OpenFGA[OpenFGA admin/tool tuples]
Login[POST /v1/auth/login] --> UserTable
UserTable --> Session[session principal]
Binding --> Claims[access.nav.allowedIds]
OpenFGA --> Claims
Claims --> AuthSession[/v1/auth/session or /v1/users/me]
AuthSession --> WebNav[Cloud Web root nav]
AuthSession --> RouteGuard[router guard]
RouteGuard --> BackendGuard[API capability guard]
数据流要求:明文密码只从 owner-only sourceRef 进入本地 transform;运行面验收只看 password hash/Secret presence/fingerprint。access.nav.allowedIds 是给前端的非敏感摘要,不包含 password、hash、Secret、OpenFGA 内部 tuple dump 或用户不可见能力细节。
4.3 Bootstrap/sync 时序图
sequenceDiagram
participant Op as Operator
participant CLI as UniDesk/HWLAB controlled CLI
participant FS as owner-only .env sourceRef
participant K8S as hwlab-v03 Secret
participant API as Cloud API bootstrap/sync
participant DB as Local users DB
participant FGA as OpenFGA
participant Web as Cloud Web
Op->>CLI: generate-password --user huiggao@163.com
CLI->>FS: write plaintext to gitignored sourceRef
CLI-->>Op: sourceRef/key/presence/fingerprint only
Op->>CLI: plan/status or sync --node D601 --lane v03
CLI->>FS: read plaintext sourceRef
CLI->>K8S: apply password hash Secret and config refs
CLI->>API: trigger bootstrap/sync through rollout or startup
API->>DB: upsert user and nav profile binding
API->>FGA: upsert system/admin/tool relations as needed
Web->>API: /v1/auth/session
API-->>Web: actor + access.nav.allowedIds + valuesRedacted=true
同步必须幂等。重复执行不得生成新用户 id、重复 OpenFGA tuple 或泄漏旧密码;当 sourceRef 缺失、key 缺失、profile 不存在、navId 不存在或 role/profile 冲突时,CLI/status 应返回 YAML path 与 redacted blocker。
5. 内部分工与规格索引
| 编号 | 内部模块 | 规格文档 | 主责边界 | 上游依赖 | 下游支撑 |
|---|---|---|---|---|---|
| PJ2026-0105010501 | YAML配置 | 本规格 6.1 | users/navProfiles/accessControl configRef schema 与 redacted plan/status | YAML运维、D601 lane 配置 | bootstrap/sync、发布流水 |
| PJ2026-0105010502 | 密码来源 | 本规格 6.2 | 强密码生成、owner-only sourceRef、hash transform 和 Secret 输出边界 | YAML配置、Secret source | 用户同步、运行面启动 |
| PJ2026-0105010503 | 用户同步 | 本规格 6.3 | 多用户幂等 upsert、role/status/nav profile binding | 账号会话、平台数据库 | 登录、session、用户管理 |
| PJ2026-0105010504 | ACL映射 | 本规格 6.4 | OpenFGA 与应用层 nav claim 的组合、capability 矩阵 | 权限配额、API契约 | 后端 API guard、session access claims |
| PJ2026-0105010505 | Web导航 | 本规格 6.5 | 根导航 navId、AppShell 过滤、router guard 和直接访问拒绝 | Web工作台、项目管理、API契约 | 用户入口、web-probe |
| PJ2026-0105010506 | 发布验收 | 本规格 6.6 | D601 v0.3 env-reuse/git-mirror/GitOps/Tekton/Argo 与登录导航验收 | 发布流水、源码同步、公开入口 | 验收收口、runtime evidence |
| PJ2026-0105010507 | 代码引用 | 本规格 6.7 | SPEC-first 文件头、实现引用版本和阶段边界 | 规格治理 | 实现审计 |
5.1 YAML schema 草案
# config/hwlab-access-control/users.d601-v03.yaml
version: 1
kind: HwlabAccessControlUsers
metadata:
name: d601-v03-users
target:
repo: pikasTech/HWLAB
node: D601
lane: v03
users:
- id: usr_v03_admin
username: admin
displayName: HWLAB v0.3 Admin
role: admin
status: active
passwordSourceRef:
kind: envFile
path: .env/hwlab/d601-v03-admin.env
key: HWLAB_BOOTSTRAP_ADMIN_PASSWORD
passwordHashTransform: hwlab-sha256
navProfile: admin-full
- id: usr_huiggao_163
username: huiggao@163.com
email: huiggao@163.com
displayName: huiggao
role: user
status: active
passwordSourceRef:
kind: envFile
path: .env/hwlab/d601-v03-users.env
key: HWLAB_USER_HUIGGAO_PASSWORD
passwordHashTransform: hwlab-sha256
navProfile: code-mdtodo-only
# config/hwlab-access-control/nav-profiles.yaml
version: 1
kind: HwlabAccessControlNavProfiles
metadata:
name: hwlab-nav-profiles
navProfiles:
- id: admin-full
allowedNavIds: ["*"]
- id: code-mdtodo-only
allowedNavIds:
- workbench.code
- project.mdtodo
# config/hwlab-node-lanes.yaml excerpt
lanes:
v03:
targets:
D601:
accessControl:
usersRef: config/hwlab-access-control/users.d601-v03.yaml#users
navProfilesRef: config/hwlab-access-control/nav-profiles.yaml#navProfiles
rollout:
namespace: hwlab-v03
deployment: hwlab-cloud-api
sessionClaims:
exposeNavAllowedIds: true
Schema 校验只验证字段形状、类型、引用存在性、navProfile/navId 交叉引用、sourceRef path/key presence 和 target node/lane 一致性;具体用户、profile、navId 集合和 sourceRef 路径以 YAML 为准,不在代码中藏默认。
5.2 根导航与 enforcement 矩阵
| navId | 前端路由示例 | 后端 API capability | admin-full | code-mdtodo-only |
|---|---|---|---|---|
workbench.code |
/workbench, /workbench/sessions/:sessionId |
workbench.code |
allow | allow |
project.mdtodo |
/projects/mdtodo |
project.mdtodo |
allow | allow |
project.list |
/projects |
project.list |
allow | deny |
dashboard.overview |
/dashboard |
dashboard.overview |
allow | deny |
user.apiKeys |
/api-keys |
user.apiKeys |
allow | deny |
usage.read |
/usage |
usage.read |
allow | deny |
billing.read |
/billing |
billing.read |
allow | deny |
performance.read |
/performance |
performance.read |
allow | deny |
skills.read |
/skills |
skills.read |
allow | deny |
settings.read |
/settings |
settings.read |
allow | deny |
gate.read |
/gate |
gate.read |
allow | deny |
help.read |
/help |
help.read |
allow | deny |
admin.access |
/admin/access |
admin.access + role admin |
allow | deny |
admin.users |
/admin/users |
admin.users + role admin |
allow | deny |
admin.billing |
/admin/billing |
admin.billing + role admin |
allow | deny |
admin.hwpodGroups |
/admin/hwpod-groups |
admin.hwpodGroups + role admin |
allow | deny |
admin.providerProfiles |
/admin/provider-profiles |
admin.providerProfiles + role admin |
allow | deny |
直接访问 deny 项时,Cloud Web router 应显示 403/redirect 到允许的默认入口;后端 API 应返回 403 和 nav_access_denied,不得返回越权数据。/v1/admin/* 继续要求 admin role;普通 nav claim 不能提升为 admin。
6. 原子需求
6.1 USER-NAV-REQ-001 YAML-first 用户与导航配置真相
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-001 | YAML配置 | PJ2026-0105010501 YAML配置 | YAML运维、账号会话 |
D601 v0.3 的预置用户和根导航 ACL 必须由 YAML 声明,并通过 node/lane accessControl configRef 串联 users、navProfiles、runtime namespace、rollout target 和 session claim 输出开关。
admin 必须由 YAML 管理其全量导航访问能力;huiggao@163.com 必须由 YAML 声明为 active 普通用户并绑定 code-mdtodo-only nav profile。代码不得内置这些用户、邮箱、role、nav profile、namespace、Secret 名或 navId 集合作为隐藏默认;缺少 YAML 或引用冲突时应报告配置路径和 redacted blocker。
CLI plan/status 默认输出应包含 usersRef、navProfilesRef、node/lane、namespace、用户 logical id、username、role、status、navProfile、allowedNavIds 摘要、sourceRef/key、presence、fingerprint、target Secret/key、mutation 摘要和下一步命令;不得默认展开完整 YAML 或 Secret 值。
6.2 USER-NAV-REQ-002 密码 sourceRef 与强密码生成
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-002 | 密码来源 | PJ2026-0105010502 密码来源 | YAML运维、发布流水 |
受控 CLI 必须提供强密码生成入口,使 operator 可以为 huiggao@163.com 生成强密码并写入 gitignored owner-only plaintext sourceRef。该入口只能在本地显示 sourceRef、key、presence、byte count、fingerprint 和写入状态;不得把明文回显到普通 stdout、issue、PR、CI 日志或 runtime status。
运行面只接收 password hash 或等价安全摘要。hash transform 名称由 YAML 声明;transform 实现只能作为小型 adapter 或共享 Secret helper 存在,不得把明文、hash 或历史默认密码写入代码。缺少 sourceRef、key 缺失、sourceRef 未被 git ignore、fingerprint 不匹配或 transform 未知时,sync 必须停止并输出 redacted blocker。
6.3 USER-NAV-REQ-003 多用户 bootstrap/sync 幂等落库
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-003 | 用户同步 | PJ2026-0105010503 用户同步 | 账号会话、权限配额 |
Cloud API bootstrap/sync 必须根据 YAML 中的用户声明幂等维护本地用户表和 nav profile binding。每个用户至少维护稳定 id、username/email、displayName、role、status、password hash 摘要和 nav profile id。
重复 sync 应只更新声明字段,不创建重复用户,不删除未被本 YAML owner 声明的运行面用户,除非 YAML 明确提供 retirement policy。禁用、role 变化、password rotation 和 nav profile 变化都必须有可审计 mutation 摘要;摘要只显示对象 id、字段名、sourceRef、fingerprint 和变更类型,不显示敏感值。
6.4 USER-NAV-REQ-004 OpenFGA 与应用层 ACL 映射
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-004 | ACL映射 | PJ2026-0105010504 ACL映射 | 权限配额、API契约 |
admin 必须继续获得 OpenFGA system:hwlab#admin 或等价全访问能力;huiggao@163.com 不得获得 system admin。Code 和 MDTODO 的访问可以采用“OpenFGA 管核心资源 + 应用层 nav claim”的组合,也可以扩展 OpenFGA model 增加 nav_item,但 YAML 必须是 source of truth。
应用层 ACL 必须从 authenticated canonical principal、user status、role、nav profile 和必要 OpenFGA 判断生成 access claim。/v1/auth/session 与 /v1/users/me 应返回 access.nav.allowedIds 和必要 capability 摘要,并设置 valuesRedacted=true;响应不得包含 password hash、Secret、完整 OpenFGA tuple dump 或内部敏感字段。
后端 API 必须按 capability 兜底拒绝:Code/workbench 相关 API 需要 workbench.code 或对应业务 capability;MDTODO/project-management 相关 API 需要 project.mdtodo 或对应业务 capability;/v1/admin/* 继续要求 admin。未授权时返回 403、nav_access_denied 和脱敏 route/capability 摘要。
6.5 USER-NAV-REQ-005 Cloud Web 根导航与 router guard
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-005 | Web导航 | PJ2026-0105010505 Web导航 | Web工作台、项目管理、API契约 |
Cloud Web 每个根导航项必须声明稳定 navId,并在 route meta 中声明所需 nav/capability。AppShell 不得无条件渲染完整硬编码导航;它必须消费 /v1/auth/session 或 /v1/users/me 返回的 access claim 过滤根导航。
router guard 必须同时处理 requiresAuth、requiresAdmin 和 navId/capability。用户登录后访问自己无权的页面时,应显示 403 或跳转到第一个允许的默认入口;直接输入 URL、刷新、深链、浏览器后退和 session hydrate 都必须走同一 guard。前端隐藏不是安全边界,后端 API guard 必须独立执行。
huiggao@163.com 登录后左侧只能显示 Code 和 MDTODO 两个入口;admin 登录后显示全部现有导航入口。禁止通过 localStorage、fake flag、cookie prefix、role 字符串硬编码或用户邮箱特判控制导航。
6.6 USER-NAV-REQ-006 D601 v0.3 受控发布与原入口验收
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-006 | 发布验收 | PJ2026-0105010506 发布验收 | 发布流水、YAML运维、公开入口 |
该能力必须进入 D601 v0.3 env-reuse、git-mirror checkout、GitOps、Tekton、Argo 和 public Web 验收链路。正式交付不得通过手工热改 runtime Secret、临时 SQL、pod env patch、直接 kubectl 或数据库 update 完成。
发布前 plan/status 应证明 users/navProfiles configRef 可解析、password sourceRef present、fingerprint 可见、target namespace/deployment 对齐 D601/v03 YAML。发布后验收至少覆盖:admin 登录可见全部导航并可访问 admin API;huiggao@163.com 登录只见 Code 与 MDTODO;允许路由 /workbench、/workbench/sessions/:sessionId、/projects/mdtodo 可访问;deny 矩阵中的前端路由被拒绝或重定向;对应后端 API 返回 403 nav_access_denied;session/me 响应只含非敏感 access 摘要。
判定相关实现完成前,必须在 D601/v03 正式 public origin 或 YAML 声明的等价 web-probe origin 完成原入口登录/导航/API 验收,并记录 source commit、PipelineRun、Argo/runtime revision、public origin、用户名摘要、navId 列表、403 错误码和 valuesRedacted=true。若 D601 provider、git-mirror、Tekton、Argo 或 public entry 不可用,应将其作为 blocker 记录,不能用源码检查或源码合并单独判定完成。
6.7 USER-NAV-REQ-007 SPEC-first 代码引用与阶段边界
| 编号 | 短名 | 主责模块 | 关联模块 |
|---|---|---|---|
| USER-NAV-REQ-007 | 代码引用 | PJ2026-0105010507 代码引用 | 规格治理、YAML运维 |
本规格的后续实现阶段必须引用本规格编号和实现引用版本。修改的 YAML parser、access-control CLI、Cloud API auth/session/users/me、OpenFGA/ACL mapper、API guard、Cloud Web nav model、router guard 和 web-probe 验收脚本都应能追溯到 PJ2026-01050105 用户导航ACL draft-2026-06-26-users-nav-acl。
实现阶段按固定顺序推进:P1 YAML schema 与受控 CLI;P2 后端 provisioning 与 enforcement;P3 前端导航与路由;P4 D601 v0.3 发布验收。P0 SPEC 完成前不得把代码实现、部署、CI/CD 或验收列为已完成。新发现事项只能分类为当前阶段 must-fix、真实领域差异或 out-of-scope parked risk,不因 grep 命中继续追加开放轮次。