use crate::error::AppError; use crate::types::{DataScope, TenantContext}; /// Check whether the `TenantContext` includes the specified permission code. /// /// Returns `Ok(())` if the permission is present, or `AppError::Forbidden` otherwise. pub fn require_permission(ctx: &TenantContext, permission: &str) -> Result<(), AppError> { if ctx.permissions.iter().any(|p| p == permission) { Ok(()) } else { Err(AppError::Forbidden("权限不足".to_string())) } } /// Check whether the `TenantContext` includes at least one of the specified permission codes. /// /// Useful when multiple permissions can grant access to the same resource. pub fn require_any_permission(ctx: &TenantContext, permissions: &[&str]) -> Result<(), AppError> { let has_any = permissions .iter() .any(|p| ctx.permissions.iter().any(|up| up == *p)); if has_any { Ok(()) } else { Err(AppError::Forbidden("权限不足".to_string())) } } /// Check whether the `TenantContext` includes the specified role code. /// /// Returns `Ok(())` if the role is present, or `AppError::Forbidden` otherwise. pub fn require_role(ctx: &TenantContext, role: &str) -> Result<(), AppError> { if ctx.roles.iter().any(|r| r == role) { Ok(()) } else { Err(AppError::Forbidden("权限不足".to_string())) } } /// 获取指定权限的数据范围。默认 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::*; use uuid::Uuid; fn test_ctx(roles: Vec<&str>, permissions: Vec<&str>) -> TenantContext { TenantContext { tenant_id: Uuid::now_v7(), user_id: Uuid::now_v7(), 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(), } } #[test] fn require_permission_succeeds_when_present() { let ctx = test_ctx(vec![], vec!["user.read", "user.write"]); assert!(require_permission(&ctx, "user.read").is_ok()); } #[test] fn require_permission_fails_when_missing() { let ctx = test_ctx(vec![], vec!["user.read"]); assert!(require_permission(&ctx, "user.delete").is_err()); } #[test] fn require_any_permission_succeeds_with_match() { let ctx = test_ctx(vec![], vec!["user.read"]); assert!(require_any_permission(&ctx, &["user.delete", "user.read"]).is_ok()); } #[test] fn require_any_permission_fails_with_no_match() { let ctx = test_ctx(vec![], vec!["user.read"]); assert!(require_any_permission(&ctx, &["user.delete", "user.admin"]).is_err()); } #[test] fn require_role_succeeds_when_present() { let ctx = test_ctx(vec!["admin", "user"], vec![]); assert!(require_role(&ctx, "admin").is_ok()); } #[test] fn require_role_fails_when_missing() { let ctx = test_ctx(vec!["user"], vec![]); assert!(require_role(&ctx, "admin").is_err()); } }