pub struct TokenCache { /* private fields */ }Expand description
Fast permission lookup cache.
Keyed by (subject EntityId, channel_hash). Each slot holds a
list of tokens — previous versions kept a single token per
slot, which silently dropped tokens when the same subject needed
multiple distinct scopes on the same channel (e.g. one PUBLISH
token and one SUBSCRIBE token). On insert the incoming token
replaces any existing entry with an identical scope bitfield
so a refresh doesn’t stack duplicates, but tokens with different
scopes coexist.
Entries are not evicted automatically — callers should check
is_valid() on retrieved tokens, or call Self::evict_expired
on a cadence.
Capacity is bounded by MAX_TOKEN_SLOTS (slot count) and
MAX_TOKENS_PER_SLOT (tokens-with-distinct-scope per slot).
Implementations§
Source§impl TokenCache
impl TokenCache
Sourcepub fn new() -> Self
pub fn new() -> Self
Create an empty token cache with a fresh revocation registry and strict (zero) clock-skew tolerance.
Sourcepub fn with_clock_skew(skew_secs: u64) -> Self
pub fn with_clock_skew(skew_secs: u64) -> Self
Create an empty token cache with the supplied clock-skew
tolerance (in seconds) applied to every Self::check
time-bound evaluation. See
TOKEN_CLOCK_SKEW_SECS_RECOMMENDED for the production-
recommended value.
Sourcepub fn with_revocation_registry(revocation: Arc<RevocationRegistry>) -> Self
pub fn with_revocation_registry(revocation: Arc<RevocationRegistry>) -> Self
Create an empty token cache that shares the supplied revocation registry. Use this when several caches in the same process must observe the same revocation floors (e.g. per-channel caches that all need to honour issuer-wide rotation).
Sourcepub fn set_clock_skew(&mut self, skew_secs: u64)
pub fn set_clock_skew(&mut self, skew_secs: u64)
Set the cache’s clock-skew tolerance. Tokens cleared the
freshness checks in Self::check are admitted while
now >= not_before - skew AND now < not_after + skew.
Default is 0 (strict).
Sourcepub fn clock_skew_secs(&self) -> u64
pub fn clock_skew_secs(&self) -> u64
Current clock-skew tolerance (seconds).
Sourcepub fn revocation(&self) -> &Arc<RevocationRegistry> ⓘ
pub fn revocation(&self) -> &Arc<RevocationRegistry> ⓘ
Borrow the cache’s revocation registry. Use this to drive floor bumps without holding a separate handle.
Sourcepub fn insert(&self, token: PermissionToken) -> Result<(), TokenError>
pub fn insert(&self, token: PermissionToken) -> Result<(), TokenError>
Insert a token into the cache after verifying its signature.
Returns an error if the token’s signature is invalid. This prevents self-signed or tampered tokens from being cached.
Tokens with distinct scope bitfields for the same
(subject, channel_hash) are stored side-by-side.
A new token with the same scope as an existing entry
replaces the existing one — latest-issued wins so
refreshing via re-issue doesn’t leak growth.
Sourcepub fn insert_unchecked(&self, token: PermissionToken)
pub fn insert_unchecked(&self, token: PermissionToken)
Insert a token without verification (for trusted internal use).
Only use this when the token is known to be valid (e.g., just issued locally).
WILDCARD-scoped tokens are always stored under the dedicated
wildcard slot (channel_hash = 0) regardless of the token’s
own channel_hash field — that slot is where check() looks
for a cross-channel fallback. Non-wildcard tokens live in
their exact channel_hash slot.
Bounded by MAX_TOKEN_SLOTS and
MAX_TOKENS_PER_SLOT. When the slot cap is hit, novel
keys are silently dropped (existing slot keys still
refresh); when the within-slot cap is hit, novel scope
bitfields are silently dropped (existing-scope refresh
still wins). evict_expired reclaims slots as tokens
lapse, restoring admission.
Sourcepub fn check(
&self,
subject: &EntityId,
action: TokenScope,
channel_hash: ChannelHash,
) -> Result<(), TokenError>
pub fn check( &self, subject: &EntityId, action: TokenScope, channel_hash: ChannelHash, ) -> Result<(), TokenError>
Check if an entity is authorized for an action on a channel.
Returns Ok(()) if any cached token for this subject grants
action, else an error. Walks the exact-channel slot first,
then the wildcard (channel_hash = 0) slot. Within a slot,
any valid token that authorizes the requested action wins —
an expired or otherwise-invalid token in the same slot is
ignored, not blocking.
Sourcepub fn get(
&self,
subject: &EntityId,
channel_hash: ChannelHash,
) -> Option<PermissionToken>
pub fn get( &self, subject: &EntityId, channel_hash: ChannelHash, ) -> Option<PermissionToken>
Fetch any cached token for (subject, channel_hash). Exact
match only — the wildcard (channel_hash = 0) entry is a
separate key. Returns the first valid token in the slot; if
none are valid, returns any entry (so callers can still
inspect for debugging). Callers that need a specific scope
should use Self::check instead.
Sourcepub fn evict_expired(&self)
pub fn evict_expired(&self)
Remove expired tokens.
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Total number of cached tokens across all slots.
A slot is keyed by (subject, channel_hash) and can hold
multiple tokens with distinct scopes (e.g. one PUBLISH and
one SUBSCRIBE for the same peer-on-channel). An earlier
storage change from a single PermissionToken per slot to
a Vec<PermissionToken> left this method returning the
slot count instead of the token count — FFI / binding
metrics that surfaced “tokens cached” silently undercounted
whenever a slot carried more than one scope. Sum the slot
lengths so the number matches the observable cache
contents.