Files
YG_FT/docs/permissions-design.md
wuyongtao 5ecca9f0bc feat: 权限与日志治理完善,MinIO 独立部署与 tiktoken 离线打包适配
- 后端:强化平台/审批/资源/系统接口权限校验与操作日志,更新权限设计文档与测试用例
- 存储:新增 MinIO 独立部署适配(端口 19000/19001),外部端点与 host-gateway 互通
- 离线:打包 tiktoken cl100k_base 词表进镜像,避免无网环境联网下载
- 其他:算力节点接口微调,前端微调创建页小修,忽略 MinIO 运行时数据

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-12 15:21:42 +08:00

36 KiB
Raw Blame History

平台权限设计文档

版本v1.0 日期2026-08-02 状态:设计基线,供后端实现和前端联调参照


目录

  1. 设计目标
  2. 整体架构
  3. 角色体系
  4. 页面权限码
  5. 资源所有权与可见性
  6. 资源级 ACL访问控制列表
  7. GPU 算力分配与隔离
  8. 审批拦截机制
  9. 审计日志
  10. 接口鉴权流程
  11. 数据库表结构
  12. API 接口清单
  13. 前端权限控制
  14. 安全设计补充
  15. 实施计划

1. 设计目标

目标 说明
数据隔离 用户自己创建的数据集、模型、训练任务默认只有自己可见可操作;管理员可见全部
权限分层 页面级(菜单/路由可见性)+ 资源级(单条数据的读/写/删)两层控制
GPU 管控 多卡服务器上,管理员可指定哪些用户能使用哪些 GPU 卡
审批拦截 删除他人资源、停止他人任务、发布模型等高风险操作需审批或管理员旁路
审计可追溯 所有写操作和敏感操作产生审计日志,可按用户、动作、资源、时间筛选
权限最小变更 只有管理员可修改用户角色和权限码;普通用户无法提权

2. 整体架构

┌──────────────────────────────────────────────────────┐
│                     前端Vue3                       │
│  ┌────────────┐  ┌────────────┐  ┌───────────────┐ │
│  │ 路由守卫    │  │ 侧边栏过滤  │  │ 页面内按钮控制  │ │
│  │ (permission)│  │ (permission)│  │ (ACL/owner)   │ │
│  └──────┬─────┘  └──────┬─────┘  └───────┬───────┘ │
│         └───────────────┴─────────────────┘          │
│                         │ HTTP (Bearer token)        │
└─────────────────────────┼────────────────────────────┘
                          │
┌─────────────────────────┼────────────────────────────┐
│                    后端FastAPI                     │
│  ┌──────────────┐  ┌────┴───────┐  ┌──────────────┐  │
│  │ get_current  │  │ 资源可见性  │  │ GPU 分配校验  │  │
│  │ _user (鉴权) │  │ 过滤器      │  │              │  │
│  └──────┬───────┘  └────┬───────┘  └──────┬───────┘  │
│         │               │                  │          │
│  ┌──────┴───────────────┴──────────────────┘          │
│  │              PlatformStore                        │  │
│  │  users | acls | roles | gpu_assignments |         │  │
│  │  datasets | models | fine_tune_tasks | ...        │  │
│  └───────────────────────────────────────────────────│  │
│  │              audit_logs (审计日志)                │  │
│  └───────────────────────────────────────────────────│  │
└─────────────────────────────────────────────────────────┘

鉴权链路

  1. 请求到达 → get_current_userAuthorization: Bearer platform-token-{user_id} 解析当前用户
  2. 页面级权限 → 检查 user.permissions 是否包含路由对应的权限码
  3. 资源级权限 → 检查资源的 created_by 字段(所有权)或 aclsACL 授权)
  4. GPU 权限 → 检查 gpu_assignments 表确认用户是否被分配了请求的 GPU

3. 角色体系

3.1 内置角色

角色 code 中文名 说明
admin 超级管理员 拥有全部权限码;可见全部资源;可管理用户和 GPU 分配
operator 操作员 可创建/操作自己的数据集、模型、训练任务;不可管理用户
viewer 观察员 只读权限;可查看被授权的资源;不可创建或修改
guest 访客 仅登录和看板;无业务操作权限(扩展预留)

3.2 角色与权限码映射

权限码 admin operator viewer
dashboard
fine-tune
model-eval
model-inference
model-manage
dataset
data-process
data-convert
compute
hardware
logs
user-settings

3.3 权限修改规则

  • 只有 admin 角色的用户可以修改其他用户的角色和权限码
  • admin 用户的 protected=True 标记,防止被删除或降级
  • 权限修改操作产生审计日志:action=user.permission.update
  • 用户可以查看自己的权限,不能修改自己的权限

4. 页面权限码

权限码 对应路由 功能
dashboard /dashboard 服务看板
fine-tune /fine-tune, /fine-tune/create, /training-log/:id 模型训练
model-eval /model-eval, /model-eval/create, /model-eval/:id 模型评测
model-inference /model-inference, /model-inference/create, /model-inference/chat/:id 模型推理
model-manage /model-manage, /model-manage/create, /model-manage/:id/edit, /model-manage/merge 模型管理
dataset /dataset, /dataset/create, /dataset/:id/preview 数据集管理
data-process /data-process, /data-process/create, /data-process/:id 数据处理
data-convert /data-convert, /tools 数据转换与工具
compute /compute 算力节点
hardware /hardware 平台性能
logs /logs, /training-log/:id 查看日志
user-settings /user-settings, /tenants, /projects, /approvals, /audit-logs 系统设置与平台治理

5. 资源所有权与可见性

5.1 所有权模型

每个用户可创建的资源都携带 created_by(或 owner_id)字段,标识资源所有者。

资源类型 所有者字段 说明
数据集 datasets created_by 用户上传/创建的数据集
基座模型 models created_by 登记的本地/API 模型
训练产物 trained_models created_by 微调产出的模型
训练任务 fine_tune_tasks payload.created_by 微调任务
评测任务 eval_tasks created_by 评测任务
推理任务 compare_tasks (payload) created_by 推理/对比任务
数据处理任务 data_process_tasks created_by 数据处理任务
数据转换任务 data_convert_jobs created_by 数据转换任务

5.2 可见性规则

资源列表查询过滤逻辑:

if user.role == "admin":
    返回全部资源
elif resource.created_by == user.id:
    返回(资源所有者可见自己的资源)
elif acl 中存在 (principal_type="user", principal_id=user.id, permission 包含 "read"):
    返回(被 ACL 显式授权的资源)
elif acl 中存在 (principal_type="role", principal_id=user.role, permission 包含 "read"):
    返回(被角色级 ACL 授权的资源)
else:
    不可见

5.3 所有权操作矩阵

操作 admin 资源所有者 其他被授权用户 其他用户
查看资源 全部 自己的 ACL 授权范围内
编辑资源 自己的 ACL 含 write 时
删除资源 自己的(需审批)
分享/授权 自己的
使用资源(训练/推理/评测) 自己的 ACL 含 execute 时

5.4 数据集可见性示例

用户 A 创建了数据集 ds_A1 → 只有 A 和 admin 可见
用户 A 通过 ACL 把 ds_A1 的 read 权限授给用户 B → B 也可见
用户 A 通过 ACL 把 ds_A1 的 write 权限授给 operator 角色 → 所有 operator 可编辑
管理员可在任何数据集上设置 ACL

6. 资源级 ACL访问控制列表

6.1 ACL 表结构

CREATE TABLE IF NOT EXISTS acls (
    id              TEXT PRIMARY KEY,
    resource_type   TEXT NOT NULL,      -- 资源类型: dataset / model / trained_model / fine_tune_task / ...
    resource_id     TEXT NOT NULL,      -- 资源 ID
    principal_type  TEXT NOT NULL,      -- 授权主体类型: user / role
    principal_id    TEXT NOT NULL,      -- 授权主体 ID: user_id 或 role name
    permission      TEXT NOT NULL,      -- 权限: read / write / execute / download / delete / admin
    create_time     TEXT
);

6.2 权限粒度

权限值 含义 覆盖关系
read 查看资源详情、列表
write 编辑资源内容/元数据 覆盖 read
execute 使用资源(如用数据集训练、用模型推理) 覆盖 read
download 下载资源文件 独立权限
delete 删除资源 独立权限(通常需审批)
admin 完全控制(含 ACL 管理) 覆盖以上全部

6.3 ACL 管理接口

接口 方法 权限要求 说明
/resources/{type}/{id}/acl GET admin 或资源所有者 查询资源 ACL
/resources/{type}/{id}/acl PUT admin 或资源所有者 设置资源 ACL全量替换

6.4 ACL 管理规则

  • admin 可以在任何资源上设置 ACL
  • 资源所有者 可以在自己的资源上设置 ACL
  • 被授权用户 不能转授自己获得的权限
  • ACL 变更产生审计日志:action=resource.acl.set
  • 设置 ACL 时全量替换该资源的所有 ACL 条目

6.5 前端 ACL 管理入口

在数据集详情、模型详情、训练任务详情页面提供「资源授权」按钮,弹出 ACL 管理对话框:

  • 显示当前 ACL 列表(主体类型 + 主体名称 + 权限勾选)
  • 支持按用户或按角色添加授权
  • 权限以多选框形式展示read / write / execute / download / delete

7. GPU 算力分配与隔离

7.1 设计背景

服务器可能安装多张 GPU 卡(如 8×A100需要精细化管控

  • 管理员指定哪些用户可以使用哪些 GPU 卡
  • 未被分配的 GPU 卡对用户不可见或不可选
  • admin 可以使用全部 GPU

7.2 GPU 分配表

CREATE TABLE IF NOT EXISTS gpu_assignments (
    id          TEXT PRIMARY KEY,
    gpu_id      TEXT NOT NULL,          -- gpus 表的外键node_id + gpu_index 组合)
    node_id     TEXT NOT NULL,          -- 算力节点 ID
    gpu_index   INTEGER NOT NULL,       -- GPU 卡序号
    user_id     TEXT NOT NULL,          -- 被分配的用户 ID
    assigned_by TEXT,                  -- 分配操作人 IDadmin
    assigned_at TEXT NOT NULL,          -- 分配时间
    UNIQUE (node_id, gpu_index, user_id)  -- 一张卡可分配给多个用户,但每对 (卡, 用户) 唯一
);

7.3 分配规则

规则 说明
谁可分配 只有 admin 角色可以分配 GPU
admin 使用 admin 可使用全部 GPU不需要显式分配
普通用户 只能使用 gpu_assignments 中分配给自己的 GPU
共享分配 一张 GPU 可分配给多个用户(非独占),但同时只能被一个任务占用
默认策略 新用户默认不分配任何 GPU由管理员显式分配

7.4 GPU 分配接口

接口 方法 权限 说明
/compute/gpu-assignments GET admin 查看全部分配关系
/compute/gpu-assignments POST admin 批量分配body: { assignments: [{ node_id, gpu_index, user_id }] }
/compute/gpu-assignments/{id} DELETE admin 撤销某条分配
/compute/my-gpus GET 登录用户 查看自己可用的 GPU 列表

7.5 训练/评测/推理 GPU 选择校验

当普通用户创建训练任务、评测任务、推理任务并选择 GPU 时:

  1. 后端检查 gpu_assignments 表,确认用户被分配了所选 GPU
  2. 未被分配的 GPU → 返回 403 "无权使用 GPU {node}:{index}"
  3. admin 用户跳过此校验

7.6 前端 GPU 选择交互

  • 普通用户在创建任务选择 GPU 时,下拉列表只显示自己被分配的 GPU
  • admin 用户在下拉列表中可看到全部 GPU
  • 未分配任何 GPU 的用户GPU 选择区域显示提示:"未分配 GPU请联系管理员"

8. 审批拦截机制

8.1 需要审批的操作

操作 触发条件 审批动作 code
删除他人数据集 非 admin 删除 created_by != user.id 的数据集 dataset.delete
删除他人模型 非 admin 删除 created_by != user.id 的模型 model.delete
停止他人训练任务 非 admin 停止 created_by != user.id 的任务 fine_tune.stop
发布模型到推理服务 任何用户(含 admin发布到生产环境 model_service.publish
删除项目空间 存在待审批变更时拒绝 project.delete
归档项目空间 存在待审批变更时拒绝 project.archive
导出训练产物 非 admin 导出他人训练的模型 trained_model.export

8.2 审批流程

普通用户发起高风险操作
         │
         ▼
  ┌──────────────┐        ┌──────────────────────┐
  │ admin 旁路? │───是──▶│ 直接执行 + 审计日志   │
  └──────┬───────┘        └──────────────────────┘
         │ 否
         ▼
  ┌──────────────────────┐
  │ 创建审批实例          │
  │ status=pending       │
  │ 返回 202待审批    │
  └──────────┬───────────┘
             │
             ▼
  ┌──────────────────────┐
  │ 管理员审批            │
  │ POST /approvals/:id  │
  │ /steps/:idx/decision │
  └──────────┬───────────┘
             │
      ┌──────┴──────┐
      │             │
   approved      rejected
      │             │
      ▼             ▼
  执行操作      不执行
  +审计日志    +审计日志

8.3 审批模板

审批模板定义了特定操作需要几步审批、每步的审批人是谁:

{
  "id": "tpl_001",
  "name": "删除数据集审批",
  "action": "dataset.delete",
  "steps": [
    { "approver_id": "u_admin", "step_name": "管理员审核" }
  ]
}

8.4 审批拦截点

在项目模块的 _require_no_pending_approval 函数中,当存在待审批实例时拒绝执行新操作。其他模块通过 _require_approval_or_admin 函数实现 admin 旁路或创建审批实例。


9. 审计日志

9.1 审计范围

所有写操作和敏感操作必须产生审计日志:

动作分类 action 示例
用户管理 user.create, user.update, user.delete, user.permission.update
租户管理 tenant.create, tenant.update, tenant.quota.set, tenant.retention.set
项目管理 project.create, project.update, project.archive, project.delete, project.member.add, project.member.update, project.member.remove
资源 ACL resource.acl.set
模型管理 model.create, model.update, model.delete, model.merge
数据集 dataset.create, dataset.update, dataset.delete, dataset.upload
训练任务 fine_tune.create, fine_tune.start, fine_tune.stop, fine_tune.delete
评测任务 eval.create, eval.start, eval.stop
推理任务 inference.create, inference.start, inference.stop
审批 approval.create, approval.decide
留存策略 retention.create, retention.update, retention.delete
GPU 分配 gpu.assign, gpu.unassign

9.2 审计日志字段

CREATE TABLE IF NOT EXISTS audit_logs (
    id          TEXT PRIMARY KEY,
    time        TEXT NOT NULL,         -- ISO8601 时间戳
    tenant_id   TEXT,                 -- 租户 ID可选
    project_id  TEXT,                 -- 项目 ID可选
    actor_id    TEXT,                 -- 操作人 ID
    action      TEXT NOT NULL,         -- 动作类型
    target_type TEXT NOT NULL,         -- 目标资源类型
    target_id   TEXT,                 -- 目标资源 ID
    detail      TEXT,                 -- 详情摘要
    client_ip   TEXT                  -- 客户端 IP
);

9.3 查询与导出

接口 方法 说明
/system/audit-logs GET 分页查询,支持按 tenant_id / project_id / actor_id / action / target_type / start_time / end_time 筛选
/system/audit-logs/export GET CSV 导出,与应用查询相同的过滤条件

10. 接口鉴权流程

10.1 Token 格式

Authorization: Bearer platform-token-{user_id}

登录成功后返回 tokenuser 信息。Token 中编码了 user_id,后端通过 get_current_user 解析。

10.2 鉴权层级

请求到达
  │
  ├─ 1. 公开路径检查(/health, /login, /system-info→ 直接放行
  │
  ├─ 2. Token 解析 → get_current_user
  │     ├─ 无 token / token 无效 → 401
  │     └─ 用户不存在 / 状态 disabled → 401
  │
  ├─ 3. 页面级权限码检查(路由守卫 / Depends
  │     └─ user.permissions 不含所需权限码 → 403
  │
  ├─ 4. 资源级权限检查(路由函数内)
  │     ├─ admin → 全部放行
  │     ├─ resource.created_by == user.id → 放行
  │     ├─ ACL 检查 has_resource_access() → 有授权则放行
  │     └─ 否则 → 403
  │
  ├─ 5. GPU 权限检查(训练/评测/推理创建时)
  │     ├─ admin → 全部放行
  │     ├─ gpu_assignments 检查 → 有分配则放行
  │     └─ 否则 → 403
  │
  └─ 6. 审批拦截检查(高风险操作)
        ├─ admin → 旁路,直接执行
        ├─ 无待审批实例 → 可执行
        ├─ 有待审批实例 → 409 "存在待审批的变更"
        └─ 需要审批 → 202 "已创建审批实例"

10.3 FastAPI 依赖注入

# 任何需要登录的接口
@router.get("/datasets")
async def list_datasets(user: dict = Depends(get_current_user)):
    ...

# 需要管理员权限的接口
@router.post("/users")
async def create_user(user: dict = Depends(require_admin)):
    ...

# 需要资源级权限检查的接口
@router.delete("/datasets/{dataset_id}")
async def delete_dataset(
    dataset_id: str,
    user: dict = Depends(get_current_user),
):
    if not has_resource_access("dataset", dataset_id, user, "delete"):
        raise HTTPException(403, "forbidden")
    ...

11. 数据库表结构

11.1 现有表(已实现)

表名 用途
users 用户表id, username, password_hash, role, status, permissions, protected
roles 角色定义name, permissions
sessions 登录会话user_id, issued_at, expires_at, ip
acls 资源访问控制列表resource_type, resource_id, principal_type, principal_id, permission
audit_logs 审计日志actor_id, action, target_type, target_id, time
datasets 数据集(需补充 created_by 字段)
models 基座模型(需补充 created_by 字段)
trained_models 训练产物(需补充 created_by 字段)
fine_tune_tasks 训练任务payload 中存储 created_by
gpus GPU 设备node_id, gpu_index, uuid, name, memory
compute_nodes 算力节点
tenants 租户
projects 项目空间
project_members 项目成员
approval_templates 审批模板
approval_instances 审批实例
retention_policies 留存策略

11.2 需新增/补充的表和字段

新增 gpu_assignments

CREATE TABLE IF NOT EXISTS gpu_assignments (
    id           TEXT PRIMARY KEY,
    node_id      TEXT NOT NULL REFERENCES compute_nodes(id) ON DELETE CASCADE,
    gpu_index    INTEGER NOT NULL,
    user_id      TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    assigned_by  TEXT,
    assigned_at  TEXT NOT NULL,
    UNIQUE (node_id, gpu_index, user_id)
);
CREATE INDEX IF NOT EXISTS idx_gpu_assignments_user ON gpu_assignments(user_id);
CREATE INDEX IF NOT EXISTS idx_gpu_assignments_gpu ON gpu_assignments(node_id, gpu_index);

补充 created_by 字段

-- 数据集表补充所有者
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS created_by TEXT;

-- 基座模型表补充所有者
ALTER TABLE models ADD COLUMN IF NOT EXISTS created_by TEXT;

-- 训练产物表补充所有者
ALTER TABLE trained_models ADD COLUMN IF NOT EXISTS created_by TEXT;

-- 评测任务表补充所有者
ALTER TABLE eval_tasks ADD COLUMN IF NOT EXISTS created_by TEXT;

-- 推理任务表补充所有者
-- 注意inference_tasks 表尚未创建,后续建表时直接包含 created_by 字段

12. API 接口清单

12.1 鉴权接口

接口 方法 鉴权 说明
/modelTF/login POST 公开 登录,返回 token + user
/modelTF/me GET Bearer token 获取当前用户信息
/modelTF/users GET admin 用户列表
/modelTF/users POST admin 创建用户
/modelTF/users/:id PUT admin 更新用户(角色/状态/权限)
/modelTF/users/:id DELETE admin 删除用户protected 用户不可删)
/modelTF/users/:id/reset-password POST admin 重置密码
/modelTF/system/permissions/codes GET 登录 权限码清单
/modelTF/system/permissions GET 登录 权限码 + 角色定义

12.2 资源 ACL 接口

接口 方法 鉴权 说明
/modelTF/resources/:type/:id/acl GET admin 或所有者 查询资源 ACL
/modelTF/resources/:type/:id/acl PUT admin 或所有者 设置资源 ACL

12.3 GPU 分配接口

接口 方法 鉴权 说明
/modelTF/compute/gpu-assignments GET admin 查看全部分配
/modelTF/compute/gpu-assignments POST admin 批量分配
/modelTF/compute/gpu-assignments/:id DELETE admin 撤销分配
/modelTF/compute/my-gpus GET 登录 查看自己可用 GPU

12.4 审批接口

接口 方法 鉴权 说明
/modelTF/approvals/templates GET/POST admin 审批模板列表/创建
/modelTF/approvals GET/POST 登录 审批实例列表/创建
/modelTF/approvals/:id GET 登录 审批实例详情
/modelTF/approvals/:id/steps/:idx/decision POST 审批人 审批决策

12.5 审计接口

接口 方法 鉴权 说明
/modelTF/system/audit-logs GET admin 审计日志分页查询
/modelTF/system/audit-logs/export GET admin CSV 导出

12.6 租户/项目接口

接口 方法 鉴权 说明
/modelTF/tenants GET/POST admin 租户列表/创建
/modelTF/tenants/:id GET/PUT admin 租户详情/更新
/modelTF/tenants/:id/quota PUT admin 设置配额
/modelTF/tenants/:id/retention-policy PUT admin 绑定留存策略
/modelTF/projects GET/POST 登录 项目列表/创建
/modelTF/projects/:id GET/PUT 登录 项目详情/更新
/modelTF/projects/:id/archive POST admin 或所有者 归档(审批拦截)
/modelTF/projects/:id/members GET/POST 登录 成员列表/添加
/modelTF/projects/:id/members/:uid PUT/DELETE admin 或所有者 改角色/移除

12.7 留存策略接口

接口 方法 鉴权 说明
/modelTF/retention-policies GET/POST admin 策略列表/创建
/modelTF/retention-policies/:id GET/PUT/DELETE admin 策略详情/更新/删除

13. 前端权限控制

13.1 路由守卫

// router/index.ts
router.beforeEach((to, _from, next) => {
  const auth = useAuthStore()
  auth.syncSession()

  if (to.meta.public) {
    next()
    return
  }

  if (!auth.isLoggedIn) {
    next({ name: 'login' })
    return
  }

  if (!to.meta.skipPermission) {
    const permission = requiredPermission(to.path, to.meta.permission)
    if (permission && !auth.hasPermission(permission)) {
      next({ name: 'permission-denied', replace: true })
      return
    }
  }

  next()
})

13.2 侧边栏过滤

// layouts/MainLayout.vue
const visibleMenus = computed(() =>
  allMenus.filter(menu => {
    if (!menu.permission) return true
    return auth.hasPermission(menu.permission)
  })
)

13.3 资源级按钮控制

<!-- 数据集详情页 -->
<template>
  <el-button v-if="canEdit" @click="handleEdit">编辑</el-button>
  <el-button v-if="canDelete" @click="handleDelete">删除</el-button>
  <el-button v-if="canManageAcl" @click="showAclDialog = true">资源授权</el-button>
</template>

<script setup>
const canEdit = computed(() =>
  isAdmin.value || resource.value.created_by === userId.value
)
const canDelete = computed(() =>
  isAdmin.value || resource.value.created_by === userId.value
)
const canManageAcl = computed(() =>
  isAdmin.value || resource.value.created_by === userId.value
)
</script>

13.4 GPU 选择过滤

<!-- 创建训练任务页 -->
<template>
  <el-select v-model="selectedGpus" multiple>
    <el-option
      v-for="gpu in availableGpus"
      :key="gpu.id"
      :label="`${gpu.node_name} GPU ${gpu.gpu_index}`"
      :value="gpu.id"
    />
  </el-select>
  <el-alert v-if="availableGpus.length === 0 && !isAdmin" type="warning">
    未分配 GPU请联系管理员
  </el-alert>
</template>

<script setup>
// 普通用户只看到 my-gpus 返回的列表
// admin 看到全部 GPU
const availableGpus = ref([])
async function loadGpus() {
  if (isAdmin.value) {
    availableGpus.value = await getAllGpus()
  } else {
    availableGpus.value = await getMyGpus()
  }
}
</script>

14. 安全设计补充

14.1 密码安全

  • 密码使用 PBKDF2-SHA256 存储salt + 390000 次迭代)
  • 旧系统明文密码在首次登录时自动升级为哈希
  • 管理员可重置用户密码,用户不可自行修改密码(本期设计)
  • 默认密码:platform123(创建用户时由管理员设定)

14.2 会话安全

规则 说明
Token 格式 platform-token-{user_id}
会话超时 默认 30 分钟无操作自动过期
并发会话 同一用户可有多会话,各自独立计时
会话续期 前端定时调用 auth.refresh() 续期
强制下线 admin 可通过修改用户 status=disabled 使其 token 失效

14.3 操作限流

接口 限制
/login 同一 IP 5 次/分钟,失败后 30 秒冷却
文件上传 单文件最大由配置控制,默认 2GB
训练任务创建 同一用户并发运行任务数受 GPU 分配限制

14.4 数据安全

规则 说明
软删除 数据集、模型、任务使用 deleted_at 标记,保留审计可追溯
敏感字段 API 密钥(api_key)在列表接口不返回明文
下载审计 数据集下载产生审计日志,记录下载人和时间
导出审计 训练产物导出产生审计日志

14.5 多租户隔离

规则 说明
租户隔离 同一租户内的资源相互可见;跨租户默认不可见
项目隔离 项目内资源受项目 ACL 控制;项目间默认不可见
admin 旁路 admin 可跨租户/项目访问全部资源
配额管控 租户级配额限制 GPU 并发数、存储容量、最大项目数

15. 实施计划

15.1 已实现

功能 状态
登录/会话/Token 已实现
用户 CRUD + 权限码 已实现
角色定义 已实现
资源 ACLacls 表 + 接口) 已实现
审计日志(查询 + 导出) 已实现
审批模板/实例 已实现
项目空间 + 成员 已实现
租户 + 配额 + 留存 已实现
审批拦截(项目归档/删除) 已实现
资源所有权 ACL 字段适配subject_type/permissions[] 已实现

15.2 待实现

功能 优先级 涉及表/接口
GPU 分配表 + 接口 P0 gpu_assignments 表 + /compute/gpu-assignments + /compute/my-gpus
资源 created_by 字段补充 P0 datasets / models / trained_models / eval_tasks 表 ALTER
资源列表按 created_by + ACL 过滤 P0 platform_store.py 中 datasets/models/tasks 列表方法
GPU 选择校验(训练/评测/推理创建时) P0 platform.py 中 create_task/eval/inference
前端 GPU 下拉过滤 P1 前端创建任务页面
前端资源授权按钮 P1 前端数据集/模型/任务详情页
前端权限管理页面优化 P1 前端用户设置页面
审批拦截扩展(删除数据集/模型/停止任务) P1 platform.py 中 delete/stop 接口
密码安全策略(用户自行修改) P2 新增 /users/me/password 接口
操作限流login 限流) P2 中间件或 SlowAPI
多租户隔离(按 tenant_id 过滤) P2 各列表接口增加 tenant_id 过滤

15.3 实施步骤

  1. 数据库迁移:创建 gpu_assignments 表,为资源表补充 created_by 字段
  2. 后端接口:实现 GPU 分配 CRUD + my-gpus + 创建任务时的 GPU 权限校验
  3. 资源过滤:在 datasets() / models() / tasks() 等列表方法中按 created_by + ACL 过滤
  4. 前端适配GPU 下拉过滤、资源授权按钮、权限管理页面优化
  5. 审批扩展:在删除/停止接口中接入 _require_approval_or_admin
  6. 测试补充:扩展 test_governance.py 覆盖 GPU 分配、资源过滤、审批扩展场景

16. 2.0 权限增强基线MinIO、缓存与跨节点场景

16.1 默认拒绝

所有受保护接口采用 deny by default。权限判定依次执行认证、租户边界、项目成员关系、页面权限、资源动作权限、审批校验和审计记录。任何一步无法确定时返回 403不得因为字段缺失、资源不存在或 ACL 查询异常而自动放行。

16.2 资源归属和继承

资源统一使用 tenant_idproject_idcreated_byvisibility 表达边界。训练任务继承模型、数据集和项目边界;训练模型继承训练任务边界;合并模型继承被合并模型边界;评测必须同时校验模型和数据集;推理必须校验模型、项目和算力节点。

16.3 MinIO 对象权限

  • 所有对象访问必须经过 Backend 鉴权,前端不得持有 MinIO 密钥。
  • Compute API 只使用 Backend 签发的预签名 URL。
  • 预签名 URL 默认有效期不超过 15 分钟,上传和下载分别签发。
  • 生成 URL 前必须校验资源权限、对象状态和版本归属。
  • bucket 由服务端配置,禁止客户端提交任意 bucket。
  • 禁止通过修改 object_keyversion_id 或文件名越权访问对象。
  • 删除对象使用 deleting -> deleted 状态,失败时保留错误信息并可重试。

16.4 缓存和算力节点权限

  • 缓存是资源副本,不产生新的资源所有权。
  • 只有拥有源模型或数据集 execute 权限的用户才能触发缓存。
  • 用户不能直接调用 Compute API 的缓存、文件、上传和推理管理接口。
  • Compute API 只接受 Backend 服务令牌,不能转发用户 Token。
  • 产物归档必须校验任务、节点、资源和项目关联关系。
  • 任务运行期间缓存引用不可被普通用户清理。
  • 缓存清理只能删除节点副本,不得删除 MinIO 正式对象。

16.5 训练、合并、推理和评测动作矩阵

动作 必要权限 额外约束
创建训练 项目 write + 模型/数据集 execute GPU、配额和节点权限同时通过
查看训练 任务 read 日志、曲线、checkpoint 继承任务权限
停止训练 任务 writeadmin 停止他人任务需要审批或管理员权限
权重合并 训练模型 execute 使用绑定节点或有权限的指定节点
归档训练产物 任务 write 只能归档任务输出目录内文件
创建推理 模型 execute 节点、GPU 配额和项目权限通过
删除推理 推理服务 delete 或管理员 释放 GPU 和缓存引用
创建评测 模型/数据集 execute 两个资源必须在允许范围内
下载报告 评测任务 read + 报告 download 预签名 URL 短时有效

16.6 服务身份和密钥边界

身份 用途 禁止事项
用户 Token 调用 Backend 业务接口 直接调用 Compute 或 MinIO
Backend 服务令牌 调用 Compute API 返回前端或写入任务参数
MinIO 管理密钥 Backend 对象操作 注入浏览器、Compute 容器或日志
Compute 节点身份 节点心跳和任务执行 访问其他节点本地路径

生产环境禁止使用默认凭据;服务令牌必须从环境变量或密钥管理系统读取并脱敏记录。

16.7 必须补充的接口保护

POST /storage/objects/presign
POST /storage/resources/{type}/{id}/prepare/{node_id}
POST /storage/resources/{type}/{id}/archive-node/{node_id}
GET  /storage/cache/jobs/{node_id}
POST /model-manage/merge
POST /model-chat/local/preload
POST /model-chat/trained/preload
POST /model-chat/local/unload
POST /model-compare/{task_id}/load
POST /model-compare/{task_id}/unload

请求体中的所有资源 ID 都必须校验。node_idmodel_iddataset_idtask_id 不一致时返回 403 或 409。

16.8 权限审计验收用例

必须覆盖:用户 A 不能读取用户 B 资源;项目 A 不能使用项目 B 数据集;评测必须同时拥有模型和数据集 execute 权限;修改对象 key、版本或资源 ID 不能获取预签名 URL普通用户不能调用 Compute API产物路径不能越出任务数据根目录用户不能清理其他项目正在使用的缓存无节点权限时推理返回 403删除、停止、合并和归档他人资源按规则进入审批。

17. 2.0 实施顺序

  1. 建立统一 authorize_resource_action()authorize_task_resources() 后端辅助函数。
  2. 为模型、数据集、训练、推理、评测和对象接口补齐租户/项目过滤。
  3. 统一预签名 URL 权限校验、有效期和审计日志。
  4. 为 Compute API 增加服务令牌、节点归属和路径范围校验。
  5. 将 GPU、缓存、配额和节点选择校验合并到任务创建事务中。
  6. 增加跨资源权限测试和越权回归测试。
  7. 前端仅负责隐藏操作按钮,最终权限以 Backend 返回为准。