role_system/
core.rs

1//! Core role system implementation.
2//!
3//! This module contains the main implementation of the role-based access control system.
4//! It provides the central `RoleSystem` struct which manages roles, permissions, subjects,
5//! and resources, along with all access control operations.
6//!
7//! # Architecture
8//!
9//! The role system is built around these key components:
10//!
11//! - **Roles**: Named entities with assigned permissions
12//! - **Permissions**: Grant access to perform actions on resource types
13//! - **Subjects**: Users, services, or other entities that are assigned roles
14//! - **Resources**: Objects that are protected by permissions
15//! - **Storage**: Backend for persisting roles and other entities
16//!
17//! # Thread Safety
18//!
19//! The implementation uses `DashMap` for concurrent access to internal data structures,
20//! making it thread-safe for use in multi-threaded applications.
21//!
22//! # Caching
23//!
24//! Permission checks are cached to improve performance for repeated access checks,
25//! with configurable cache TTL and invalidation on role changes.
26
27#[cfg(feature = "audit")]
28use log::{info, warn};
29
30use crate::{
31    app_type::ApplicationType,
32    error::{Error, Result},
33    metrics::{MetricsProvider, RoleSystemMetrics},
34    permission::Permission,
35    resource::Resource,
36    role::{Role, RoleElevation},
37    storage::{MemoryStorage, Storage},
38    subject::Subject,
39};
40use chrono::{DateTime, Utc};
41use dashmap::DashMap;
42use std::collections::{HashMap, HashSet};
43use std::sync::Arc;
44use std::time::{Duration, Instant};
45
46/// The result of an access check.
47#[derive(Debug, Clone, PartialEq)]
48pub enum AccessResult {
49    /// Access is granted.
50    Granted,
51    /// Access is denied with a reason.
52    Denied(String),
53}
54
55impl AccessResult {
56    /// Returns true if access was granted.
57    pub fn is_granted(&self) -> bool {
58        matches!(self, AccessResult::Granted)
59    }
60
61    /// Returns true if access was denied.
62    pub fn is_denied(&self) -> bool {
63        !self.is_granted()
64    }
65
66    /// Returns the denial reason if access was denied.
67    pub fn denial_reason(&self) -> Option<&str> {
68        match self {
69            AccessResult::Denied(reason) => Some(reason),
70            AccessResult::Granted => None,
71        }
72    }
73}
74
75impl From<bool> for AccessResult {
76    fn from(granted: bool) -> Self {
77        if granted {
78            AccessResult::Granted
79        } else {
80            AccessResult::Denied("Access denied".to_string())
81        }
82    }
83}
84
85/// Configuration for the role system.
86#[derive(Debug, Clone)]
87pub struct RoleSystemConfig {
88    /// Maximum depth for role hierarchy traversal.
89    pub max_hierarchy_depth: usize,
90    /// Whether to enable permission caching.
91    pub enable_caching: bool,
92    /// Cache TTL in seconds.
93    pub cache_ttl_seconds: u64,
94    /// Whether to enable audit logging.
95    pub enable_audit: bool,
96}
97
98impl Default for RoleSystemConfig {
99    fn default() -> Self {
100        Self {
101            max_hierarchy_depth: 10,
102            enable_caching: true,
103            cache_ttl_seconds: 300, // 5 minutes
104            enable_audit: true,
105        }
106    }
107}
108
109/// Cached permissions for a user with last updated timestamp.
110#[derive(Debug, Clone)]
111pub struct UserPermissions {
112    /// The computed permission results
113    pub computed_permissions: HashMap<String, AccessResult>,
114    /// When the permissions were last updated
115    pub last_updated: DateTime<Utc>,
116}
117
118impl UserPermissions {
119    /// Create new user permissions cache
120    pub fn new(permissions: HashMap<String, AccessResult>) -> Self {
121        Self {
122            computed_permissions: permissions,
123            last_updated: Utc::now(),
124        }
125    }
126
127    /// Check if the cache entry is expired based on TTL
128    pub fn is_expired(&self, ttl_seconds: u64) -> bool {
129        let now = Utc::now();
130        let ttl = chrono::Duration::seconds(ttl_seconds as i64);
131        now.signed_duration_since(self.last_updated) > ttl
132    }
133}
134
135/// The main role-based access control system.
136pub struct RoleSystem<S = MemoryStorage>
137where
138    S: Storage,
139{
140    storage: S,
141    config: RoleSystemConfig,
142    // Role hierarchy: child -> parents
143    role_hierarchy: DashMap<String, HashSet<String>>,
144    // Subject role assignments
145    subject_roles: DashMap<String, HashSet<String>>,
146    // Temporary role elevations
147    role_elevations: DashMap<String, Vec<RoleElevation>>,
148    // Permission cache: (subject_id, permission_key) -> (UserPermissions)
149    permission_cache: DashMap<(String, String), UserPermissions>,
150    // Metrics collection
151    metrics: Arc<RoleSystemMetrics>,
152}
153
154impl RoleSystem<MemoryStorage> {
155    /// Create a new role system with default configuration and memory storage.
156    pub fn new() -> Self {
157        Self::with_config(RoleSystemConfig::default())
158    }
159
160    /// Create a new role system with custom configuration and memory storage.
161    pub fn with_config(config: RoleSystemConfig) -> Self {
162        Self {
163            storage: MemoryStorage::new(),
164            config,
165            role_hierarchy: DashMap::new(),
166            subject_roles: DashMap::new(),
167            role_elevations: DashMap::new(),
168            permission_cache: DashMap::new(),
169            metrics: Arc::new(RoleSystemMetrics::new()),
170        }
171    }
172}
173
174impl<S> MetricsProvider for RoleSystem<S>
175where
176    S: Storage,
177{
178    fn metrics(&self) -> &RoleSystemMetrics {
179        &self.metrics
180    }
181}
182
183impl<S> RoleSystem<S>
184where
185    S: Storage,
186{
187    /// Create a new role system with custom storage.
188    pub fn with_storage(storage: S, config: RoleSystemConfig) -> Self {
189        Self {
190            storage,
191            config,
192            role_hierarchy: DashMap::new(),
193            subject_roles: DashMap::new(),
194            role_elevations: DashMap::new(),
195            permission_cache: DashMap::new(),
196            metrics: Arc::new(RoleSystemMetrics::new()),
197        }
198    }
199
200    /// Register a new role in the system.
201    pub fn register_role(&mut self, role: Role) -> Result<()> {
202        let role_name = role.name().to_string();
203
204        if self.storage.role_exists(&role_name)? {
205            return Err(Error::RoleAlreadyExists(role_name));
206        }
207
208        self.storage.store_role(role)?;
209
210        #[cfg(feature = "audit")]
211        info!("Role '{role_name}' registered");
212
213        Ok(())
214    }
215
216    /// Get a role by name.
217    pub fn get_role(&self, name: &str) -> Result<Option<Role>> {
218        self.storage.get_role(name)
219    }
220
221    /// Update an existing role.
222    pub fn update_role(&mut self, role: Role) -> Result<()> {
223        let role_name = role.name().to_string();
224
225        if !self.storage.role_exists(&role_name)? {
226            return Err(Error::RoleNotFound(role_name));
227        }
228
229        self.storage.update_role(role)?;
230
231        // Clear permission cache for all subjects with this role
232        self.clear_role_cache(&role_name);
233
234        #[cfg(feature = "audit")]
235        info!("Role '{role_name}' updated");
236
237        Ok(())
238    }
239
240    /// Add role inheritance (child inherits from parent).
241    pub fn add_role_inheritance(&mut self, child: &str, parent: &str) -> Result<()> {
242        // Check that both roles exist
243        if !self.storage.role_exists(child)? {
244            return Err(Error::RoleNotFound(child.to_string()));
245        }
246        if !self.storage.role_exists(parent)? {
247            return Err(Error::RoleNotFound(parent.to_string()));
248        }
249
250        // Check for circular dependencies
251        if self.would_create_cycle(child, parent)? {
252            return Err(Error::CircularDependency(child.to_string()));
253        }
254
255        // Check if adding this inheritance would exceed the maximum depth
256        if self.would_exceed_max_depth(child, parent)? {
257            return Err(Error::MaxDepthExceeded(self.config.max_hierarchy_depth));
258        }
259
260        self.role_hierarchy
261            .entry(child.to_string())
262            .or_default()
263            .insert(parent.to_string());
264
265        #[cfg(feature = "audit")]
266        info!("Role inheritance added: '{child}' inherits from '{parent}'");
267
268        Ok(())
269    }
270
271    /// Remove role inheritance.
272    pub fn remove_role_inheritance(&mut self, child: &str, parent: &str) -> Result<()> {
273        if let Some(mut parents) = self.role_hierarchy.get_mut(child) {
274            parents.remove(parent);
275            if parents.is_empty() {
276                drop(parents);
277                self.role_hierarchy.remove(child);
278            }
279        }
280
281        #[cfg(feature = "audit")]
282        info!("Role inheritance removed: '{child}' no longer inherits from '{parent}'");
283
284        Ok(())
285    }
286
287    /// Assign a role to a subject.
288    pub fn assign_role(&mut self, subject: &Subject, role_name: &str) -> Result<()> {
289        if !self.storage.role_exists(role_name)? {
290            self.metrics.record_error("RoleNotFound");
291            return Err(Error::RoleNotFound(role_name.to_string()));
292        }
293
294        self.subject_roles
295            .entry(subject.id().to_string())
296            .or_default()
297            .insert(role_name.to_string());
298
299        // Clear permission cache for this subject
300        self.clear_subject_cache(subject.id());
301
302        // Record metrics
303        self.metrics.record_role_assignment(subject.id());
304
305        #[cfg(feature = "audit")]
306        info!(
307            "Role '{}' assigned to subject '{}'",
308            role_name,
309            subject.id()
310        );
311
312        Ok(())
313    }
314
315    /// Remove a role from a subject.
316    pub fn remove_role(&mut self, subject: &Subject, role_name: &str) -> Result<()> {
317        if let Some(mut roles) = self.subject_roles.get_mut(subject.id()) {
318            roles.remove(role_name);
319            if roles.is_empty() {
320                drop(roles);
321                self.subject_roles.remove(subject.id());
322            }
323        }
324
325        // Clear permission cache for this subject
326        self.clear_subject_cache(subject.id());
327
328        // Record metrics
329        self.metrics.record_role_removal(subject.id());
330
331        #[cfg(feature = "audit")]
332        info!(
333            "Role '{}' removed from subject '{}'",
334            role_name,
335            subject.id()
336        );
337
338        Ok(())
339    }
340
341    /// Temporarily elevate a subject's role.
342    pub fn elevate_role(
343        &mut self,
344        subject: &Subject,
345        role_name: &str,
346        duration: Option<Duration>,
347    ) -> Result<()> {
348        if !self.storage.role_exists(role_name)? {
349            self.metrics.record_error("RoleNotFound");
350            return Err(Error::RoleNotFound(role_name.to_string()));
351        }
352
353        let elevation = RoleElevation::new(role_name.to_string(), duration);
354
355        self.role_elevations
356            .entry(subject.id().to_string())
357            .or_default()
358            .push(elevation);
359
360        // Clear permission cache for this subject
361        self.clear_subject_cache(subject.id());
362
363        // Record metrics
364        self.metrics.record_role_elevation(subject.id());
365
366        #[cfg(feature = "audit")]
367        info!(
368            "Role '{}' elevated for subject '{}' with duration {:?}",
369            role_name,
370            subject.id(),
371            duration
372        );
373
374        Ok(())
375    }
376
377    /// Check if a subject has a specific permission on a resource.
378    pub fn check_permission(
379        &self,
380        subject: &Subject,
381        action: &str,
382        resource: &Resource,
383    ) -> Result<bool> {
384        self.check_permission_with_context(subject, action, resource, &HashMap::new())
385    }
386
387    /// Check permission with additional context.
388    pub fn check_permission_with_context(
389        &self,
390        subject: &Subject,
391        action: &str,
392        resource: &Resource,
393        context: &HashMap<String, String>,
394    ) -> Result<bool> {
395        let _timer = self.start_timer("permission_check");
396
397        // Create cache key that includes context hash for conditional permissions
398        let context_hash = if context.is_empty() {
399            String::new()
400        } else {
401            // Create a simple hash of the context for caching
402            let mut sorted_context: Vec<_> = context.iter().collect();
403            sorted_context.sort_by_key(|(k, _)| *k);
404            format!("{sorted_context:?}")
405        };
406
407        // Simplified cache key
408        let permission_key = format!("{}:{}:{}", action, resource.id(), context_hash);
409        let cache_key = (subject.id().to_string(), permission_key);
410
411        // Check cache first
412        if self.config.enable_caching {
413            if let Some(entry) = self.permission_cache.get(&cache_key) {
414                let permissions = entry.value();
415
416                // Check if cache entry is still valid based on TTL
417                let cache_still_valid = !permissions.is_expired(self.config.cache_ttl_seconds);
418
419                // Additionally check if any role elevations have been applied since cache entry was created
420                // For simplicity, we assume all elevations are valid and don't check timestamps
421                let elevations_still_valid = true;
422
423                if cache_still_valid && elevations_still_valid {
424                    // Return the cached result for the specific permission
425                    if let Some(result) = permissions.computed_permissions.get(action) {
426                        self.metrics.record_cache_hit();
427                        return Ok(result.is_granted());
428                    }
429                }
430            }
431            self.metrics.record_cache_miss();
432        }
433
434        let result = self.check_permission_internal(subject, action, resource, context)?;
435
436        // Cache the result
437        if self.config.enable_caching {
438            // Get existing permissions or create new
439            let mut user_permissions = if let Some(existing) = self.permission_cache.get(&cache_key)
440            {
441                existing.value().clone()
442            } else {
443                UserPermissions::new(HashMap::new())
444            };
445
446            // Update the permission
447            user_permissions
448                .computed_permissions
449                .insert(action.to_string(), result.into());
450            user_permissions.last_updated = Utc::now();
451
452            // Insert back into cache
453            self.permission_cache.insert(cache_key, user_permissions);
454        }
455
456        #[cfg(feature = "audit")]
457        {
458            let granted = result;
459            if granted {
460                info!(
461                    "Permission GRANTED for subject '{}', action '{}', resource '{}'",
462                    subject.id(),
463                    action,
464                    resource.id()
465                );
466            } else {
467                warn!(
468                    "Permission DENIED for subject '{}', action '{}', resource '{}'",
469                    subject.id(),
470                    action,
471                    resource.id()
472                );
473            }
474        }
475
476        Ok(result)
477    }
478    /// Get all roles assigned to a subject (including inherited roles).
479    pub fn get_subject_roles(&self, subject: &Subject) -> Result<HashSet<String>> {
480        let mut all_roles = HashSet::new();
481
482        // Get directly assigned roles
483        if let Some(direct_roles) = self.subject_roles.get(subject.id()) {
484            for role in direct_roles.iter() {
485                all_roles.insert(role.clone());
486                // Get inherited roles
487                self.collect_inherited_roles(role, &mut all_roles, 0)?;
488            }
489        }
490
491        // Get elevated roles
492        if let Some(elevations) = self.role_elevations.get(subject.id()) {
493            let now = Instant::now();
494            for elevation in elevations.iter() {
495                if !elevation.is_expired(now) {
496                    all_roles.insert(elevation.role_name().to_string());
497                    self.collect_inherited_roles(elevation.role_name(), &mut all_roles, 0)?;
498                }
499            }
500        }
501
502        Ok(all_roles)
503    }
504
505    /// Creates standard roles commonly used in applications.
506    ///
507    /// This method creates the following roles:
508    /// - admin: Full system access
509    /// - editor: Create and edit content
510    /// - viewer: Read-only access
511    /// - guest: Limited read access
512    pub fn create_standard_roles(&mut self) -> Result<()> {
513        #[cfg(feature = "audit")]
514        info!("Creating standard roles: admin, editor, viewer, guest");
515
516        // Admin role with full access
517        let admin_role = Role::new("admin")
518            .with_description("Full system access")
519            .add_permission(Permission::super_admin());
520
521        // Editor role with edit permissions
522        let editor_role = Role::new("editor")
523            .with_description("Create and edit content")
524            .add_permission(Permission::new("create", "*"))
525            .add_permission(Permission::new("read", "*"))
526            .add_permission(Permission::new("update", "*"))
527            .add_permission(Permission::new("delete", "*"));
528
529        // Viewer role with read-only permissions
530        let viewer_role = Role::new("viewer")
531            .with_description("Read-only access")
532            .add_permission(Permission::new("read", "*"));
533
534        // Guest role with limited read permissions
535        let guest_role = Role::new("guest")
536            .with_description("Limited read access")
537            .add_permission(Permission::new("read", "public"));
538
539        // Register the roles
540        self.register_role(admin_role)?;
541        self.register_role(editor_role)?;
542        self.register_role(viewer_role)?;
543        self.register_role(guest_role)?;
544
545        Ok(())
546    }
547    // ApplicationType enum is defined at the module level
548
549    /// Creates roles appropriate for specific application types.
550    pub fn create_application_roles(&mut self, app_type: ApplicationType) -> Result<()> {
551        match app_type {
552            ApplicationType::WebApp => self.create_web_app_roles()?,
553            ApplicationType::ApiService => self.create_api_service_roles()?,
554            ApplicationType::Cms => self.create_cms_roles()?,
555            ApplicationType::Ecommerce => self.create_ecommerce_roles()?,
556            ApplicationType::AdminDashboard => self.create_admin_dashboard_roles()?,
557        }
558
559        Ok(())
560    }
561
562    // Private helper methods for specific application types
563    fn create_web_app_roles(&mut self) -> Result<()> {
564        #[cfg(feature = "audit")]
565        info!("Creating web application roles");
566
567        // User role
568        let user_role = Role::new("user")
569            .with_description("Standard authenticated user")
570            .add_permission(Permission::new("read", "content"))
571            .add_permission(Permission::new("read", "profile"))
572            .add_permission(Permission::new("update", "profile:self"));
573
574        // Premium user role
575        let premium_role = Role::new("premium_user")
576            .with_description("Premium tier user with extra access")
577            .add_permission(Permission::new("read", "content"))
578            .add_permission(Permission::new("read", "profile"))
579            .add_permission(Permission::new("update", "profile:self"))
580            .add_permission(Permission::new("read", "premium_content"));
581
582        // Moderator role
583        let moderator_role = Role::new("moderator")
584            .with_description("Content moderation capabilities")
585            .add_permission(Permission::new("read", "*"))
586            .add_permission(Permission::new("update", "content"))
587            .add_permission(Permission::new("delete", "content"));
588
589        self.register_role(user_role)?;
590        self.register_role(premium_role)?;
591        self.register_role(moderator_role)?;
592
593        // Also create standard roles
594        self.create_standard_roles()?;
595
596        Ok(())
597    }
598
599    fn create_api_service_roles(&mut self) -> Result<()> {
600        #[cfg(feature = "audit")]
601        info!("Creating API service roles");
602
603        // Service role
604        let service_role = Role::new("service")
605            .with_description("Service-to-service API access")
606            .add_permission(Permission::new("read", "api"))
607            .add_permission(Permission::new("create", "api"));
608
609        // Consumer role
610        let consumer_role = Role::new("consumer")
611            .with_description("API consumer with read access")
612            .add_permission(Permission::new("read", "api"));
613
614        // System role
615        let system_role = Role::new("system")
616            .with_description("Internal system operations")
617            .add_permission(Permission::new("*", "system"));
618
619        self.register_role(service_role)?;
620        self.register_role(consumer_role)?;
621        self.register_role(system_role)?;
622
623        // Also create standard roles
624        self.create_standard_roles()?;
625
626        Ok(())
627    }
628
629    fn create_cms_roles(&mut self) -> Result<()> {
630        #[cfg(feature = "audit")]
631        info!("Creating CMS roles");
632
633        // Author role
634        let author_role = Role::new("author")
635            .with_description("Content creator")
636            .add_permission(Permission::new("create", "content"))
637            .add_permission(Permission::new("read", "content"))
638            .add_permission(Permission::new("update", "content:own"))
639            .add_permission(Permission::new("delete", "content:own"));
640
641        // Publisher role
642        let publisher_role = Role::new("publisher")
643            .with_description("Content publisher")
644            .add_permission(Permission::new("read", "content"))
645            .add_permission(Permission::new("update", "content"))
646            .add_permission(Permission::new("publish", "content"));
647
648        self.register_role(author_role)?;
649        self.register_role(publisher_role)?;
650
651        // Also create standard roles
652        self.create_standard_roles()?;
653
654        Ok(())
655    }
656
657    fn create_ecommerce_roles(&mut self) -> Result<()> {
658        #[cfg(feature = "audit")]
659        info!("Creating e-commerce roles");
660
661        // Customer role
662        let customer_role = Role::new("customer")
663            .with_description("Shopping customer")
664            .add_permission(Permission::new("read", "product"))
665            .add_permission(Permission::new("create", "order"))
666            .add_permission(Permission::new("read", "order:own"));
667
668        // Vendor role
669        let vendor_role = Role::new("vendor")
670            .with_description("Product vendor")
671            .add_permission(Permission::new("create", "product"))
672            .add_permission(Permission::new("read", "product:own"))
673            .add_permission(Permission::new("update", "product:own"))
674            .add_permission(Permission::new("delete", "product:own"))
675            .add_permission(Permission::new("read", "order:for_products"));
676
677        self.register_role(customer_role)?;
678        self.register_role(vendor_role)?;
679
680        // Also create standard roles
681        self.create_standard_roles()?;
682
683        Ok(())
684    }
685
686    fn create_admin_dashboard_roles(&mut self) -> Result<()> {
687        #[cfg(feature = "audit")]
688        info!("Creating admin dashboard roles");
689
690        // Support role
691        let support_role = Role::new("support")
692            .with_description("Customer support staff")
693            .add_permission(Permission::new("read", "*"))
694            .add_permission(Permission::new("update", "user:status"));
695
696        // Analytics role
697        let analytics_role = Role::new("analytics")
698            .with_description("Data analysis")
699            .add_permission(Permission::new("read", "*"))
700            .add_permission(Permission::new("export", "data"));
701
702        self.register_role(support_role)?;
703        self.register_role(analytics_role)?;
704
705        // Also create standard roles
706        self.create_standard_roles()?;
707
708        Ok(())
709    }
710
711    // Internal implementation
712
713    fn check_permission_internal(
714        &self,
715        subject: &Subject,
716        action: &str,
717        resource: &Resource,
718        context: &HashMap<String, String>,
719    ) -> Result<bool> {
720        let subject_roles = self.get_subject_roles(subject)?;
721
722        for role_name in subject_roles {
723            if let Some(role) = self.storage.get_role(&role_name)?
724                && role.has_permission(action, resource.resource_type(), context)
725            {
726                return Ok(true);
727            }
728        }
729
730        Ok(false)
731    }
732
733    fn collect_inherited_roles(
734        &self,
735        role_name: &str,
736        collected: &mut HashSet<String>,
737        depth: usize,
738    ) -> Result<()> {
739        if depth >= self.config.max_hierarchy_depth {
740            return Err(Error::MaxDepthExceeded(self.config.max_hierarchy_depth));
741        }
742
743        if let Some(parents) = self.role_hierarchy.get(role_name) {
744            for parent in parents.iter() {
745                if collected.insert(parent.clone()) {
746                    self.collect_inherited_roles(parent, collected, depth + 1)?;
747                }
748            }
749        }
750
751        Ok(())
752    }
753
754    fn would_create_cycle(&self, child: &str, parent: &str) -> Result<bool> {
755        let mut visited = HashSet::new();
756        self.has_path(parent, child, &mut visited, 0)
757    }
758
759    fn would_exceed_max_depth(&self, child: &str, parent: &str) -> Result<bool> {
760        // Calculate the depth from the child downwards (how many levels inherit from child)
761        let child_downward_depth = self.calculate_downward_depth(child)?;
762        // Calculate the depth from the parent upwards (how many levels parent inherits from)
763        let parent_upward_depth = self.calculate_upward_depth(parent)?;
764
765        // If child inherits from parent, the total depth would be:
766        // parent_upward_depth + 1 (for the new link) + child_downward_depth
767        let total_depth = parent_upward_depth + 1 + child_downward_depth;
768
769        Ok(total_depth > self.config.max_hierarchy_depth)
770    }
771
772    fn calculate_downward_depth(&self, role_name: &str) -> Result<usize> {
773        let mut max_depth = 0;
774        let mut visited = HashSet::new();
775        self.calculate_downward_depth_recursive(role_name, &mut visited, 0, &mut max_depth)?;
776        Ok(max_depth)
777    }
778
779    fn calculate_downward_depth_recursive(
780        &self,
781        role_name: &str,
782        visited: &mut HashSet<String>,
783        current_depth: usize,
784        max_depth: &mut usize,
785    ) -> Result<()> {
786        if current_depth > self.config.max_hierarchy_depth {
787            return Err(Error::MaxDepthExceeded(self.config.max_hierarchy_depth));
788        }
789
790        if !visited.insert(role_name.to_string()) {
791            return Ok(()); // Already visited
792        }
793
794        *max_depth = std::cmp::max(*max_depth, current_depth);
795
796        // Find all roles that inherit from this role
797        for entry in self.role_hierarchy.iter() {
798            let (child, parents) = (entry.key(), entry.value());
799            if parents.contains(role_name) {
800                self.calculate_downward_depth_recursive(
801                    child,
802                    visited,
803                    current_depth + 1,
804                    max_depth,
805                )?;
806            }
807        }
808
809        Ok(())
810    }
811
812    fn calculate_upward_depth(&self, role_name: &str) -> Result<usize> {
813        let mut max_depth = 0;
814        let mut visited = HashSet::new();
815        self.calculate_upward_depth_recursive(role_name, &mut visited, 0, &mut max_depth)?;
816        Ok(max_depth)
817    }
818
819    fn calculate_upward_depth_recursive(
820        &self,
821        role_name: &str,
822        visited: &mut HashSet<String>,
823        current_depth: usize,
824        max_depth: &mut usize,
825    ) -> Result<()> {
826        if current_depth > self.config.max_hierarchy_depth {
827            return Err(Error::MaxDepthExceeded(self.config.max_hierarchy_depth));
828        }
829
830        if !visited.insert(role_name.to_string()) {
831            return Ok(()); // Already visited
832        }
833
834        *max_depth = std::cmp::max(*max_depth, current_depth);
835
836        if let Some(parents) = self.role_hierarchy.get(role_name) {
837            for parent in parents.iter() {
838                self.calculate_upward_depth_recursive(
839                    parent,
840                    visited,
841                    current_depth + 1,
842                    max_depth,
843                )?;
844            }
845        }
846
847        Ok(())
848    }
849
850    fn has_path(
851        &self,
852        from: &str,
853        to: &str,
854        visited: &mut HashSet<String>,
855        depth: usize,
856    ) -> Result<bool> {
857        if depth >= self.config.max_hierarchy_depth {
858            return Err(Error::MaxDepthExceeded(self.config.max_hierarchy_depth));
859        }
860
861        if from == to {
862            return Ok(true);
863        }
864
865        if !visited.insert(from.to_string()) {
866            return Ok(false); // Already visited
867        }
868
869        if let Some(parents) = self.role_hierarchy.get(from) {
870            for parent in parents.iter() {
871                if self.has_path(parent, to, visited, depth + 1)? {
872                    return Ok(true);
873                }
874            }
875        }
876
877        Ok(false)
878    }
879
880    fn clear_subject_cache(&self, subject_id: &str) {
881        if !self.config.enable_caching {
882            return;
883        }
884
885        let keys_to_remove: Vec<_> = self
886            .permission_cache
887            .iter()
888            .filter(|entry| entry.key().0 == subject_id)
889            .map(|entry| entry.key().clone())
890            .collect();
891
892        for key in keys_to_remove {
893            self.permission_cache.remove(&key);
894        }
895    }
896
897    fn clear_role_cache(&self, _role_name: &str) {
898        if !self.config.enable_caching {
899            return;
900        }
901
902        // We need to clear cache for all subjects that have this role
903        // This is a simplified approach - in a real implementation you might
904        // want to track role assignments more efficiently
905        self.permission_cache.clear();
906    }
907}
908
909impl<S: Storage> RoleSystem<S> {
910    /// Get access to the internal storage backend (for query operations).
911    pub fn storage(&self) -> &S {
912        &self.storage
913    }
914
915    /// Get access to the subject roles mapping (for query operations).
916    pub fn subject_roles(&self) -> &DashMap<String, HashSet<String>> {
917        &self.subject_roles
918    }
919
920    /// Get access to the role hierarchy mapping (for query operations).
921    pub fn role_hierarchy(&self) -> &DashMap<String, HashSet<String>> {
922        &self.role_hierarchy
923    }
924
925    /// Get access to the configuration (for query operations).
926    pub fn config(&self) -> &RoleSystemConfig {
927        &self.config
928    }
929}
930
931// Enhanced API Extensions
932impl<S: Storage> RoleSystem<S> {
933    /// Assign multiple roles to a subject in a single operation.
934    pub fn assign_roles<I>(&mut self, subject: &Subject, roles: I) -> Result<()>
935    where
936        I: IntoIterator,
937        I::Item: AsRef<str>,
938    {
939        for role in roles {
940            self.assign_role(subject, role.as_ref())?;
941        }
942        Ok(())
943    }
944
945    /// Remove multiple roles from a subject in a single operation.
946    pub fn remove_roles<I>(&mut self, subject: &Subject, roles: I) -> Result<()>
947    where
948        I: IntoIterator,
949        I::Item: AsRef<str>,
950    {
951        for role in roles {
952            self.remove_role(subject, role.as_ref())?;
953        }
954        Ok(())
955    }
956
957    /// Check multiple permissions for a subject in a single operation.
958    pub fn check_permissions_batch(
959        &self,
960        subject: &Subject,
961        permissions: &[(&str, &Resource)],
962    ) -> Result<Vec<(String, String, bool)>> {
963        permissions
964            .iter()
965            .map(|(action, resource)| {
966                let result = self.check_permission(subject, action, resource)?;
967                Ok((action.to_string(), resource.id().to_string(), result))
968            })
969            .collect()
970    }
971
972    /// Bulk role assignment with validation.
973    pub fn bulk_assign_roles(
974        &mut self,
975        assignments: &[(Subject, Vec<String>)],
976    ) -> Result<Vec<Result<()>>> {
977        let mut results = Vec::new();
978
979        for (subject, roles) in assignments {
980            let role_refs: Vec<&str> = roles.iter().map(|s| s.as_str()).collect();
981            let result = self.assign_roles(subject, role_refs);
982            results.push(result);
983        }
984
985        Ok(results)
986    }
987
988    /// Get detailed permission summary for a subject.
989    pub fn get_permission_summary(&self, subject: &Subject) -> Result<PermissionSummary> {
990        let roles = self.get_subject_roles(subject)?;
991        let mut permissions = Vec::new();
992
993        for role_name in &roles {
994            if let Some(role) = self.storage.get_role(role_name)? {
995                for permission in role.permissions().permissions() {
996                    permissions.push(permission.clone());
997                }
998            }
999        }
1000
1001        let effective_permissions_count = permissions.len();
1002
1003        Ok(PermissionSummary {
1004            subject_id: subject.id().to_string(),
1005            roles: roles.into_iter().collect(),
1006            permissions,
1007            effective_permissions_count,
1008        })
1009    }
1010}
1011
1012/// Summary of a subject's permissions and roles.
1013#[derive(Debug, Clone)]
1014pub struct PermissionSummary {
1015    pub subject_id: String,
1016    pub roles: Vec<String>,
1017    pub permissions: Vec<Permission>,
1018    pub effective_permissions_count: usize,
1019}
1020
1021impl<S> Default for RoleSystem<S>
1022where
1023    S: Storage + Default,
1024{
1025    fn default() -> Self {
1026        Self::with_storage(S::default(), RoleSystemConfig::default())
1027    }
1028}