charging-cabinet/tasks/012-permission-checks-supplement.md
2026-07-02 05:38:01 +08:00

173 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 任务012补充权限校验
## 目标
为 cabinets.rs、projects.rs、organizations.rs 三个模块补充完整的权限校验。
## 问题描述
第二轮代码审查发现这3个模块的handler函数签名中缺少 `CurrentUser` 参数,导致无法进行权限校验。任何登录用户都可以访问这些敏感接口。
## 修复清单
### 1. 权限码定义
首先在权限表中添加缺失的权限码:
```sql
-- 组织管理
INSERT INTO permissions (code, name) VALUES ('org:view', '查看组织');
INSERT INTO permissions (code, name) VALUES ('org:create', '创建组织');
INSERT INTO permissions (code, name) VALUES ('org:edit', '编辑组织');
INSERT INTO permissions (code, name) VALUES ('org:delete', '删除组织');
-- 项目管理
INSERT INTO permissions (code, name) VALUES ('project:view', '查看项目');
INSERT INTO permissions (code, name) VALUES ('project:create', '创建项目');
INSERT INTO permissions (code, name) VALUES ('project:edit', '编辑项目');
INSERT INTO permissions (code, name) VALUES ('project:delete', '删除项目');
-- 设备管理(补充)
INSERT INTO permissions (code, name) VALUES ('device:edit', '编辑设备');
```
### 2. organizations.rs 修复
**文件:** `src/routes/organizations.rs`
**修改内容:**
- 所有handler函数签名添加 `user: CurrentUser` 参数
- 每个handler开头调用 `check_permission`
```rust
// 示例:获取组织列表
#[get("/api/organizations")]
async fn list_organizations(
user: CurrentUser, // 新增
pool: State<MySqlPool>,
) -> Result<Json<Vec<Organization>>> {
auth::check_permission(&user, "org:view")?; // 新增
// ... 原有逻辑
}
// 示例:创建组织
#[post("/api/organizations")]
async fn create_organization(
user: CurrentUser, // 新增
pool: State<MySqlPool>,
json: Json<CreateOrganizationRequest>,
) -> Result<Json<Organization>> {
auth::check_permission(&user, "org:create")?; // 新增
// ... 原有逻辑
}
```
**需要修复的端点:**
- `GET /api/organizations``org:view`
- `POST /api/organizations``org:create`
- `PUT /api/organizations/:id``org:edit`
- `DELETE /api/organizations/:id``org:delete`
### 3. projects.rs 修复
**文件:** `src/routes/projects.rs`
**修改内容:**
- 所有handler函数签名添加 `user: CurrentUser` 参数
- 每个handler开头调用 `check_permission`
**需要修复的端点:**
- `GET /api/organizations/:org_id/projects``project:view`
- `POST /api/organizations/:org_id/projects``project:create`
- `PUT /api/projects/:id``project:edit`
- `DELETE /api/projects/:id``project:delete`
### 4. cabinets.rs 修复
**文件:** `src/routes/cabinets.rs`
**修改内容:**
- 所有handler函数签名添加 `user: CurrentUser` 参数
- 每个handler开头调用 `check_permission`
- `list_cabinets``get_cabinet` 需要额外验证用户是否有权访问该项目
**需要修复的端点:**
- `GET /api/projects/:project_id/cabinets``device:view` + 项目权限验证
- `POST /api/cabinets``device:create`
- `PUT /api/cabinets/:id``device:edit`
- `DELETE /api/cabinets/:id``device:delete`
- `POST /api/cabinets/:id/regenerate-auth``device:edit`
- `GET /api/cabinets/:id``device:view` + 项目权限验证
- `GET /api/cabinets/options``device:view`
- `GET /api/cabin-boards/options``device:view`
- `GET /api/compartments/options``device:view`
**项目权限验证逻辑:**
```rust
// 验证用户是否有权访问指定项目
async fn check_project_access(
user: &CurrentUser,
pool: &MySqlPool,
project_id: i64,
) -> Result<()> {
if user.role_level >= 2 {
// 总管理员可访问所有项目
return Ok(());
}
if let Some(org_id) = user.organization_id {
// 验证项目是否属于用户的组织
let project: Option<(i64,)> = sqlx::query_as(
"SELECT id FROM projects WHERE id = ? AND organization_id = ?"
)
.bind(project_id)
.bind(org_id)
.fetch_optional(pool)
.await?;
if project.is_none() {
return Err(AppError::Forbidden("无权访问该项目".into()));
}
} else {
return Err(AppError::Forbidden("无组织关联".into()));
}
Ok(())
}
```
### 5. users.rs 修复(额外)
**文件:** `src/routes/users.rs`
**修改内容:**
- `list_users` 添加组织隔离,企业管理员只能看到本组织的用户
```rust
// 修改 list_users 查询
let users = if user.role_level >= 2 {
// 总管理员看所有用户
sqlx::query_as("SELECT * FROM users")
.fetch_all(&pool)
.await?
} else if let Some(org_id) = user.organization_id {
// 企业管理员只看本组织用户
sqlx::query_as("SELECT * FROM users WHERE organization_id = ?")
.bind(org_id)
.fetch_all(&pool)
.await?
} else {
vec![] // 无组织用户看不到任何人
};
```
## 质量约束
1. 完成代码后执行 `cargo check` + `cargo clippy`,零报错零警告
2. 分层拆分单函数≤80行命名语义化完整注释
3. 所有外部IO/网络请求异常捕获禁止裸panic
4. 分支逻辑全覆盖,不遗漏兜底分支
5. 常量抽离不使用废弃API
6. Rust内存安全合理管理所有权禁用unsafe无合理理由
7. 修复后重新运行 `cargo test`(如有测试)确保无回归