Skip to main content

backbone_authorization/
service.rs

1//! Authorization service implementation
2//!
3//! Provides RBAC authorization with pluggable permission caching.
4//! Default uses in-memory cache; enable `redis` feature for distributed caching.
5
6use crate::cache::{InMemoryPermissionCache, PermissionCacheBackend};
7use crate::traits::*;
8use crate::types::*;
9use async_trait::async_trait;
10use std::collections::{HashMap, HashSet};
11use std::sync::Arc;
12use tokio::sync::RwLock;
13use tracing::{debug, info};
14
15/// Default authorization service with in-memory storage
16pub struct AuthorizationService {
17    /// Role-based permissions storage
18    role_permissions: Arc<RwLock<HashMap<String, HashSet<Permission>>>>,
19
20    /// User roles storage
21    user_roles: Arc<RwLock<HashMap<String, HashSet<String>>>>,
22
23    /// Pluggable permission cache backend
24    cache: Arc<dyn PermissionCacheBackend>,
25
26    /// Configuration
27    config: AuthorizationConfig,
28}
29
30impl Default for AuthorizationService {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl AuthorizationService {
37    /// Create a new authorization service with default roles and in-memory cache
38    pub fn new() -> Self {
39        Self::with_cache(Arc::new(InMemoryPermissionCache::new()))
40    }
41
42    /// Create a new authorization service with a custom cache backend
43    pub fn with_cache(cache: Arc<dyn PermissionCacheBackend>) -> Self {
44        Self::with_config(AuthorizationConfig::default(), cache)
45    }
46
47    /// Create a new authorization service with custom config and cache backend
48    pub fn with_config(config: AuthorizationConfig, cache: Arc<dyn PermissionCacheBackend>) -> Self {
49        let service = Self {
50            role_permissions: Arc::new(RwLock::new(HashMap::new())),
51            user_roles: Arc::new(RwLock::new(HashMap::new())),
52            cache,
53            config,
54        };
55
56        // Initialize with default roles and permissions
57        let role_perms = service.role_permissions.clone();
58        tokio::spawn(async move {
59            Self::initialize_default_roles_static(role_perms).await;
60        });
61
62        service
63    }
64
65    /// Initialize default roles and permissions (static version for spawn)
66    async fn initialize_default_roles_static(
67        role_permissions: Arc<RwLock<HashMap<String, HashSet<Permission>>>>,
68    ) {
69        info!("Initializing default RBAC roles and permissions");
70
71        let mut role_perms = role_permissions.write().await;
72
73        // Super Admin - all permissions
74        let super_admin_perms: HashSet<Permission> = Permission::all_permissions();
75        role_perms.insert("super_admin".to_string(), super_admin_perms);
76
77        // Admin - most permissions except user management
78        let admin_perms: HashSet<Permission> = Permission::all_permissions()
79            .into_iter()
80            .filter(|p| !matches!(p, Permission::User(_)))
81            .collect();
82        role_perms.insert("admin".to_string(), admin_perms);
83
84        // User - basic read and self-modify permissions
85        let user_perms: HashSet<Permission> = [
86            Permission::User(UserAction::Read),
87            Permission::Settings(SettingsAction::Read),
88            Permission::Role(RoleAction::Read),
89            Permission::Permission(PermissionAction::Read),
90        ]
91        .into_iter()
92        .collect();
93        role_perms.insert("user".to_string(), user_perms);
94
95        // Guest - read-only
96        let guest_perms: HashSet<Permission> = [
97            Permission::User(UserAction::Read),
98            Permission::Settings(SettingsAction::Read),
99        ]
100        .into_iter()
101        .collect();
102        role_perms.insert("guest".to_string(), guest_perms);
103
104        info!("Initialized {} default roles", role_perms.len());
105    }
106
107    /// Get cached permissions for a user, or fetch if not cached
108    async fn get_cached_permissions(&self, user_id: &str) -> HashSet<Permission> {
109        // Check cache first
110        if self.config.enable_permission_caching {
111            if let Ok(Some(permissions)) = self.cache.get(user_id).await {
112                debug!("Using cached permissions for user '{}'", user_id);
113                return permissions;
114            }
115        }
116
117        // Fetch fresh permissions
118        let permissions = self.fetch_user_permissions(user_id).await;
119
120        // Cache with configured TTL
121        if self.config.enable_permission_caching {
122            if let Err(e) = self.cache.set(user_id, &permissions, self.config.cache_ttl_seconds).await {
123                tracing::warn!("Failed to cache permissions for user '{}': {}", user_id, e);
124            }
125        }
126
127        permissions
128    }
129
130    /// Fetch all permissions for a user from their roles
131    async fn fetch_user_permissions(&self, user_id: &str) -> HashSet<Permission> {
132        let roles = self.user_roles.read().await;
133        let user_roles = roles.get(user_id).cloned().unwrap_or_default();
134
135        let role_perms = self.role_permissions.read().await;
136        let mut permissions = HashSet::new();
137
138        for role in user_roles {
139            if let Some(role_perms) = role_perms.get(&role) {
140                permissions.extend(role_perms.clone());
141            }
142        }
143
144        // Default to guest permissions if no roles
145        if permissions.is_empty() {
146            if let Some(guest_perms) = role_perms.get("guest") {
147                permissions.extend(guest_perms.clone());
148            }
149        }
150
151        permissions
152    }
153
154    /// Clear permission cache for a user
155    pub async fn clear_user_cache(&self, user_id: &str) {
156        if let Err(e) = self.cache.delete(user_id).await {
157            tracing::warn!("Failed to clear cache for user '{}': {}", user_id, e);
158        }
159        debug!("Cleared permission cache for user '{}'", user_id);
160    }
161
162    /// Clear all permission caches
163    pub async fn clear_all_cache(&self) {
164        if let Err(e) = self.cache.clear().await {
165            tracing::warn!("Failed to clear all permission caches: {}", e);
166        }
167        info!("Cleared all permission caches");
168    }
169}
170
171#[async_trait]
172impl AuthorizationServiceTrait for AuthorizationService {
173    async fn check_authorization(
174        &self,
175        request: AuthorizationRequest,
176    ) -> Result<AuthorizationResponse, AuthorizationError> {
177        let user_id = &request.user.user_id;
178
179        // Get user's permissions
180        let permissions = self.get_cached_permissions(user_id).await;
181
182        // Check if user has the required permission
183        let required_permission = Permission::from_action_resource(
184            request.action.clone(),
185            request.resource.clone(),
186        );
187
188        let has_permission = permissions.contains(&required_permission);
189
190        let response = AuthorizationResponse {
191            allowed: has_permission,
192            checks: vec![PermissionCheck {
193                allowed: has_permission,
194                permission: required_permission.clone(),
195                reason: if has_permission {
196                    "Permission granted".to_string()
197                } else {
198                    format!("Missing permission: {:?}", required_permission)
199                },
200            }],
201        };
202
203        debug!(
204            "Authorization check for user '{}': {}",
205            user_id,
206            if has_permission { "ALLOWED" } else { "DENIED" }
207        );
208
209        Ok(response)
210    }
211
212    async fn grant_permission_to_role(
213        &self,
214        role: &str,
215        permission: Permission,
216    ) -> Result<(), AuthorizationError> {
217        info!("Granted permission {:?} to role '{}'", permission, role);
218
219        let mut role_perms = self.role_permissions.write().await;
220        role_perms
221            .entry(role.to_string())
222            .or_insert_with(HashSet::new)
223            .insert(permission);
224
225        Ok(())
226    }
227
228    async fn revoke_permission_from_role(
229        &self,
230        role: &str,
231        permission: &Permission,
232    ) -> Result<(), AuthorizationError> {
233        let mut role_perms = self.role_permissions.write().await;
234        if let Some(perms) = role_perms.get_mut(role) {
235            perms.remove(permission);
236            info!("Revoked permission {:?} from role '{}'", permission, role);
237        }
238        Ok(())
239    }
240
241    async fn assign_role_to_user(
242        &self,
243        user_id: &str,
244        role: &str,
245    ) -> Result<(), AuthorizationError> {
246        let mut user_roles = self.user_roles.write().await;
247        user_roles
248            .entry(user_id.to_string())
249            .or_insert_with(HashSet::new)
250            .insert(role.to_string());
251
252        // Clear cache for this user
253        self.clear_user_cache(user_id).await;
254
255        info!("Assigned role '{}' to user '{}'", role, user_id);
256        Ok(())
257    }
258
259    async fn remove_role_from_user(
260        &self,
261        user_id: &str,
262        role: &str,
263    ) -> Result<(), AuthorizationError> {
264        let mut user_roles = self.user_roles.write().await;
265        if let Some(roles) = user_roles.get_mut(user_id) {
266            roles.remove(role);
267        }
268
269        // Clear cache for this user
270        self.clear_user_cache(user_id).await;
271
272        info!("Removed role '{}' from user '{}'", role, user_id);
273        Ok(())
274    }
275
276    async fn get_user_permissions(
277        &self,
278        user_id: &str,
279    ) -> Result<Vec<Permission>, AuthorizationError> {
280        let permissions = self.get_cached_permissions(user_id).await;
281        Ok(permissions.into_iter().collect())
282    }
283
284    async fn user_has_permission(
285        &self,
286        user_id: &str,
287        permission: &Permission,
288    ) -> Result<bool, AuthorizationError> {
289        let permissions = self.get_cached_permissions(user_id).await;
290        Ok(permissions.contains(permission))
291    }
292}
293
294/// Type alias for in-memory auth service
295pub type InMemoryAuthService = AuthorizationService;