karbon_framework/security/
role_hierarchy.rs1use std::collections::{HashMap, HashSet};
2
3#[derive(Debug, Clone)]
22pub struct RoleHierarchy {
23 map: HashMap<String, Vec<String>>,
24}
25
26impl RoleHierarchy {
27 pub fn new(entries: &[(&str, &[&str])]) -> Self {
30 let map: HashMap<String, Vec<String>> = entries
31 .iter()
32 .map(|(role, children)| {
33 (
34 role.to_string(),
35 children.iter().map(|c| c.to_string()).collect(),
36 )
37 })
38 .collect();
39
40 Self { map }
41 }
42
43 pub fn resolve(&self, user_roles: &[String]) -> HashSet<String> {
46 let mut resolved = HashSet::new();
47 for role in user_roles {
48 self.resolve_recursive(role, &mut resolved);
49 }
50 resolved
51 }
52
53 pub fn has_role(&self, user_roles: &[String], required_role: &str) -> bool {
56 if user_roles.iter().any(|r| r == required_role) {
58 return true;
59 }
60 self.resolve(user_roles).contains(required_role)
62 }
63
64 fn resolve_recursive(&self, role: &str, resolved: &mut HashSet<String>) {
65 if !resolved.insert(role.to_string()) {
66 return; }
68 if let Some(children) = self.map.get(role) {
69 for child in children {
70 self.resolve_recursive(child, resolved);
71 }
72 }
73 }
74}
75
76pub fn default_hierarchy() -> RoleHierarchy {
78 RoleHierarchy::new(&[
79 ("ROLE_SUPER_ADMIN", &["ROLE_ADMIN"]),
80 ("ROLE_ADMIN", &["ROLE_REDACTEUR", "ROLE_MODERATEUR"]),
81 ("ROLE_REDACTEUR", &["ROLE_USER"]),
82 ("ROLE_MODERATEUR", &["ROLE_USER"]),
83 ])
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn super_admin_has_all_roles() {
92 let h = default_hierarchy();
93 let roles = vec!["ROLE_SUPER_ADMIN".to_string()];
94 assert!(h.has_role(&roles, "ROLE_SUPER_ADMIN"));
95 assert!(h.has_role(&roles, "ROLE_ADMIN"));
96 assert!(h.has_role(&roles, "ROLE_REDACTEUR"));
97 assert!(h.has_role(&roles, "ROLE_MODERATEUR"));
98 assert!(h.has_role(&roles, "ROLE_USER"));
99 }
100
101 #[test]
102 fn admin_does_not_have_super_admin() {
103 let h = default_hierarchy();
104 let roles = vec!["ROLE_ADMIN".to_string()];
105 assert!(h.has_role(&roles, "ROLE_ADMIN"));
106 assert!(h.has_role(&roles, "ROLE_REDACTEUR"));
107 assert!(h.has_role(&roles, "ROLE_USER"));
108 assert!(!h.has_role(&roles, "ROLE_SUPER_ADMIN"));
109 }
110
111 #[test]
112 fn basic_user_only_has_user() {
113 let h = default_hierarchy();
114 let roles = vec!["ROLE_USER".to_string()];
115 assert!(h.has_role(&roles, "ROLE_USER"));
116 assert!(!h.has_role(&roles, "ROLE_ADMIN"));
117 assert!(!h.has_role(&roles, "ROLE_SUPER_ADMIN"));
118 }
119}