oxigdal_security/access_control/
rbac.rs1use crate::access_control::{
4 AccessControlEvaluator, AccessDecision, AccessRequest, Action, ResourceType,
5 permissions::Permission, roles::Role,
6};
7use crate::error::{Result, SecurityError};
8use dashmap::DashMap;
9use std::collections::HashSet;
10use std::sync::Arc;
11
12pub struct RbacEngine {
14 role_assignments: Arc<DashMap<String, HashSet<String>>>,
16 roles: Arc<DashMap<String, Role>>,
18 permissions: Arc<DashMap<String, Permission>>,
20 role_inheritance: Arc<DashMap<String, HashSet<String>>>,
22}
23
24impl RbacEngine {
25 pub fn new() -> Self {
27 Self {
28 role_assignments: Arc::new(DashMap::new()),
29 roles: Arc::new(DashMap::new()),
30 permissions: Arc::new(DashMap::new()),
31 role_inheritance: Arc::new(DashMap::new()),
32 }
33 }
34
35 pub fn add_role(&self, role: Role) -> Result<()> {
37 self.roles.insert(role.id.clone(), role);
38 Ok(())
39 }
40
41 pub fn get_role(&self, role_id: &str) -> Option<Role> {
43 self.roles.get(role_id).map(|r| r.clone())
44 }
45
46 pub fn remove_role(&self, role_id: &str) -> Result<()> {
48 self.roles.remove(role_id);
49 self.role_inheritance.remove(role_id);
50
51 for mut assignment in self.role_assignments.iter_mut() {
53 assignment.value_mut().remove(role_id);
54 }
55
56 Ok(())
57 }
58
59 pub fn list_roles(&self) -> Vec<Role> {
61 self.roles.iter().map(|r| r.value().clone()).collect()
62 }
63
64 pub fn add_permission(&self, permission: Permission) -> Result<()> {
66 self.permissions.insert(permission.id.clone(), permission);
67 Ok(())
68 }
69
70 pub fn get_permission(&self, permission_id: &str) -> Option<Permission> {
72 self.permissions.get(permission_id).map(|p| p.clone())
73 }
74
75 pub fn assign_role(&self, subject_id: &str, role_id: &str) -> Result<()> {
77 if !self.roles.contains_key(role_id) {
79 return Err(SecurityError::role_not_found(role_id));
80 }
81
82 self.role_assignments
83 .entry(subject_id.to_string())
84 .or_default()
85 .insert(role_id.to_string());
86
87 Ok(())
88 }
89
90 pub fn revoke_role(&self, subject_id: &str, role_id: &str) -> Result<()> {
92 if let Some(mut roles) = self.role_assignments.get_mut(subject_id) {
93 roles.remove(role_id);
94 }
95 Ok(())
96 }
97
98 pub fn get_subject_roles(&self, subject_id: &str) -> Vec<String> {
100 self.role_assignments
101 .get(subject_id)
102 .map(|roles| roles.iter().cloned().collect())
103 .unwrap_or_default()
104 }
105
106 pub fn set_role_inheritance(&self, child_role_id: &str, parent_role_id: &str) -> Result<()> {
108 if !self.roles.contains_key(child_role_id) {
110 return Err(SecurityError::role_not_found(child_role_id));
111 }
112 if !self.roles.contains_key(parent_role_id) {
113 return Err(SecurityError::role_not_found(parent_role_id));
114 }
115
116 if self.would_create_cycle(child_role_id, parent_role_id) {
118 return Err(SecurityError::policy_evaluation(
119 "Circular role inheritance detected",
120 ));
121 }
122
123 self.role_inheritance
124 .entry(child_role_id.to_string())
125 .or_default()
126 .insert(parent_role_id.to_string());
127
128 Ok(())
129 }
130
131 pub fn get_effective_roles(&self, subject_id: &str) -> HashSet<String> {
133 let mut effective_roles = HashSet::new();
134 let direct_roles = self.get_subject_roles(subject_id);
135
136 for role_id in direct_roles {
137 self.collect_inherited_roles(&role_id, &mut effective_roles);
138 }
139
140 effective_roles
141 }
142
143 fn collect_inherited_roles(&self, role_id: &str, collected: &mut HashSet<String>) {
145 if collected.contains(role_id) {
146 return;
147 }
148
149 collected.insert(role_id.to_string());
150
151 if let Some(parents) = self.role_inheritance.get(role_id) {
152 for parent_id in parents.iter() {
153 self.collect_inherited_roles(parent_id, collected);
154 }
155 }
156 }
157
158 fn would_create_cycle(&self, child_id: &str, parent_id: &str) -> bool {
160 let mut visited = HashSet::new();
161 self.has_cycle(parent_id, child_id, &mut visited)
162 }
163
164 fn has_cycle(&self, current: &str, target: &str, visited: &mut HashSet<String>) -> bool {
166 if current == target {
167 return true;
168 }
169
170 if visited.contains(current) {
171 return false;
172 }
173
174 visited.insert(current.to_string());
175
176 if let Some(parents) = self.role_inheritance.get(current) {
177 for parent in parents.iter() {
178 if self.has_cycle(parent, target, visited) {
179 return true;
180 }
181 }
182 }
183
184 false
185 }
186
187 pub fn has_permission(
189 &self,
190 subject_id: &str,
191 action: Action,
192 resource_type: ResourceType,
193 ) -> bool {
194 let effective_roles = self.get_effective_roles(subject_id);
195
196 for role_id in effective_roles {
197 if let Some(role) = self.roles.get(&role_id) {
198 for permission_id in &role.permissions {
199 if let Some(permission) = self.permissions.get(permission_id) {
200 if permission.action == action && permission.resource_type == resource_type
201 {
202 return true;
203 }
204 }
205 }
206 }
207 }
208
209 false
210 }
211
212 pub fn get_subject_permissions(&self, subject_id: &str) -> Vec<Permission> {
214 let effective_roles = self.get_effective_roles(subject_id);
215 let mut permissions = Vec::new();
216 let mut seen = HashSet::new();
217
218 for role_id in effective_roles {
219 if let Some(role) = self.roles.get(&role_id) {
220 for permission_id in &role.permissions {
221 if !seen.contains(permission_id) {
222 if let Some(permission) = self.permissions.get(permission_id) {
223 permissions.push(permission.clone());
224 seen.insert(permission_id.clone());
225 }
226 }
227 }
228 }
229 }
230
231 permissions
232 }
233
234 pub fn clear_assignments(&self) {
236 self.role_assignments.clear();
237 }
238
239 pub fn clear_roles(&self) {
241 self.roles.clear();
242 self.role_inheritance.clear();
243 }
244
245 pub fn clear_permissions(&self) {
247 self.permissions.clear();
248 }
249}
250
251impl Default for RbacEngine {
252 fn default() -> Self {
253 Self::new()
254 }
255}
256
257impl AccessControlEvaluator for RbacEngine {
258 fn evaluate(&self, request: &AccessRequest) -> Result<AccessDecision> {
259 let has_permission = self.has_permission(
260 &request.subject.id,
261 request.action,
262 request.resource.resource_type,
263 );
264
265 if has_permission {
266 Ok(AccessDecision::Allow)
267 } else {
268 Ok(AccessDecision::Deny)
269 }
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276 use crate::access_control::permissions::Permission;
277 use crate::access_control::roles::Role;
278
279 #[test]
280 fn test_role_assignment() {
281 let engine = RbacEngine::new();
282 let role = Role::new("admin".to_string(), "Administrator".to_string());
283
284 engine.add_role(role).expect("Failed to add role");
285 engine
286 .assign_role("user-123", "admin")
287 .expect("Failed to assign role");
288
289 let roles = engine.get_subject_roles("user-123");
290 assert_eq!(roles.len(), 1);
291 assert!(roles.contains(&"admin".to_string()));
292 }
293
294 #[test]
295 fn test_role_revocation() {
296 let engine = RbacEngine::new();
297 let role = Role::new("admin".to_string(), "Administrator".to_string());
298
299 engine.add_role(role).expect("Failed to add role");
300 engine
301 .assign_role("user-123", "admin")
302 .expect("Failed to assign role");
303 engine
304 .revoke_role("user-123", "admin")
305 .expect("Failed to revoke role");
306
307 let roles = engine.get_subject_roles("user-123");
308 assert_eq!(roles.len(), 0);
309 }
310
311 #[test]
312 fn test_role_inheritance() {
313 let engine = RbacEngine::new();
314
315 let admin_role = Role::new("admin".to_string(), "Administrator".to_string());
316 let user_role = Role::new("user".to_string(), "User".to_string());
317
318 engine
319 .add_role(admin_role)
320 .expect("Failed to add admin role");
321 engine.add_role(user_role).expect("Failed to add user role");
322
323 engine
324 .set_role_inheritance("admin", "user")
325 .expect("Failed to set inheritance");
326
327 engine
328 .assign_role("user-123", "admin")
329 .expect("Failed to assign role");
330
331 let effective_roles = engine.get_effective_roles("user-123");
332 assert_eq!(effective_roles.len(), 2);
333 assert!(effective_roles.contains("admin"));
334 assert!(effective_roles.contains("user"));
335 }
336
337 #[test]
338 fn test_circular_inheritance_prevention() {
339 let engine = RbacEngine::new();
340
341 let role_a = Role::new("role-a".to_string(), "Role A".to_string());
342 let role_b = Role::new("role-b".to_string(), "Role B".to_string());
343
344 engine.add_role(role_a).expect("Failed to add role A");
345 engine.add_role(role_b).expect("Failed to add role B");
346
347 engine
348 .set_role_inheritance("role-a", "role-b")
349 .expect("Failed to set inheritance");
350
351 let result = engine.set_role_inheritance("role-b", "role-a");
353 assert!(result.is_err());
354 }
355
356 #[test]
357 fn test_permission_check() {
358 let engine = RbacEngine::new();
359
360 let permission = Permission::new(
361 "read-dataset".to_string(),
362 "Read Dataset".to_string(),
363 Action::Read,
364 ResourceType::Dataset,
365 );
366
367 let mut role = Role::new("viewer".to_string(), "Viewer".to_string());
368 role.add_permission("read-dataset".to_string());
369
370 engine
371 .add_permission(permission)
372 .expect("Failed to add permission");
373 engine.add_role(role).expect("Failed to add role");
374 engine
375 .assign_role("user-123", "viewer")
376 .expect("Failed to assign role");
377
378 assert!(engine.has_permission("user-123", Action::Read, ResourceType::Dataset));
379 assert!(!engine.has_permission("user-123", Action::Write, ResourceType::Dataset));
380 }
381}