feat: 权限与日志治理完善,MinIO 独立部署与 tiktoken 离线打包适配

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wuyongtao
2026-08-12 15:21:23 +08:00
parent 2f64086177
commit 5ecca9f0bc
26 changed files with 101600 additions and 129 deletions

View File

@@ -0,0 +1,423 @@
# 权限与日志改造功能测试用例
## 1. 测试范围
本文档用于验证本次权限和日志相关开发内容:
- 训练、评测、推理资源权限校验。
- 模型合并和训练模型导出权限。
- 算力节点及 GPU 分配权限。
- 资源所有者、ACL、租户/项目归属字段。
- 资源软删除和审计记录。
- 登录失败限流与 Token 会话有效期。
- 后端轮询日志降噪。
- 数据库初始化和运行时字段迁移。
## 2. 测试环境
| 项目 | 配置 |
|---|---|
| 前端地址 | `http://172.25.179.69:16801` |
| Backend 地址 | `http://172.25.179.69:17861/modelTF` |
| MinIO 地址 | `http://172.25.179.69:19000` |
| WSL IP | `172.25.179.69` |
| 运行方式 | Docker Compose |
| 数据库 | 远程 PostgreSQL |
## 3. 前置数据
准备以下账号和资源:
| 数据 | 要求 |
|---|---|
| 管理员账号 | 具有 `admin` 角色 |
| 普通操作员 | 具有训练、评测或推理页面权限 |
| 只读用户 | 具有 `dashboard``logs` 权限,不具有业务写权限 |
| 测试模型 | 一个基座模型和一个训练模型 |
| 测试数据集 | 一个训练数据集和一个评测数据集 |
| 算力节点 | 至少一个在线节点,最好有 2 张及以上 GPU |
| ACL 资源 | 将测试数据集授权给普通用户进行验证 |
## 4. 鉴权与会话测试
### AUTH-001 登录成功生成会话 Token
**步骤**
1. 使用有效账号调用登录接口或登录页面。
2. 查看响应中的 `token``session_id`
3. 使用 Token 调用 `/modelTF/me`
**预期**
- 登录返回 HTTP 200。
- Token 格式包含用户 ID 和 session ID。
- `/me` 能返回当前用户信息。
- `sessions` 表生成一条未注销记录。
### AUTH-002 无 Token 访问受保护接口
**步骤**
1. 不携带 `Authorization` 调用数据集、模型或训练列表接口。
**预期**
- 返回 HTTP 401。
- 不返回资源数据。
### AUTH-003 注销后 Token 失效
**步骤**
1. 登录并记录 Token、`session_id`
2. 调用注销接口。
3. 使用原 Token 调用 `/me` 或资源接口。
**预期**
- 注销成功。
- `sessions.logout_at` 已写入。
- 原 Token 返回 HTTP 401。
### AUTH-004 登录失败限流
**步骤**
1. 同一客户端 IP 连续输入错误密码 5 次。
2. 第 6 次继续登录。
**预期**
- 前 5 次返回登录失败。
- 第 6 次返回 HTTP 429。
- 使用正确密码也应在冷却窗口内被限制。
- 登录成功后失败计数清除。
## 5. 资源所有权与 ACL
### ACL-001 资源所有者访问自己的资源
**步骤**
1. 普通用户创建数据集或模型。
2. 使用该用户查看列表和详情。
3. 修改资源元数据。
**预期**
- 资源出现在自己的列表中。
- 详情访问返回 HTTP 200。
- 资源所有者可以执行允许的写操作。
### ACL-002 未授权用户访问资源
**步骤**
1. 用户 A 创建数据集。
2. 用户 B 未获得 ACL 授权时查看该数据集详情。
**预期**
- 用户 B 不应在列表中看到该资源。
- 直接访问详情返回 HTTP 403。
### ACL-003 ACL 授权后访问资源
**步骤**
1. 用户 A 或管理员给用户 B 授予 `read` 权限。
2. 用户 B 刷新资源列表并访问详情。
**预期**
- 用户 B 可以看到并读取资源。
- 用户 B 不能执行 `write``delete``execute` 操作。
### ACL-004 非所有者修改 ACL
**步骤**
1. 用户 B 仅拥有资源 `read` 权限。
2. 用户 B 调用 ACL 修改接口。
**预期**
- 返回 HTTP 403。
- ACL 内容不发生变化。
### ACL-005 ACL 修改审计
**步骤**
1. 管理员或资源所有者修改 ACL。
2. 查询审计日志。
**预期**
- 出现 `resource.acl.set` 操作记录。
- 记录操作者、资源类型、资源 ID 和变更详情。
## 6. 训练权限测试
### TRAIN-001 训练创建校验模型和数据集权限
**步骤**
1. 普通用户选择无权限的基座模型或数据集创建训练任务。
2. 再使用已授权模型和数据集创建训练任务。
**预期**
- 无权限资源返回 HTTP 403。
- 已授权资源允许创建任务。
- 任务记录包含 `created_by`
### TRAIN-002 GPU 授权校验
**步骤**
1. 管理员将 GPU 0 分配给用户 A。
2. 用户 A 选择 GPU 0 创建训练任务。
3. 用户 A 选择未分配的 GPU 1 创建训练任务。
**预期**
- GPU 0 可以提交。
- GPU 1 返回 HTTP 403。
- 未指定 GPU 时,仅从用户已授权的空闲 GPU 中自动分配。
### TRAIN-003 训练任务停止、重试和删除权限
**步骤**
1. 用户 A 创建训练任务。
2. 用户 B 尝试停止、重试或删除该任务。
3. 管理员执行相同操作。
**预期**
- 用户 B 无资源权限时返回 HTTP 403。
- 删除和停止等高风险操作按配置进入审批流程。
- 管理员可以旁路审批执行。
- GPU 占用在停止、失败和删除后释放。
## 7. 评测权限测试
### EVAL-001 评测创建联合权限
**步骤**
1. 普通用户选择无权使用的模型创建评测。
2. 普通用户选择无权使用的数据集创建评测。
3. 使用同时拥有权限的模型和数据集创建评测。
**预期**
- 模型无权限返回 HTTP 403。
- 数据集无权限返回 HTTP 403。
- 两个资源均有 `execute` 权限时允许创建。
- 评测任务包含 `created_by`、模型、数据集和算力节点信息。
### EVAL-002 评测详情和删除权限
**预期**
- 无权限用户不能查看评测详情。
- 评测删除需要 `delete` 权限。
- 非管理员删除高风险评测任务时触发审批。
## 8. 推理权限测试
### INFER-001 推理创建模型权限
**步骤**
1. 普通用户选择无权模型创建推理任务。
2. 选择已授权模型创建推理任务。
**预期**
- 无权模型返回 HTTP 403。
- 有权模型允许创建。
- 推理任务包含 `created_by`
### INFER-002 推理加载和卸载权限
**步骤**
1. 用户 A 创建推理任务。
2. 用户 B 调用加载、卸载接口。
3. 用户 A 执行加载和卸载。
**预期**
- 用户 B 返回 HTTP 403。
- 用户 A 可以执行授权范围内的加载和卸载。
- 卸载后 GPU 和节点状态恢复为空闲。
### INFER-003 推理任务删除审批
**预期**
- 非管理员删除他人推理任务被拒绝或进入审批。
- 管理员可以直接删除。
- 删除操作有审计日志。
## 9. 模型合并与导出测试
### MODEL-001 权重合并权限
**步骤**
1. 普通用户选择无权限训练模型进行合并。
2. 选择已授权训练模型进行合并。
**预期**
- 无权限返回 HTTP 403。
- 已授权训练模型允许合并。
- 如指定基座模型,还必须拥有基座模型 `execute` 权限。
### MODEL-002 训练模型删除权限
**预期**
- 只有资源所有者、ACL 授权用户或管理员可操作。
- 删除采用软删除。
- `deleted_at``deleted_by` 被写入。
### MODEL-003 导出任务访问权限
**预期**
- 无权用户不能查看训练模型导出任务。
- 有权用户可以查看导出状态。
- 导出动作应记录 `trained_model.export` 审计日志。
## 10. 算力节点与 GPU 管理测试
### GPU-001 普通用户节点可见范围
**预期**
- 普通用户只能看到被分配 GPU 所在节点。
- 普通用户只能看到已授权 GPU。
- 管理员可看到所有节点和 GPU。
### GPU-002 节点管理接口权限
验证节点创建、修改、删除、启用、禁用、排空、连接测试和健康检查。
**预期**
- 普通用户全部返回 HTTP 403。
- 管理员操作成功。
### GPU-003 多卡自动分配
**步骤**
1. 节点配置多张 GPU。
2. 启动一个任务占用其中一张卡。
3. 再启动任务并选择剩余卡。
**预期**
- 已占用 GPU 不再出现在可选列表。
- 剩余 GPU 可以被其他任务使用。
- 任务失败、停止或完成后 GPU 释放。
## 11. 软删除与数据库测试
### DB-001 初始化字段检查
执行:
```sql
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_name IN ('models', 'datasets', 'trained_models', 'eval_tasks', 'sessions')
ORDER BY table_name, ordinal_position;
```
**预期字段**
- 资源表存在 `created_by``tenant_id``project_id``deleted_at``deleted_by`
- `sessions` 存在 `issued_at``expires_at``logout_at`
### DB-002 软删除列表过滤
**步骤**
1. 删除模型、数据集或评测任务。
2. 查询列表。
3. 直接查询数据库记录。
**预期**
- 前端列表不再显示已删除资源。
- 数据库记录仍存在。
- `deleted_at``deleted_by` 有值。
## 12. 日志降噪测试
### LOG-001 正常轮询日志
**步骤**
1. 重启 Backend 容器。
2. 连续观察 1 分钟日志。
```bash
docker logs -f --tail=200 yg-ft-backend-api
```
**预期**
- 不再每次以 `INFO` 输出 `compute jobs polled`
- 健康检查成功请求不再以 `INFO` 输出应用日志。
- 正常任务同步日志仅在 `DEBUG` 级别出现。
### LOG-002 异常轮询日志
**步骤**
1. 临时停止算力节点或断开节点网络。
2. 观察 Backend 日志。
**预期**
- 轮询失败以 `WARNING``ERROR` 输出。
- 异常包含节点、任务或错误原因。
- 恢复节点后轮询继续工作。
## 13. 容器验证命令
```bash
docker compose ps
docker logs --tail=200 yg-ft-backend-api
curl -i http://172.25.179.69:17861/modelTF/health
curl -i http://172.25.179.69:16801/
```
预期 Backend 和 Frontend 均为 `healthy`,健康接口返回 HTTP 200。
## 14. 测试结果记录
| 用例编号 | 测试结果 | 实际结果 | 缺陷编号 | 测试人 | 日期 |
|---|---|---|---|---|---|
| AUTH-001 | □通过 □失败 | | | | |
| AUTH-002 | □通过 □失败 | | | | |
| AUTH-003 | □通过 □失败 | | | | |
| AUTH-004 | □通过 □失败 | | | | |
| ACL-001 | □通过 □失败 | | | | |
| TRAIN-001 | □通过 □失败 | | | | |
| TRAIN-002 | □通过 □失败 | | | | |
| EVAL-001 | □通过 □失败 | | | | |
| INFER-001 | □通过 □失败 | | | | |
| MODEL-001 | □通过 □失败 | | | | |
| GPU-001 | □通过 □失败 | | | | |
| DB-001 | □通过 □失败 | | | | |
| LOG-001 | □通过 □失败 | | | | |
| LOG-002 | □通过 □失败 | | | | |

View File

@@ -0,0 +1,122 @@
# 权限与日志改造自动化测试结果
## 1. 测试时间
2026-08-12
## 2. 测试环境
| 项目 | 地址/状态 |
|---|---|
| Frontend | `http://172.25.179.69:16801` |
| Backend | `http://172.25.179.69:17861/modelTF` |
| WSL | `172.25.179.69` |
| Docker | 已运行 |
## 3. 自动执行结果
| 用例 | 结果 | 实际结果 |
|---|---|---|
| 容器状态 | 通过 | Compute、Frontend、Backend、Redis、MinIO 均为 `healthy` |
| Backend 健康检查 | 通过 | `/modelTF/health` 返回 HTTP 200 |
| Frontend 首页 | 通过 | `/` 返回 HTTP 200 |
| 未登录访问数据集接口 | 通过 | `/modelTF/dataset-manage` 返回 HTTP 401 |
| Redis 容器状态 | 通过 | 容器状态为 healthy |
| 轮询正常日志降噪 | 通过 | 最近 120 秒未发现 `compute jobs polled``health check requested` 高频日志 |
| 轮询异常日志保留 | 未触发 | 当前未人为停止算力节点,未产生轮询失败日志 |
| 前端构建 | 通过 | `npm run build` 成功 |
| 后端编译 | 通过 | 相关 Python 模块 `py_compile` 成功 |
| Git 差异检查 | 通过 | `git diff --check` 无格式错误 |
## 4. 数据库字段迁移
### 静态检查结果
初始化 SQL 和运行时迁移逻辑已包含以下字段:
- `models.deleted_at`
- `models.deleted_by`
- `models.tenant_id`
- `models.project_id`
- `datasets.deleted_at`
- `datasets.deleted_by`
- `datasets.tenant_id`
- `datasets.project_id`
- `trained_models.deleted_at`
- `trained_models.deleted_by`
- `trained_models.tenant_id`
- `trained_models.project_id`
- `eval_tasks.deleted_at`
- `eval_tasks.deleted_by`
- `sessions.issued_at`
- `sessions.expires_at`
### 远程数据库检查状态
已通过当前 Backend 容器使用一次性 PostgreSQL 连接检查远程数据库,字段迁移已完成。
确认存在:
- `models`: `created_by``tenant_id``project_id``deleted_at``deleted_by`
- `datasets`: `created_by``tenant_id``project_id``deleted_at``deleted_by`
- `trained_models`: `created_by``tenant_id``project_id``deleted_at``deleted_by`
- `eval_tasks`: `created_by``tenant_id``project_id``deleted_at``deleted_by`
- `sessions`: `issued_at``expires_at``logout_at`
使用的检查 SQL
```sql
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_name IN ('models', 'datasets', 'trained_models', 'eval_tasks', 'sessions')
ORDER BY table_name, ordinal_position;
```
## 5. Redis 说明
已从当前环境确认 Redis 实际密码配置为 `Tvhrf659WaX-S1B8FG6c2kSZK07XTv82`,使用该密码验证返回 `PONG`
项目配置已同步为:
```env
REDIS_PASSWORD=Tvhrf659WaX-S1B8FG6c2kSZK07XTv82
REDIS_URL=redis://:Tvhrf659WaX-S1B8FG6c2kSZK07XTv82@redis:6379/0
```
后续验证应从当前 Docker 环境读取实际密码后执行:
```bash
docker compose config
docker exec yg-ft-redis printenv REDISCLI_AUTH
docker exec yg-ft-redis redis-cli -a "$REDISCLI_AUTH" ping
```
预期返回:
```text
PONG
```
## 6. 未自动执行的破坏性用例
以下用例需要测试账号、测试资源或人工确认,未自动执行,以避免影响现有数据:
- ACL 授权和撤销。
- 训练任务创建、停止、重试和删除。
- 评测任务启动和删除。
- 推理模型加载、卸载和删除。
- 模型权重合并和导出。
- GPU 分配、占用和释放。
- 软删除后资源恢复和数据完整性。
- 人为停止算力节点验证轮询失败恢复。
- 登录失败 5 次后的限流验证。
- Token 过期和注销后的访问验证。
## 7. 当前结论
当前服务基础可用,页面和 Backend 健康接口正常,未登录鉴权正常,日志降噪逻辑生效,前后端构建通过。
当前仍需人工或使用专用测试数据验证:
1. 权限矩阵中的创建、执行、删除、审批和 GPU 占用场景。
2. Token 过期和登录失败限流场景。

View File

@@ -803,3 +803,88 @@ async function loadGpus() {
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_id``project_id``created_by``visibility` 表达边界。训练任务继承模型、数据集和项目边界;训练模型继承训练任务边界;合并模型继承被合并模型边界;评测必须同时校验模型和数据集;推理必须校验模型、项目和算力节点。
### 16.3 MinIO 对象权限
- 所有对象访问必须经过 Backend 鉴权,前端不得持有 MinIO 密钥。
- Compute API 只使用 Backend 签发的预签名 URL。
- 预签名 URL 默认有效期不超过 15 分钟,上传和下载分别签发。
- 生成 URL 前必须校验资源权限、对象状态和版本归属。
- bucket 由服务端配置,禁止客户端提交任意 bucket。
- 禁止通过修改 `object_key``version_id` 或文件名越权访问对象。
- 删除对象使用 `deleting -> deleted` 状态,失败时保留错误信息并可重试。
### 16.4 缓存和算力节点权限
- 缓存是资源副本,不产生新的资源所有权。
- 只有拥有源模型或数据集 `execute` 权限的用户才能触发缓存。
- 用户不能直接调用 Compute API 的缓存、文件、上传和推理管理接口。
- Compute API 只接受 Backend 服务令牌,不能转发用户 Token。
- 产物归档必须校验任务、节点、资源和项目关联关系。
- 任务运行期间缓存引用不可被普通用户清理。
- 缓存清理只能删除节点副本,不得删除 MinIO 正式对象。
### 16.5 训练、合并、推理和评测动作矩阵
| 动作 | 必要权限 | 额外约束 |
|---|---|---|
| 创建训练 | 项目 `write` + 模型/数据集 `execute` | GPU、配额和节点权限同时通过 |
| 查看训练 | 任务 `read` | 日志、曲线、checkpoint 继承任务权限 |
| 停止训练 | 任务 `write``admin` | 停止他人任务需要审批或管理员权限 |
| 权重合并 | 训练模型 `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 必须补充的接口保护
```text
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_id``model_id``dataset_id``task_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 返回为准。