Skip to main content

TokenCache

Struct TokenCache 

Source
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

Source

pub fn new() -> Self

Create an empty token cache with a fresh revocation registry and strict (zero) clock-skew tolerance.

Source

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.

Source

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).

Source

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).

Source

pub fn clock_skew_secs(&self) -> u64

Current clock-skew tolerance (seconds).

Source

pub fn revocation(&self) -> &Arc<RevocationRegistry>

Borrow the cache’s revocation registry. Use this to drive floor bumps without holding a separate handle.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn evict_expired(&self)

Remove expired tokens.

Source

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.

Source

pub fn is_empty(&self) -> bool

Check if cache is empty.

evict_expired already drops empty slots, and insert_unchecked never creates one, so a zero slot-count and a zero token-count coincide in practice — but checking the slot count keeps is_empty() O(1) instead of walking every slot.

Trait Implementations§

Source§

impl Debug for TokenCache

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TokenCache

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more