feat(handler): add app
This commit is contained in:
129
docs/APP_API.md
Normal file
129
docs/APP_API.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# App 生命周期管理接口(平台级)
|
||||
|
||||
本模块用于维护“允许的 App 注册表(apps)”,并提供应用上下线审批(含生效时间)与审计记录能力。
|
||||
|
||||
## 权限与访问级别
|
||||
|
||||
本模块所有接口均为平台级接口:
|
||||
- URL 前缀:`/platform`
|
||||
- 认证:`Authorization: Bearer <access_token>`
|
||||
- 权限:平台权限(系统角色 `SuperAdmin`)校验
|
||||
|
||||
权限码:
|
||||
- `iam:app:read`:查询 apps 与审批单
|
||||
- `iam:app:write`:创建/更新 apps、提交上下线申请
|
||||
- `iam:app:approve`:审批上下线申请
|
||||
- `iam:app:delete`:删除(软删除)app
|
||||
|
||||
## 通用响应结构
|
||||
|
||||
成功:
|
||||
|
||||
```json
|
||||
{ "code": 0, "message": "Success|Created", "data": {}, "trace_id": null }
|
||||
```
|
||||
|
||||
错误(示例):
|
||||
|
||||
```json
|
||||
{ "code": 20003, "message": "Permission denied: iam:app:read", "details": null, "trace_id": null }
|
||||
```
|
||||
|
||||
常见错误码:
|
||||
- 20000/20006:未认证(缺少 Authorization)
|
||||
- 20003:无权限(缺少平台权限码)
|
||||
- 30000:参数错误(格式/长度/取值非法)
|
||||
- 30002:资源不存在(app / request_id 不存在)
|
||||
- 30003:资源冲突(app_id 重复)
|
||||
|
||||
## 1) 新增 App
|
||||
|
||||
**POST** `/platform/apps`
|
||||
|
||||
Body:
|
||||
- `id`:应用标识符(2~32,`[a-z0-9_-]`,建议小写,如 `cms`/`tms`)
|
||||
- `name`:名称(必填,<=100)
|
||||
- `description`:描述(可选)
|
||||
- `app_type`:类型(可选,默认 `generic`,建议小写短标识)
|
||||
- `owner`:负责人/团队(可选,<=100)
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://127.0.0.1:3000/platform/apps" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "id":"dms","name":"DMS","description":"Document","app_type":"product","owner":"team-a" }'
|
||||
```
|
||||
|
||||
## 2) 查询 App 列表(分页/筛选/排序)
|
||||
|
||||
**GET** `/platform/apps`
|
||||
|
||||
Query:
|
||||
- `page`:默认 1
|
||||
- `page_size`:默认 20,范围 1..=200
|
||||
- `status`:可选(`active|disabled|deleted`)
|
||||
- `app_type`:可选
|
||||
- `created_from` / `created_to`:可选(RFC3339)
|
||||
- `sort_by`:`id|name|status|app_type|created_at|updated_at`(默认 `created_at`)
|
||||
- `sort_order`:`asc|desc`(默认 `desc`)
|
||||
|
||||
## 3) 查询 App 详情
|
||||
|
||||
**GET** `/platform/apps/{app_id}`
|
||||
|
||||
## 4) 更新 App 基础信息
|
||||
|
||||
**PATCH** `/platform/apps/{app_id}`
|
||||
|
||||
Body(全部可选):
|
||||
- `name`
|
||||
- `description`
|
||||
- `app_type`
|
||||
- `owner`
|
||||
|
||||
## 5) 申请 App 上下线(审批 + 生效时间)
|
||||
|
||||
**POST** `/platform/apps/{app_id}/status-change-requests`
|
||||
|
||||
Body:
|
||||
- `to_status`:`active|disabled`
|
||||
- `effective_at`:可选(RFC3339;不填表示审批后立即生效)
|
||||
- `reason`:可选
|
||||
|
||||
说明:
|
||||
- 新建审批单初始 `status=pending`
|
||||
- 审批通过后:
|
||||
- 若 `effective_at` 为空或已到达,则自动应用并把审批单状态置为 `applied`
|
||||
- 若 `effective_at` 在未来,则审批单状态为 `approved`,在后续列表/查询时会尝试自动应用到期变更
|
||||
|
||||
## 6) 查询审批单列表
|
||||
|
||||
**GET** `/platform/app-status-change-requests`
|
||||
|
||||
Query:
|
||||
- `status`:可选(`pending|approved|applied|rejected`)
|
||||
- `page` / `page_size`
|
||||
|
||||
## 7) 审批通过 / 驳回
|
||||
|
||||
通过:
|
||||
|
||||
**POST** `/platform/app-status-change-requests/{request_id}/approve`
|
||||
|
||||
Body:
|
||||
- `effective_at`:可选(用于覆盖/补充生效时间)
|
||||
|
||||
驳回:
|
||||
|
||||
**POST** `/platform/app-status-change-requests/{request_id}/reject?reason=...`
|
||||
|
||||
## 8) 删除 App(软删除)
|
||||
|
||||
**DELETE** `/platform/apps/{app_id}`
|
||||
|
||||
说明:
|
||||
- 软删除会将 `apps.status` 标记为 `deleted`
|
||||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,必须同时提供 Header:`X-Sensitive-Token: <token>`
|
||||
|
||||
53
docs/PERF_TEST.md
Normal file
53
docs/PERF_TEST.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# 性能压测说明(App 管理接口)
|
||||
|
||||
本仓库默认不携带外部压测工具(如 wrk/oha/hey),因此提供“可复现的压测方法 + 指标口径 + 建议阈值”,并建议在 CI/CD 或预发环境执行并归档结果。
|
||||
|
||||
## 1) 压测对象
|
||||
|
||||
- `GET /platform/apps`:分页列表(典型读流量)
|
||||
- `POST /platform/apps`:创建(写流量)
|
||||
- `POST /platform/apps/{app_id}/status-change-requests`:写流量
|
||||
|
||||
## 2) 指标口径
|
||||
|
||||
- 吞吐:RPS
|
||||
- 延迟:p50 / p95 / p99
|
||||
- 错误率:非 2xx 比例
|
||||
- 资源:CPU / 内存 / DB 连接池占用
|
||||
|
||||
## 3) 环境准备
|
||||
|
||||
- 数据库完成迁移:`./scripts/db/migrate.sh && ./scripts/db/verify.sh`
|
||||
- 用平台租户 token 作为压测 token(必须具备 `iam:app:read` 等权限)
|
||||
- 建议在压测前预填充一定数量 apps(如 1k/10k)
|
||||
|
||||
## 4) 示例(wrk)
|
||||
|
||||
若环境已安装 `wrk`:
|
||||
|
||||
```bash
|
||||
wrk -t4 -c32 -d30s \\
|
||||
-H "Authorization: Bearer <token>" \\
|
||||
"http://127.0.0.1:3000/platform/apps?page=1&page_size=20"
|
||||
```
|
||||
|
||||
## 5) 示例(纯 curl 简易压测)
|
||||
|
||||
该方式不统计精确吞吐与分位数,但可快速验证稳定性与错误率:
|
||||
|
||||
```bash
|
||||
for i in $(seq 1 200); do
|
||||
curl -s -o /dev/null -w "%{http_code}\\n" \\
|
||||
-H "Authorization: Bearer <token>" \\
|
||||
"http://127.0.0.1:3000/platform/apps?page=1&page_size=20" &
|
||||
done
|
||||
wait
|
||||
```
|
||||
|
||||
## 6) 建议阈值(参考)
|
||||
|
||||
- `GET /platform/apps`:p95 < 100ms(本机/同 AZ),错误率 < 0.1%
|
||||
- `POST /platform/apps`:p95 < 200ms,错误率 < 0.1%
|
||||
|
||||
实际阈值应结合数据库规模、索引、网关链路与部署规格确定。
|
||||
|
||||
@@ -77,6 +77,8 @@
|
||||
|
||||
- Tag:`Tenant`
|
||||
- Header:无
|
||||
- 二次验证(可选但建议生产启用):
|
||||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,则必须传 Header:`X-Sensitive-Token: <token>`,否则返回 403。
|
||||
- Body:
|
||||
|
||||
```json
|
||||
@@ -212,6 +214,82 @@ enabled_apps 维护建议:
|
||||
- 下线应用:将 `apps.status` 置为非 `active`(例如 `disabled`),之后将无法再被设置进任何租户的 enabled_apps。
|
||||
- 查询可用应用(示例 SQL):`SELECT id, name, status FROM apps ORDER BY id;`
|
||||
|
||||
### Step 4.2:平台层 App 生命周期管理(SuperAdmin)
|
||||
|
||||
用于维护“允许的 App 注册表”,并提供应用上下线(审批 + 生效时间)能力。
|
||||
|
||||
权限要求(平台级):
|
||||
- `iam:app:read`
|
||||
- `iam:app:write`
|
||||
- `iam:app:approve`
|
||||
- `iam:app:delete`
|
||||
|
||||
#### 4.2.1 新增 App
|
||||
|
||||
**POST** `/platform/apps`
|
||||
|
||||
- Tag:`App`
|
||||
- Header:`Authorization: Bearer <access_token>`(平台租户下登录得到的 token)
|
||||
- Body(示例):
|
||||
|
||||
```json
|
||||
{ "id": "dms", "name": "DMS", "description": "Document Management System", "app_type": "product", "owner": "team-a" }
|
||||
```
|
||||
|
||||
#### 4.2.2 查询 App 列表(分页/筛选/排序)
|
||||
|
||||
**GET** `/platform/apps?page=1&page_size=20&status=active&app_type=product&sort_by=created_at&sort_order=desc`
|
||||
|
||||
- Tag:`App`
|
||||
- Header:`Authorization: Bearer <access_token>`
|
||||
|
||||
#### 4.2.3 更新 App 基础信息
|
||||
|
||||
**PATCH** `/platform/apps/{app_id}`
|
||||
|
||||
- Tag:`App`
|
||||
- Header:`Authorization: Bearer <access_token>`
|
||||
- Body(示例):
|
||||
|
||||
```json
|
||||
{ "description": "DMS v2", "owner": "team-b" }
|
||||
```
|
||||
|
||||
#### 4.2.4 申请 App 上下线(需要审批,可设置生效时间)
|
||||
|
||||
**POST** `/platform/apps/{app_id}/status-change-requests`
|
||||
|
||||
- Tag:`App`
|
||||
- Header:`Authorization: Bearer <access_token>`
|
||||
- Body(示例:立即禁用):
|
||||
|
||||
```json
|
||||
{ "to_status": "disabled", "reason": "security patch" }
|
||||
```
|
||||
|
||||
- Body(示例:延迟生效):
|
||||
|
||||
```json
|
||||
{ "to_status": "disabled", "effective_at": "2026-02-01T00:00:00Z", "reason": "maintenance window" }
|
||||
```
|
||||
|
||||
#### 4.2.5 审批上下线申请单
|
||||
|
||||
**GET** `/platform/app-status-change-requests?status=pending&page=1&page_size=20`
|
||||
|
||||
**POST** `/platform/app-status-change-requests/{request_id}/approve`
|
||||
|
||||
**POST** `/platform/app-status-change-requests/{request_id}/reject?reason=...`
|
||||
|
||||
#### 4.2.6 删除 App(软删除)
|
||||
|
||||
**DELETE** `/platform/apps/{app_id}`
|
||||
|
||||
- Tag:`App`
|
||||
- Header:`Authorization: Bearer <access_token>`
|
||||
- 二次验证(可选但建议生产启用):
|
||||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,则必须同时传 Header:`X-Sensitive-Token: <token>`,否则返回 403。
|
||||
|
||||
### Step 5:列出用户(User)
|
||||
|
||||
**GET** `/users?page=1&page_size=20`
|
||||
@@ -272,3 +350,25 @@ enabled_apps 维护建议:
|
||||
- `/auth/login`:约 2 req/s,burst 10(同一 IP)
|
||||
- `/auth/register`:约 1 req/s,burst 5(同一 IP)
|
||||
- 触发后返回:HTTP 429 + `code=40000`
|
||||
|
||||
## 密码重置(User)
|
||||
|
||||
用户自助重置(需要旧密码):
|
||||
|
||||
- **POST** `/users/me/password/reset`
|
||||
- Tag:`User`
|
||||
- Header:`Authorization: Bearer <access_token>`
|
||||
- Body:
|
||||
|
||||
```json
|
||||
{ "current_password": "oldPassword123", "new_password": "newPassword456" }
|
||||
```
|
||||
|
||||
租户管理员重置任意用户(生成临时密码):
|
||||
|
||||
- **POST** `/users/{id}/password/reset`
|
||||
- Tag:`User`
|
||||
- Header:`Authorization: Bearer <access_token>`
|
||||
- 权限:需要 `user:password:reset:any`
|
||||
- 二次验证(可选但建议生产启用):
|
||||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,则必须传 Header:`X-Sensitive-Token: <token>`,否则返回 403。
|
||||
|
||||
90
docs/SECURITY_GOVERNANCE.md
Normal file
90
docs/SECURITY_GOVERNANCE.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 安全评估与接口治理建议(IAM Service)
|
||||
|
||||
本文面向本仓库当前实现,给出“暴露面审查、风险识别、接口分级、敏感操作二次验证、隐藏策略”建议,并标注已落地的控制点。
|
||||
|
||||
## 1) 当前接口暴露面概览(按访问级别)
|
||||
|
||||
### 公开接口(Public)
|
||||
|
||||
特点:无需 Bearer Token;可被外部直接调用(应最小化)。
|
||||
|
||||
- `POST /tenants/register`:创建租户
|
||||
- `POST /auth/register`:租户内注册用户(依赖 Header `X-Tenant-ID`)
|
||||
- `POST /auth/login`:租户内登录(依赖 Header `X-Tenant-ID`)
|
||||
- `GET /scalar`:OpenAPI/Scalar 文档
|
||||
|
||||
### 认证接口(Authenticated)
|
||||
|
||||
特点:要求 Bearer Token;应默认通过网关/WAF 暴露。
|
||||
|
||||
- `GET /tenants/me`、`PATCH /tenants/me`、`DELETE /tenants/me`
|
||||
- `POST /tenants/me/status`
|
||||
- `GET /me/permissions`
|
||||
- `GET/PATCH/DELETE /users...`、`GET/PUT /users/{id}/roles`
|
||||
- `GET/POST /roles`
|
||||
|
||||
### 内部/平台接口(Internal / Platform)
|
||||
|
||||
特点:平台权限(系统角色)才能访问,建议网关层做更严格限制(IP allowlist、mTLS、独立域名等)。
|
||||
|
||||
- `GET/PUT /platform/tenants/{tenant_id}/enabled-apps`
|
||||
- `GET/POST /platform/apps`、`GET/PATCH/DELETE /platform/apps/{app_id}`
|
||||
- `POST /platform/apps/{app_id}/status-change-requests`
|
||||
- `GET /platform/app-status-change-requests`
|
||||
- `POST /platform/app-status-change-requests/{request_id}/approve|reject`
|
||||
|
||||
## 2) 风险识别(重点)
|
||||
|
||||
- **租户创建为公开接口**:可能被滥用批量创建租户,造成资源耗尽、审计噪声、后续越权风险面扩大。
|
||||
- **注册为公开接口**:对任意租户 ID 可注册用户(虽然要求 `X-Tenant-ID`,但租户 ID 可被枚举/泄露)。
|
||||
- **平台接口暴露风险**:即便有平台权限控制,一旦 token 泄露将影响全局(enabled_apps 与 apps 注册表)。
|
||||
- **文档暴露**:`/scalar` 默认公开,可能暴露内部接口与参数细节。
|
||||
|
||||
## 3) 已落地控制点(本次实现)
|
||||
|
||||
- **敏感操作二次验证(可选开关)**
|
||||
- 通过环境变量 `IAM_SENSITIVE_ACTION_TOKEN` 启用二次验证。
|
||||
- 当启用时,下列操作必须额外携带 Header `X-Sensitive-Token: <token>`:
|
||||
- `POST /tenants/register`
|
||||
- `DELETE /platform/apps/{app_id}`
|
||||
- 目的:降低“单一 Bearer Token 泄露”或“误调用”带来的破坏面。
|
||||
|
||||
- **平台权限细分**
|
||||
- apps 生命周期管理引入平台权限码:`iam:app:read|write|approve|delete`
|
||||
- enabled_apps 管理权限码:`iam:tenant:enabled_apps:read|write`
|
||||
|
||||
## 4) 建议的接口分级制度(治理)
|
||||
|
||||
建议把接口按“公开 / 认证 / 内部平台”分级,并在网关与代码侧同时落实:
|
||||
|
||||
- Public(公开)
|
||||
- 强制:限流、验证码/人机校验(可选)、IP 风控、灰度开关、审计
|
||||
- 建议:对 `tenants/register` 改为“邀请制/工单制”,或迁移到平台后台
|
||||
|
||||
- Authenticated(认证)
|
||||
- 强制:最小权限、全链路审计、错误码统一、分页上限、输入校验
|
||||
|
||||
- Internal / Platform(内部)
|
||||
- 强制:网关层隔离(独立域名/路由、mTLS 或 IP allowlist)、更严格限流、短 TTL token、专用账号
|
||||
- 建议:默认不在公开文档展示(需要登录后才能看到或独立文档入口)
|
||||
|
||||
## 5) 接口隐藏策略建议(不改变业务语义)
|
||||
|
||||
- 网关层
|
||||
- 路由隔离:`/platform/*` 走独立 upstream 或仅内网可达
|
||||
- 访问控制:IP allowlist、mTLS、JWT audience/issuer 分离
|
||||
- 速率限制:对平台接口与租户创建设置更低阈值
|
||||
|
||||
- 版本管理
|
||||
- 对内部平台接口采用独立版本前缀:如 `/platform/v1/...`
|
||||
- 对外接口保持稳定;内部接口可快速迭代
|
||||
|
||||
- 文档权限
|
||||
- 将 `/scalar` 拆分为 public 与 internal 两份,或对 internal 文档加登录保护
|
||||
|
||||
## 6) 下一步改造建议(按风险优先级)
|
||||
|
||||
1. 将 `POST /tenants/register` 从公开接口迁移为平台接口(或邀请制),并引入更强风控。
|
||||
2. 对 `POST /auth/register` 引入租户级注册策略(开关、邀请、允许域名白名单等)。
|
||||
3. 对 `/platform/*` 接口在网关实现“物理隔离”(内网 + mTLS)而不仅是逻辑权限校验。
|
||||
|
||||
44
docs/TEST_REPORT.md
Normal file
44
docs/TEST_REPORT.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# 测试报告(App 生命周期管理)
|
||||
|
||||
## 1) 目标与范围
|
||||
|
||||
覆盖本次新增的 App 生命周期管理能力:
|
||||
- app id/name/type 等输入校验与清洗
|
||||
- apps 注册表的创建/更新/查询
|
||||
- 上下线审批(申请/审批/生效)
|
||||
- 软删除
|
||||
|
||||
## 2) 测试类型
|
||||
|
||||
### 单元测试(无需数据库)
|
||||
|
||||
- `src/services/app.rs`:包含 app_id 等关键字段校验用例
|
||||
|
||||
### 集成/冒烟测试(需要 DATABASE_URL)
|
||||
|
||||
当设置 `DATABASE_URL` 时会执行:
|
||||
- `tests/enabled_apps_smoke.rs`:enabled_apps 读写与版本冲突
|
||||
- `tests/app_lifecycle_smoke.rs`:apps 创建/更新/审批禁用/软删除
|
||||
|
||||
若未设置 `DATABASE_URL`,上述集成测试会自动跳过(返回 Ok)。
|
||||
|
||||
## 3) 执行方式
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
若要执行集成测试,需提供可用的 PostgreSQL 连接串,并保证已完成迁移:
|
||||
|
||||
```bash
|
||||
export DATABASE_URL='postgres://...'
|
||||
./scripts/db/migrate.sh
|
||||
./scripts/db/verify.sh
|
||||
cargo test
|
||||
```
|
||||
|
||||
## 4) 结果说明
|
||||
|
||||
- 已在当前仓库执行 `cargo test`,测试通过。
|
||||
- 代码仓库内测试已保持可重复执行,并在无数据库时不阻塞开发体验。
|
||||
- 覆盖率统计需依赖覆盖率工具(如 cargo-llvm-cov 或 tarpaulin);本仓库当前未内置该工具链。
|
||||
Reference in New Issue
Block a user