Skip to main content

AccessScope

Struct AccessScope 

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

A disjunction (OR) of scope constraints defining what data is accessible.

Each constraint is an independent access path (OR-ed). Filters within a constraint are AND-ed. An unconstrained scope bypasses row-level filtering.

§Examples

use toolkit_security::access_scope::{AccessScope, ScopeConstraint, ScopeFilter, pep_properties};
use uuid::Uuid;

// deny-all (default)
let scope = AccessScope::deny_all();
assert!(scope.is_deny_all());

// single tenant
let tid = Uuid::new_v4();
let scope = AccessScope::for_tenant(tid);
assert!(!scope.is_deny_all());
assert!(scope.contains_uuid(pep_properties::OWNER_TENANT_ID, tid));

Implementations§

Source§

impl AccessScope

Source

pub fn from_constraints(constraints: Vec<ScopeConstraint>) -> Self

Create an access scope from a list of constraints (OR-ed).

Source

pub fn single(constraint: ScopeConstraint) -> Self

Create an access scope with a single constraint.

Source

pub fn allow_all() -> Self

Create an “allow all” (unconstrained) scope.

This represents a legitimate PDP decision with no row-level filtering. Not a bypass — it’s a valid authorization outcome.

Source

pub fn deny_all() -> Self

Create a “deny all” scope (no access).

Source

pub fn for_tenants(ids: Vec<Uuid>) -> Self

Create a scope for a set of tenant IDs.

Source

pub fn for_tenant(id: Uuid) -> Self

Create a scope for a single tenant ID.

Source

pub fn for_resources(ids: Vec<Uuid>) -> Self

Create a scope for a set of resource IDs.

Source

pub fn for_resource(id: Uuid) -> Self

Create a scope for a single resource ID.

Source

pub fn constraints(&self) -> &[ScopeConstraint]

The constraints in this scope (OR-ed).

Source

pub fn is_unconstrained(&self) -> bool

Returns true if this scope is unconstrained (allow-all).

Source

pub fn is_deny_all(&self) -> bool

Returns true if this scope denies all access.

A scope is deny-all when it is not unconstrained and has no constraints.

Source

pub fn all_values_for(&self, property: &str) -> Vec<&ScopeValue>

Collect all values for a given property across all constraints.

Reports on the constraint list only. An allow-all scope has no constraints, so this returns an empty Vec for it — which means “no constraint names this property”, never “this scope permits nothing”. An allow-all scope permits every value, and no finite list can say so. Check AccessScope::is_unconstrained before reading anything into an empty result.

Source

pub fn all_uuid_values_for(&self, property: &str) -> Vec<Uuid>

Collect all UUID values for a given property across all constraints.

Convenience wrapper — skips non-UUID values.

Reports on the constraint list only, with the same caveat as AccessScope::all_values_for: empty on an allow-all scope, which permits everything rather than nothing.

Source

pub fn contains_uuid(&self, property: &str, id: Uuid) -> bool

Whether any filter, in any constraint, names property with this UUID.

Matches both ScopeValue::Uuid and ScopeValue::String variants so that UUID-as-string values are treated consistently with AccessScope::all_uuid_values_for, which also parses strings via ScopeValue::as_uuid.

§This is not an authorization decision

It searches filter values. It does not evaluate a constraint, which is a conjunction: for a grant of [owner_tenant_id = A AND owner_id = Alice] this answers true for (owner_tenant_id, A) even when the row in question belongs to Bob. A true here means “the scope mentions this value somewhere”, nothing more.

It also reports on the constraint list alone, so an allow-all scope — which has no constraints — answers false for a value it permits, and a subquery filter (InGroup, InGroupSubtree, InTenantSubtree) exposes no in-memory values at all, so it answers false for a grant that does apply.

Authorize a write by passing the scope to the insert and letting SecureORM evaluate it — validate_insert_scope ANDs across the filters of a constraint and ORs across constraints, which is the whole decision.

Not marked #[deprecated] yet: the workspace builds with -D warnings, so the attribute would break the build at all of its current call sites at once. It goes on once the three gear gates (resource-group, ledger, pricing) have moved to SecureORM.

Source

pub fn has_property(&self, property: &str) -> bool

Check if any constraint references the given property.

Reports on the constraint list only: an allow-all scope has no constraints and so answers false, which is not a statement about what it permits. Check AccessScope::is_unconstrained first.

Source

pub fn tenant_only(&self) -> Self

Create a new scope retaining only owner_tenant_id filters.

Useful for entities declared with no_owner (e.g., messages, reactions), where owner_id constraints cannot be resolved and would cause fail-closed deny-all behaviour.

  • Unconstrained scopes become deny-all (fail-closed).
  • Constraints that contain no owner_tenant_id filter are dropped entirely.
  • If all constraints are dropped, the result is deny-all.
§This widens the grant, by design — check that you want it

Filters on other properties are removed from surviving constraints, and a constraint is a conjunction, so dropping one of its terms admits everything that term excluded. [owner_tenant_id = T, id IN (r1)] becomes owner_tenant_id = T: one resource turned into the whole tenant.

That is correct for the case this exists for — re-targeting a scope at a different entity, one with no owner_id/id column of its own, where the removed terms never applied to the rows being filtered. It is wrong if you are narrowing a scope for the same entity, and the resulting scope must not be the only thing authorizing the access: mini-chat, for example, checks the parent chat against the full scope first and only then uses tenant_only() for its messages.

Source

pub fn tenant_and_owner(&self) -> Self

Create a new scope retaining only owner_tenant_id and owner_id filters.

Useful for entities that have both tenant and owner columns but no resource-level constraints (e.g., reactions scoped to the acting user).

  • Unconstrained scopes become deny-all (fail-closed).
  • Constraints that contain neither retained property are dropped.
  • Filters on other properties are removed from surviving constraints, which widens them — see the warning on AccessScope::tenant_only; it applies here in full.
  • If all constraints are dropped, the result is deny-all.
Source

pub fn ensure_owner(&self, owner_id: Uuid) -> Self

Create a new scope that guarantees an owner_id equality filter matching exactly the supplied owner_id is present in every constraint.

Intersection semantics: if a constraint already contains an owner_id filter, the supplied value must be among its values — otherwise the constraint is dropped. When it matches, the filter is narrowed to exactly that single value.

  • Unconstrained → single constraint with only the owner_id filter.
  • Deny-all → stays deny-all.
  • No existing owner filter → owner_id is injected.
  • Existing owner filter containing owner_id → narrowed to Eq.
  • Existing owner filter NOT containing owner_id → constraint dropped (constraints use OR semantics, so dropping one narrows access; dropping all yields deny-all).

Use this as a defence-in-depth measure for user-owned resources when the PDP may not always return owner_id constraints or may return a broader set than the current subject.

Trait Implementations§

Source§

impl Clone for AccessScope

Source§

fn clone(&self) -> AccessScope

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 AccessScope

Source§

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

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

impl Default for AccessScope

Source§

fn default() -> Self

Default is deny-all: no constraints and not unconstrained.

Source§

impl PartialEq for AccessScope

Source§

fn eq(&self, other: &AccessScope) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for AccessScope

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