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

5.3 KiB
Raw Blame History

任务012补充权限校验

目标

为 cabinets.rs、projects.rs、organizations.rs 三个模块补充完整的权限校验。

问题描述

第二轮代码审查发现这3个模块的handler函数签名中缺少 CurrentUser 参数,导致无法进行权限校验。任何登录用户都可以访问这些敏感接口。

修复清单

1. 权限码定义

首先在权限表中添加缺失的权限码:

-- 组织管理
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
// 示例:获取组织列表
#[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/organizationsorg:view
  • POST /api/organizationsorg:create
  • PUT /api/organizations/:idorg:edit
  • DELETE /api/organizations/:idorg:delete

3. projects.rs 修复

文件: src/routes/projects.rs

修改内容:

  • 所有handler函数签名添加 user: CurrentUser 参数
  • 每个handler开头调用 check_permission

需要修复的端点:

  • GET /api/organizations/:org_id/projectsproject:view
  • POST /api/organizations/:org_id/projectsproject:create
  • PUT /api/projects/:idproject:edit
  • DELETE /api/projects/:idproject:delete

4. cabinets.rs 修复

文件: src/routes/cabinets.rs

修改内容:

  • 所有handler函数签名添加 user: CurrentUser 参数
  • 每个handler开头调用 check_permission
  • list_cabinetsget_cabinet 需要额外验证用户是否有权访问该项目

需要修复的端点:

  • GET /api/projects/:project_id/cabinetsdevice:view + 项目权限验证
  • POST /api/cabinetsdevice:create
  • PUT /api/cabinets/:iddevice:edit
  • DELETE /api/cabinets/:iddevice:delete
  • POST /api/cabinets/:id/regenerate-authdevice:edit
  • GET /api/cabinets/:iddevice:view + 项目权限验证
  • GET /api/cabinets/optionsdevice:view
  • GET /api/cabin-boards/optionsdevice:view
  • GET /api/compartments/optionsdevice:view

项目权限验证逻辑:

// 验证用户是否有权访问指定项目
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 添加组织隔离,企业管理员只能看到本组织的用户
// 修改 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(如有测试)确保无回归