1use crate::cap::Cap;
12use crate::error::CapSecError;
13use crate::permission::Permission;
14use std::marker::PhantomData;
15use std::sync::Arc;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::time::{Duration, Instant};
18
19pub struct RuntimeCap<P: Permission> {
28 _phantom: PhantomData<P>,
29 _not_send: PhantomData<*const ()>,
31 active: Arc<AtomicBool>,
32}
33
34impl<P: Permission> RuntimeCap<P> {
35 pub fn new(_cap: Cap<P>) -> (Self, Revoker) {
40 let active = Arc::new(AtomicBool::new(true));
41 let revoker = Revoker {
42 active: Arc::clone(&active),
43 };
44 let cap = Self {
45 _phantom: PhantomData,
46 _not_send: PhantomData,
47 active,
48 };
49 (cap, revoker)
50 }
51
52 pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
57 if self.active.load(Ordering::Acquire) {
58 Ok(Cap::new())
59 } else {
60 Err(CapSecError::Revoked)
61 }
62 }
63
64 pub fn is_active(&self) -> bool {
69 self.active.load(Ordering::Acquire)
70 }
71
72 pub fn make_send(self) -> RuntimeSendCap<P> {
77 RuntimeSendCap {
78 _phantom: PhantomData,
79 active: self.active,
80 }
81 }
82}
83
84impl<P: Permission> Clone for RuntimeCap<P> {
85 fn clone(&self) -> Self {
86 Self {
87 _phantom: PhantomData,
88 _not_send: PhantomData,
89 active: Arc::clone(&self.active),
90 }
91 }
92}
93
94pub struct Revoker {
101 active: Arc<AtomicBool>,
102}
103
104impl Revoker {
105 pub fn revoke(&self) {
110 self.active.store(false, Ordering::Release);
111 }
112
113 pub fn is_revoked(&self) -> bool {
115 !self.active.load(Ordering::Acquire)
116 }
117}
118
119impl Clone for Revoker {
120 fn clone(&self) -> Self {
121 Self {
122 active: Arc::clone(&self.active),
123 }
124 }
125}
126
127pub struct RuntimeSendCap<P: Permission> {
132 _phantom: PhantomData<P>,
133 active: Arc<AtomicBool>,
134}
135
136unsafe impl<P: Permission> Send for RuntimeSendCap<P> {}
140unsafe impl<P: Permission> Sync for RuntimeSendCap<P> {}
141
142impl<P: Permission> RuntimeSendCap<P> {
143 pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
148 if self.active.load(Ordering::Acquire) {
149 Ok(Cap::new())
150 } else {
151 Err(CapSecError::Revoked)
152 }
153 }
154
155 pub fn is_active(&self) -> bool {
159 self.active.load(Ordering::Acquire)
160 }
161}
162
163impl<P: Permission> Clone for RuntimeSendCap<P> {
164 fn clone(&self) -> Self {
165 Self {
166 _phantom: PhantomData,
167 active: Arc::clone(&self.active),
168 }
169 }
170}
171
172pub struct TimedCap<P: Permission> {
181 _phantom: PhantomData<P>,
182 _not_send: PhantomData<*const ()>,
184 expires_at: Instant,
185}
186
187impl<P: Permission> TimedCap<P> {
188 pub fn new(_cap: Cap<P>, ttl: Duration) -> Self {
192 Self {
193 _phantom: PhantomData,
194 _not_send: PhantomData,
195 expires_at: Instant::now() + ttl,
196 }
197 }
198
199 pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
204 if Instant::now() < self.expires_at {
205 Ok(Cap::new())
206 } else {
207 Err(CapSecError::Expired)
208 }
209 }
210
211 pub fn is_active(&self) -> bool {
216 Instant::now() < self.expires_at
217 }
218
219 pub fn remaining(&self) -> Duration {
223 self.expires_at.saturating_duration_since(Instant::now())
224 }
225
226 pub fn make_send(self) -> TimedSendCap<P> {
231 TimedSendCap {
232 _phantom: PhantomData,
233 expires_at: self.expires_at,
234 }
235 }
236}
237
238impl<P: Permission> Clone for TimedCap<P> {
239 fn clone(&self) -> Self {
240 Self {
241 _phantom: PhantomData,
242 _not_send: PhantomData,
243 expires_at: self.expires_at,
244 }
245 }
246}
247
248pub struct TimedSendCap<P: Permission> {
253 _phantom: PhantomData<P>,
254 expires_at: Instant,
255}
256
257unsafe impl<P: Permission> Send for TimedSendCap<P> {}
261unsafe impl<P: Permission> Sync for TimedSendCap<P> {}
262
263impl<P: Permission> TimedSendCap<P> {
264 pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
269 if Instant::now() < self.expires_at {
270 Ok(Cap::new())
271 } else {
272 Err(CapSecError::Expired)
273 }
274 }
275
276 pub fn is_active(&self) -> bool {
280 Instant::now() < self.expires_at
281 }
282
283 pub fn remaining(&self) -> Duration {
287 self.expires_at.saturating_duration_since(Instant::now())
288 }
289}
290
291impl<P: Permission> Clone for TimedSendCap<P> {
292 fn clone(&self) -> Self {
293 Self {
294 _phantom: PhantomData,
295 expires_at: self.expires_at,
296 }
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303 use crate::permission::FsRead;
304 use std::mem::size_of;
305
306 #[test]
307 fn runtime_cap_try_cap_succeeds_when_active() {
308 let root = crate::root::test_root();
309 let cap = root.grant::<FsRead>();
310 let (rcap, _revoker) = RuntimeCap::new(cap);
311 assert!(rcap.try_cap().is_ok());
312 }
313
314 #[test]
315 fn runtime_cap_try_cap_fails_after_revocation() {
316 let root = crate::root::test_root();
317 let cap = root.grant::<FsRead>();
318 let (rcap, revoker) = RuntimeCap::new(cap);
319 revoker.revoke();
320 assert!(matches!(rcap.try_cap(), Err(CapSecError::Revoked)));
321 }
322
323 #[test]
324 fn revoker_is_idempotent() {
325 let root = crate::root::test_root();
326 let cap = root.grant::<FsRead>();
327 let (_rcap, revoker) = RuntimeCap::new(cap);
328 revoker.revoke();
329 revoker.revoke(); assert!(revoker.is_revoked());
331 }
332
333 #[test]
334 fn revoker_is_send_and_sync() {
335 fn assert_send_sync<T: Send + Sync>() {}
336 assert_send_sync::<Revoker>();
337 }
338
339 #[test]
340 fn runtime_send_cap_crosses_threads() {
341 let root = crate::root::test_root();
342 let cap = root.grant::<FsRead>();
343 let (rcap, _revoker) = RuntimeCap::new(cap);
344 let send_cap = rcap.make_send();
345
346 std::thread::spawn(move || {
347 assert!(send_cap.try_cap().is_ok());
348 })
349 .join()
350 .unwrap();
351 }
352
353 #[test]
354 fn runtime_send_cap_revocation_crosses_threads() {
355 let root = crate::root::test_root();
356 let cap = root.grant::<FsRead>();
357 let (rcap, revoker) = RuntimeCap::new(cap);
358 let send_cap = rcap.make_send();
359
360 revoker.revoke();
361
362 std::thread::spawn(move || {
363 assert!(matches!(send_cap.try_cap(), Err(CapSecError::Revoked)));
364 })
365 .join()
366 .unwrap();
367 }
368
369 #[test]
370 fn cloned_runtime_cap_shares_revocation() {
371 let root = crate::root::test_root();
372 let cap = root.grant::<FsRead>();
373 let (rcap, revoker) = RuntimeCap::new(cap);
374 let rcap2 = rcap.clone();
375
376 revoker.revoke();
377
378 assert!(matches!(rcap.try_cap(), Err(CapSecError::Revoked)));
379 assert!(matches!(rcap2.try_cap(), Err(CapSecError::Revoked)));
380 }
381
382 #[test]
383 fn runtime_cap_is_small() {
384 assert!(size_of::<RuntimeCap<FsRead>>() <= 2 * size_of::<usize>());
385 }
386
387 #[test]
388 fn timed_cap_succeeds_before_expiry() {
389 let root = crate::root::test_root();
390 let cap = root.grant::<FsRead>();
391 let tcap = TimedCap::new(cap, Duration::from_secs(60));
392 assert!(tcap.try_cap().is_ok());
393 }
394
395 #[test]
396 fn timed_cap_fails_after_expiry() {
397 let root = crate::root::test_root();
398 let cap = root.grant::<FsRead>();
399 let tcap = TimedCap::new(cap, Duration::from_millis(5));
400 std::thread::sleep(Duration::from_millis(50));
401 assert!(matches!(tcap.try_cap(), Err(CapSecError::Expired)));
402 }
403
404 #[test]
405 fn timed_cap_remaining_decreases() {
406 let root = crate::root::test_root();
407 let cap = root.grant::<FsRead>();
408 let tcap = TimedCap::new(cap, Duration::from_secs(60));
409 let r1 = tcap.remaining();
410 std::thread::sleep(Duration::from_millis(10));
411 let r2 = tcap.remaining();
412 assert!(r2 < r1);
413 }
414
415 #[test]
416 fn timed_cap_remaining_is_zero_after_expiry() {
417 let root = crate::root::test_root();
418 let cap = root.grant::<FsRead>();
419 let tcap = TimedCap::new(cap, Duration::from_millis(5));
420 std::thread::sleep(Duration::from_millis(50));
421 assert_eq!(tcap.remaining(), Duration::ZERO);
422 }
423}