Skip to main content

ApiKeyStore

Struct ApiKeyStore 

Source
pub struct ApiKeyStore { /* private fields */ }
Expand description

In-memory store for API keys with Argon2 hashing.

Validation uses a two-stage lookup: a fast SHA-256 prefix narrows the candidate set to (usually) one entry, avoiding a linear Argon2 scan that would otherwise take O(N * ~100ms) and create a CPU-exhaustion denial-of-service vector.

**** Rate limiting is applied per key prefix, so brute-force attempts targeting one key cannot lock out unrelated keys. A global counter provides an additional safety net against distributed attacks.

Implementations§

Source§

impl ApiKeyStore

Source

pub fn new() -> Self

Create a new empty key store.

Source

pub fn key_exists(&self, name: &str) -> bool

Check whether a key with the given name is registered (not revoked).

This is an O(1) lookup that avoids the expensive Argon2 re-hash performed by validate. Use this for runtime revocation checks when the key has already been authenticated.

Source

pub fn create_key( &mut self, name: &str, expires_at: Option<u64>, ) -> Result<String, AuthError>

Create a new API key with the given name and optional expiry.

Returns the raw key string that should be given to the user. The store only keeps the Argon2 hash.

Source

pub fn bind_namespaces(&mut self, name: &str, namespaces: Vec<String>) -> bool

Confine an existing key to a set of namespaces.

An empty list leaves the key unrestricted. Returns false when no key of that name is stored.

Source

pub fn set_admin(&mut self, name: &str, admin: bool) -> bool

Grant or revoke the administrative capability on an existing key.

Returns false when no key of that name is stored.

Source

pub fn authenticate(&self, token: &str) -> Result<AuthContext, AuthError>

Authenticate token and build the context it grants.

The one place an API key becomes an AuthContext. chronixd has a second key store for keys minted at run time, and it assembled the context by hand from three accessors — so when the credential grew a roles list, a key created through POST /admin/auth/keys carried none of it and every role-based policy silently stopped matching for exactly those keys. Three accessors are three chances to forget the fourth.

§Errors

Whatever validate returns.

Source

pub fn entry(&self, name: &str) -> Option<&ApiKeyEntry>

The stored entry for name, if any.

The whole record, so a caller that must persist a key change writes exactly what the store holds rather than reassembling it from four accessors — which is the mistake that lost roles once already.

Source

pub fn set_roles(&mut self, name: &str, roles: Vec<String>) -> bool

Assign the Cedar roles an existing key carries.

Returns false when no key of that name is stored.

Source

pub fn roles(&self, name: &str) -> Vec<String>

The Cedar roles a key carries.

Source

pub fn is_admin(&self, name: &str) -> bool

Whether a key carries the administrative capability.

Source

pub fn namespaces_for(&self, name: &str) -> &[String]

The namespaces a key is confined to; empty means unrestricted.

Source

pub fn unconfined_keys(&self) -> Vec<&str>

Names of keys that may act in any namespace.

A multi-tenant server refuses to start while this is non-empty: an unconfined key is the whole isolation boundary gone, and it fails open, so nothing in normal operation would reveal it.

Source

pub fn add_entry(&mut self, entry: ApiKeyEntry)

Add a pre-hashed key entry (for loading from config).

Note: pre-hashed entries are not indexed by prefix and will only be found via the fallback linear Argon2 scan. Use register_plaintext when the raw key is available.

Source

pub fn register_plaintext( &mut self, name: &str, raw_key: &str, expires_at: Option<u64>, ) -> Result<(), AuthError>

Register a plaintext key (hashing it with Argon2).

Used to pre-configure keys from server configuration files where the raw key is specified in cleartext.

§Errors

Returns an error if a key with the same name already exists.

Source

pub fn validate(&self, raw_key: &str) -> Result<String, AuthError>

Validate a raw API key against the store.

Uses a two-stage strategy:

  1. Fast path – look up the SHA-256 prefix of raw_key in the prefix index and Argon2-verify only the (usually ≤ 1) matching candidates. This covers keys created via create_key and register_plaintext.
  2. Fallback – if no indexed candidate matched, fall back to a linear Argon2 scan for entries loaded via add_entry (pre-hashed, no prefix).

Returns the key name on success.

Source

pub fn revoke(&mut self, name: &str) -> bool

Revoke a key by name.

Also cleans up the prefix index. Stale prefix entries from pre-hashed keys (added via add_entry) are harmless — they simply won’t find a matching keys entry during validation.

Source

pub fn list_keys(&self) -> Vec<&str>

List all key names.

Source

pub fn len(&self) -> usize

Get the number of stored keys.

Source

pub fn is_empty(&self) -> bool

Check if the store is empty.

Trait Implementations§

Source§

impl Clone for ApiKeyStore

Source§

fn clone(&self) -> ApiKeyStore

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ApiKeyStore

Source§

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

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

impl Default for ApiKeyStore

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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