122 lines
3.8 KiB
Rust
122 lines
3.8 KiB
Rust
use crate::utils::authz::filter_permissions_by_enabled_apps;
|
||
use common_telemetry::AppError;
|
||
use sqlx::PgPool;
|
||
use tracing::instrument;
|
||
use uuid::Uuid;
|
||
|
||
#[derive(Clone)]
|
||
pub struct AuthorizationService {
|
||
pool: PgPool,
|
||
}
|
||
|
||
impl AuthorizationService {
|
||
/// 创建权限服务实例。
|
||
pub fn new(pool: PgPool) -> Self {
|
||
Self { pool }
|
||
}
|
||
|
||
#[instrument(skip(self))]
|
||
/// 获取用户在指定租户下的权限编码集合(去重)。
|
||
///
|
||
/// 说明:
|
||
/// - 权限来源于用户所属角色(user_roles → roles)及角色绑定权限(role_permissions → permissions)。
|
||
///
|
||
/// 输入:
|
||
/// - `tenant_id`:租户 ID
|
||
/// - `user_id`:用户 ID
|
||
///
|
||
/// 输出:
|
||
/// - 权限编码数组(如 `tenant:read` / `user:write`)
|
||
///
|
||
/// 异常:
|
||
/// - 数据库查询失败
|
||
pub async fn list_permissions_for_user(
|
||
&self,
|
||
tenant_id: Uuid,
|
||
user_id: Uuid,
|
||
) -> Result<Vec<String>, AppError> {
|
||
let enabled_apps: Vec<String> =
|
||
sqlx::query_scalar("SELECT enabled_apps FROM tenant_entitlements WHERE tenant_id = $1")
|
||
.bind(tenant_id)
|
||
.fetch_optional(&self.pool)
|
||
.await?
|
||
.unwrap_or_default();
|
||
|
||
let query = r#"
|
||
SELECT DISTINCT p.code
|
||
FROM permissions p
|
||
JOIN role_permissions rp ON rp.permission_id = p.id
|
||
JOIN user_roles ur ON ur.role_id = rp.role_id
|
||
JOIN roles r ON r.id = ur.role_id
|
||
WHERE r.tenant_id = $1 AND ur.user_id = $2
|
||
"#;
|
||
let rows = sqlx::query_scalar::<_, String>(query)
|
||
.bind(tenant_id)
|
||
.bind(user_id)
|
||
.fetch_all(&self.pool)
|
||
.await?;
|
||
Ok(filter_permissions_by_enabled_apps(rows, &enabled_apps))
|
||
}
|
||
|
||
#[instrument(skip(self))]
|
||
/// 校验用户是否具备指定权限,不满足则直接返回权限拒绝错误。
|
||
///
|
||
/// 业务规则:
|
||
/// - 若用户权限集合中不包含 `permission_code`,返回 `PermissionDenied(permission_code)`。
|
||
///
|
||
/// 输入:
|
||
/// - `tenant_id`:租户 ID
|
||
/// - `user_id`:用户 ID
|
||
/// - `permission_code`:权限编码
|
||
///
|
||
/// 输出:
|
||
/// - 成功返回 `()`;失败返回权限拒绝错误
|
||
pub async fn require_permission(
|
||
&self,
|
||
tenant_id: Uuid,
|
||
user_id: Uuid,
|
||
permission_code: &str,
|
||
) -> Result<(), AppError> {
|
||
let permissions = self.list_permissions_for_user(tenant_id, user_id).await?;
|
||
if permissions.iter().any(|p| p == permission_code) {
|
||
Ok(())
|
||
} else {
|
||
Err(AppError::PermissionDenied(permission_code.to_string()))
|
||
}
|
||
}
|
||
|
||
#[instrument(skip(self))]
|
||
pub async fn list_platform_permissions_for_user(
|
||
&self,
|
||
user_id: Uuid,
|
||
) -> Result<Vec<String>, AppError> {
|
||
let query = r#"
|
||
SELECT DISTINCT p.code
|
||
FROM permissions p
|
||
JOIN role_permissions rp ON rp.permission_id = p.id
|
||
JOIN user_roles ur ON ur.role_id = rp.role_id
|
||
JOIN roles r ON r.id = ur.role_id
|
||
WHERE ur.user_id = $1 AND r.is_system = TRUE
|
||
"#;
|
||
let rows = sqlx::query_scalar::<_, String>(query)
|
||
.bind(user_id)
|
||
.fetch_all(&self.pool)
|
||
.await?;
|
||
Ok(rows)
|
||
}
|
||
|
||
#[instrument(skip(self))]
|
||
pub async fn require_platform_permission(
|
||
&self,
|
||
user_id: Uuid,
|
||
permission_code: &str,
|
||
) -> Result<(), AppError> {
|
||
let permissions = self.list_platform_permissions_for_user(user_id).await?;
|
||
if permissions.iter().any(|p| p == permission_code) {
|
||
Ok(())
|
||
} else {
|
||
Err(AppError::PermissionDenied(permission_code.to_string()))
|
||
}
|
||
}
|
||
}
|