Skip to main content

claw10_auth/
rbac.rs

1use std::collections::HashMap;
2
3use claw10_domain::{Permission, RoleId};
4
5pub struct RbacService {
6    role_permissions: HashMap<RoleId, Vec<Permission>>,
7}
8
9impl RbacService {
10    #[must_use]
11    pub fn new() -> Self {
12        Self {
13            role_permissions: HashMap::new(),
14        }
15    }
16
17    pub fn assign_permissions(&mut self, role_id: RoleId, permissions: Vec<Permission>) {
18        self.role_permissions.insert(role_id, permissions);
19    }
20
21    #[must_use]
22    pub fn get_role_permissions(&self, role_id: &RoleId) -> Vec<Permission> {
23        self.role_permissions
24            .get(role_id)
25            .cloned()
26            .unwrap_or_default()
27    }
28
29    #[must_use]
30    pub fn get_roles_permissions(&self, role_ids: &[RoleId]) -> Vec<Permission> {
31        let mut perms: Vec<Permission> = role_ids
32            .iter()
33            .flat_map(|rid| self.get_role_permissions(rid))
34            .collect();
35        perms.sort();
36        perms.dedup();
37        perms
38    }
39
40    #[must_use]
41    pub fn child_permissions(
42        parent_delegable: &[Permission],
43        requested: &[Permission],
44    ) -> Vec<Permission> {
45        let parent_set: std::collections::HashSet<&Permission> = parent_delegable.iter().collect();
46        requested
47            .iter()
48            .filter(|p| parent_set.contains(*p))
49            .cloned()
50            .collect()
51    }
52}
53
54impl Default for RbacService {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use uuid::Uuid;
64
65    fn create_role_id() -> RoleId {
66        RoleId(Uuid::now_v7())
67    }
68
69    fn create_permission(name: &str) -> Permission {
70        Permission(name.to_string())
71    }
72
73    #[test]
74    fn test_get_roles_permissions_empty() {
75        let rbac = RbacService::new();
76        let perms = rbac.get_roles_permissions(&[]);
77        assert!(perms.is_empty());
78    }
79
80    #[test]
81    fn test_get_roles_permissions_single_role() {
82        let mut rbac = RbacService::new();
83        let role1 = create_role_id();
84        let p1 = create_permission("read");
85        let p2 = create_permission("write");
86        rbac.assign_permissions(role1.clone(), vec![p1.clone(), p2.clone()]);
87
88        let perms = rbac.get_roles_permissions(&[role1]);
89        assert_eq!(perms.len(), 2);
90        assert!(perms.contains(&p1));
91        assert!(perms.contains(&p2));
92    }
93
94    #[test]
95    fn test_get_roles_permissions_multiple_roles() {
96        let mut rbac = RbacService::new();
97
98        let role1 = create_role_id();
99        let p1 = create_permission("read");
100        rbac.assign_permissions(role1.clone(), vec![p1.clone()]);
101
102        let role2 = create_role_id();
103        let p2 = create_permission("write");
104        rbac.assign_permissions(role2.clone(), vec![p2.clone()]);
105
106        let perms = rbac.get_roles_permissions(&[role1, role2]);
107        assert_eq!(perms.len(), 2);
108        assert!(perms.contains(&p1));
109        assert!(perms.contains(&p2));
110    }
111
112    #[test]
113    fn test_get_roles_permissions_duplicate_permissions() {
114        let mut rbac = RbacService::new();
115
116        let role1 = create_role_id();
117        let p1 = create_permission("read");
118        let p2 = create_permission("write");
119        rbac.assign_permissions(role1.clone(), vec![p1.clone(), p2.clone()]);
120
121        let role2 = create_role_id();
122        let p3 = create_permission("write"); // same as p2
123        let p4 = create_permission("delete");
124        rbac.assign_permissions(role2.clone(), vec![p3.clone(), p4.clone()]);
125
126        let perms = rbac.get_roles_permissions(&[role1, role2]);
127        assert_eq!(perms.len(), 3);
128        assert!(perms.contains(&p1));
129        assert!(perms.contains(&p2));
130        assert!(perms.contains(&p4));
131    }
132
133    #[test]
134    fn test_get_roles_permissions_missing_roles() {
135        let mut rbac = RbacService::new();
136
137        let role1 = create_role_id();
138        let p1 = create_permission("read");
139        rbac.assign_permissions(role1.clone(), vec![p1.clone()]);
140
141        let role_missing = create_role_id();
142
143        let perms = rbac.get_roles_permissions(&[role1, role_missing]);
144        assert_eq!(perms.len(), 1);
145        assert!(perms.contains(&p1));
146    }
147}