backbone_authorization/
cache.rs1use crate::types::Permission;
8use async_trait::async_trait;
9use std::collections::{HashMap, HashSet};
10use std::sync::Arc;
11use tokio::sync::RwLock;
12
13#[derive(Debug, thiserror::Error)]
15pub enum CacheError {
16 #[error("Cache operation failed: {0}")]
18 OperationFailed(String),
19
20 #[error("Serialization error: {0}")]
22 SerializationError(String),
23
24 #[error("Connection error: {0}")]
26 ConnectionError(String),
27}
28
29#[async_trait]
33pub trait PermissionCacheBackend: Send + Sync {
34 async fn get(&self, user_id: &str) -> Result<Option<HashSet<Permission>>, CacheError>;
38
39 async fn set(
41 &self,
42 user_id: &str,
43 permissions: &HashSet<Permission>,
44 ttl_seconds: u64,
45 ) -> Result<(), CacheError>;
46
47 async fn delete(&self, user_id: &str) -> Result<(), CacheError>;
49
50 async fn clear(&self) -> Result<(), CacheError>;
52}
53
54struct CachedEntry {
56 permissions: HashSet<Permission>,
57 expires_at: chrono::DateTime<chrono::Utc>,
58}
59
60pub struct InMemoryPermissionCache {
65 cache: Arc<RwLock<HashMap<String, CachedEntry>>>,
66}
67
68impl InMemoryPermissionCache {
69 pub fn new() -> Self {
71 Self {
72 cache: Arc::new(RwLock::new(HashMap::new())),
73 }
74 }
75}
76
77impl Default for InMemoryPermissionCache {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83#[async_trait]
84impl PermissionCacheBackend for InMemoryPermissionCache {
85 async fn get(&self, user_id: &str) -> Result<Option<HashSet<Permission>>, CacheError> {
86 let cache = self.cache.read().await;
87 if let Some(entry) = cache.get(user_id) {
88 if entry.expires_at > chrono::Utc::now() {
89 return Ok(Some(entry.permissions.clone()));
90 }
91 }
92 Ok(None)
93 }
94
95 async fn set(
96 &self,
97 user_id: &str,
98 permissions: &HashSet<Permission>,
99 ttl_seconds: u64,
100 ) -> Result<(), CacheError> {
101 let expires_at = chrono::Utc::now() + chrono::Duration::seconds(ttl_seconds as i64);
102 let entry = CachedEntry {
103 permissions: permissions.clone(),
104 expires_at,
105 };
106 let mut cache = self.cache.write().await;
107 cache.insert(user_id.to_string(), entry);
108 Ok(())
109 }
110
111 async fn delete(&self, user_id: &str) -> Result<(), CacheError> {
112 let mut cache = self.cache.write().await;
113 cache.remove(user_id);
114 Ok(())
115 }
116
117 async fn clear(&self) -> Result<(), CacheError> {
118 let mut cache = self.cache.write().await;
119 cache.clear();
120 Ok(())
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::types::{Permission, UserAction, RoleAction};
128
129 fn test_permissions() -> HashSet<Permission> {
130 let mut perms = HashSet::new();
131 perms.insert(Permission::User(UserAction::Read));
132 perms.insert(Permission::Role(RoleAction::List));
133 perms
134 }
135
136 #[tokio::test]
137 async fn test_inmemory_get_set() {
138 let cache = InMemoryPermissionCache::new();
139 let perms = test_permissions();
140
141 cache.set("user1", &perms, 300).await.unwrap();
142
143 let result = cache.get("user1").await.unwrap();
144 assert!(result.is_some());
145 assert_eq!(result.unwrap(), perms);
146
147 let result = cache.get("user2").await.unwrap();
149 assert!(result.is_none());
150 }
151
152 #[tokio::test]
153 async fn test_inmemory_ttl_expiry() {
154 let cache = InMemoryPermissionCache::new();
155 let perms = test_permissions();
156
157 cache.set("user1", &perms, 1).await.unwrap();
159
160 assert!(cache.get("user1").await.unwrap().is_some());
162
163 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
165
166 assert!(cache.get("user1").await.unwrap().is_none());
168 }
169
170 #[tokio::test]
171 async fn test_inmemory_delete() {
172 let cache = InMemoryPermissionCache::new();
173 let perms = test_permissions();
174
175 cache.set("user1", &perms, 300).await.unwrap();
176 assert!(cache.get("user1").await.unwrap().is_some());
177
178 cache.delete("user1").await.unwrap();
179 assert!(cache.get("user1").await.unwrap().is_none());
180
181 cache.delete("nonexistent").await.unwrap();
183 }
184
185 #[tokio::test]
186 async fn test_inmemory_clear() {
187 let cache = InMemoryPermissionCache::new();
188 let perms = test_permissions();
189
190 cache.set("user1", &perms, 300).await.unwrap();
191 cache.set("user2", &perms, 300).await.unwrap();
192 cache.set("user3", &perms, 300).await.unwrap();
193
194 cache.clear().await.unwrap();
195
196 assert!(cache.get("user1").await.unwrap().is_none());
197 assert!(cache.get("user2").await.unwrap().is_none());
198 assert!(cache.get("user3").await.unwrap().is_none());
199 }
200}