Skip to main content

AuthzEngine

Struct AuthzEngine 

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

The Cedar-based authorization engine.

Thread-safe — can be shared across threads via Arc<AuthzEngine>.

§Policy Versioning & Rollback

The engine maintains an in-memory ring buffer of the last MAX_POLICY_VERSIONS policy snapshots. Every mutating operation (load_policies, add_policy, remove_policy) bumps the version counter and pushes the previous state into the history before swapping in the new set. Rollback to any retained version is available via revert_to_version().

A dry-run mode (dryrun_load_policies()) parses and validates a policy string without modifying the active set.

§Cross-Tenant Isolation

There is no implicit “deny cross-tenant access” policy built into the engine. Tenant isolation is enforced at two layers:

  1. Namespace scoping — every request carries a namespace context (see ChronixNamespace). The storage and query layers filter by namespace before authorization is even checked.
  2. Explicit Cedar policies — operators must deploy forbid policies that deny access when resource.namespace != principal.namespace. See docs/security.md for a reference policy template.

This explicit-policy approach avoids magic built-in rules that are invisible to policy auditors.

Implementations§

Source§

impl AuthzEngine

Source

pub const CROSS_TENANT_ISOLATION_POLICY: &'static str = r#" forbid( principal, action, resource ) when { resource has namespace && principal has namespace && resource.namespace != principal.namespace } unless { principal in Chronix::Role::"admin" }; "#

Built-in Cedar policy that denies cross-tenant resource access.

Uses Cedar’s forbid with unless to deny any request where the resource’s namespace does not match the principal’s namespace, unless the principal has the admin role.

Source

pub fn new() -> Self

Create a new authorizer with the built-in cross-tenant isolation policy auto-loaded (default-deny + namespace isolation).

Schema validation is automatically enabled when a schema is loaded via with_schema() or load_schema(). Without a schema, policies are accepted without type-level validation. Call .with_require_schema(true) to force a schema requirement.

Source

pub fn with_policies(policies: PolicySet) -> Self

Create an authorizer with the given policy set.

Source

pub fn with_schema(self, schema: Schema) -> Self

Set the Cedar schema for policy validation.

When a schema is set, load_policies() and add_policy() will validate policies against it and reject those that reference unknown entity types, actions, or attributes.

Setting a schema automatically enables require_schema, which means all policies MUST validate against the schema.

Source

pub fn load_schema(&mut self, schema_src: &str) -> Result<()>

Load a Cedar schema from its string representation.

Loading a schema automatically enables require_schema.

Source

pub fn with_require_schema(self, require: bool) -> Self

Require a Cedar schema for all policy operations. When enabled, load_policies() and add_policy() will fail if no schema has been loaded.

Source

pub fn load_policies(&self, policy_src: &str) -> Result<usize>

Load policies from a Cedar policy string.

Replaces all existing policies. If a schema is set, policies are validated against it before being accepted.

The previous policy set is pushed into the version history ring buffer before being replaced.

Source

pub fn dryrun_load_policies(&self, policy_src: &str) -> Result<usize>

Dry-run policy loading: parse and validate without modifying the active policy set.

Returns the number of policies that would be loaded, or an error if parsing or schema validation fails. The active policy set is not modified.

Source

pub fn add_policy(&self, id: &str, policy_src: &str) -> Result<()>

Add a single policy from source.

Uses a clone-and-swap strategy: clones the current policy set under a brief read lock, builds and validates the candidate set without holding any lock, and only acquires a write lock for the final atomic swap. If parsing or validation fails, the current set is left untouched (automatic rollback).

Source

pub fn remove_policy(&self, id: &str) -> bool

Remove a policy by ID.

Returns true if the policy was found and removed.

Source

pub fn current_version(&self) -> u64

Return the current policy version number.

Source

pub fn policy_versions(&self) -> Vec<PolicyVersion>

List all retained policy versions (oldest first).

Source

pub fn revert_to_version(&self, version: u64) -> Result<()>

Revert the active policy set to a previously retained version .

The version must exist in the history ring buffer. On success the reverted-to snapshot becomes the active set and the pre-revert state is pushed into history (so it can itself be rolled back).

Source

pub fn load_policies_from_dir(&self, dir: &Path) -> Result<usize>

Load policies from all .cedar files in a directory.

Source

pub fn authorize( &self, principal: &ChronixPrincipal, action: ChronixAction, resource: &ChronixResource, ) -> Decision

Evaluate an authorization request.

Returns Decision::Allow if any policy explicitly permits the request, or Decision::Deny with reasons otherwise (default-deny).

Source

pub fn authorize_admin( &self, principal: &ChronixPrincipal, action: ChronixAction, resource: &ChronixResource, ) -> Decision

Evaluate a granular admin action, falling back to the Admin super-action if the specific action is denied.

Policies that grant Action::"Admin" automatically cover all granular admin actions (ManageNodes, ManageKeys, etc.). This avoids breaking existing policies while enabling least-privilege grants for new deployments.

Source

pub fn authorize_namespace( &self, principal: &ChronixPrincipal, action: ChronixAction, namespace: &ChronixNamespace, ) -> Decision

Evaluate an authorization request against a namespace resource.

This is used for namespace-level policies, e.g.:

permit(
  principal in Chronix::Role::"ns_admin",
  action,
  resource == Chronix::Namespace::"production"
);
Source

pub fn policy_count(&self) -> usize

Returns the number of active policies.

Source

pub fn policy_ids(&self) -> Vec<String>

List all policy IDs.

Source

pub fn load_cross_tenant_isolation_policy(&self) -> Result<()>

**** Load the default cross-tenant isolation policy.

This policy forbids any principal from accessing resources in a namespace that differs from the principal’s own namespace attribute. Without this, data is visible across tenants unless explicit Cedar forbid policies are deployed.

The policy is idempotent — calling this multiple times is safe (the existing built-in policy is removed before re-adding).

Source

pub fn warn_if_no_namespace_policies(&self)

**** Check if any namespace-scoped policies are loaded.

Logs a warning if no policies reference Chronix::Namespace, which likely means cross-tenant isolation is not enforced.

Trait Implementations§

Source§

impl Debug for AuthzEngine

Source§

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

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

impl Default for AuthzEngine

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> 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, 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