1use serde::{Deserialize, Serialize};
40use std::collections::{HashMap, HashSet};
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
47#[serde(rename_all = "snake_case")]
48pub enum Role {
49 Admin,
51 Operator,
53 User,
55 Auditor,
57 ReadOnly,
59}
60
61impl Role {
62 #[must_use]
64 pub const fn all() -> &'static [Self] {
65 &[
66 Self::Admin,
67 Self::Operator,
68 Self::User,
69 Self::Auditor,
70 Self::ReadOnly,
71 ]
72 }
73
74 #[must_use]
76 pub const fn display_name(&self) -> &'static str {
77 match self {
78 Self::Admin => "Administrator",
79 Self::Operator => "Operator",
80 Self::User => "User",
81 Self::Auditor => "Auditor",
82 Self::ReadOnly => "Read-Only",
83 }
84 }
85
86 #[must_use]
88 pub const fn description(&self) -> &'static str {
89 match self {
90 Self::Admin => "Full system access with all permissions",
91 Self::Operator => "Day-to-day operations and configuration",
92 Self::User => "Standard user access for capture and recall",
93 Self::Auditor => "Read-only access to audit logs and reports",
94 Self::ReadOnly => "Read-only access to data without modification",
95 }
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
101#[serde(rename_all = "snake_case")]
102pub enum Permission {
103 Capture,
106 Recall,
108 Delete,
110 Consolidate,
112
113 Sync,
116 Push,
118 Pull,
120
121 Configure,
124 ManageFeatures,
126
127 ManageUsers,
130 AssignRoles,
132
133 ViewAudit,
136 GenerateReports,
138 ExportAudit,
140
141 ExportData,
144 DeleteUserData,
146 ManageConsent,
148
149 CreatePrompt,
152 RunPrompt,
154 DeletePrompt,
156
157 ViewHealth,
160 ManageEncryption,
162 Maintenance,
164}
165
166impl Permission {
167 #[must_use]
169 pub fn all() -> Vec<Self> {
170 vec![
171 Self::Capture,
172 Self::Recall,
173 Self::Delete,
174 Self::Consolidate,
175 Self::Sync,
176 Self::Push,
177 Self::Pull,
178 Self::Configure,
179 Self::ManageFeatures,
180 Self::ManageUsers,
181 Self::AssignRoles,
182 Self::ViewAudit,
183 Self::GenerateReports,
184 Self::ExportAudit,
185 Self::ExportData,
186 Self::DeleteUserData,
187 Self::ManageConsent,
188 Self::CreatePrompt,
189 Self::RunPrompt,
190 Self::DeletePrompt,
191 Self::ViewHealth,
192 Self::ManageEncryption,
193 Self::Maintenance,
194 ]
195 }
196
197 #[must_use]
199 pub const fn display_name(&self) -> &'static str {
200 match self {
201 Self::Capture => "Capture Memories",
202 Self::Recall => "Recall Memories",
203 Self::Delete => "Delete Memories",
204 Self::Consolidate => "Consolidate Memories",
205 Self::Sync => "Sync",
206 Self::Push => "Push to Remote",
207 Self::Pull => "Pull from Remote",
208 Self::Configure => "Configure System",
209 Self::ManageFeatures => "Manage Features",
210 Self::ManageUsers => "Manage Users",
211 Self::AssignRoles => "Assign Roles",
212 Self::ViewAudit => "View Audit Logs",
213 Self::GenerateReports => "Generate Reports",
214 Self::ExportAudit => "Export Audit Data",
215 Self::ExportData => "Export User Data",
216 Self::DeleteUserData => "Delete User Data",
217 Self::ManageConsent => "Manage Consent",
218 Self::CreatePrompt => "Create Prompts",
219 Self::RunPrompt => "Run Prompts",
220 Self::DeletePrompt => "Delete Prompts",
221 Self::ViewHealth => "View Health",
222 Self::ManageEncryption => "Manage Encryption",
223 Self::Maintenance => "Maintenance",
224 }
225 }
226
227 #[must_use]
229 pub const fn category(&self) -> PermissionCategory {
230 match self {
231 Self::Capture | Self::Recall | Self::Delete | Self::Consolidate => {
232 PermissionCategory::Memory
233 },
234 Self::Sync | Self::Push | Self::Pull => PermissionCategory::Sync,
235 Self::Configure | Self::ManageFeatures => PermissionCategory::Configuration,
236 Self::ManageUsers | Self::AssignRoles => PermissionCategory::UserManagement,
237 Self::ViewAudit | Self::GenerateReports | Self::ExportAudit => {
238 PermissionCategory::Audit
239 },
240 Self::ExportData | Self::DeleteUserData | Self::ManageConsent => {
241 PermissionCategory::DataSubject
242 },
243 Self::CreatePrompt | Self::RunPrompt | Self::DeletePrompt => {
244 PermissionCategory::Prompts
245 },
246 Self::ViewHealth | Self::ManageEncryption | Self::Maintenance => {
247 PermissionCategory::System
248 },
249 }
250 }
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
255#[serde(rename_all = "snake_case")]
256pub enum PermissionCategory {
257 Memory,
259 Sync,
261 Configuration,
263 UserManagement,
265 Audit,
267 DataSubject,
269 Prompts,
271 System,
273}
274
275impl PermissionCategory {
276 #[must_use]
278 pub const fn display_name(&self) -> &'static str {
279 match self {
280 Self::Memory => "Memory Operations",
281 Self::Sync => "Synchronization",
282 Self::Configuration => "Configuration",
283 Self::UserManagement => "User Management",
284 Self::Audit => "Audit & Compliance",
285 Self::DataSubject => "Data Subject Rights",
286 Self::Prompts => "Prompt Management",
287 Self::System => "System Administration",
288 }
289 }
290}
291
292#[derive(Debug, Clone, PartialEq, Eq)]
294pub enum AccessResult {
295 Granted,
297 Denied(String),
299}
300
301impl AccessResult {
302 #[must_use]
304 pub const fn is_granted(&self) -> bool {
305 matches!(self, Self::Granted)
306 }
307
308 #[must_use]
310 pub const fn is_denied(&self) -> bool {
311 matches!(self, Self::Denied(_))
312 }
313}
314
315#[derive(Debug, Clone)]
317pub struct AccessControl {
318 role_permissions: HashMap<Role, HashSet<Permission>>,
320}
321
322impl Default for AccessControl {
323 fn default() -> Self {
324 Self::new()
325 }
326}
327
328impl AccessControl {
329 #[must_use]
331 pub fn new() -> Self {
332 let mut role_permissions = HashMap::new();
333
334 role_permissions.insert(Role::Admin, Permission::all().into_iter().collect());
336
337 role_permissions.insert(
339 Role::Operator,
340 [
341 Permission::Capture,
342 Permission::Recall,
343 Permission::Delete,
344 Permission::Consolidate,
345 Permission::Sync,
346 Permission::Push,
347 Permission::Pull,
348 Permission::Configure,
349 Permission::CreatePrompt,
350 Permission::RunPrompt,
351 Permission::DeletePrompt,
352 Permission::ViewHealth,
353 ]
354 .into_iter()
355 .collect(),
356 );
357
358 role_permissions.insert(
360 Role::User,
361 [
362 Permission::Capture,
363 Permission::Recall,
364 Permission::Sync,
365 Permission::CreatePrompt,
366 Permission::RunPrompt,
367 ]
368 .into_iter()
369 .collect(),
370 );
371
372 role_permissions.insert(
374 Role::Auditor,
375 [
376 Permission::Recall,
377 Permission::ViewAudit,
378 Permission::GenerateReports,
379 Permission::ExportAudit,
380 Permission::ViewHealth,
381 ]
382 .into_iter()
383 .collect(),
384 );
385
386 role_permissions.insert(
388 Role::ReadOnly,
389 [Permission::Recall, Permission::RunPrompt]
390 .into_iter()
391 .collect(),
392 );
393
394 Self { role_permissions }
395 }
396
397 #[must_use]
399 pub fn has_permission(&self, role: &Role, permission: &Permission) -> bool {
400 self.role_permissions
401 .get(role)
402 .is_some_and(|perms| perms.contains(permission))
403 }
404
405 #[must_use]
407 pub fn check_access(&self, role: &Role, permission: &Permission) -> AccessResult {
408 if self.has_permission(role, permission) {
409 AccessResult::Granted
410 } else {
411 AccessResult::Denied(format!(
412 "Role '{}' does not have permission '{}'",
413 role.display_name(),
414 permission.display_name()
415 ))
416 }
417 }
418
419 #[must_use]
421 pub fn permissions_for(&self, role: &Role) -> HashSet<Permission> {
422 self.role_permissions.get(role).cloned().unwrap_or_default()
423 }
424
425 #[must_use]
427 pub fn roles_with_permission(&self, permission: &Permission) -> Vec<Role> {
428 self.role_permissions
429 .iter()
430 .filter(|(_, perms)| perms.contains(permission))
431 .map(|(role, _)| *role)
432 .collect()
433 }
434
435 pub fn grant_permission(&mut self, role: &Role, permission: Permission) {
437 self.role_permissions
438 .entry(*role)
439 .or_default()
440 .insert(permission);
441 }
442
443 pub fn revoke_permission(&mut self, role: &Role, permission: &Permission) {
445 if let Some(perms) = self.role_permissions.get_mut(role) {
446 perms.remove(permission);
447 }
448 }
449
450 #[must_use]
452 pub fn summary(&self) -> RbacSummary {
453 let role_summaries: Vec<RoleSummary> = Role::all()
454 .iter()
455 .map(|role| {
456 let permissions = self.permissions_for(role);
457 RoleSummary {
458 role: *role,
459 permission_count: permissions.len(),
460 permissions: permissions.into_iter().collect(),
461 }
462 })
463 .collect();
464
465 RbacSummary {
466 total_roles: Role::all().len(),
467 total_permissions: Permission::all().len(),
468 role_summaries,
469 }
470 }
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize)]
475pub struct RoleSummary {
476 pub role: Role,
478 pub permission_count: usize,
480 pub permissions: Vec<Permission>,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct RbacSummary {
487 pub total_roles: usize,
489 pub total_permissions: usize,
491 pub role_summaries: Vec<RoleSummary>,
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 #[test]
500 fn test_admin_has_all_permissions() {
501 let ac = AccessControl::new();
502 for permission in Permission::all() {
503 assert!(
504 ac.has_permission(&Role::Admin, &permission),
505 "Admin should have {permission:?}"
506 );
507 }
508 }
509
510 #[test]
511 fn test_readonly_limited_permissions() {
512 let ac = AccessControl::new();
513
514 assert!(ac.has_permission(&Role::ReadOnly, &Permission::Recall));
516 assert!(ac.has_permission(&Role::ReadOnly, &Permission::RunPrompt));
517
518 assert!(!ac.has_permission(&Role::ReadOnly, &Permission::Capture));
520 assert!(!ac.has_permission(&Role::ReadOnly, &Permission::Delete));
521 assert!(!ac.has_permission(&Role::ReadOnly, &Permission::Configure));
522 }
523
524 #[test]
525 fn test_auditor_audit_permissions() {
526 let ac = AccessControl::new();
527
528 assert!(ac.has_permission(&Role::Auditor, &Permission::ViewAudit));
530 assert!(ac.has_permission(&Role::Auditor, &Permission::GenerateReports));
531 assert!(ac.has_permission(&Role::Auditor, &Permission::ExportAudit));
532
533 assert!(!ac.has_permission(&Role::Auditor, &Permission::Capture));
535 assert!(!ac.has_permission(&Role::Auditor, &Permission::Delete));
536 assert!(!ac.has_permission(&Role::Auditor, &Permission::Configure));
537 }
538
539 #[test]
540 fn test_user_basic_permissions() {
541 let ac = AccessControl::new();
542
543 assert!(ac.has_permission(&Role::User, &Permission::Capture));
545 assert!(ac.has_permission(&Role::User, &Permission::Recall));
546 assert!(ac.has_permission(&Role::User, &Permission::Sync));
547
548 assert!(!ac.has_permission(&Role::User, &Permission::ManageUsers));
550 assert!(!ac.has_permission(&Role::User, &Permission::Configure));
551 assert!(!ac.has_permission(&Role::User, &Permission::Delete));
552 }
553
554 #[test]
555 fn test_operator_operations_permissions() {
556 let ac = AccessControl::new();
557
558 assert!(ac.has_permission(&Role::Operator, &Permission::Capture));
560 assert!(ac.has_permission(&Role::Operator, &Permission::Recall));
561 assert!(ac.has_permission(&Role::Operator, &Permission::Delete));
562 assert!(ac.has_permission(&Role::Operator, &Permission::Configure));
563
564 assert!(!ac.has_permission(&Role::Operator, &Permission::ManageUsers));
566 assert!(!ac.has_permission(&Role::Operator, &Permission::AssignRoles));
567 }
568
569 #[test]
570 fn test_check_access_granted() {
571 let ac = AccessControl::new();
572 let result = ac.check_access(&Role::Admin, &Permission::Delete);
573 assert!(result.is_granted());
574 }
575
576 #[test]
577 fn test_check_access_denied() {
578 let ac = AccessControl::new();
579 let result = ac.check_access(&Role::ReadOnly, &Permission::Delete);
580 assert!(result.is_denied());
581 if let AccessResult::Denied(reason) = result {
582 assert!(reason.contains("Read-Only"));
583 assert!(reason.contains("Delete"));
584 }
585 }
586
587 #[test]
588 fn test_permissions_for_role() {
589 let ac = AccessControl::new();
590 let admin_perms = ac.permissions_for(&Role::Admin);
591 assert_eq!(admin_perms.len(), Permission::all().len());
592
593 let readonly_perms = ac.permissions_for(&Role::ReadOnly);
594 assert!(readonly_perms.len() < admin_perms.len());
595 }
596
597 #[test]
598 fn test_roles_with_permission() {
599 let ac = AccessControl::new();
600
601 let recall_roles = ac.roles_with_permission(&Permission::Recall);
603 assert!(recall_roles.contains(&Role::Admin));
604 assert!(recall_roles.contains(&Role::User));
605 assert!(recall_roles.contains(&Role::Auditor));
606 assert!(recall_roles.contains(&Role::ReadOnly));
607
608 let manage_roles = ac.roles_with_permission(&Permission::ManageUsers);
610 assert_eq!(manage_roles.len(), 1);
611 assert!(manage_roles.contains(&Role::Admin));
612 }
613
614 #[test]
615 fn test_grant_permission() {
616 let mut ac = AccessControl::new();
617
618 assert!(!ac.has_permission(&Role::ReadOnly, &Permission::Capture));
619 ac.grant_permission(&Role::ReadOnly, Permission::Capture);
620 assert!(ac.has_permission(&Role::ReadOnly, &Permission::Capture));
621 }
622
623 #[test]
624 fn test_revoke_permission() {
625 let mut ac = AccessControl::new();
626
627 assert!(ac.has_permission(&Role::User, &Permission::Capture));
628 ac.revoke_permission(&Role::User, &Permission::Capture);
629 assert!(!ac.has_permission(&Role::User, &Permission::Capture));
630 }
631
632 #[test]
633 fn test_permission_categories() {
634 assert_eq!(Permission::Capture.category(), PermissionCategory::Memory);
635 assert_eq!(Permission::Sync.category(), PermissionCategory::Sync);
636 assert_eq!(
637 Permission::Configure.category(),
638 PermissionCategory::Configuration
639 );
640 assert_eq!(
641 Permission::ManageUsers.category(),
642 PermissionCategory::UserManagement
643 );
644 assert_eq!(Permission::ViewAudit.category(), PermissionCategory::Audit);
645 assert_eq!(
646 Permission::ExportData.category(),
647 PermissionCategory::DataSubject
648 );
649 assert_eq!(
650 Permission::CreatePrompt.category(),
651 PermissionCategory::Prompts
652 );
653 assert_eq!(
654 Permission::ViewHealth.category(),
655 PermissionCategory::System
656 );
657 }
658
659 #[test]
660 fn test_role_display_names() {
661 assert_eq!(Role::Admin.display_name(), "Administrator");
662 assert_eq!(Role::ReadOnly.display_name(), "Read-Only");
663 }
664
665 #[test]
666 fn test_permission_display_names() {
667 assert_eq!(Permission::Capture.display_name(), "Capture Memories");
668 assert_eq!(Permission::ViewAudit.display_name(), "View Audit Logs");
669 }
670
671 #[test]
672 fn test_rbac_summary() {
673 let ac = AccessControl::new();
674 let summary = ac.summary();
675
676 assert_eq!(summary.total_roles, 5);
677 assert_eq!(summary.total_permissions, Permission::all().len());
678 assert_eq!(summary.role_summaries.len(), 5);
679
680 let admin_summary = summary
682 .role_summaries
683 .iter()
684 .find(|s| s.role == Role::Admin)
685 .unwrap();
686 assert_eq!(admin_summary.permission_count, Permission::all().len());
687 }
688
689 #[test]
690 fn test_role_serialization() {
691 let role = Role::Admin;
692 let json = serde_json::to_string(&role).unwrap();
693 assert_eq!(json, "\"admin\"");
694
695 let deserialized: Role = serde_json::from_str(&json).unwrap();
696 assert_eq!(deserialized, Role::Admin);
697 }
698
699 #[test]
700 fn test_permission_serialization() {
701 let perm = Permission::Capture;
702 let json = serde_json::to_string(&perm).unwrap();
703 assert_eq!(json, "\"capture\"");
704
705 let deserialized: Permission = serde_json::from_str(&json).unwrap();
706 assert_eq!(deserialized, Permission::Capture);
707 }
708
709 #[test]
710 fn test_access_result_methods() {
711 let granted = AccessResult::Granted;
712 assert!(granted.is_granted());
713 assert!(!granted.is_denied());
714
715 let denied = AccessResult::Denied("test".to_string());
716 assert!(!denied.is_granted());
717 assert!(denied.is_denied());
718 }
719
720 #[test]
721 fn test_separation_of_duties() {
722 let ac = AccessControl::new();
723
724 assert!(ac.has_permission(&Role::Auditor, &Permission::ViewAudit));
726 assert!(!ac.has_permission(&Role::Auditor, &Permission::Capture));
727 assert!(!ac.has_permission(&Role::Auditor, &Permission::Delete));
728
729 assert!(!ac.has_permission(&Role::User, &Permission::ManageUsers));
731 assert!(!ac.has_permission(&Role::User, &Permission::AssignRoles));
732
733 assert!(!ac.has_permission(&Role::Operator, &Permission::ManageUsers));
735 }
736}