Skip to main content

orbit_auth/
cache.rs

1use std::num::NonZeroUsize;
2use std::sync::{Arc, Mutex};
3
4use lru::LruCache;
5
6use crate::Principal;
7
8/// Opaque, keyed token fingerprint scoped to the keyring and validation context.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct CacheKey(pub(crate) [u8; 32]);
11
12impl CacheKey {
13    pub fn as_bytes(&self) -> &[u8; 32] {
14        &self.0
15    }
16}
17
18/// Successful authenticated snapshot, never a raw token. Constructed only by the validator.
19#[derive(Clone, Debug)]
20pub struct CachedPrincipal {
21    pub(crate) key: CacheKey,
22    pub(crate) principal: Arc<Principal>,
23    pub(crate) cached_at: u64,
24    pub(crate) valid_until: u64
25}
26
27impl CachedPrincipal {
28    pub fn principal(&self) -> &Arc<Principal> {
29        &self.principal
30    }
31    pub fn valid_until(&self) -> u64 {
32        self.valid_until
33    }
34}
35
36/// Trusted optimization only; misses and failures must return `None`.
37///
38/// Preserve entries as supplied and bound memory. No invalidation guarantee is required:
39/// expiry and the current validation hook are checked on every hit. This interface does
40/// not provide an atomic replay store. Concurrent cold misses may independently decrypt.
41pub trait PrincipalCache {
42    fn get(
43        &self,
44        key: &CacheKey
45    ) -> Option<CachedPrincipal>;
46    fn insert(
47        &self,
48        key: CacheKey,
49        entry: CachedPrincipal
50    );
51}
52
53/// Bounded process-local decoded-principal LRU. A hit only clones an `Arc`.
54pub struct MemoryCache(Mutex<LruCache<CacheKey, CachedPrincipal>>);
55
56impl MemoryCache {
57    pub fn new(capacity: NonZeroUsize) -> Self {
58        Self(Mutex::new(LruCache::new(capacity)))
59    }
60
61    pub fn clear(&self) {
62        self.0.lock().unwrap_or_else(|error| error.into_inner()).clear();
63    }
64}
65
66impl PrincipalCache for MemoryCache {
67    fn get(
68        &self,
69        key: &CacheKey
70    ) -> Option<CachedPrincipal> {
71        self.0.lock().unwrap_or_else(|error| error.into_inner()).get(key).cloned()
72    }
73
74    fn insert(
75        &self,
76        key: CacheKey,
77        entry: CachedPrincipal
78    ) {
79        self.0.lock().unwrap_or_else(|error| error.into_inner()).put(key, entry);
80    }
81}