9.4 KiB
v0.2 用户和权限管理规格
本文定义 HWLAB v0.2 用户、session、API key、Code Agent owner 和工具能力的最小权限口径。当前 HWPOD 快速闭环阶段只设计核心业务功能;不为旧设备路由、旧授权表或旧 REST/job 保留兼容路径。
登录入口、Keycloak OIDC、Web session、CLI API key 和 AuthPrincipal 归一见 spec-v02-auth.md。OpenFGA、Admin Access WebUI 和同路径 CLI 细节见 spec-v02-openfga-authorization.md。HWPOD 概念、hwpod-spec、hwpod-cli、hwpod-ctl、hwpod-compiler-cli、hwpod-node-ops 和 hwpod-node 见 spec-hwpod-harness.md。
在系统中的职责划分
用户和权限管理不拆独立微服务,权威实现收敛在 hwlab-cloud-api:它消费认证模块产出的 AuthPrincipal,负责角色、OpenFGA check/write、用户 API key、tool capability 和 Code Agent session owner 校验。hwlab-cloud-web 只提供浏览器 UI 和同源代理;AgentRun v0.1 只消费 cloud-api 按用户权限注入的 actor/session/tool 上下文,不成为 HWLAB 用户权限 authority。
Postgres 是用户、session、业务对象和迁移 ledger 的持久化边界;OpenFGA 是细粒度授权关系与授权判定边界。Kubernetes namespace、ServiceAccount、Service 直连和 host route 都不能替代用户权限模型;普通用户不获得 kubeconfig、内部 Service 直连能力、OpenFGA token 或长期 Secret。
规格目标
- 只保留两类基础角色:
admin和user。 - Code Agent session 直接归属于创建它的用户;普通用户只能查看、继续和取消自己的 session。
- 工具能力独立授权,例如
hwpod、unidesk_ssh、trans_cmd和 GitHub 写工具;拥有 Code Agent session 不等于拥有这些工具。 - 当前 HWPOD 快速阶段不引入用户级 hwpod 授权矩阵;
hwpod-spec位于 Code Agent workspace,执行链路按 owner session 和工具能力约束。 - 普通用户不获得 Kubernetes 用户、kubeconfig、namespace 管理权或直接访问内部 Service 的权限;所有用户权限判断在 cloud-api 应用层完成。
- MVP 不新增产品级
audit_events用户审计表,也不把用户权限依赖到 audit。 - v0.2 权限数据必须与
hwlab-dev/hwlab-prod运行数据隔离。
表结构
users
用户身份和角色 source of truth。
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL DEFAULT '',
role TEXT NOT NULL CHECK (role IN ('admin', 'user')),
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
password_hash TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
user_sessions
浏览器 server session/cookie 的持久状态。cookie 中只保存不可逆 session token;数据库保存 token hash。
CREATE TABLE IF NOT EXISTS user_sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id),
session_token_hash TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
revoked_at TEXT
);
api_keys
CLI 和 runner 的用户 API key 必须映射到 users.id。API key 只能收窄用户权限,不能授予超过用户和 OpenFGA tuple 的能力。
agent_sessions
复用现有 agent_sessions 作为 Code Agent session 归属记录,不新增同义表。
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS owner_user_id TEXT REFERENCES users(id);
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS conversation_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS thread_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS last_trace_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS session_json TEXT NOT NULL DEFAULT '{}';
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS updated_at TEXT;
CREATE INDEX IF NOT EXISTS idx_agent_sessions_owner ON agent_sessions(owner_user_id);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_conversation ON agent_sessions(conversation_id);
新建 Code Agent session 必须写入 owner_user_id。历史 ownerless session 在迁移时可以一次性归属 bootstrap admin 或标记为 expired;迁移完成后不再允许 ownerless 活跃 session。
权限矩阵
| 操作 | admin |
user |
|---|---|---|
| 管理用户和权限 | 可以;应镜像为 system:hwlab#admin 或 access_manager |
只有被授予 access_manager 才可以 |
| 创建自己的 Code Agent session | 可以 | 可以 |
| 查看、继续、取消自己的 Code Agent session | 可以 | 拥有该 session 的 viewer/operator 时可以 |
| 查看、取消别人的 Code Agent session | 可以 | 需要该 session 的显式 relation |
调用 hwpod-cli / hwpod-ctl |
可以,但仍受 session owner 和工具边界约束 | 需要 tool:hwpod#can_use |
| 调用 UniDesk SSH / trans cmd / GitHub 写工具 | 可以,但仍受工具边界约束 | 需要对应 tool:*#can_use |
请求链路
cloud-api 每个用户态请求都按同一顺序处理:
authenticate -> actor -> authorize(actor, action, resource) -> execute
登录、API key 和 session 恢复
browser
-> Keycloak OIDC login/callback
-> cloud-api auth controller
-> users issuer+sub/status/role mapping
-> user_sessions insert token hash
-> Set-Cookie httpOnly sameSite
-> browser
cli
-> Authorization: Bearer hwl_live_...
-> cloud-api api_keys lookup
-> users status/role check
-> AuthPrincipal(authMethod='api-key')
用户创建或继续 Code Agent session
browser
-> cloud-api POST /v1/agent/chat
-> authenticate actor
-> find or create agent_sessions(owner_user_id=actor.id, conversation_id)
-> run Code Agent turn
-> persist last_trace_id/thread_id/session_json
-> return 202 trace/result polling pointer
GET /v1/agent/chat/result/{traceId}、GET /v1/agent/chat/trace/{traceId} 和 POST /v1/agent/chat/cancel 必须通过 agent_sessions.owner_user_id 校验 owner;admin 可跨用户查看和取消。
Code Agent 使用 HWPOD
code agent turn
-> hwpod-cli in workspace
-> hwpod-compiler-cli reads .hwlab/hwpod-spec.yaml
-> hwpod-node-ops plan
-> cloud-api /v1/hwpod-node-ops
-> hwpod-node
Cloud API 给 AgentRun runner 注入 hwpod、UniDesk SSH、trans_cmd 或 GitHub 写工具前,必须先按 spec-v02-openfga-authorization.md 检查对应 tool:*#can_use。
API 接口说明
| 接口 | 说明 |
|---|---|
GET /auth/oidc/login、GET /auth/oidc/callback |
Web 登录入口;按 spec-v02-auth.md 接入 Keycloak 并写入 24 小时 user_sessions token hash。 |
GET /auth/session |
从 cookie 恢复 actor、role 和 session 状态;API key actor 摘要见 /v1/users/me。 |
GET /v1/auth/session、GET /v1/users/me、GET /v1/access/status、GET /v1/setup/status |
REST 状态入口;不得读取或返回 password hash、session token 原文或 Secret 值。 |
GET/POST /v1/api-keys... |
用户 API key 管理入口;CLI HWLAB_API_KEY 规则见 spec-v02-auth.md。 |
POST /v1/setup/first-admin |
仅当 users 表为空时创建第一个 admin 并建立 session;一旦已有用户必须返回 409 setup_already_completed。 |
POST /auth/logout |
设置 revoked_at,撤销当前 browser session。 |
POST /v1/admin/users |
admin 创建用户,响应不得返回 password_hash 或 token。 |
GET/PATCH/PUT/DELETE /v1/admin/access... |
admin Access API,读写 OpenFGA 授权、tool capability、role/status 和 effective matrix。 |
POST /v1/hwpod-node-ops |
HWPOD node-ops 转发入口;业务翻译必须已由 workspace-local compiler 完成。 |
POST /v1/agent/chat 及 result/trace/cancel |
必须校验 agent_sessions.owner_user_id;admin 可跨用户查看和取消。 |
测试规格
T1
阅读 docs/reference/spec-user-access.md,然后用 CLI 手动测试以下内容:创建普通用户,确认 role=user、status=active,响应不包含 password hash、session token 或 Secret 值。
T2
阅读 docs/reference/spec-user-access.md,然后用普通用户创建 Code Agent session,确认 agent_sessions.owner_user_id 写入当前用户;另一个普通用户不能读取或取消该 session。
T3
阅读 docs/reference/spec-user-access.md,然后撤销普通用户的 tool:hwpod 能力,创建新的 Code Agent session 并调用 HWPOD,确认 cloud-api 或 runner 装配返回结构化 authorization blocker。
T4
阅读 docs/reference/spec-user-access.md,然后检查 source schema、migration 和 live Postgres,确认不存在旧设备授权表或旧 profile authority 表作为授权来源。
规格的实现情况
| 规格项 | 状态 | 说明 |
|---|---|---|
| admin/user 两角色模型 | 已实现/持续约束 | cloud-api 已实现 /auth/*、bootstrap admin、admin/user 创建和 admin-only route。 |
users、user_sessions、api_keys、OpenFGA relation |
已实现/持续约束 | 当前授权只表达用户、session 和 tool capability;不保留旧设备授权 source。 |
| Code Agent owner 绑定 | 已实现/持续约束 | session owner、trace/result/cancel 必须按 owner 校验。 |
| HWPOD 工具能力授权 | 目标状态 | tool:hwpod 控制 runner 是否注入 HWPOD 能力;具体 HWPOD 执行链路见 spec-hwpod-harness.md。 |