337 lines
25 KiB
Markdown
337 lines
25 KiB
Markdown
# v0.2 用户和权限管理规格
|
||
|
||
本文是 HWLAB `v0.2` 用户和权限管理的规格说明。目标是用最少概念支持真实用户使用 code agent session,并让管理员能按用户独立调整 HWPOD/profile、Code Agent session 和工具功能权限,同时避免把用户体系、Kubernetes 租户、设备授权、硬件证据链和审计系统混成一套复杂门禁。
|
||
|
||
本规格与 [spec-hwpod-harness.md](spec-hwpod-harness.md) 配套:用户和权限规格定义谁可以看见、创建和使用 HWPOD;HWPOD 规格定义当前 `hwpod`、`hwpod-spec`、node-ops 和硬件执行边界。[spec-device-pod.md](spec-device-pod.md) 只作为旧 API/table 命名的迁移对照。
|
||
|
||
登录入口、Keycloak OIDC、Web session、CLI API key 和 `AuthPrincipal` 归一见 [spec-v02-auth.md](spec-v02-auth.md)。OpenFGA、Admin Access WebUI 和同路径 CLI 细节见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。本文只定义认证完成后的角色、资源归属、HWPOD capability、tool capability 和 code agent owner 授权;正式用户鉴权只有 Web session 与 CLI/API key 两类。
|
||
|
||
实施跟踪见 [pikasTech/HWLAB#531](https://github.com/pikasTech/HWLAB/issues/531),原 `docs/plan/v02-multi-user-migration.md` 迁移计划全文已迁入该 issue 评论。
|
||
|
||
## 在系统中的职责划分
|
||
|
||
用户和权限管理不是独立微服务,权威实现收敛在 `hwlab-cloud-api`:它消费 [spec-v02-auth.md](spec-v02-auth.md) 产出的 `AuthPrincipal`,负责角色、OpenFGA check/write、用户 API key、hwpod capability、tool capability 和 code agent session owner 校验。`hwlab-cloud-web` 只提供浏览器 UI 和同源代理;HWPOD 执行节点只执行受控硬件语义;AgentRun v0.1 只消费 cloud-api 按用户权限注入的 actor/session/hwpod/tool 上下文,不成为 HWLAB 用户权限 authority。
|
||
|
||
Postgres 是用户、session、业务对象和迁移 ledger 的持久化边界;OpenFGA 是细粒度授权关系与授权判定边界。Kubernetes namespace、ServiceAccount、Service 直连和 gateway route 都不能替代用户权限模型;普通用户不获得 kubeconfig、内部 Service 直连能力、OpenFGA token 或长期 Secret。
|
||
|
||
`v0.2` 本地 bootstrap 管理员账号固定为 `admin`,默认登录密码固定为 `hwlab2026`,只用于空库初始化。Keycloak 接入后的目标 Web 登录以 OIDC 为准,CLI 以 `HWLAB_API_KEY` 为准。
|
||
运行时仍只通过 `hwlab-v02-bootstrap-admin/password-hash` SecretRef 注入本地 bootstrap password hash;Postgres、API 响应、日志、CLI session 和文档不得保存或输出 password hash、session token 原文或 Secret 值。
|
||
如果 live Secret 需要重建或旋转,必须保持目标 OIDC/Web session 与 API key 登录链路可用。
|
||
|
||
## 当前实现状态
|
||
|
||
- 当前 v0.2 runtime 的权限相关组件包含 `hwlab-cloud-api`、`hwlab-cloud-web`、HWPOD 相关实现、OpenFGA、v0.2 Postgres 和 Keycloak 外部 issuer。`hwlab-cloud-api` 是应用层用户身份恢复、OpenFGA check/write、Admin Access 写入、hwpod/profile/job 和 Code Agent owner 校验的收口点。
|
||
- 用户、session、API key、account workspace、HWPOD profile、HWPOD job 和访问摘要保存在 v0.2 Postgres;细粒度 relation 的判定 authority 是 OpenFGA tuple。Postgres 中的摘要或缓存不能在 OpenFGA 不可用时变成独立 allow source。
|
||
- 当前授权管理入口是 Admin Access API、Cloud Web Access 页面和同路径 `hwlab-cli client access ...`。管理员或具备 `access_manager` 的用户通过这些入口调整 role/status、HWPOD relation 和 tool capability。
|
||
- 迁移期 hwpod/profile 的创建和修改仍通过 `POST /v1/admin/device-pods`、`PUT /v1/admin/device-pods/{devicePodId}` 和 `profile_editor`/admin 授权完成;这些 path 名称属于实现残留。执行节点只执行 cloud-api 已授权的内部请求,不拥有用户权限判断或 profile 修改权。
|
||
- AgentRun runner 只能收到 cloud-api 按当前 Code Agent session owner 装配的用户级 `HWLAB_API_KEY` 和已授权工具面。`trans_cmd` 是独立 tool capability,表示允许进入受控 UniDesk passthrough 命令面,不授予 Kubernetes Secret、OpenFGA token、残留执行链路内部 token 或任意控制面写权限。
|
||
|
||
## 当前状态收敛规则
|
||
|
||
- 代码、测试和长期文档只能表达当前 Web session/API key/OpenFGA/Admin Access 权限系统。发现绕过当前 `AuthPrincipal -> OpenFGA/Admin Access` 判定的授权入口、共享用户绕过凭据、兼容写分支或只为已移除路径存在的断言时,处理方式是删除或改写为当前合同。
|
||
- 不把历史授权路径迁移成 feature flag、legacy mode、兼容表、负向测试清单或新增门禁。需要证明当前状态时,优先写当前 allow/deny 行为、当前 authority 和当前用户入口验收。
|
||
- issue/PR 评论可以保留排障证据;`docs/reference/` 只保留当前状态、目标状态和稳定判定标准,不保存过程流水账或已移除对象名称清单。
|
||
|
||
## 规格目标
|
||
|
||
- 只保留两类角色:`admin` 和 `user`。
|
||
- `code agent session` 直接归属于创建它的用户;普通用户只能查看、继续和取消自己的 session。
|
||
- HWPOD/profile 由 `admin` 或被授予 `profile_editor` 的用户管理;普通用户只有在被授权后才能看到、操作或提交对应 HWPOD job。
|
||
- HWPOD 授权按 `viewer`、`operator`、`profile_editor`、`job_submitter` 等 OpenFGA relation 表达。
|
||
- 工具能力必须独立授权,例如 `hwpod`、`unidesk_ssh`、`trans_cmd` 和 GitHub 写工具;拥有 Code Agent session 不等于拥有这些工具。
|
||
- MVP 不新增产品级 `audit_events` 用户审计表,也不把用户权限依赖到 audit。现有硬件 trace/evidence/audit 字段属于硬件闭环证据,不是多用户权限模型的一部分。
|
||
- 强副作用 HWPOD job 只额外要求业务 `reason`;设备互斥由 executor、gateway 和硬件 host 串行化或返回 blocker,不进入用户权限模型。
|
||
- 普通用户不获得 Kubernetes 用户、kubeconfig、namespace 管理权或直接访问残留执行 Service 的权限;所有用户权限判断在 cloud-api 应用层完成。
|
||
- v0.2 权限数据必须与 `hwlab-dev`/`hwlab-prod` 运行数据隔离。优先在 `hwlab-v02` namespace 内使用独立 Postgres StatefulSet/PVC;若未来显式复用共享 Postgres 实例,也必须使用独立 database 或 schema、独立 Secret 和独立 migration ledger,不得直接复用 `hwlab-dev` 的 pgdata。
|
||
|
||
## 简化边界
|
||
|
||
| 模块 | 不采用的复杂方案 | v0.2 规格方案 |
|
||
| --- | --- | --- |
|
||
| 用户角色 | `platform_admin`、`device_admin`、`developer`、`viewer` | `admin`、`user` |
|
||
| 用户组 | `groups`、`group_members` | 不引入 |
|
||
| 项目隔离 | `projects`、`project_members` | 不引入 |
|
||
| Code Agent 会话 | `ownerUserId + projectId + sessionId` | `owner_user_id + session id` |
|
||
| HWPOD 管理 | 平台管理员和设备管理员分工 | `admin` 统一管理 |
|
||
| HWPOD 授权 | 可按 group/project 授权 | 第一版只按具体 `user_id` 授权,relation 由 OpenFGA 表达 |
|
||
| 设备权限粒度 | `io.read`、`io.write` 等硬件寄存器级 capability | `viewer`、`operator`、`profile_editor`、`job_submitter` 等产品级 relation |
|
||
| Viewer | 单独只读角色 | 不引入 |
|
||
| Audit | 独立用户审计表 | 不引入 |
|
||
| Lease | 权限和互斥可能混用 | 只作为设备互斥锁 |
|
||
|
||
## 表结构
|
||
|
||
v0.2 多用户实现复用 cloud-api 和现有 Postgres runtime store,不新增独立用户管理微服务。推荐新增一个 `0002_multi_user_access_v1` 数据库迁移,保持现有 `0001_cloud_core_skeleton.sql` 的硬件 runtime 表不被误用为用户权限表。
|
||
|
||
### `users`
|
||
|
||
用户身份和角色 source of truth。
|
||
|
||
```sql
|
||
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
|
||
);
|
||
```
|
||
|
||
- bootstrap 阶段必须至少有一个 `admin`。
|
||
- `password_hash` 只用于 v0.2 本地 bootstrap 账号;OIDC identity 扩展字段、`api_keys` 表和 API key 规则见 [spec-v02-auth.md](spec-v02-auth.md)。接入 OIDC 后仍保留 `users.id`、`role` 和授权表稳定,不把外部 IdP subject 直接暴露给业务授权。
|
||
- `disabled` 用户不能创建 session、继续 session 或使用 HWPOD。
|
||
|
||
### `user_sessions`
|
||
|
||
浏览器 server session/cookie 的持久状态。cookie 中只保存不可逆 session token;数据库保存 token hash。
|
||
|
||
```sql
|
||
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
|
||
);
|
||
```
|
||
|
||
- `/auth/session`、OIDC callback、API key 认证和 `/auth/logout` 的最终 authority 是 cloud-api。`/v1/auth/session`、`/v1/users/me`、`/v1/access/status` 和 `/v1/setup/status` 只作为同一 authority 的 REST 状态入口。cloud-web 可以保留同名浏览器路由,但只能作为静态 UI 或代理层。
|
||
- logout 是设置 `revoked_at`,不是仅删除浏览器本地状态。
|
||
|
||
### `agent_sessions`
|
||
|
||
复用现有 `agent_sessions` 作为 code agent session 归属记录,不再新增同义的 `code_agent_sessions` 表。v0.2 迁移应在现有表上增加 owner 和 chat/session 绑定字段:
|
||
|
||
```sql
|
||
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。
|
||
|
||
### `device_pods`
|
||
|
||
迁移期 hwpod/profile 管理表;字段名仍是实现残留,目标概念以 [spec-hwpod-harness.md](spec-hwpod-harness.md) 为准。profile/spec 必须由 `admin` 或具备 `profile_editor` 的用户通过 cloud-api 管理,不能由 code agent 本地 `.device-pod/` 文件决定。
|
||
|
||
```sql
|
||
CREATE TABLE IF NOT EXISTS device_pods (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL DEFAULT '',
|
||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
|
||
profile_json TEXT NOT NULL DEFAULT '{}',
|
||
profile_hash TEXT NOT NULL DEFAULT '',
|
||
created_at TEXT NOT NULL,
|
||
updated_at TEXT NOT NULL
|
||
);
|
||
```
|
||
|
||
`profile_json` 中的 gateway route、host workspace、probe UID、串口端口和 host CLI 都是服务端权威字段;普通用户响应只能看到脱敏 profile 摘要和 `profile_hash`。
|
||
|
||
## 权限矩阵
|
||
|
||
| 操作 | `admin` | `user` |
|
||
| --- | --- | --- |
|
||
| 管理用户和权限 | 可以;应镜像为 `system:hwlab#admin` 或 `access_manager` | 只有被授予 `access_manager` 才可以 |
|
||
| 创建自己的 code agent session | 可以 | 可以 |
|
||
| 查看、继续、取消自己的 code agent session | 可以 | 拥有该 session 的 `viewer/operator` 时可以 |
|
||
| 查看、取消别人的 code agent session | 可以 | 需要该 session 的显式 relation |
|
||
| 创建、更新、删除 HWPOD/profile | 可以 | 需要目标 HWPOD 的 `profile_editor` |
|
||
| 给用户授权或撤销 HWPOD/tool | 可以 | 需要 `access_manager` |
|
||
| 查看 HWPOD | 可以查看全部 | 需要目标 HWPOD 的 `viewer` 或更高 relation |
|
||
| 使用 HWPOD 的 workspace/debug/io 能力 | 可以使用全部 | 需要 `tool:hwpod#can_use` 且目标 HWPOD 具备 `operator/job_submitter` |
|
||
| 提交强副作用 HWPOD job | 必须填写 reason | 被授权后仍必须填写 reason |
|
||
| 调用 UniDesk SSH / trans cmd / GitHub 写工具 | 可以,但仍受工具边界约束 | 需要对应 `tool:*#can_use` |
|
||
|
||
## 请求链路
|
||
|
||
cloud-api 每个用户态请求都按同一顺序处理:
|
||
|
||
```text
|
||
authenticate -> actor -> authorize(actor, action, resource) -> reason check for mutating device jobs -> execute
|
||
```
|
||
|
||
### 登录、API key 和 session 恢复
|
||
|
||
```text
|
||
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')
|
||
```
|
||
|
||
登录、Keycloak issuer、CLI API key 和 24 小时 Web session 规则见 [spec-v02-auth.md](spec-v02-auth.md)。资源授权模块只消费已经恢复出的 actor/AuthPrincipal。`/auth/session` 使用 cookie 查 `user_sessions`,再查 `users` 得到 `actor`;API key 认证直接从 `api_keys -> users` 得到同一 actor。`/auth/logout` 标记 `user_sessions.revoked_at`。Keycloak access token、refresh token、realm role 和内部 service token 都不能绕过这里的 actor 恢复与资源授权。
|
||
|
||
### admin 创建用户
|
||
|
||
```text
|
||
browser admin UI
|
||
-> cloud-web proxy
|
||
-> cloud-api POST /v1/admin/users
|
||
-> authenticate admin
|
||
-> insert users(role='user')
|
||
-> return redacted user record
|
||
```
|
||
|
||
普通用户请求该接口必须返回 `403`。响应不得返回 `password_hash`、session token 或 Secret 值。
|
||
|
||
### admin 授权 HWPOD
|
||
|
||
```text
|
||
browser admin Access UI
|
||
-> cloud-api PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}
|
||
-> authenticate admin
|
||
-> validate users.status='active'
|
||
-> validate device_pods.status='active'
|
||
-> write OpenFGA tuple
|
||
-> return effective permission matrix
|
||
```
|
||
|
||
撤销授权走 `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}`。管理员只能通过 Admin Access API 写入或删除 OpenFGA relation。
|
||
|
||
需要给用户开通 HWPOD 或工具能力时,统一使用 Admin Access API、Admin Access WebUI 或同路径 `hwlab-cli client access ...`,并按具体 relation 或 `tool:*#can_use` 写入 OpenFGA tuple。
|
||
|
||
### 用户列出 HWPOD
|
||
|
||
```text
|
||
browser or code agent tool
|
||
-> cloud-api GET /v1/device-pods
|
||
-> authenticate actor
|
||
-> if admin/access manager: list all active device_pods
|
||
-> if user: check OpenFGA viewer/operator relation for each active device_pod
|
||
-> return visible HWPOD summaries
|
||
```
|
||
|
||
未授权普通用户看到空列表或对单个未授权 HWPOD 收到 `403`;不得回退到 fake default HWPOD。
|
||
|
||
### 用户创建或继续 code agent session
|
||
|
||
```text
|
||
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
|
||
|
||
```text
|
||
code agent turn
|
||
-> cloud-api device operation route
|
||
-> authenticate actor from owning session or user API key in runner
|
||
-> verify agent_sessions.owner_user_id == actor.id
|
||
-> authorize OpenFGA tool:hwpod and device_pod relation
|
||
-> require reason for mutating operations
|
||
-> cloud-api -> HWPOD execution path
|
||
-> gateway/device-host-cli/hardware path
|
||
```
|
||
|
||
code agent prompt、runner 或 worker 不得直接绕过 cloud-api 调用残留执行 Service。残留执行服务只信任来自 cloud-api 的内部调用,不做最终用户权限判断。Cloud API 给 AgentRun runner 注入 `hwpod`、UniDesk SSH、`trans_cmd` 或 GitHub 写工具前,必须先按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 检查对应 `tool:*#can_use`。
|
||
|
||
## 内部架构
|
||
|
||
`hwlab-cloud-api` 内部应按 auth/session/API key、authorization、agent session owner、HWPOD relation 和 admin API 模块分层。所有模块共享同一 Postgres runtime store 和 migration ledger,避免拆出早期 `hwlab-user-api` 造成跨服务一致性成本。
|
||
|
||
`user_sessions` 存浏览器 session token hash;`api_keys` 存映射到用户的 CLI/runner API key;`agent_sessions.owner_user_id` 绑定 Code Agent session;迁移期 `device_pods` 存 HWPOD profile authority;OpenFGA tuple 表示用户对 HWPOD、agent session 和工具的细粒度能力。cloud-api 调用残留执行服务使用内部 service token,该 token 不参与用户鉴权、不写入 runner env,也不产生 actor。
|
||
|
||
## API 接口说明
|
||
|
||
| 接口 | 说明 |
|
||
| --- | --- |
|
||
| `GET /auth/oidc/login`、`GET /auth/oidc/callback` | Web 登录入口;按 [spec-v02-auth.md](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](spec-v02-auth.md)。 |
|
||
| `POST /v1/setup/first-admin` | 仅当 `users` 表为空时创建第一个 `admin` 并建立 session;可选 `devicePod` 或 `devicePods[]` 一次性种下首批服务端权威 profile 并授权给首个 admin;一旦已有用户必须返回 `409 setup_already_completed`。该入口不读取 Kubernetes Secret,不替代正常 admin API。 |
|
||
| `POST /auth/logout` | 设置 `revoked_at`,撤销当前 browser session。 |
|
||
| `POST /v1/admin/users` | admin 创建用户,响应不得返回 `password_hash` 或 token。 |
|
||
| `POST /v1/admin/device-pods`、`PUT /v1/admin/device-pods/{devicePodId}` | admin 管理 HWPOD profile authority;URL path 是迁移期实现名。 |
|
||
| `GET/PATCH/PUT/DELETE /v1/admin/access...` | admin Access API,读写 OpenFGA 授权、tool capability、role/status 和 effective matrix;见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 |
|
||
| `GET /v1/device-pods` 和 HWPOD 操作 API | 按 actor role、OpenFGA relation 和 tool capability 过滤可见/可用 HWPOD。 |
|
||
| `POST /v1/agent/chat` 及 result/trace/cancel | 必须校验 `agent_sessions.owner_user_id`;admin 可跨用户查看和取消。 |
|
||
|
||
`POST /v1/setup/first-admin` 的 HWPOD/profile 初始化只用于空库首次进入系统,不能作为长期 profile 管理入口。每个 seed 必须包含迁移期 `devicePodId` 和 object `profile`;cloud-api 会写入 `device_pods.profile_json/profile_hash`,并通过 Admin Access/OpenFGA relation 授权给首个 admin。响应只能返回脱敏 profile、profileHash 和授权摘要,不得返回 `gatewaySessionId`、`hostWorkspaceRoot`、password 或 session token 原文。
|
||
|
||
## 微服务设计
|
||
|
||
v0.2 不新增独立用户管理微服务。Keycloak 是独立身份提供方,不是 HWLAB 应用层授权服务;OpenFGA 是内部授权 PDP,不对用户暴露独立 API;用户映射、session/API key 消费、OpenFGA check/write、HWPOD relation、tool capability 和 code agent owner 校验全部放在 `hwlab-cloud-api` 内,理由是:
|
||
|
||
- 当前权限入口必须和 `/v1/agent/*`、`/v1/device-pods/*`、AgentRun transient env 注入和 runtime store 保持强一致,拆出新的 HWLAB 用户微服务会增加网络、部署、Secret、迁移和一致性成本。
|
||
- cloud-api 已经是 `/v1/agent/*`、迁移期 `/v1/device-pods/*` 和 runtime store 的统一入口,最适合做应用层授权收口。
|
||
- 后续若出现组织、计费、批量用户导入或跨产品用户中心,再把 cloud-api 内的 user/auth 模块抽成 `hwlab-user-api`;抽服务前接口和表结构仍以本文和 [spec-v02-auth.md](spec-v02-auth.md) 为准。
|
||
|
||
各服务职责如下:
|
||
|
||
| 服务 | v0.2 职责 |
|
||
| --- | --- |
|
||
| `hwlab-cloud-web` | Keycloak 登录入口、普通用户工作台、API key 管理入口和 Admin Access 授权 UI;浏览器 `/auth/*` 和 `/v1/admin/access*` 由 cloud-web 代理到 cloud-api。 |
|
||
| `hwlab-cloud-api` | 用户映射、Web session/API key 消费、OpenFGA 授权、HWPOD relation、tool capability、code agent owner 校验和对 HWPOD 执行路径的受控转发。 |
|
||
| OpenFGA | `hwlab-v02` 内部稳定授权服务,只接受 cloud-api 调用,不向普通用户或公网暴露。 |
|
||
| AgentRun v0.1 runner | 执行 code agent session;接收 cloud-api 提供的 owner/session/HWPOD 上下文方便观测,但不作为最终权限 authority。 |
|
||
| HWPOD execution path | 执行受控硬件语义;不保存用户权限,不直接面向浏览器或普通用户 session Pod。 |
|
||
| `hwlab-edge-proxy` | 公网/FRP 入口和 HTTP 转发;不做业务权限,只转发 cookie/header,不注入伪 actor。 |
|
||
| Postgres | v0.2 用户、session、授权、HWPOD/profile 和既有 runtime durable state。 |
|
||
|
||
## Kubernetes 落点
|
||
|
||
Kubernetes 只做运行时隔离和资源兜底,不承载 HWLAB 用户权限模型:
|
||
|
||
- `v0.2` 使用 `hwlab-v02` namespace,不按用户创建 namespace。
|
||
- 普通用户不直接持有 Kubernetes RBAC、ServiceAccount token 或 kubeconfig。
|
||
- `hwlab-v02` 优先拥有独立 Postgres StatefulSet/PVC,例如 `data-hwlab-v02-postgres-0`;不得把 `hwlab-dev/data-hwlab-g14-postgres-0` 当作 v0.2 权限数据源。
|
||
- code agent worker、session Pod/PVC/Job 必须带稳定 label,例如 `hwlab.pikastech.local/owner-user-id`、`hwlab.pikastech.local/session-id`。
|
||
- 迁移期执行工作负载如果仍承载多 HWPOD,必须带能映射 HWPOD/profile 的稳定 label;普通用户不以该 label 作为授权来源。
|
||
- code agent 到 HWPOD 的访问应收敛到 `code agent -> cloud-api -> HWPOD execution path`,避免普通 session Pod 直接调用残留执行 Service 绕过应用层授权。
|
||
- Keycloak 按 [spec-v02-auth.md](spec-v02-auth.md) 作为独立 `keycloak` namespace 的外部身份源接入;OpenFGA 按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 部署在 `hwlab-v02` namespace 作为内部授权服务;Kubernetes 租户隔离第一轮仍不引入 Dex、oauth2-proxy、Capsule、vCluster、Kyverno 或 service mesh。
|
||
|
||
当前态、差距和迁移步骤已迁入 [pikasTech/HWLAB#531](https://github.com/pikasTech/HWLAB/issues/531) 评论。
|
||
|
||
## 测试规格
|
||
|
||
## T1
|
||
|
||
阅读 docs/reference/spec-user-access.md 和 docs/reference/spec-v02-auth.md,然后用 Web session 或 `HWLAB_API_KEY` 访问 `/v1/users/me`,确认响应包含 actor、role 和 active auth method,不包含 password hash、session token 原文、完整 API key 或 Secret 值。空库 bootstrap fallback 可额外验证 `/auth/login`,但不得把它作为目标登录体验。
|
||
|
||
## T2
|
||
|
||
阅读 docs/reference/spec-user-access.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:用 admin 给普通用户授予某个 HWPOD 的 `viewer` 但不授予 `operator/job_submitter`,确认普通用户只能看到 HWPOD 摘要,提交 job 返回 403;授予 `operator/job_submitter` 后 job 可提交;撤销 relation 后同一用户不能再看到或使用该 HWPOD。
|
||
|
||
## T3
|
||
|
||
阅读 docs/reference/spec-user-access.md,然后用 cli 手动测试以下内容:普通用户创建 Code Agent session 后,只能读取、继续和取消自己的 trace/result;另一个普通用户访问该 session 必须失败,admin 可以跨用户查看或取消。
|
||
|
||
## 规格的实现情况
|
||
|
||
| 规格项 | 状态 | 说明 |
|
||
| --- | --- | --- |
|
||
| admin/user 两角色模型 | 部分实现 | cloud-api 已实现 `/auth/*`、bootstrap admin、admin/user 创建和 admin-only 路由。 |
|
||
| `users`、`user_sessions`、OpenFGA relation 和 job 表 | 部分实现 | access-control bootstrap 覆盖 users、sessions、迁移期 device_pods、access_tuples 和 jobs;HWPOD 强副作用 job 已接入 reason 校验,真实硬件执行仍依赖 gateway/device-host-cli 在线。 |
|
||
| Code Agent owner 绑定 | 已实现 | 已在 `agent_sessions` 写入 `owner_user_id`、conversation/thread/trace 和脱敏 session evidence;trace/result cache 也按 owner/admin 限制访问。 |
|
||
| OpenFGA 细粒度授权模型 | 核心已实现/持续约束 | v0.2 enforce runtime 已通过 Admin Access API 和同路径 CLI 管理 HWPOD relation 与 tool capability;后续扩展仍必须按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 保持同一 authority。 |
|
||
| 不用 Kubernetes 表达用户权限 | 已实现/持续约束 | 规格明确禁止普通用户持有 kubeconfig 或直连 Service 权限。 |
|