Skip to main content

auth_framework/authorization_enhanced/
service.rs

1//! Enhanced Authorization Service using role-system v1.0
2//!
3//! This service provides a unified interface for all authorization operations,
4//! replacing the fragmented authorization systems in AuthFramework.
5
6use crate::errors::{AuthError, Result};
7use role_system::{
8    Permission, Resource, Role, Subject,
9    async_support::{AsyncRoleSystem, AsyncRoleSystemBuilder},
10    storage::{MemoryStorage, Storage},
11};
12use std::{collections::HashMap, sync::Arc};
13use tokio::sync::Mutex as TokioMutex;
14use tracing::{debug, info, warn};
15
16/// Enhanced authorization service providing enterprise-grade RBAC
17pub struct AuthorizationService<S = MemoryStorage>
18where
19    S: Storage + Send + Sync + Clone,
20{
21    /// The async role system from role-system v1.0
22    pub role_system: AsyncRoleSystem<S>,
23
24    /// Configuration for the service
25    config: AuthorizationConfig,
26
27    /// Cloned handle to the underlying storage, held behind a Mutex so that
28    /// mutation operations not exposed by AsyncRoleSystem (e.g. delete_role)
29    /// can be performed without requiring mutable access to the whole service.
30    ///
31    /// For MemoryStorage (the default) the clone shares the same `Arc<DashMap>`
32    /// as the storage inside role_system, so mutations are immediately visible.
33    storage_handle: Arc<TokioMutex<S>>,
34}
35
36/// Configuration for the authorization service
37#[derive(Debug, Clone)]
38pub struct AuthorizationConfig {
39    /// Enable audit logging
40    pub enable_audit: bool,
41
42    /// Enable permission caching
43    pub enable_caching: bool,
44
45    /// Cache TTL in seconds
46    pub cache_ttl_seconds: u64,
47
48    /// Maximum role hierarchy depth
49    pub max_hierarchy_depth: usize,
50}
51
52impl Default for AuthorizationConfig {
53    fn default() -> Self {
54        Self {
55            enable_audit: true,
56            enable_caching: true,
57            cache_ttl_seconds: 300, // 5 minutes
58            max_hierarchy_depth: 10,
59        }
60    }
61}
62
63impl AuthorizationService<MemoryStorage> {
64    /// Create a new authorization service with default configuration
65    pub async fn new() -> Result<Self> {
66        Self::with_config(AuthorizationConfig::default()).await
67    }
68
69    /// Create a new authorization service with custom configuration
70    pub async fn with_config(config: AuthorizationConfig) -> Result<Self> {
71        let storage = MemoryStorage::new();
72        // The clone shares the same Arc<DashMap> so mutations via storage_handle
73        // are immediately reflected in the role_system's internal storage.
74        let storage_handle = Arc::new(TokioMutex::new(storage.clone()));
75
76        let role_system = AsyncRoleSystemBuilder::new().build_with_storage(storage);
77
78        let service = Self {
79            role_system,
80            config,
81            storage_handle,
82        };
83
84        // Initialize with standard AuthFramework roles
85        service.initialize_authframework_roles().await?;
86
87        info!("AuthorizationService initialized with enhanced RBAC");
88        Ok(service)
89    }
90}
91
92impl<S> AuthorizationService<S>
93where
94    S: Storage + Send + Sync + Clone + Default,
95{
96    /// Create authorization service with custom storage
97    pub async fn with_storage(storage: S, config: AuthorizationConfig) -> Result<Self> {
98        let storage_handle = Arc::new(TokioMutex::new(storage.clone()));
99        // Build role system without RoleSystemConfig for now
100        let role_system = AsyncRoleSystemBuilder::new().build_with_storage(storage);
101
102        let service = Self {
103            role_system,
104            config,
105            storage_handle,
106        };
107
108        service.initialize_authframework_roles().await?;
109
110        info!("AuthorizationService initialized with custom storage");
111        Ok(service)
112    }
113
114    /// Initialize standard AuthFramework roles
115    async fn initialize_authframework_roles(&self) -> Result<()> {
116        info!("Initializing AuthFramework standard roles");
117
118        // Create guest role (minimal permissions)
119        let guest_role = Role::new("guest")
120            .with_description("Unauthenticated user with minimal access")
121            .add_permission(Permission::new("read", "public"));
122
123        // Create user role (authenticated user)
124        let user_role = Role::new("user")
125            .with_description("Authenticated user")
126            .add_permission(Permission::new("read", "profile"))
127            .add_permission(Permission::new("update", "profile:own"))
128            .add_permission(Permission::new("read", "public"));
129
130        // Create moderator role (content moderation)
131        let moderator_role = Role::new("moderator")
132            .with_description("Content moderator")
133            .add_permission(Permission::new("read", "*"))
134            .add_permission(Permission::new("update", "content"))
135            .add_permission(Permission::new("delete", "content"));
136
137        // Create admin role (system administration)
138        let admin_role = Role::new("admin")
139            .with_description("System administrator")
140            .add_permission(Permission::super_admin());
141
142        // Register roles
143        self.role_system
144            .register_role(guest_role)
145            .await
146            .map_err(|e| {
147                AuthError::authorization(format!("Failed to register guest role: {}", e))
148            })?;
149
150        self.role_system
151            .register_role(user_role)
152            .await
153            .map_err(|e| {
154                AuthError::authorization(format!("Failed to register user role: {}", e))
155            })?;
156
157        self.role_system
158            .register_role(moderator_role)
159            .await
160            .map_err(|e| {
161                AuthError::authorization(format!("Failed to register moderator role: {}", e))
162            })?;
163
164        self.role_system
165            .register_role(admin_role)
166            .await
167            .map_err(|e| {
168                AuthError::authorization(format!("Failed to register admin role: {}", e))
169            })?;
170
171        // Set up role hierarchy: admin -> moderator -> user -> guest
172        self.role_system
173            .add_role_inheritance("admin", "moderator")
174            .await
175            .map_err(|e| {
176                AuthError::authorization(format!(
177                    "Failed to set admin->moderator inheritance: {}",
178                    e
179                ))
180            })?;
181
182        self.role_system
183            .add_role_inheritance("moderator", "user")
184            .await
185            .map_err(|e| {
186                AuthError::authorization(format!(
187                    "Failed to set moderator->user inheritance: {}",
188                    e
189                ))
190            })?;
191
192        self.role_system
193            .add_role_inheritance("user", "guest")
194            .await
195            .map_err(|e| {
196                AuthError::authorization(format!("Failed to set user->guest inheritance: {}", e))
197            })?;
198
199        info!("AuthFramework standard roles initialized successfully");
200        Ok(())
201    }
202
203    /// Check if a user has permission to perform an action on a resource
204    pub async fn check_permission(
205        &self,
206        user_id: &str,
207        action: &str,
208        resource_type: &str,
209        context: Option<&HashMap<String, String>>,
210    ) -> Result<bool> {
211        debug!(
212            "Checking permission for user '{}': {}:{}",
213            user_id, action, resource_type
214        );
215
216        let subject = Subject::user(user_id);
217        // Create resource with resource_type as the type and no specific instance
218        let resource = Resource::new("", resource_type); // Empty ID, resource_type as type
219
220        let result = if let Some(context) = context {
221            self.role_system
222                .check_permission_with_context(&subject, action, &resource, context)
223                .await
224        } else {
225            self.role_system
226                .check_permission(&subject, action, &resource)
227                .await
228        };
229
230        // Audit logging based on configuration
231        if self.config.enable_audit {
232            info!(
233                target: "authorization_audit",
234                user_id = user_id,
235                action = action,
236                resource_type = resource_type,
237                permission_granted = result.is_ok() && *result.as_ref().unwrap_or(&false),
238                timestamp = chrono::Utc::now().to_rfc3339(),
239                "Permission check performed"
240            );
241        }
242
243        match result {
244            Ok(granted) => {
245                debug!("Permission check result: {}", granted);
246                Ok(granted)
247            }
248            Err(e) => {
249                warn!("Permission check failed: {}", e);
250                Err(AuthError::authorization(format!(
251                    "Permission check failed: {}",
252                    e
253                )))
254            }
255        }
256    }
257
258    /// Check API endpoint permission
259    pub async fn check_api_permission(
260        &self,
261        user_id: &str,
262        method: &str,
263        endpoint: &str,
264        context: &HashMap<String, String>,
265    ) -> Result<bool> {
266        // Convert HTTP method to action
267        let action = match method.to_uppercase().as_str() {
268            "GET" => "read",
269            "POST" => "create",
270            "PUT" | "PATCH" => "update",
271            "DELETE" => "delete",
272            _ => "access",
273        };
274
275        self.check_permission(user_id, action, endpoint, Some(context))
276            .await
277    }
278
279    /// Assign a role to a user
280    pub async fn assign_role(&self, user_id: &str, role_name: &str) -> Result<()> {
281        debug!("Assigning role '{}' to user '{}'", role_name, user_id);
282
283        let subject = Subject::user(user_id);
284
285        self.role_system
286            .assign_role(&subject, role_name)
287            .await
288            .map_err(|e| AuthError::authorization(format!("Failed to assign role: {}", e)))?;
289
290        info!("Role '{}' assigned to user '{}'", role_name, user_id);
291        Ok(())
292    }
293
294    /// Remove a role from a user
295    pub async fn remove_role(&self, user_id: &str, role_name: &str) -> Result<()> {
296        debug!("Removing role '{}' from user '{}'", role_name, user_id);
297
298        let subject = Subject::user(user_id);
299
300        self.role_system
301            .remove_role(&subject, role_name)
302            .await
303            .map_err(|e| AuthError::authorization(format!("Failed to remove role: {}", e)))?;
304
305        info!("Role '{}' removed from user '{}'", role_name, user_id);
306        Ok(())
307    }
308
309    /// Temporarily elevate a user's role
310    pub async fn elevate_role(
311        &self,
312        user_id: &str,
313        role_name: &str,
314        duration_seconds: Option<u64>,
315    ) -> Result<()> {
316        debug!(
317            "Elevating user '{}' to role '{}' for {:?} seconds",
318            user_id, role_name, duration_seconds
319        );
320
321        let subject = Subject::user(user_id);
322        let duration = duration_seconds.map(std::time::Duration::from_secs);
323
324        self.role_system
325            .elevate_role(&subject, role_name, duration)
326            .await
327            .map_err(|e| AuthError::authorization(format!("Failed to elevate role: {}", e)))?;
328
329        info!(
330            "User '{}' elevated to role '{}' for {:?} seconds",
331            user_id, role_name, duration_seconds
332        );
333        Ok(())
334    }
335
336    /// Get all roles assigned to a user
337    pub async fn get_user_roles(&self, user_id: &str) -> Result<Vec<String>> {
338        let subject = Subject::user(user_id);
339
340        let roles = self
341            .role_system
342            .get_subject_roles(&subject)
343            .await
344            .map_err(|e| AuthError::authorization(format!("Failed to get user roles: {}", e)))?;
345
346        Ok(roles.into_iter().collect())
347    }
348
349    /// Create a new role
350    pub async fn create_role(
351        &self,
352        name: &str,
353        description: &str,
354        permissions: Vec<Permission>,
355        parent_roles: Option<Vec<String>>,
356    ) -> Result<()> {
357        debug!(
358            "Creating role '{}' with {} permissions",
359            name,
360            permissions.len()
361        );
362
363        let mut role = Role::new(name).with_description(description);
364
365        for permission in permissions {
366            role = role.add_permission(permission);
367        }
368
369        self.role_system
370            .register_role(role)
371            .await
372            .map_err(|e| AuthError::authorization(format!("Failed to create role: {}", e)))?;
373
374        // Set up inheritance if specified
375        if let Some(parents) = parent_roles {
376            for parent in parents {
377                self.role_system
378                    .add_role_inheritance(name, &parent)
379                    .await
380                    .map_err(|e| {
381                        AuthError::authorization(format!("Failed to set role inheritance: {}", e))
382                    })?;
383            }
384        }
385
386        info!("Role '{}' created successfully", name);
387        Ok(())
388    }
389
390    /// Get role hierarchy (using new role-system v1.1.1 features)
391    pub async fn get_role_hierarchy(&self, role_id: &str) -> Result<Vec<String>> {
392        // For now, use the working single role approach with parent_role_id
393        if let Ok(Some(role)) = self.role_system.get_role(role_id).await {
394            let mut result = vec![role.id().to_string()];
395            if let Some(parent_id) = role.parent_role_id() {
396                result.push(parent_id.to_string());
397            }
398            Ok(result)
399        } else {
400            Ok(vec![])
401        }
402    }
403
404    /// Test role hierarchy metadata access
405    pub async fn get_role_metadata(&self, role_id: &str) -> Result<String> {
406        if let Ok(Some(role)) = self.role_system.get_role(role_id).await {
407            let depth = role.hierarchy_depth();
408            let is_root = role.is_root_role();
409            let is_leaf = role.is_leaf_role();
410            let children = role.child_role_ids();
411
412            Ok(format!(
413                "Role '{}': depth={}, root={}, leaf={}, children={:?}",
414                role.name(),
415                depth,
416                is_root,
417                is_leaf,
418                children
419            ))
420        } else {
421            Err(AuthError::authorization("Role not found".to_string()))
422        }
423    }
424
425    /// Delete a role
426    pub async fn delete_role(&self, name: &str) -> Result<()> {
427        // Verify the role actually exists before attempting deletion.
428        let exists = self
429            .role_system
430            .get_role(name)
431            .await
432            .map_err(|e| AuthError::authorization(format!("Failed to look up role: {}", e)))?;
433
434        if exists.is_none() {
435            return Err(AuthError::authorization(format!(
436                "Role '{}' not found",
437                name
438            )));
439        }
440
441        // Acquire the storage handle and call delete_role.
442        // For MemoryStorage the underlying Arc<DashMap> is shared between this
443        // handle and the role_system, so the deletion is immediately visible.
444        let mut storage = self.storage_handle.lock().await;
445        match storage.delete_role(name) {
446            Ok(true) => {
447                info!("Role deleted: {}", name);
448                Ok(())
449            }
450            Ok(false) => {
451                // Row already gone – treat as success (idempotent delete).
452                warn!("delete_role: role '{}' was not found in storage", name);
453                Ok(())
454            }
455            Err(e) => Err(AuthError::authorization(format!(
456                "Failed to delete role '{}': {}",
457                name, e
458            ))),
459        }
460    }
461
462    /// Get role by name
463    pub async fn get_role(&self, name: &str) -> Result<Option<Role>> {
464        self.role_system
465            .get_role(name)
466            .await
467            .map_err(|e| AuthError::authorization(format!("Failed to get role: {}", e)))
468    }
469
470    /// Update an existing role's description and/or parent relationship.
471    ///
472    /// Permissions are not changed by this method; use create_role to start fresh
473    /// or extend the API with a dedicated permission-update path if needed.
474    pub async fn update_role(
475        &self,
476        name: &str,
477        new_description: Option<&str>,
478        new_parent_id: Option<Option<&str>>,
479    ) -> Result<()> {
480        let role = self
481            .role_system
482            .get_role(name)
483            .await
484            .map_err(|e| AuthError::authorization(format!("Failed to look up role: {}", e)))?
485            .ok_or_else(|| AuthError::authorization(format!("Role '{}' not found", name)))?;
486
487        // Rebuild the role with the updated fields, preserving id and permissions
488        let mut updated = Role::with_id(role.id(), role.name());
489
490        let description = new_description
491            .map(|s| s.to_string())
492            .or_else(|| role.description().map(|s| s.to_string()));
493        if let Some(desc) = description {
494            updated = updated.with_description(desc);
495        }
496
497        // Carry over existing permissions
498        for perm in role.permissions().permissions() {
499            updated = updated.add_permission(perm.clone());
500        }
501
502        // Apply the update to underlying storage
503        let mut storage = self.storage_handle.lock().await;
504        storage
505            .update_role(updated)
506            .map_err(|e| AuthError::authorization(format!("Failed to update role: {}", e)))?;
507        drop(storage);
508
509        // Handle parent-role change if requested
510        if let Some(new_parent) = new_parent_id {
511            // Remove old inheritance if any parent was set
512            if let Some(old_parent) = role.parent_role_id() {
513                let _ = self
514                    .role_system
515                    .remove_role_inheritance(name, old_parent)
516                    .await;
517            }
518            if let Some(parent) = new_parent {
519                self.role_system
520                    .add_role_inheritance(name, parent)
521                    .await
522                    .map_err(|e| {
523                        AuthError::authorization(format!(
524                            "Failed to update role inheritance: {}",
525                            e
526                        ))
527                    })?;
528            }
529        }
530
531        info!("Role '{}' updated", name);
532        Ok(())
533    }
534
535    /// List all registered role names.
536    pub async fn list_roles(&self) -> Result<Vec<Role>> {
537        let storage = self.storage_handle.lock().await;
538        let names = storage
539            .list_roles()
540            .map_err(|e| AuthError::authorization(format!("Failed to list roles: {}", e)))?;
541        drop(storage);
542
543        // Fetch the full Role object for each name
544        let mut roles = Vec::with_capacity(names.len());
545        for name in names {
546            if let Ok(Some(role)) = self.role_system.get_role(&name).await {
547                roles.push(role);
548            }
549        }
550        Ok(roles)
551    }
552
553    /// Batch check multiple permissions
554    pub async fn batch_check_permissions(
555        &self,
556        user_id: &str,
557        checks: &[(String, String)], // (action, resource) pairs
558    ) -> Result<Vec<(String, String, bool)>> {
559        let subject = Subject::user(user_id);
560
561        let resource_checks: Vec<(String, Resource)> = checks
562            .iter()
563            .map(|(action, resource)| (action.clone(), Resource::new(resource, "api")))
564            .collect();
565
566        let results = self
567            .role_system
568            .batch_check_permissions(&subject, &resource_checks)
569            .await
570            .map_err(|e| {
571                AuthError::authorization(format!("Batch permission check failed: {}", e))
572            })?;
573
574        Ok(results
575            .into_iter()
576            .map(|(action, resource, granted)| {
577                (
578                    action,
579                    resource.name().unwrap_or("unknown").to_string(),
580                    granted,
581                )
582            })
583            .collect())
584    }
585}
586
587#[cfg(test)]
588mod tests {
589    use super::*;
590
591    #[tokio::test]
592    async fn test_authorization_service_creation() {
593        let service = AuthorizationService::new().await.unwrap();
594
595        // Test that standard roles were created
596        let roles = ["guest", "user", "moderator", "admin"];
597        for role_name in &roles {
598            let role = service.get_role(role_name).await.unwrap();
599            assert!(role.is_some(), "Role '{}' should exist", role_name);
600        }
601    }
602
603    #[tokio::test]
604    async fn test_role_assignment_and_permission_check() {
605        let service = AuthorizationService::new().await.unwrap();
606
607        // Assign user role
608        service.assign_role("test_user", "user").await.unwrap();
609
610        // Check permissions
611        let can_read_profile = service
612            .check_permission("test_user", "read", "profile", None)
613            .await
614            .unwrap();
615        assert!(can_read_profile, "User should have read access to profile");
616
617        let can_admin = service
618            .check_permission("test_user", "admin", "system", None)
619            .await
620            .unwrap();
621        assert!(!can_admin);
622    }
623
624    #[tokio::test]
625    async fn test_role_hierarchy() {
626        let service = AuthorizationService::new().await.unwrap();
627
628        // Assign admin role
629        service.assign_role("admin_user", "admin").await.unwrap();
630
631        // Admin should have user permissions through inheritance
632        let can_read_profile = service
633            .check_permission("admin_user", "read", "profile", None)
634            .await
635            .unwrap();
636        assert!(can_read_profile);
637
638        // Admin should have admin permissions
639        let can_admin = service
640            .check_permission("admin_user", "admin", "system", None)
641            .await
642            .unwrap();
643        assert!(can_admin);
644    }
645}