docs: expand v0.2 multi-user access plan
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# v0.2 多用户访问迁移计划
|
||||
|
||||
本文记录 v0.2 当前源码状态、与目标多用户访问模型的差距,以及推荐迁移路径。长期目标和稳定判定标准以 [../reference/multi-user-access.md](../reference/multi-user-access.md) 为准。
|
||||
|
||||
## 当前态
|
||||
|
||||
从 v0.2 源码看,当前系统还没有真正的多用户访问控制:
|
||||
|
||||
- `docs/reference/multi-user-access.md` 已定义 `admin/user`、session owner 和 device pod grant 的目标模型,但还没有对应实现。
|
||||
- `internal/db/migrations/0001_cloud_core_skeleton.sql` 已有 `projects`、`gateway_sessions`、`hardware_operations`、`audit_events`、`agent_sessions`、`worker_sessions`、`agent_trace_events`、`evidence_records` 和 migration ledger;没有 `users`、`user_sessions`、`device_pods`、`device_pod_grants`、`device_leases`,`agent_sessions` 也没有 `owner_user_id`。
|
||||
- `internal/db/runtime-store.mjs` 支持 memory/postgres runtime store 和 readiness,但主要服务 L1 硬件 runtime 数据;用户身份和授权尚未进入 runtime store。
|
||||
- `web/hwlab-cloud-web/auth.mjs` 是轻量登录前端,默认 `admin/hwlab2026`,并有 local session fallback;它不是正式用户体系。
|
||||
- `internal/dev-entrypoint/cloud-web-routes.mjs` 仍把 `POST /v1/agent/chat`、`POST /v1/agent/chat/cancel`、`GET /v1/agent/chat/result/*`、`GET /v1/agent/chat/trace/*` 视为 public proxy route,多用户下必须收敛为 auth-required。
|
||||
- `internal/cloud/code-agent-session-registry.mjs` 以内存 Map 维护 session/conversation/facts,没有 owner、用户 session、Postgres 持久归属或跨 Pod 恢复。
|
||||
- `internal/cloud/server.mjs` 的 `/v1/device-pods` 只是代理 `hwlab-device-pod` 或回退 fake payload,没有按 actor 过滤,也没有 POST job/lease 权限入口。
|
||||
- `cmd/hwlab-device-pod/main.mjs` 和 `internal/device-pod/fake-data.mjs` 当前只提供 fake GET 数据,不连接真实硬件,也不持久化 device pod 管理数据。
|
||||
- G14 当前集群里 `hwlab-dev` 有 `hwlab-g14-postgres` 和 PVC;`hwlab-v02` namespace 还未成为独立运行面。v0.2 权限数据不能混用 `hwlab-dev` pgdata。
|
||||
|
||||
## 目标差距
|
||||
|
||||
| 领域 | 当前态 | 目标态 |
|
||||
| --- | --- | --- |
|
||||
| 用户 | 前端默认账号和本地 session fallback | Postgres `users` + `user_sessions`,`admin/user` 两角色 |
|
||||
| Code Agent session | 内存 ownerless registry | `agent_sessions.owner_user_id` 持久归属,result/trace/cancel 按 owner 校验 |
|
||||
| Device Pod | fake GET 数据,所有人可见 | `device_pods` 管理表 + `device_pod_grants` 授权过滤 |
|
||||
| Device 操作 | 还不是正式 job/lease 权限入口 | cloud-api 授权后转发到 device-pod,独占操作用 `device_leases` |
|
||||
| Cloud Web 路由 | 部分 agent route public | 除 health/static/login 外,用户态 API 都 auth-required |
|
||||
| 微服务 | 没有用户服务 | 不新增用户服务,cloud-api 内置 user/auth/access 模块 |
|
||||
| v0.2 数据面 | namespace/Postgres 目标未落地 | `hwlab-v02` 独立 namespace + 独立 Postgres DB/PVC/Secret |
|
||||
|
||||
## 迁移原则
|
||||
|
||||
- 不做 group/project/capability/read-operate 分级迁移;直接收敛到 `admin/user` 和 grant-exists-is-full-access。
|
||||
- 不新增 user-management 微服务;先把用户管理做成 cloud-api 内部模块和 admin API。
|
||||
- 不把 `audit_events` 复用成用户审计;保留它作为既有硬件证据链数据。
|
||||
- 不把普通用户映射成 Kubernetes user/namespace/RBAC。
|
||||
- 不为了兼容旧 public route 长期保留双路径;不兼容变更在同一阶段同步改后端、前端和测试。
|
||||
- v0.2 数据面不复用 `hwlab-dev` pgdata;如需导入历史数据,显式写一次性迁移脚本并记录来源和目标。
|
||||
|
||||
## 推荐实施阶段
|
||||
|
||||
### 阶段 1:Schema 和 bootstrap admin
|
||||
|
||||
新增 `0002_multi_user_access_v1` 迁移:
|
||||
|
||||
- 新增 `users`、`user_sessions`、`device_pods`、`device_pod_grants`、`device_leases`。
|
||||
- 扩展现有 `agent_sessions`:`owner_user_id`、`conversation_id`、`thread_id`、`last_trace_id`、`session_json`、`updated_at`。
|
||||
- 写入 bootstrap `admin`,密码只存 hash。
|
||||
- 为 fake/default `device-pod-71-freq` 写入一条 `device_pods` 记录,便于前端从 fake 数据迁到 DB 管理数据。
|
||||
|
||||
不兼容处理:已有 ownerless `agent_sessions` 不继续作为普通用户 session。迁移时一次性归属 bootstrap `admin` 或标记 `expired`,迁移后新 session 必须有 owner。
|
||||
|
||||
### 阶段 2:cloud-api auth/access 模块
|
||||
|
||||
在 cloud-api 内新增模块:
|
||||
|
||||
- `authenticateRequest(request)`:从 cookie/session token 解析 actor。
|
||||
- `requireActor(request)`:未登录返回 `401`。
|
||||
- `requireAdmin(actor)`:非 admin 返回 `403`。
|
||||
- `authorizeAgentSession(actor, sessionId|traceId|conversationId)`:admin 或 owner。
|
||||
- `authorizeDevicePod(actor, devicePodId)`:admin 或存在 `device_pod_grants`。
|
||||
|
||||
不兼容处理:除 `/health`、`/health/live`、静态资源和登录接口外,用户态 `/v1/*` 不再允许匿名访问。旧 public code agent poll route 直接改为 auth-required,不保留 legacy public mode。
|
||||
|
||||
### 阶段 3:Cloud Web 登录和 admin UI
|
||||
|
||||
调整 cloud-web:
|
||||
|
||||
- `/auth/session`、`/auth/login`、`/auth/logout` 代理或落到 cloud-api authority。
|
||||
- 移除多用户运行态下的 local auth fallback;没有 server session 时显示登录页。
|
||||
- 增加最小 admin UI:用户列表、创建/禁用用户、device pod 授权/撤销。
|
||||
- 普通用户只显示自己的 code agent session 和被授权 device pod。
|
||||
|
||||
不兼容处理:默认 `admin/hwlab2026` 只能作为 bootstrap 初始化入口;完成初始化后应由管理员改密码或替换。前端测试 fixture 同步改成 server-session 模式。
|
||||
|
||||
### 阶段 4:Code Agent session owner 持久化
|
||||
|
||||
调整 `/v1/agent/chat` 及相关路由:
|
||||
|
||||
- 创建或复用 session 时写入 `agent_sessions.owner_user_id`。
|
||||
- `conversation_id -> session_id` 绑定迁到 Postgres 或至少写入 session record。
|
||||
- result/trace/cancel/inspect 根据 owner 校验。
|
||||
- code agent prompt 中只暴露当前 actor 可见的 device pod 列表。
|
||||
|
||||
不兼容处理:部署前存在的浏览器本地 conversation/session 可能失效;前端应在 `401/403/session_expired` 时提示重新登录或新建会话,不做 ownerless session 兼容恢复。
|
||||
|
||||
### 阶段 5:Device Pod 管理和授权过滤
|
||||
|
||||
调整 `/v1/device-pods`:
|
||||
|
||||
- list/status 从 `device_pods` 读取管理数据,再按 actor 过滤。
|
||||
- `admin` 可创建/更新/禁用 device pod。
|
||||
- `admin` 可 upsert/delete `device_pod_grants`。
|
||||
- 未授权用户访问具体 device pod 返回 `403`。
|
||||
|
||||
不兼容处理:普通用户不再默认看到 `device-pod-71-freq`。如果需要演示用户看到它,必须显式给该用户授权。
|
||||
|
||||
### 阶段 6:Device jobs 和 lease
|
||||
|
||||
为真实设备操作增加 cloud-api job routes:
|
||||
|
||||
- `POST /v1/device-pods/{devicePodId}/jobs`
|
||||
- `GET /v1/device-pods/{devicePodId}/jobs/{jobId}`
|
||||
- `POST /v1/device-pods/{devicePodId}/jobs/{jobId}/cancel`
|
||||
|
||||
cloud-api 先校验 actor 和 grant,再按操作类型获取 `device_leases`,最后转发到 `hwlab-device-pod` 内部 Service。
|
||||
|
||||
不兼容处理:device-pod 服务不面向普通用户直接暴露,不接受浏览器或 worker 直连作为授权依据;所有真实操作必须经过 cloud-api。
|
||||
|
||||
### 阶段 7:v0.2 namespace 和数据面
|
||||
|
||||
部署面调整:
|
||||
|
||||
- 创建 `hwlab-v02` namespace。
|
||||
- 建立 v0.2 独立 Postgres StatefulSet、Service、PVC 和 DB Secret。
|
||||
- cloud-api v0.2 指向 v0.2 DB URL 和独立 migration ledger。
|
||||
- Cloud Web/API FRP 使用 v0.2 规划入口 `19666/19667`。
|
||||
|
||||
不兼容处理:不把 `hwlab-dev` 数据自动复制到 v0.2。需要保留的数据必须写明迁移对象、来源、目标和回滚方式;用户权限数据默认从 bootstrap admin 开始重建。
|
||||
|
||||
## 其他微服务调整
|
||||
|
||||
| 微服务 | 必要调整 |
|
||||
| --- | --- |
|
||||
| `hwlab-cloud-api` | 新增 user/auth/access 模块、admin API、session owner 校验、device grant/lease 校验。 |
|
||||
| `hwlab-cloud-web` | 登录改为 server-session authority;增加 admin UI;普通用户视图按授权过滤。 |
|
||||
| `hwlab-edge-proxy` | 保持透明转发,确保 cookie/header 不被丢弃;不注入业务 actor。 |
|
||||
| `hwlab-agent-mgr` | session 创建参数和状态摘要带 `owner_user_id`、`session_id` label;不自行做最终授权。 |
|
||||
| `hwlab-agent-worker` | Pod/Job/PVC label 带 owner/session,工具调用 device pod 时走 cloud-api。 |
|
||||
| `hwlab-device-pod` | 从 fake GET 逐步扩展到 jobs API;信任 cloud-api 内部调用,不保存用户 grant。 |
|
||||
| `hwlab-agent-skills` | device-pod-cli 默认目标改为 cloud-api 授权入口,不直连 device-pod Service。 |
|
||||
| GitOps/render | v0.2 增加 namespace、Postgres、SecretRef、ServiceAccount、PVC 和 env 注入。 |
|
||||
|
||||
## 最小验证
|
||||
|
||||
- 未登录访问 `/v1/agent/chat/result/*`、`/v1/agent/chat/trace/*` 和 `/v1/device-pods` 返回 `401`。
|
||||
- `admin` 能创建用户、创建设备、授权和撤销授权。
|
||||
- 未授权 `user` 看不到 device pod,访问具体 device pod 返回 `403`。
|
||||
- 授权后 `user` 能看到并使用 device pod。
|
||||
- `user A` 不能读取或取消 `user B` 的 code agent session。
|
||||
- `admin` 可以跨用户查看和取消 session。
|
||||
- 同一个 device pod 的独占操作只能被一个 active lease 持有。
|
||||
- v0.2 cloud-api health 报告连接的是 v0.2 DB,不是 `hwlab-dev` pgdata。
|
||||
@@ -1,16 +1,17 @@
|
||||
# v0.2 多用户访问模型
|
||||
|
||||
本文是 HWLAB `v0.2` 多用户系统的长期参考口径。目标是先用最少概念支持真实用户使用 code agent session 和管理员分配 device pod 权限,同时避免把用户体系、Kubernetes 租户、设备授权和硬件证据链混成一套复杂门禁。
|
||||
本文是 HWLAB `v0.2` 多用户系统的长期参考口径。目标是用最少概念支持真实用户使用 code agent session 和管理员分配 device pod 权限,同时避免把用户体系、Kubernetes 租户、设备授权、硬件证据链和审计系统混成一套复杂门禁。
|
||||
|
||||
## 设计边界
|
||||
## 设计目标
|
||||
|
||||
- 用户角色只保留 `admin` 和 `user`。不引入 `viewer`、`platform_admin`、`device_admin`、group 或 project。
|
||||
- 只保留两类角色:`admin` 和 `user`。
|
||||
- `code agent session` 直接归属于创建它的用户;普通用户只能查看、继续和取消自己的 session。
|
||||
- `device pod` 由 `admin` 管理;普通用户只有在被 `admin` 授权后才能看到和使用对应 device pod。
|
||||
- device pod 授权不拆分 `read`、`operate` 或其他 capability;授权关系存在即代表该用户拥有该 device pod 的完整使用权限。
|
||||
- MVP 不新增产品级 `audit_events` 表,也不把用户权限设计依赖到 audit。现有 M3 trace/evidence/audit 字段属于硬件闭环验收证据,不是多用户权限模型的一部分。
|
||||
- device pod 授权不拆分 `read`、`operate` 或 capability;授权关系存在即代表该用户拥有该 device pod 的完整使用权限。
|
||||
- MVP 不新增产品级 `audit_events` 用户审计表,也不把用户权限依赖到 audit。现有硬件 trace/evidence/audit 字段属于硬件闭环证据,不是多用户权限模型的一部分。
|
||||
- `device lease` 可以保留,但它只解决物理设备并发互斥,不表达用户权限。
|
||||
- 普通用户不获得 Kubernetes 用户、kubeconfig、namespace 管理权或直接访问 device pod Service 的权限;所有用户权限判断在 cloud-api 应用层完成。
|
||||
- v0.2 权限数据必须与 `hwlab-dev`/`hwlab-prod` 运行数据隔离。优先在 `hwlab-v02` namespace 内使用独立 Postgres StatefulSet/PVC;若未来显式复用共享 Postgres 实例,也必须使用独立 database 或 schema、独立 Secret 和独立 migration ledger,不得直接复用 `hwlab-dev` 的 pgdata。
|
||||
|
||||
## 简化前后
|
||||
|
||||
@@ -19,27 +20,128 @@
|
||||
| 用户角色 | `platform_admin`、`device_admin`、`developer`、`viewer` | `admin`、`user` |
|
||||
| 用户组 | `groups`、`group_members` | 不引入 |
|
||||
| 项目隔离 | `projects`、`project_members` | 不引入 |
|
||||
| Code Agent 会话 | `ownerUserId + projectId + sessionId` | `ownerUserId + sessionId` |
|
||||
| Code Agent 会话 | `ownerUserId + projectId + sessionId` | `owner_user_id + session id` |
|
||||
| Device Pod 管理 | 平台管理员和设备管理员分工 | `admin` 统一管理 |
|
||||
| Device Pod 授权 | 可按 group/project/user 授权 | 只按具体 `userId` 授权 |
|
||||
| Device Pod 授权 | 可按 group/project/user 授权 | 只按具体 `user_id` 授权 |
|
||||
| 设备权限粒度 | `device.view`、`io.read`、`io.write`、`debug.reset` 等 capability | 授权即全权限 |
|
||||
| Viewer | 单独只读角色 | 不引入 |
|
||||
| Audit | 独立用户审计表 | 不引入 |
|
||||
| Lease | 权限和互斥可能混用 | 只作为设备互斥锁 |
|
||||
|
||||
## 核心数据模型
|
||||
## 表结构
|
||||
|
||||
MVP 只需要以下长期对象:
|
||||
v0.2 多用户实现复用 cloud-api 和现有 Postgres runtime store,不新增独立用户管理微服务。推荐新增一个 `0002_multi_user_access_v1` 数据库迁移,保持现有 `0001_cloud_core_skeleton.sql` 的硬件 runtime 表不被误用为用户权限表。
|
||||
|
||||
| 表 | 关键字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `users` | `id`、`username`、`role`、`status` | `role` 只能是 `admin` 或 `user`;停用用户不能创建 session 或使用 device pod。 |
|
||||
| `code_agent_sessions` | `id`、`owner_user_id`、`status`、`created_at`、`updated_at` | 记录 code agent session 的一等归属。 |
|
||||
| `device_pods` | `id`、`name`、`status`、`profile_ref`、`gateway_ref` | `admin` 管理的设备能力单元;profile 仍遵循 [device-pod.md](device-pod.md)。 |
|
||||
| `device_pod_grants` | `device_pod_id`、`user_id`、`created_by_admin_id`、`created_at` | 授权关系表;存在即全权限,不含 capability、scope 或 expires 字段。 |
|
||||
| `device_leases` | `device_pod_id`、`holder_session_id`、`expires_at` | 可选互斥锁;防止两个 session 同时烧录、复位或占用同一物理设备。 |
|
||||
### `users`
|
||||
|
||||
`device_pod_grants` 应以 `(device_pod_id, user_id)` 作为唯一约束。撤销授权就是删除该行;如果被撤销用户还有活动 lease,撤销流程必须先释放或标记失效该 lease。
|
||||
用户身份和角色 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 本地账号;未来接 OIDC 时仍保留 `users.id`、`role` 和授权表稳定,不把外部 IdP subject 直接暴露给业务授权。
|
||||
- `disabled` 用户不能创建 session、继续 session 或使用 device pod。
|
||||
|
||||
### `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`、`/auth/login`、`/auth/logout` 的最终 authority 是 cloud-api;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`
|
||||
|
||||
设备能力单元的管理表;profile 语义仍以 [device-pod.md](device-pod.md) 为准。
|
||||
|
||||
```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_ref TEXT NOT NULL DEFAULT '',
|
||||
gateway_ref TEXT NOT NULL DEFAULT '',
|
||||
device_pod_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
### `device_pod_grants`
|
||||
|
||||
普通用户对 device pod 的授权关系;存在即全权限。
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS device_pod_grants (
|
||||
device_pod_id TEXT NOT NULL REFERENCES device_pods(id) ON DELETE CASCADE,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_by_admin_id TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (device_pod_id, user_id)
|
||||
);
|
||||
```
|
||||
|
||||
- 不包含 `capability`、`scope`、`expires_at`。
|
||||
- 撤销授权就是删除对应行;如果用户仍持有该 device pod 的活动 lease,撤销流程必须先释放或标记失效 lease。
|
||||
|
||||
### `device_leases`
|
||||
|
||||
设备互斥锁;不表达权限。
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS device_leases (
|
||||
device_pod_id TEXT PRIMARY KEY REFERENCES device_pods(id) ON DELETE CASCADE,
|
||||
holder_session_id TEXT NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE,
|
||||
holder_user_id TEXT NOT NULL REFERENCES users(id),
|
||||
lease_token_hash TEXT NOT NULL UNIQUE,
|
||||
created_at TEXT NOT NULL,
|
||||
expires_at TEXT NOT NULL,
|
||||
released_at TEXT
|
||||
);
|
||||
```
|
||||
|
||||
- 下载、复位、长时间采样、串口独占等会占用真实物理设备的操作必须先拿 lease。
|
||||
- 读短状态可以不拿 lease,但仍必须通过 device grant 授权。
|
||||
|
||||
## 权限矩阵
|
||||
|
||||
@@ -55,7 +157,7 @@ MVP 只需要以下长期对象:
|
||||
| 使用 device pod 的 workspace/debug/io 能力 | 可以使用全部 | 只能使用被授权的 device pod |
|
||||
| 获取 device lease | 可以 | 只能对被授权的 device pod 获取 |
|
||||
|
||||
## 请求授权链路
|
||||
## 请求链路
|
||||
|
||||
cloud-api 每个用户态请求都按同一顺序处理:
|
||||
|
||||
@@ -63,48 +165,118 @@ cloud-api 每个用户态请求都按同一顺序处理:
|
||||
authenticate -> actor -> authorize(actor, action, resource) -> optional lease check -> execute
|
||||
```
|
||||
|
||||
- `authenticate` 只负责把 cookie、JWT 或可信 edge identity 解析成本地 `actor`。
|
||||
- `authorize` 只使用本地 `users`、`code_agent_sessions`、`device_pods` 和 `device_pod_grants` 判断权限。
|
||||
- code agent result、trace、cancel 和 resume 接口必须校验 `session.owner_user_id === actor.id`,除非 actor 是 `admin`。
|
||||
- device pod list/status/job 接口必须校验 actor 是 `admin`,或 `device_pod_grants` 中存在 `(device_pod_id, actor.id)`。
|
||||
- code agent 调用 device pod 时,cloud-api 必须同时校验 session owner 和 device pod grant,避免用户把自己的 session 指向未授权设备。
|
||||
### 登录和 session 恢复
|
||||
|
||||
```text
|
||||
browser
|
||||
-> cloud-web /auth/login
|
||||
-> cloud-api auth controller
|
||||
-> users password_hash/status/role check
|
||||
-> user_sessions insert token hash
|
||||
-> Set-Cookie httpOnly sameSite
|
||||
-> browser
|
||||
```
|
||||
|
||||
`/auth/session` 使用 cookie 查 `user_sessions`,再查 `users` 得到 `actor`。`/auth/logout` 标记 `user_sessions.revoked_at`。
|
||||
|
||||
### 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 授权 device pod
|
||||
|
||||
```text
|
||||
browser admin UI
|
||||
-> cloud-api POST /v1/admin/device-pod-grants
|
||||
-> authenticate admin
|
||||
-> validate users.status='active'
|
||||
-> validate device_pods.status='active'
|
||||
-> upsert device_pod_grants(device_pod_id, user_id)
|
||||
-> return grant summary
|
||||
```
|
||||
|
||||
撤销授权走 `DELETE /v1/admin/device-pod-grants/{devicePodId}/{userId}`,先释放或失效该用户对该 device pod 的活动 lease。
|
||||
|
||||
### 用户列出 device pod
|
||||
|
||||
```text
|
||||
browser or code agent tool
|
||||
-> cloud-api GET /v1/device-pods
|
||||
-> authenticate actor
|
||||
-> if admin: list all active device_pods
|
||||
-> if user: inner join device_pod_grants by actor.id
|
||||
-> return visible device pod summaries
|
||||
```
|
||||
|
||||
未授权普通用户看到空列表或对单个未授权 device pod 收到 `403`;不得回退到 fake default device pod。
|
||||
|
||||
### 用户创建或继续 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 使用 device pod
|
||||
|
||||
```text
|
||||
code agent turn
|
||||
-> cloud-api device operation route
|
||||
-> authenticate actor from owning session
|
||||
-> verify agent_sessions.owner_user_id == actor.id
|
||||
-> authorize device_pod_grants or admin
|
||||
-> acquire or validate device_leases when operation is exclusive
|
||||
-> cloud-api -> hwlab-device-pod internal Service
|
||||
-> gateway/device-host-cli/hardware path
|
||||
```
|
||||
|
||||
code agent prompt、runner 或 worker 不得直接绕过 cloud-api 调用 device pod Service。device pod 服务只信任来自 cloud-api 的内部调用,不做最终用户权限判断。
|
||||
|
||||
## 微服务设计
|
||||
|
||||
v0.2 不新增独立用户管理微服务。用户管理、登录、session、device pod grant 和 code agent owner 校验全部放在 `hwlab-cloud-api` 内,理由是:
|
||||
|
||||
- 当前权限模型只有 `admin/user`、session owner 和 device grant 三类判断,拆服务会增加网络、部署、Secret、迁移和一致性成本。
|
||||
- cloud-api 已经是 `/v1/agent/*`、`/v1/device-pods/*` 和 runtime store 的统一入口,最适合做应用层授权收口。
|
||||
- 后续若出现组织、计费、外部 IdP、批量用户导入或跨产品用户中心,再把 cloud-api 内的 user/auth 模块抽成 `hwlab-user-api`;抽服务前接口和表结构仍以本文为准。
|
||||
|
||||
各服务职责如下:
|
||||
|
||||
| 服务 | v0.2 职责 |
|
||||
| --- | --- |
|
||||
| `hwlab-cloud-web` | 登录页、普通用户工作台、admin 用户/授权 UI;浏览器 `/auth/*` 可由 cloud-web 代理到 cloud-api。 |
|
||||
| `hwlab-cloud-api` | 用户、session、授权、device grant、lease、code agent owner 校验和对 device pod 的受控转发。 |
|
||||
| `hwlab-agent-mgr` / `hwlab-agent-worker` | 执行 code agent session;接收 owner/session label 或 env 方便观测,但不作为最终权限 authority。 |
|
||||
| `hwlab-device-pod` | 暴露设备语义 API;不保存用户权限,不直接面向浏览器或普通用户 session Pod。 |
|
||||
| `hwlab-edge-proxy` | 公网/FRP 入口和 HTTP 转发;不做业务权限,只转发 cookie/header,不注入伪 actor。 |
|
||||
| Postgres | v0.2 用户、session、授权、device pod、lease 和既有 runtime durable state。 |
|
||||
|
||||
## Kubernetes 落点
|
||||
|
||||
Kubernetes 只做运行时隔离和资源兜底,不承载 HWLAB 用户权限模型:
|
||||
|
||||
- `v0.2` 默认仍使用单 namespace `hwlab-v02`,不按用户创建 namespace。
|
||||
- `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`。
|
||||
- device pod 工作负载必须带 `hwlab.pikastech.local/device-pod-id` label,并通过 Service 暴露稳定内部地址。
|
||||
- code agent 到 device pod 的访问应优先收敛到 `code agent -> cloud-api -> device-pod`,避免普通 session Pod 直接调用 device pod Service 绕过应用层授权。
|
||||
- ResourceQuota、LimitRange、Pod Security 和 NetworkPolicy 只作为高价值兜底;不要为了多用户 MVP 引入租户级控制面或复杂 admission 门禁。
|
||||
- code agent 到 device pod 的访问应收敛到 `code agent -> cloud-api -> device-pod`,避免普通 session Pod 直接调用 device pod Service 绕过应用层授权。
|
||||
- 第一轮不引入 Keycloak、Dex、oauth2-proxy、OpenFGA、Capsule、vCluster、Kyverno 或 service mesh;需要正式外部身份源或集群 admission 兜底时再单独设计。
|
||||
|
||||
## 中间件选择
|
||||
|
||||
v0.2 的推荐接入顺序是“先应用层清晰,再逐步加 Kubernetes 兜底”:
|
||||
|
||||
1. **PostgreSQL**:作为 `users`、`code_agent_sessions`、`device_pods`、`device_pod_grants` 和 `device_leases` 的 source of truth。授权查询简单、可迁移,也能与现有 cloud-api runtime store 收敛。
|
||||
2. **OIDC 身份源**:需要正式多用户登录时优先选 Keycloak;它同时提供用户、角色和管理控制台。若只需要接入外部 IdP,可用 Dex 做 OIDC broker。
|
||||
3. **oauth2-proxy 或等价 edge auth**:适合快速保护 Cloud Web,并把 OIDC identity 传给上游;cloud-api 仍必须校验 JWT 或只信任受控内网代理注入的签名身份。
|
||||
4. **Kubernetes ServiceAccount + RBAC**:只给 HWLAB 服务组件和 session worker 使用,不暴露给最终用户。
|
||||
5. **NetworkPolicy**:用于阻止 code agent Pod 横向直连 device pod Service;若当前 CNI 不支持,应先作为计划项,不把它伪装成已生效门禁。
|
||||
6. **ResourceQuota、LimitRange、Pod Security Standards**:防止 session worker 抢占集群资源或使用越权 Pod 配置。
|
||||
7. **Kyverno 或 Gatekeeper**:仅在需要集群 admission 兜底时引入,第一批策略只覆盖必需 label、禁止 privileged/hostPath、限制镜像来源等高价值规则。
|
||||
|
||||
暂不作为 MVP 起点的组件:
|
||||
|
||||
- **OpenFGA**:适合后续出现组织、项目、继承、临时共享或委托授权后再接入;当前 `device_pod_grants` 足够。
|
||||
- **Capsule**:适合团队自服务 namespace 多租户;HWLAB 当前不把普通用户映射成 Kubernetes tenant。
|
||||
- **vCluster**:适合给租户独立虚拟 Kubernetes API;对 v0.2 权限模型过重。
|
||||
- **Service mesh**:可做 mTLS 和细粒度流量治理,但不是两角色 device pod 授权 MVP 的第一依赖。
|
||||
|
||||
## 实现路径
|
||||
|
||||
1. 先实现本地 `users`、`role`、server session/JWT actor 解析,并保留一个 bootstrap `admin`。
|
||||
2. 给 code agent session registry 和持久层增加 `owner_user_id`,所有 result/trace/cancel/resume 路由按 owner 校验。
|
||||
3. 增加 `device_pods` 和 `device_pod_grants`,把 `/v1/device-pods` 的 list/status/job 响应改成按 actor 过滤。
|
||||
4. 把 device pod 操作统一封装到 cloud-api 授权入口,禁止 code agent prompt 或 runner 绕过 cloud-api 直连 device pod Service。
|
||||
5. 增加 `device_leases` 互斥逻辑;下载、复位、长时间采样等操作先拿 lease,完成或超时后释放。
|
||||
6. 在 `hwlab-v02` namespace 添加最小资源兜底:session label、ServiceAccount 边界、resource requests/limits、必要的 NetworkPolicy 和 Pod Security。
|
||||
7. 身份从 bootstrap 用户升级到 OIDC 时,保持 `users.id` 和授权表稳定,只把登录来源替换为 Keycloak/Dex/oauth2-proxy/JWT。
|
||||
当前态、差距和迁移步骤见 [../plan/v02-multi-user-migration.md](../plan/v02-multi-user-migration.md)。
|
||||
|
||||
Reference in New Issue
Block a user