aegis_delegate/
revocation.rs1use std::collections::HashSet;
9use std::sync::RwLock;
10
11pub struct RevocationRegistry {
17 revoked: RwLock<HashSet<String>>,
18}
19
20impl RevocationRegistry {
21 pub fn new() -> Self {
23 Self {
24 revoked: RwLock::new(HashSet::new()),
25 }
26 }
27
28 pub fn revoke(&self, delegation_id: &str) {
30 if let Ok(mut set) = self.revoked.write() {
31 set.insert(delegation_id.to_string());
32 }
33 }
34
35 pub fn is_revoked(&self, delegation_id: &str) -> bool {
37 match self.revoked.read() {
38 Ok(set) => set.contains(delegation_id),
39 Err(_) => {
40 true
42 }
43 }
44 }
45
46 pub fn revoke_cascade(&self, ids: &[String]) {
51 if let Ok(mut set) = self.revoked.write() {
52 for id in ids {
53 set.insert(id.clone());
54 }
55 }
56 }
57}
58
59impl Default for RevocationRegistry {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn new_registry_is_empty() {
71 let reg = RevocationRegistry::new();
72 assert!(!reg.is_revoked("any-id"));
73 }
74
75 #[test]
76 fn revoke_and_check() {
77 let reg = RevocationRegistry::new();
78 reg.revoke("del-1");
79 assert!(reg.is_revoked("del-1"));
80 assert!(!reg.is_revoked("del-2"));
81 }
82
83 #[test]
84 fn revoke_cascade_marks_all() {
85 let reg = RevocationRegistry::new();
86 let ids = vec!["d1".to_string(), "d2".to_string(), "d3".to_string()];
87 reg.revoke_cascade(&ids);
88
89 assert!(reg.is_revoked("d1"));
90 assert!(reg.is_revoked("d2"));
91 assert!(reg.is_revoked("d3"));
92 assert!(!reg.is_revoked("d4"));
93 }
94
95 #[test]
96 fn revoke_is_idempotent() {
97 let reg = RevocationRegistry::new();
98 reg.revoke("del-1");
99 reg.revoke("del-1");
100 assert!(reg.is_revoked("del-1"));
101 }
102
103 #[test]
104 fn default_creates_empty_registry() {
105 let reg = RevocationRegistry::default();
106 assert!(!reg.is_revoked("anything"));
107 }
108
109 #[test]
110 fn concurrent_access() {
111 use std::sync::Arc;
112 use std::thread;
113
114 let reg = Arc::new(RevocationRegistry::new());
115
116 let mut handles = vec![];
117 for i in 0..10 {
118 let reg_clone = Arc::clone(®);
119 let handle = thread::spawn(move || {
120 let id = format!("del-{i}");
121 reg_clone.revoke(&id);
122 });
123 handles.push(handle);
124 }
125
126 for h in handles {
127 let _ = h.join();
128 }
129
130 for i in 0..10 {
131 let id = format!("del-{i}");
132 assert!(reg.is_revoked(&id));
133 }
134 }
135}