Files
pikasTech-HWLAB/docs/reference/spec-user-access.md
T
2026-05-28 18:09:50 +08:00

13 KiB
Raw Blame History

v0.2 用户和权限管理规格

本文是 HWLAB v0.2 用户和权限管理的规格说明。目标是用最少概念支持真实用户使用 code agent session 和管理员分配 device pod 权限,同时避免把用户体系、Kubernetes 租户、设备授权、硬件证据链和审计系统混成一套复杂门禁。

本规格与 spec-device-pod.md 配套:用户和权限规格定义谁可以看见、创建和使用 device poddevice-pod 规格定义 profile authority、REST/job 和硬件执行边界。

规格目标

  • 只保留两类角色:adminuser
  • code agent session 直接归属于创建它的用户;普通用户只能查看、继续和取消自己的 session。
  • device podadmin 管理;普通用户只有在被 admin 授权后才能看到和使用对应 device pod。
  • device pod 授权不拆分 readoperate 或 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。

简化边界

模块 不采用的复杂方案 v0.2 规格方案
用户角色 platform_admindevice_admindeveloperviewer adminuser
用户组 groupsgroup_members 不引入
项目隔离 projectsproject_members 不引入
Code Agent 会话 ownerUserId + projectId + sessionId owner_user_id + session id
Device Pod 管理 平台管理员和设备管理员分工 admin 统一管理
Device Pod 授权 可按 group/project/user 授权 只按具体 user_id 授权
设备权限粒度 device.viewio.readio.writedebug.reset 等 capability 授权即全权限
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。

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.idrole 和授权表稳定,不把外部 IdP subject 直接暴露给业务授权。
  • disabled 用户不能创建 session、继续 session 或使用 device pod。

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
);
  • /auth/session/auth/login/auth/logout 的最终 authority 是 cloud-apicloud-web 可以保留同名浏览器路由,但只能作为静态 UI 或代理层。
  • logout 是设置 revoked_at,不是仅删除浏览器本地状态。

agent_sessions

复用现有 agent_sessions 作为 code agent session 归属记录,不再新增同义的 code_agent_sessions 表。v0.2 迁移应在现有表上增加 owner 和 chat/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。

device_pods

设备能力单元的管理表;正式 profile authority 和执行语义以 spec-device-pod.md 为准。profile 定义 device pod,因此 profile 必须由 admin 通过 cloud-api 管理,不能由 code agent 本地 .device-pod/ 文件决定。

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

device_pod_grants

普通用户对 device pod 的授权关系;存在即全权限。

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)
);
  • 不包含 capabilityscopeexpires_at
  • 撤销授权就是删除对应行;如果用户仍持有该 device pod 的活动 lease,撤销流程必须先释放或标记失效 lease。

device_leases

设备互斥锁;不表达权限。

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 授权。

权限矩阵

操作 admin user
管理用户 可以 不可以
创建自己的 code agent session 可以 可以
查看、继续、取消自己的 code agent session 可以 可以
查看、取消别人的 code agent session 可以 不可以
创建、更新、删除 device pod 可以 不可以
给用户授权或撤销 device pod 可以 不可以
查看 device pod 可以查看全部 只能查看被授权的 device pod
使用 device pod 的 workspace/debug/io 能力 可以使用全部 只能使用被授权的 device pod
获取 device lease 可以 只能对被授权的 device pod 获取

请求链路

cloud-api 每个用户态请求都按同一顺序处理:

authenticate -> actor -> authorize(actor, action, resource) -> optional lease check -> execute

登录和 session 恢复

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 创建用户

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

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

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

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 校验 owneradmin 可跨用户查看和取消。

code agent 使用 device pod

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 使用 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-idhwlab.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 绕过应用层授权。
  • 第一轮不引入 Keycloak、Dex、oauth2-proxy、OpenFGA、Capsule、vCluster、Kyverno 或 service mesh;需要正式外部身份源或集群 admission 兜底时再单独设计。

当前态、差距和迁移步骤见 ../plan/v02-multi-user-migration.md