sz_rust_capability/
permission.rs1use async_trait::async_trait;
2use serde_json::Value;
3
4use crate::error::{CapError, CapResult};
5
6#[async_trait]
11pub trait PermissionChecker: Send + Sync + 'static {
12 async fn check(&self, cap_name: &str, args: &Value, tenant_id: i64) -> CapResult<()>;
16}
17
18pub struct AllowAll;
20
21#[async_trait]
22impl PermissionChecker for AllowAll {
23 async fn check(&self, _cap_name: &str, _args: &Value, _tenant_id: i64) -> CapResult<()> {
24 Ok(())
25 }
26}
27
28pub struct TenantScopeChecker {
33 allowed: parking_lot::RwLock<std::collections::HashMap<String, std::collections::HashSet<i64>>>,
34}
35
36impl TenantScopeChecker {
37 pub fn new() -> Self {
38 Self {
39 allowed: parking_lot::RwLock::new(std::collections::HashMap::new()),
40 }
41 }
42
43 pub fn grant(&self, cap_name: &str, tenant_id: i64) {
45 let mut map = self.allowed.write();
46 map.entry(cap_name.to_string())
47 .or_default()
48 .insert(tenant_id);
49 }
50
51 pub fn revoke(&self, cap_name: &str, tenant_id: i64) {
53 let mut map = self.allowed.write();
54 if let Some(set) = map.get_mut(cap_name) {
55 set.remove(&tenant_id);
56 }
57 }
58}
59
60impl Default for TenantScopeChecker {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66#[async_trait]
67impl PermissionChecker for TenantScopeChecker {
68 async fn check(&self, cap_name: &str, _args: &Value, tenant_id: i64) -> CapResult<()> {
69 let map = self.allowed.read();
70 match map.get(cap_name) {
71 Some(set) if set.contains(&tenant_id) => Ok(()),
72 _ => Err(CapError::PermissionDenied(format!(
73 "租户 {tenant_id} 无权调用能力 {cap_name}"
74 ))),
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[tokio::test]
84 async fn test_allow_all() {
85 let checker = AllowAll;
86 assert!(checker.check("any.cap", &Value::Null, 1).await.is_ok());
87 }
88
89 #[tokio::test]
90 async fn test_tenant_scope_grant_revoke() {
91 let checker = TenantScopeChecker::new();
92 checker.grant("cap.a", 100);
93 assert!(checker.check("cap.a", &Value::Null, 100).await.is_ok());
94 assert!(checker.check("cap.a", &Value::Null, 200).await.is_err());
95 checker.revoke("cap.a", 100);
96 assert!(checker.check("cap.a", &Value::Null, 100).await.is_err());
97 }
98}