1use crate::{
4 error::{Error, Result},
5 permission::{Permission, PermissionSet},
6};
7use std::{
8 collections::HashMap,
9 time::{Duration, Instant},
10};
11use uuid::Uuid;
12
13#[derive(Debug, Clone)]
15#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
16pub struct RoleHierarchy {
17 parent_map: HashMap<String, String>,
19 children_map: HashMap<String, Vec<String>>,
21 roles: HashMap<String, Role>,
23}
24
25impl RoleHierarchy {
26 pub fn new() -> Self {
28 Self {
29 parent_map: HashMap::new(),
30 children_map: HashMap::new(),
31 roles: HashMap::new(),
32 }
33 }
34
35 pub fn add_role(&mut self, role: Role) -> Result<()> {
37 let role_id = role.id().to_string();
38 self.roles.insert(role_id, role);
39 Ok(())
40 }
41
42 pub fn set_parent(&mut self, child_id: &str, parent_id: &str) -> Result<()> {
44 if !self.roles.contains_key(child_id) {
46 return Err(Error::RoleNotFound(child_id.to_string()));
47 }
48 if !self.roles.contains_key(parent_id) {
49 return Err(Error::RoleNotFound(parent_id.to_string()));
50 }
51
52 if self.would_create_cycle(child_id, parent_id) {
54 return Err(Error::CircularDependency(format!(
55 "{} -> {}",
56 child_id, parent_id
57 )));
58 }
59
60 if let Some(old_parent) = self.parent_map.remove(child_id)
62 && let Some(siblings) = self.children_map.get_mut(&old_parent)
63 {
64 siblings.retain(|id| id != child_id);
65 }
66
67 self.parent_map
69 .insert(child_id.to_string(), parent_id.to_string());
70 self.children_map
71 .entry(parent_id.to_string())
72 .or_default()
73 .push(child_id.to_string());
74
75 Ok(())
76 }
77
78 pub fn get_parent(&self, role_id: &str) -> Option<&Role> {
80 self.parent_map
81 .get(role_id)
82 .and_then(|parent_id| self.roles.get(parent_id))
83 }
84
85 pub fn get_children(&self, role_id: &str) -> Vec<&Role> {
87 self.children_map
88 .get(role_id)
89 .map(|child_ids| {
90 child_ids
91 .iter()
92 .filter_map(|id| self.roles.get(id))
93 .collect()
94 })
95 .unwrap_or_default()
96 }
97
98 pub fn get_ancestors(&self, role_id: &str) -> Vec<&Role> {
100 let mut ancestors = Vec::new();
101 let mut current = role_id;
102
103 while let Some(parent_id) = self.parent_map.get(current) {
104 if let Some(parent_role) = self.roles.get(parent_id) {
105 ancestors.push(parent_role);
106 current = parent_id;
107 } else {
108 break;
109 }
110 }
111
112 ancestors
113 }
114
115 pub fn get_effective_permissions(&self, role_id: &str) -> Result<PermissionSet> {
117 let role = self
118 .roles
119 .get(role_id)
120 .ok_or_else(|| Error::RoleNotFound(role_id.to_string()))?;
121
122 let mut permissions = role.permissions().clone();
123
124 for ancestor in self.get_ancestors(role_id) {
126 for permission in ancestor.permissions().permissions() {
127 permissions.add(permission.clone());
128 }
129 }
130
131 Ok(permissions)
132 }
133
134 pub fn has_permission(
136 &self,
137 role_id: &str,
138 action: &str,
139 resource: &str,
140 context: &HashMap<String, String>,
141 ) -> Result<bool> {
142 let effective_permissions = self.get_effective_permissions(role_id)?;
143 Ok(effective_permissions.grants(action, resource, context))
144 }
145
146 pub fn get_role(&self, role_id: &str) -> Option<&Role> {
148 self.roles.get(role_id)
149 }
150
151 pub fn get_all_roles(&self) -> Vec<&Role> {
153 self.roles.values().collect()
154 }
155
156 pub fn remove_role(&mut self, role_id: &str) -> Result<()> {
158 if self.roles.remove(role_id).is_none() {
160 return Err(Error::RoleNotFound(role_id.to_string()));
161 }
162
163 if let Some(parent_id) = self.parent_map.remove(role_id)
165 && let Some(siblings) = self.children_map.get_mut(&parent_id)
166 {
167 siblings.retain(|id| id != role_id);
168 }
169
170 if let Some(child_ids) = self.children_map.remove(role_id) {
172 for child_id in child_ids {
173 self.parent_map.remove(&child_id);
174 }
175 }
176
177 Ok(())
178 }
179
180 fn would_create_cycle(&self, child_id: &str, proposed_parent_id: &str) -> bool {
182 if child_id == proposed_parent_id {
184 return true;
185 }
186
187 let mut current = proposed_parent_id;
189 while let Some(parent_id) = self.parent_map.get(current) {
190 if parent_id == child_id {
191 return true;
192 }
193 current = parent_id;
194 }
195
196 false
197 }
198}
199
200impl Default for RoleHierarchy {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206#[derive(Debug, Clone)]
208#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
209pub struct Role {
210 id: String,
212 name: String,
214 description: Option<String>,
216 permissions: PermissionSet,
218 metadata: HashMap<String, String>,
220 active: bool,
222}
223
224impl Role {
225 pub fn new(name: impl Into<String>) -> Self {
227 let name = name.into();
228 Self {
229 id: Uuid::new_v4().to_string(),
230 name,
231 description: None,
232 permissions: PermissionSet::new(),
233 metadata: HashMap::new(),
234 active: true,
235 }
236 }
237
238 pub fn with_id(id: impl Into<String>, name: impl Into<String>) -> Self {
240 let mut role = Self::new(name);
241 role.id = id.into();
242 role
243 }
244
245 pub fn id(&self) -> &str {
247 &self.id
248 }
249
250 pub fn name(&self) -> &str {
252 &self.name
253 }
254
255 pub fn with_description(mut self, description: impl Into<String>) -> Self {
257 self.description = Some(description.into());
258 self
259 }
260
261 pub fn description(&self) -> Option<&str> {
263 self.description.as_deref()
264 }
265
266 pub fn add_permission(mut self, permission: Permission) -> Self {
268 self.permissions.add(permission);
269 self
270 }
271
272 pub fn add_permissions(mut self, permissions: impl IntoIterator<Item = Permission>) -> Self {
274 for permission in permissions {
275 self.permissions.add(permission);
276 }
277 self
278 }
279
280 pub fn remove_permission(&mut self, permission: &Permission) {
282 self.permissions.remove(permission);
283 }
284
285 pub fn has_permission_exact(&self, permission: &Permission) -> bool {
287 self.permissions.contains(permission)
288 }
289
290 pub fn has_permission(
292 &self,
293 action: &str,
294 resource_type: &str,
295 context: &HashMap<String, String>,
296 ) -> bool {
297 if !self.active {
298 return false;
299 }
300 self.permissions.grants(action, resource_type, context)
301 }
302
303 pub fn permissions(&self) -> &PermissionSet {
305 &self.permissions
306 }
307
308 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
310 self.metadata.insert(key.into(), value.into());
311 self
312 }
313
314 pub fn metadata(&self, key: &str) -> Option<&str> {
316 self.metadata.get(key).map(|s| s.as_str())
317 }
318
319 pub fn all_metadata(&self) -> &HashMap<String, String> {
321 &self.metadata
322 }
323
324 pub fn set_active(&mut self, active: bool) {
326 self.active = active;
327 }
328
329 pub fn is_active(&self) -> bool {
331 self.active
332 }
333
334 pub fn deactivate(mut self) -> Self {
336 self.active = false;
337 self
338 }
339
340 pub fn merge_permissions(&mut self, other: &Role) {
342 for permission in other.permissions() {
343 self.permissions.add(permission.clone());
344 }
345 }
346
347 pub fn parent_role_id(&self) -> Option<&str> {
374 None
378 }
379
380 pub fn child_role_ids(&self) -> Vec<&str> {
406 Vec::new()
410 }
411
412 pub fn is_root_role(&self) -> bool {
432 self.parent_role_id().is_none()
433 }
434
435 pub fn is_leaf_role(&self) -> bool {
454 self.child_role_ids().is_empty()
455 }
456
457 pub fn hierarchy_depth(&self) -> usize {
476 0
477 }
478
479 pub fn hierarchy_metadata(&self) -> HashMap<String, String> {
502 let mut metadata = HashMap::new();
503 metadata.insert("depth".to_string(), "0".to_string());
504 metadata.insert("parent_count".to_string(), "0".to_string());
505 metadata.insert("child_count".to_string(), "0".to_string());
506 metadata.insert("descendant_count".to_string(), "0".to_string());
507 metadata.insert("is_root".to_string(), "true".to_string());
508 metadata.insert("is_leaf".to_string(), "true".to_string());
509 metadata
510 }
511}
512
513#[derive(Debug, Clone)]
515#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
516pub struct RoleElevation {
517 role_name: String,
519 #[cfg_attr(feature = "persistence", serde(with = "instant_serde"))]
521 created_at: Instant,
522 duration: Option<Duration>,
524 reason: Option<String>,
526}
527
528impl RoleElevation {
529 pub fn new(role_name: String, duration: Option<Duration>) -> Self {
531 Self {
532 role_name,
533 created_at: Instant::now(),
534 duration,
535 reason: None,
536 }
537 }
538
539 pub fn with_reason(role_name: String, duration: Option<Duration>, reason: String) -> Self {
541 Self {
542 role_name,
543 created_at: Instant::now(),
544 duration,
545 reason: Some(reason),
546 }
547 }
548
549 pub fn role_name(&self) -> &str {
551 &self.role_name
552 }
553
554 pub fn created_at(&self) -> Instant {
556 self.created_at
557 }
558
559 pub fn duration(&self) -> Option<Duration> {
561 self.duration
562 }
563
564 pub fn reason(&self) -> Option<&str> {
566 self.reason.as_deref()
567 }
568
569 pub fn is_expired(&self, now: Instant) -> bool {
571 if let Some(duration) = self.duration {
572 now.duration_since(self.created_at) > duration
573 } else {
574 false }
576 }
577
578 pub fn time_remaining(&self, now: Instant) -> Option<Duration> {
580 if let Some(duration) = self.duration {
581 let elapsed = now.duration_since(self.created_at);
582 if elapsed < duration {
583 Some(duration - elapsed)
584 } else {
585 Some(Duration::ZERO)
586 }
587 } else {
588 None }
590 }
591}
592
593#[derive(Debug, Default)]
595pub struct RoleBuilder {
596 name: Option<String>,
597 description: Option<String>,
598 permissions: Vec<Permission>,
599 metadata: HashMap<String, String>,
600 active: bool,
601}
602
603impl RoleBuilder {
604 pub fn new() -> Self {
606 Self {
607 active: true,
608 ..Default::default()
609 }
610 }
611
612 pub fn name(mut self, name: impl Into<String>) -> Self {
614 self.name = Some(name.into());
615 self
616 }
617
618 pub fn description(mut self, description: impl Into<String>) -> Self {
620 self.description = Some(description.into());
621 self
622 }
623
624 pub fn permission(mut self, permission: Permission) -> Self {
626 self.permissions.push(permission);
627 self
628 }
629
630 pub fn permissions(mut self, permissions: impl IntoIterator<Item = Permission>) -> Self {
632 self.permissions.extend(permissions);
633 self
634 }
635
636 pub fn allow<I, S>(mut self, resource: impl Into<String>, actions: I) -> Self
648 where
649 I: IntoIterator<Item = S>,
650 S: Into<String>,
651 {
652 let resource = resource.into();
653 for action in actions {
654 self.permissions
655 .push(Permission::new(action.into(), resource.clone()));
656 }
657 self
658 }
659
660 pub fn deny<I, S>(mut self, resource: impl Into<String>, actions: I) -> Self
672 where
673 I: IntoIterator<Item = S>,
674 S: Into<String>,
675 {
676 let resource = resource.into();
677 for action in actions {
678 let deny_permission =
680 Permission::with_condition(action.into(), resource.clone(), |_| false);
681 self.permissions.push(deny_permission);
682 }
683 self
684 }
685
686 pub fn allow_when<I, S, F>(
698 mut self,
699 resource: impl Into<String>,
700 actions: I,
701 condition: F,
702 ) -> Self
703 where
704 I: IntoIterator<Item = S>,
705 S: Into<String>,
706 F: Fn(&std::collections::HashMap<String, String>) -> bool + Send + Sync + 'static + Clone,
707 {
708 let resource = resource.into();
709 for action in actions {
710 let conditional_permission =
711 Permission::with_condition(action.into(), resource.clone(), condition.clone());
712 self.permissions.push(conditional_permission);
713 }
714 self
715 }
716
717 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
719 self.metadata.insert(key.into(), value.into());
720 self
721 }
722
723 pub fn active(mut self, active: bool) -> Self {
725 self.active = active;
726 self
727 }
728
729 pub fn build(self) -> Result<Role> {
731 let name = self
732 .name
733 .ok_or_else(|| Error::InvalidConfiguration("Role name is required".to_string()))?;
734
735 let mut role = Role::new(name);
736
737 if let Some(description) = self.description {
738 role = role.with_description(description);
739 }
740
741 for permission in self.permissions {
742 role = role.add_permission(permission);
743 }
744
745 for (key, value) in self.metadata {
746 role = role.with_metadata(key, value);
747 }
748
749 role.set_active(self.active);
750
751 Ok(role)
752 }
753}
754
755#[cfg(feature = "persistence")]
757mod instant_serde {
758 use serde::{Deserialize, Deserializer, Serialize, Serializer};
759 use std::time::{Instant, SystemTime, UNIX_EPOCH};
760
761 pub fn serialize<S>(_instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
762 where
763 S: Serializer,
764 {
765 let duration_since_epoch = SystemTime::now()
768 .duration_since(UNIX_EPOCH)
769 .unwrap()
770 .as_nanos();
771 duration_since_epoch.serialize(serializer)
772 }
773
774 pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
775 where
776 D: Deserializer<'de>,
777 {
778 let _nanos = u128::deserialize(deserializer)?;
779 Ok(Instant::now())
782 }
783}
784
785#[cfg(test)]
786mod tests {
787 use super::*;
788 use crate::permission::Permission;
789
790 #[test]
791 fn test_role_creation() {
792 let role = Role::new("admin")
793 .with_description("Administrator role")
794 .add_permission(Permission::new("read", "documents"))
795 .add_permission(Permission::new("write", "documents"));
796
797 assert_eq!(role.name(), "admin");
798 assert_eq!(role.description(), Some("Administrator role"));
799 assert_eq!(role.permissions().len(), 2);
800 assert!(role.is_active());
801 }
802
803 #[test]
804 fn test_role_permissions() {
805 let role = Role::new("reader").add_permission(Permission::new("read", "documents"));
806
807 let context = HashMap::new();
808 assert!(role.has_permission("read", "documents", &context));
809 assert!(!role.has_permission("write", "documents", &context));
810 }
811
812 #[test]
813 fn test_role_builder() {
814 let role = RoleBuilder::new()
815 .name("test-role")
816 .description("A test role")
817 .permission(Permission::new("read", "documents"))
818 .metadata("department", "IT")
819 .active(true)
820 .build()
821 .unwrap();
822
823 assert_eq!(role.name(), "test-role");
824 assert_eq!(role.description(), Some("A test role"));
825 assert_eq!(role.metadata("department"), Some("IT"));
826 assert!(role.is_active());
827 }
828
829 #[test]
830 fn test_role_elevation() {
831 let elevation = RoleElevation::new("admin".to_string(), Some(Duration::from_secs(3600)));
832
833 assert_eq!(elevation.role_name(), "admin");
834 assert_eq!(elevation.duration(), Some(Duration::from_secs(3600)));
835 assert!(!elevation.is_expired(Instant::now()));
836 }
837
838 #[test]
839 fn test_inactive_role_permissions() {
840 let mut role = Role::new("inactive").add_permission(Permission::new("read", "documents"));
841
842 role.set_active(false);
843
844 let context = HashMap::new();
845 assert!(!role.has_permission("read", "documents", &context));
846 }
847}