diff --git a/crates/erp-auth/src/middleware/jwt_auth.rs b/crates/erp-auth/src/middleware/jwt_auth.rs index 4fc1cdc..00a7c94 100644 --- a/crates/erp-auth/src/middleware/jwt_auth.rs +++ b/crates/erp-auth/src/middleware/jwt_auth.rs @@ -5,7 +5,7 @@ use axum::response::Response; use erp_core::error::AppError; use erp_core::request_info::REQUEST_INFO; use erp_core::request_info::RequestInfo; -use erp_core::types::TenantContext; +use erp_core::types::{DataScope, TenantContext}; use crate::service::token_service::TokenService; @@ -63,6 +63,12 @@ pub async fn jwt_auth_middleware_fn( None => vec![], }; + // 查询每个权限的数据范围 + let permission_data_scopes = match &db { + Some(conn) => fetch_permission_data_scopes(claims.sub, claims.tid, conn).await, + None => std::collections::HashMap::new(), + }; + // 提取请求来源信息(IP + User-Agent),用于审计日志 let request_info = RequestInfo::from_headers(req.headers()); @@ -72,6 +78,7 @@ pub async fn jwt_auth_middleware_fn( roles: claims.roles, permissions: claims.permissions, department_ids, + permission_data_scopes, }; // Reconstruct the request with the TenantContext injected into extensions. @@ -105,3 +112,58 @@ async fn fetch_user_department_ids( vec![] }) } + +/// 查询用户每个权限的数据范围(从 role_permissions 表) +async fn fetch_permission_data_scopes( + user_id: uuid::Uuid, + tenant_id: uuid::Uuid, + db: &sea_orm::DatabaseConnection, +) -> std::collections::HashMap { + use sea_orm::ConnectionTrait; + + let sql = r#" + SELECT p.code, MIN( + CASE rp.data_scope + WHEN 'all' THEN 0 + WHEN 'department_tree' THEN 1 + WHEN 'department' THEN 2 + WHEN 'self' THEN 3 + ELSE 0 + END + ) AS scope_rank, + MIN(rp.data_scope) AS data_scope + FROM user_roles ur + JOIN role_permissions rp ON ur.role_id = rp.role_id AND ur.tenant_id = rp.tenant_id + JOIN permissions p ON rp.permission_id = p.id + WHERE ur.user_id = $1 + AND ur.tenant_id = $2 + AND ur.deleted_at IS NULL + AND rp.deleted_at IS NULL + GROUP BY p.code + "#; + + let stmt = sea_orm::Statement::from_sql_and_values( + sea_orm::DatabaseBackend::Postgres, + sql, + [user_id.into(), tenant_id.into()], + ); + + match db.query_all(stmt).await { + Ok(rows) => { + let mut scopes = std::collections::HashMap::new(); + for row in rows { + if let (Ok(code), Ok(scope)) = ( + row.try_get_by_index::(0), + row.try_get_by_index::(2), + ) { + scopes.insert(code, DataScope::from_str(&scope)); + } + } + scopes + } + Err(e) => { + tracing::warn!(error = %e, "查询权限数据范围失败,默认全部 All"); + std::collections::HashMap::new() + } + } +} diff --git a/crates/erp-core/src/rbac.rs b/crates/erp-core/src/rbac.rs index 4fce2f0..e3b588e 100644 --- a/crates/erp-core/src/rbac.rs +++ b/crates/erp-core/src/rbac.rs @@ -1,5 +1,5 @@ use crate::error::AppError; -use crate::types::TenantContext; +use crate::types::{DataScope, TenantContext}; /// Check whether the `TenantContext` includes the specified permission code. /// @@ -38,6 +38,16 @@ pub fn require_role(ctx: &TenantContext, role: &str) -> Result<(), AppError> { } } +/// 获取指定权限的数据范围。默认 All(向后兼容)。 +/// +/// Service 层根据返回值追加对应的查询过滤条件。 +pub fn get_data_scope(ctx: &TenantContext, permission: &str) -> DataScope { + ctx.permission_data_scopes + .get(permission) + .cloned() + .unwrap_or(DataScope::All) +} + #[cfg(test)] mod tests { use super::*; @@ -50,6 +60,7 @@ mod tests { roles: roles.into_iter().map(String::from).collect(), permissions: permissions.into_iter().map(String::from).collect(), department_ids: vec![], + permission_data_scopes: std::collections::HashMap::new(), } } diff --git a/crates/erp-core/src/types.rs b/crates/erp-core/src/types.rs index 338962e..7145fc0 100644 --- a/crates/erp-core/src/types.rs +++ b/crates/erp-core/src/types.rs @@ -1,5 +1,6 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use uuid::Uuid; /// 所有数据库实体的公共字段 @@ -114,6 +115,7 @@ mod tests { roles: vec!["admin".to_string()], permissions: vec!["user.read".to_string()], department_ids: vec![], + permission_data_scopes: HashMap::new(), }; assert_eq!(ctx.roles.len(), 1); assert_eq!(ctx.permissions.len(), 1); @@ -148,6 +150,30 @@ impl ApiResponse { } } +/// 行级数据权限范围 +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum DataScope { + /// 查看所有数据 + All, + /// 仅查看自己创建的数据 + SelfOnly, + /// 仅查看本部门数据 + Department, + /// 查看本部门及下属部门数据 + DepartmentTree, +} + +impl DataScope { + pub fn from_str(s: &str) -> Self { + match s { + "self" => Self::SelfOnly, + "department" => Self::Department, + "department_tree" => Self::DepartmentTree, + _ => Self::All, + } + } +} + /// 租户上下文(中间件注入) #[derive(Debug, Clone)] pub struct TenantContext { @@ -157,4 +183,6 @@ pub struct TenantContext { pub permissions: Vec, /// 用户所属部门 ID 列表(行级数据权限使用) pub department_ids: Vec, + /// 每个权限码对应的数据范围(从 role_permissions.data_scope 加载) + pub permission_data_scopes: HashMap, }