1#[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#[derive(Debug, Clone, PartialEq)]
48pub enum AccessResult {
49 Granted,
51 Denied(String),
53}
54
55impl AccessResult {
56 pub fn is_granted(&self) -> bool {
58 matches!(self, AccessResult::Granted)
59 }
60
61 pub fn is_denied(&self) -> bool {
63 !self.is_granted()
64 }
65
66 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#[derive(Debug, Clone)]
87pub struct RoleSystemConfig {
88 pub max_hierarchy_depth: usize,
90 pub enable_caching: bool,
92 pub cache_ttl_seconds: u64,
94 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, enable_audit: true,
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct UserPermissions {
112 pub computed_permissions: HashMap<String, AccessResult>,
114 pub last_updated: DateTime<Utc>,
116}
117
118impl UserPermissions {
119 pub fn new(permissions: HashMap<String, AccessResult>) -> Self {
121 Self {
122 computed_permissions: permissions,
123 last_updated: Utc::now(),
124 }
125 }
126
127 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
135pub struct RoleSystem<S = MemoryStorage>
137where
138 S: Storage,
139{
140 storage: S,
141 config: RoleSystemConfig,
142 role_hierarchy: DashMap<String, HashSet<String>>,
144 subject_roles: DashMap<String, HashSet<String>>,
146 role_elevations: DashMap<String, Vec<RoleElevation>>,
148 permission_cache: DashMap<(String, String), UserPermissions>,
150 metrics: Arc<RoleSystemMetrics>,
152}
153
154impl RoleSystem<MemoryStorage> {
155 pub fn new() -> Self {
157 Self::with_config(RoleSystemConfig::default())
158 }
159
160 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 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 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 pub fn get_role(&self, name: &str) -> Result<Option<Role>> {
218 self.storage.get_role(name)
219 }
220
221 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 self.clear_role_cache(&role_name);
233
234 #[cfg(feature = "audit")]
235 info!("Role '{role_name}' updated");
236
237 Ok(())
238 }
239
240 pub fn add_role_inheritance(&mut self, child: &str, parent: &str) -> Result<()> {
242 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 if self.would_create_cycle(child, parent)? {
252 return Err(Error::CircularDependency(child.to_string()));
253 }
254
255 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 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 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 self.clear_subject_cache(subject.id());
301
302 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 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 self.clear_subject_cache(subject.id());
327
328 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 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 self.clear_subject_cache(subject.id());
362
363 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 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 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 let context_hash = if context.is_empty() {
399 String::new()
400 } else {
401 let mut sorted_context: Vec<_> = context.iter().collect();
403 sorted_context.sort_by_key(|(k, _)| *k);
404 format!("{sorted_context:?}")
405 };
406
407 let permission_key = format!("{}:{}:{}", action, resource.id(), context_hash);
409 let cache_key = (subject.id().to_string(), permission_key);
410
411 if self.config.enable_caching {
413 if let Some(entry) = self.permission_cache.get(&cache_key) {
414 let permissions = entry.value();
415
416 let cache_still_valid = !permissions.is_expired(self.config.cache_ttl_seconds);
418
419 let elevations_still_valid = true;
422
423 if cache_still_valid && elevations_still_valid {
424 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 if self.config.enable_caching {
438 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 user_permissions
448 .computed_permissions
449 .insert(action.to_string(), result.into());
450 user_permissions.last_updated = Utc::now();
451
452 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 pub fn get_subject_roles(&self, subject: &Subject) -> Result<HashSet<String>> {
480 let mut all_roles = HashSet::new();
481
482 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 self.collect_inherited_roles(role, &mut all_roles, 0)?;
488 }
489 }
490
491 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 pub fn create_standard_roles(&mut self) -> Result<()> {
513 #[cfg(feature = "audit")]
514 info!("Creating standard roles: admin, editor, viewer, guest");
515
516 let admin_role = Role::new("admin")
518 .with_description("Full system access")
519 .add_permission(Permission::super_admin());
520
521 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 let viewer_role = Role::new("viewer")
531 .with_description("Read-only access")
532 .add_permission(Permission::new("read", "*"));
533
534 let guest_role = Role::new("guest")
536 .with_description("Limited read access")
537 .add_permission(Permission::new("read", "public"));
538
539 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 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 fn create_web_app_roles(&mut self) -> Result<()> {
564 #[cfg(feature = "audit")]
565 info!("Creating web application roles");
566
567 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 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 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 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 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 let consumer_role = Role::new("consumer")
611 .with_description("API consumer with read access")
612 .add_permission(Permission::new("read", "api"));
613
614 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 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 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 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 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 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 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 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 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 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 self.create_standard_roles()?;
707
708 Ok(())
709 }
710
711 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 let child_downward_depth = self.calculate_downward_depth(child)?;
762 let parent_upward_depth = self.calculate_upward_depth(parent)?;
764
765 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(()); }
793
794 *max_depth = std::cmp::max(*max_depth, current_depth);
795
796 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(()); }
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); }
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 self.permission_cache.clear();
906 }
907}
908
909impl<S: Storage> RoleSystem<S> {
910 pub fn storage(&self) -> &S {
912 &self.storage
913 }
914
915 pub fn subject_roles(&self) -> &DashMap<String, HashSet<String>> {
917 &self.subject_roles
918 }
919
920 pub fn role_hierarchy(&self) -> &DashMap<String, HashSet<String>> {
922 &self.role_hierarchy
923 }
924
925 pub fn config(&self) -> &RoleSystemConfig {
927 &self.config
928 }
929}
930
931impl<S: Storage> RoleSystem<S> {
933 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 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 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 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 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#[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}