1use crate::capabilities::{Capability, CapabilitySet, ResourceType, Action};
10use crate::error::{SecurityError, Result};
11use serde::{Deserialize, Serialize};
12use std::collections::{HashMap, HashSet};
13
14pub type RoleId = String;
16
17pub type PrincipalId = String;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct Role {
23 pub id: RoleId,
25 pub name: String,
27 pub description: Option<String>,
29 pub parent_roles: HashSet<RoleId>,
31 pub capabilities: CapabilitySet,
33 pub attributes: HashMap<String, serde_json::Value>,
35 pub active: bool,
37 pub created_at: chrono::DateTime<chrono::Utc>,
39 pub updated_at: chrono::DateTime<chrono::Utc>,
41}
42
43impl Role {
44 pub fn new(id: RoleId, name: String) -> Self {
46 let now = chrono::Utc::now();
47 Self {
48 id,
49 name,
50 description: None,
51 parent_roles: HashSet::new(),
52 capabilities: CapabilitySet::new(),
53 attributes: HashMap::new(),
54 active: true,
55 created_at: now,
56 updated_at: now,
57 }
58 }
59
60 pub fn with_description(mut self, description: String) -> Self {
62 self.description = Some(description);
63 self
64 }
65
66 pub fn add_parent_role(mut self, parent_role_id: RoleId) -> Self {
68 self.parent_roles.insert(parent_role_id);
69 self.updated_at = chrono::Utc::now();
70 self
71 }
72
73 pub fn add_capability(mut self, capability: Capability) -> Self {
75 self.capabilities.add_capability(capability);
76 self.updated_at = chrono::Utc::now();
77 self
78 }
79
80 pub fn add_capabilities(mut self, capabilities: Vec<Capability>) -> Self {
82 for cap in capabilities {
83 self.capabilities.add_capability(cap);
84 }
85 self.updated_at = chrono::Utc::now();
86 self
87 }
88
89 pub fn deactivate(mut self) -> Self {
91 self.active = false;
92 self.updated_at = chrono::Utc::now();
93 self
94 }
95
96 pub fn is_active(&self) -> bool {
98 self.active
99 }
100
101 pub fn get_all_capabilities(&self, role_registry: &RoleRegistry) -> Result<CapabilitySet> {
103 let mut all_caps = self.capabilities.clone();
104
105 for parent_id in &self.parent_roles {
107 if let Some(parent_role) = role_registry.get_role(parent_id) {
108 if parent_role.is_active() {
109 let parent_caps = parent_role.get_all_capabilities(role_registry)?;
110 all_caps = all_caps.union(&parent_caps);
111 }
112 } else {
113 return Err(SecurityError::Configuration(
114 format!("Parent role '{}' not found", parent_id)
115 ));
116 }
117 }
118
119 Ok(all_caps)
120 }
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct RoleRegistry {
126 roles: HashMap<RoleId, Role>,
127}
128
129impl RoleRegistry {
130 pub fn new() -> Self {
132 Self {
133 roles: HashMap::new(),
134 }
135 }
136
137 pub fn add_role(&mut self, role: Role) -> Result<()> {
139 if self.roles.contains_key(&role.id) {
140 return Err(SecurityError::Configuration(
141 format!("Role '{}' already exists", role.id)
142 ));
143 }
144
145 for parent_id in &role.parent_roles {
147 if !self.roles.contains_key(parent_id) {
148 return Err(SecurityError::Configuration(
149 format!("Parent role '{}' does not exist", parent_id)
150 ));
151 }
152 }
153
154 self.roles.insert(role.id.clone(), role);
155 Ok(())
156 }
157
158 pub fn update_role(&mut self, role: Role) -> Result<()> {
160 if !self.roles.contains_key(&role.id) {
161 return Err(SecurityError::Configuration(
162 format!("Role '{}' does not exist", role.id)
163 ));
164 }
165
166 for parent_id in &role.parent_roles {
168 if !self.roles.contains_key(parent_id) {
169 return Err(SecurityError::Configuration(
170 format!("Parent role '{}' does not exist", parent_id)
171 ));
172 }
173 }
174
175 self.roles.insert(role.id.clone(), role);
176 Ok(())
177 }
178
179 pub fn remove_role(&mut self, role_id: &RoleId) -> Result<()> {
181 if let Some(role) = self.roles.get(role_id) {
182 for (id, r) in &self.roles {
184 if id != role_id && r.parent_roles.contains(role_id) {
185 return Err(SecurityError::Configuration(
186 format!("Cannot remove role '{}' as it is referenced by role '{}'", role_id, id)
187 ));
188 }
189 }
190 }
191
192 self.roles.remove(role_id);
193 Ok(())
194 }
195
196 pub fn get_role(&self, role_id: &RoleId) -> Option<&Role> {
198 self.roles.get(role_id)
199 }
200
201 pub fn get_all_roles(&self) -> Vec<&Role> {
203 self.roles.values().collect()
204 }
205
206 pub fn get_active_roles(&self) -> Vec<&Role> {
208 self.roles.values().filter(|r| r.is_active()).collect()
209 }
210
211 pub fn role_exists(&self, role_id: &RoleId) -> bool {
213 self.roles.contains_key(role_id)
214 }
215
216 pub fn get_child_roles(&self, role_id: &RoleId) -> Vec<&Role> {
218 self.roles.values()
219 .filter(|r| r.parent_roles.contains(role_id))
220 .collect()
221 }
222
223 pub fn get_role_hierarchy(&self, role_id: &RoleId) -> Result<Vec<RoleId>> {
225 let mut path = Vec::new();
226 let mut visited = HashSet::new();
227 let mut current_id = role_id.clone();
228
229 while !visited.contains(¤t_id) {
230 visited.insert(current_id.clone());
231
232 if let Some(role) = self.get_role(¤t_id) {
233 path.push(current_id.clone());
234
235 if let Some(parent_id) = role.parent_roles.iter().next() {
237 current_id = parent_id.clone();
238 } else {
239 break; }
241 } else {
242 return Err(SecurityError::Configuration(
243 format!("Role '{}' not found in hierarchy", current_id)
244 ));
245 }
246
247 if path.len() > 100 {
249 return Err(SecurityError::Configuration(
250 "Circular role inheritance detected".to_string()
251 ));
252 }
253 }
254
255 Ok(path)
256 }
257}
258
259impl Default for RoleRegistry {
260 fn default() -> Self {
261 Self::new()
262 }
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct RoleAssignment {
268 pub principal_id: PrincipalId,
270 pub role_id: RoleId,
272 pub scope: Option<String>,
274 pub conditions: Option<HashMap<String, serde_json::Value>>,
276 pub assigned_at: chrono::DateTime<chrono::Utc>,
278 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
280 pub active: bool,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct RoleAssignmentManager {
287 assignments: HashMap<(PrincipalId, RoleId), RoleAssignment>,
288}
289
290impl RoleAssignmentManager {
291 pub fn new() -> Self {
293 Self {
294 assignments: HashMap::new(),
295 }
296 }
297
298 pub fn assign_role(&mut self, assignment: RoleAssignment) -> Result<()> {
300 let key = (assignment.principal_id.clone(), assignment.role_id.clone());
301
302 if self.assignments.contains_key(&key) {
303 return Err(SecurityError::Configuration(
304 format!("Role '{}' is already assigned to principal '{}'",
305 assignment.role_id, assignment.principal_id)
306 ));
307 }
308
309 self.assignments.insert(key, assignment);
310 Ok(())
311 }
312
313 pub fn revoke_role(&mut self, principal_id: &PrincipalId, role_id: &RoleId) -> Result<()> {
315 let key = (principal_id.clone(), role_id.clone());
316 if self.assignments.remove(&key).is_none() {
317 return Err(SecurityError::Configuration(
318 format!("Role '{}' is not assigned to principal '{}'",
319 role_id, principal_id)
320 ));
321 }
322 Ok(())
323 }
324
325 pub fn get_principal_roles(&self, principal_id: &PrincipalId) -> Vec<&RoleAssignment> {
327 self.assignments.values()
328 .filter(|assignment| {
329 &assignment.principal_id == principal_id &&
330 assignment.active &&
331 assignment.expires_at.map_or(true, |exp| chrono::Utc::now() < exp)
332 })
333 .collect()
334 }
335
336 pub fn get_role_principals(&self, role_id: &RoleId) -> Vec<&RoleAssignment> {
338 self.assignments.values()
339 .filter(|assignment| {
340 &assignment.role_id == role_id &&
341 assignment.active &&
342 assignment.expires_at.map_or(true, |exp| chrono::Utc::now() < exp)
343 })
344 .collect()
345 }
346
347 pub fn has_role(&self, principal_id: &PrincipalId, role_id: &RoleId) -> bool {
349 self.get_principal_roles(principal_id)
350 .iter()
351 .any(|assignment| &assignment.role_id == role_id)
352 }
353
354 pub fn get_all_assignments(&self) -> Vec<&RoleAssignment> {
356 self.assignments.values()
357 .filter(|assignment| {
358 assignment.active &&
359 assignment.expires_at.map_or(true, |exp| chrono::Utc::now() < exp)
360 })
361 .collect()
362 }
363}
364
365impl Default for RoleAssignmentManager {
366 fn default() -> Self {
367 Self::new()
368 }
369}
370
371#[derive(Debug)]
373pub struct RBACService {
374 role_registry: RoleRegistry,
375 assignment_manager: RoleAssignmentManager,
376}
377
378impl RBACService {
379 pub fn new() -> Self {
381 Self {
382 role_registry: RoleRegistry::new(),
383 assignment_manager: RoleAssignmentManager::new(),
384 }
385 }
386
387 pub fn with_data(role_registry: RoleRegistry, assignment_manager: RoleAssignmentManager) -> Self {
389 Self {
390 role_registry,
391 assignment_manager,
392 }
393 }
394
395 pub fn add_role(&mut self, role: Role) -> Result<()> {
397 self.role_registry.add_role(role)
398 }
399
400 pub fn assign_role(&mut self, assignment: RoleAssignment) -> Result<()> {
402 if !self.role_registry.role_exists(&assignment.role_id) {
404 return Err(SecurityError::Configuration(
405 format!("Role '{}' does not exist", assignment.role_id)
406 ));
407 }
408
409 self.assignment_manager.assign_role(assignment)
410 }
411
412 pub fn revoke_role(&mut self, principal_id: &PrincipalId, role_id: &RoleId) -> Result<()> {
414 self.assignment_manager.revoke_role(principal_id, role_id)
415 }
416
417 pub fn check_permission(
419 &self,
420 principal_id: &PrincipalId,
421 resource_type: &ResourceType,
422 action: &Action,
423 scope: Option<&str>,
424 ) -> Result<bool> {
425 let principal_assignments = self.assignment_manager.get_principal_roles(principal_id);
426
427 for assignment in principal_assignments {
428 if let Some(role) = self.role_registry.get_role(&assignment.role_id) {
429 if !role.is_active() {
430 continue;
431 }
432
433 let all_capabilities = role.get_all_capabilities(&self.role_registry)?;
434 if all_capabilities.allows(resource_type, action, scope) {
435 return Ok(true);
436 }
437 }
438 }
439
440 Ok(false)
441 }
442
443 pub fn get_principal_capabilities(&self, principal_id: &PrincipalId) -> Result<CapabilitySet> {
445 let principal_assignments = self.assignment_manager.get_principal_roles(principal_id);
446 let mut all_capabilities = CapabilitySet::new();
447
448 for assignment in principal_assignments {
449 if let Some(role) = self.role_registry.get_role(&assignment.role_id) {
450 if role.is_active() {
451 let role_capabilities = role.get_all_capabilities(&self.role_registry)?;
452 all_capabilities = all_capabilities.union(&role_capabilities);
453 }
454 }
455 }
456
457 Ok(all_capabilities)
458 }
459
460 pub fn get_principal_roles(&self, principal_id: &PrincipalId) -> Vec<&Role> {
462 let assignments = self.assignment_manager.get_principal_roles(principal_id);
463 assignments.iter()
464 .filter_map(|assignment| self.role_registry.get_role(&assignment.role_id))
465 .collect()
466 }
467
468 pub fn list_roles(&self) -> Vec<&Role> {
470 self.role_registry.get_all_roles()
471 }
472
473 pub fn list_assignments(&self) -> Vec<&RoleAssignment> {
475 self.assignment_manager.get_all_assignments()
476 }
477
478 pub fn create_common_roles(&mut self) -> Result<()> {
480 let admin_role = Role::new("admin".to_string(), "Administrator".to_string())
482 .with_description("Full system access".to_string())
483 .add_capability(Capability::new(ResourceType::Admin, Action::Admin, None));
484
485 let user_manager_role = Role::new("user_manager".to_string(), "User Manager".to_string())
487 .with_description("Manage user accounts".to_string())
488 .add_capability(Capability::new(ResourceType::User, Action::Read, None))
489 .add_capability(Capability::new(ResourceType::User, Action::Create, None))
490 .add_capability(Capability::new(ResourceType::User, Action::Update, None));
491
492 let content_editor_role = Role::new("content_editor".to_string(), "Content Editor".to_string())
494 .with_description("Edit content and data".to_string())
495 .add_capability(Capability::new(ResourceType::Graph, Action::Read, None))
496 .add_capability(Capability::new(ResourceType::Graph, Action::Create, None))
497 .add_capability(Capability::new(ResourceType::Graph, Action::Update, None));
498
499 let content_viewer_role = Role::new("content_viewer".to_string(), "Content Viewer".to_string())
501 .with_description("View content and data".to_string())
502 .add_capability(Capability::new(ResourceType::Graph, Action::Read, None));
503
504 self.add_role(admin_role)?;
506 self.add_role(user_manager_role)?;
507 self.add_role(content_editor_role)?;
508 self.add_role(content_viewer_role)?;
509
510 Ok(())
511 }
512}
513
514impl Default for RBACService {
515 fn default() -> Self {
516 Self::new()
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523
524 #[test]
525 fn test_role_creation() {
526 let role = Role::new("test_role".to_string(), "Test Role".to_string())
527 .with_description("A test role".to_string());
528
529 assert_eq!(role.id, "test_role");
530 assert_eq!(role.name, "Test Role");
531 assert_eq!(role.description, Some("A test role".to_string()));
532 assert!(role.is_active());
533 }
534
535 #[test]
536 fn test_role_registry() {
537 let mut registry = RoleRegistry::new();
538
539 let role = Role::new("test_role".to_string(), "Test Role".to_string());
540 registry.add_role(role.clone()).unwrap();
541
542 assert!(registry.role_exists(&"test_role".to_string()));
543 assert_eq!(registry.get_role(&"test_role".to_string()).unwrap().name, "Test Role");
544 }
545
546 #[test]
547 fn test_role_assignment() {
548 let mut manager = RoleAssignmentManager::new();
549
550 let assignment = RoleAssignment {
551 principal_id: "user1".to_string(),
552 role_id: "role1".to_string(),
553 scope: None,
554 conditions: None,
555 assigned_at: chrono::Utc::now(),
556 expires_at: None,
557 active: true,
558 };
559
560 manager.assign_role(assignment).unwrap();
561
562 let user_roles = manager.get_principal_roles(&"user1".to_string());
563 assert_eq!(user_roles.len(), 1);
564 assert_eq!(user_roles[0].role_id, "role1");
565 }
566
567 #[test]
568 fn test_rbac_permission_check() {
569 let mut rbac = RBACService::new();
570
571 let role = Role::new("reader".to_string(), "Reader".to_string())
573 .add_capability(Capability::new(ResourceType::Graph, Action::Read, None));
574
575 rbac.add_role(role).unwrap();
576
577 let assignment = RoleAssignment {
579 principal_id: "user1".to_string(),
580 role_id: "reader".to_string(),
581 scope: None,
582 conditions: None,
583 assigned_at: chrono::Utc::now(),
584 expires_at: None,
585 active: true,
586 };
587
588 rbac.assign_role(assignment).unwrap();
589
590 assert!(rbac.check_permission(&"user1".to_string(), &ResourceType::Graph, &Action::Read, None).unwrap());
592 assert!(!rbac.check_permission(&"user1".to_string(), &ResourceType::Graph, &Action::Write, None).unwrap());
593 }
594
595 #[test]
596 fn test_role_hierarchy() {
597 let mut registry = RoleRegistry::new();
598
599 let parent_role = Role::new("parent".to_string(), "Parent Role".to_string())
601 .add_capability(Capability::new(ResourceType::Graph, Action::Read, None));
602
603 let child_role = Role::new("child".to_string(), "Child Role".to_string())
605 .add_parent_role("parent".to_string())
606 .add_capability(Capability::new(ResourceType::Graph, Action::Write, None));
607
608 registry.add_role(parent_role).unwrap();
609 registry.add_role(child_role).unwrap();
610
611 let child = registry.get_role(&"child".to_string()).unwrap();
612 let all_caps = child.get_all_capabilities(®istry).unwrap();
613
614 assert!(all_caps.has_capability(&ResourceType::Graph, &Action::Read, None));
616 assert!(all_caps.has_capability(&ResourceType::Graph, &Action::Write, None));
617 }
618}