Skip to main content

SecureSelect

Struct SecureSelect 

Source
pub struct SecureSelect<E: EntityTrait, S> { /* private fields */ }
Expand description

A type-safe wrapper around SeaORM’s Select that enforces scoping.

This wrapper uses the typestate pattern to ensure that queries cannot be executed without first applying access control via .scope_with().

When scoped (SecureSelect<E, Scoped>), the query carries the AccessScope internally. This allows related-entity queries (find_also_related, find_with_related) to automatically apply the same scope to related entities without requiring the scope to be passed again.

§Type Parameters

  • E: The SeaORM entity type
  • S: The typestate (Unscoped or Scoped)

§Example

use modkit_db::secure::{AccessScope, SecureEntityExt};

let scope = AccessScope::for_tenants(vec![tenant_id]);
let users = user::Entity::find()
    .secure()           // Returns SecureSelect<E, Unscoped>
    .scope_with(&scope) // Returns SecureSelect<E, Scoped>
    .all(conn)          // Now can execute
    .await?;

// Related queries auto-apply scope:
let orders_with_items = Order::find()
    .secure()
    .scope_with(&scope)
    .find_with_related(line_item::Entity)  // scope auto-applied to LineItem
    .all(conn)
    .await?;

Implementations§

Source§

impl<E> SecureSelect<E, Unscoped>

Source

pub fn scope_with(self, scope: &AccessScope) -> SecureSelect<E, Scoped>

Apply access control scope to this query, transitioning to the Scoped state.

The scope is stored internally and will be automatically applied to any related-entity queries (e.g., find_also_related, find_with_related).

This applies the implicit policy:

  • Empty scope → deny all
  • Tenants only → filter by tenant
  • Resources only → filter by resource IDs
  • Both → AND them together
Source

pub fn scope_with_arc(self, scope: Arc<AccessScope>) -> SecureSelect<E, Scoped>

Apply access control scope using an Arc<AccessScope>.

This is useful when you already have the scope in an Arc and want to avoid an extra clone.

Source§

impl<E> SecureSelect<E, Scoped>
where E: EntityTrait,

Source

pub async fn all( self, runner: &impl DBRunner, ) -> Result<Vec<E::Model>, ScopeError>

Execute the query and return all matching results.

§Errors

Returns ScopeError::Db if the database query fails.

Source

pub async fn one( self, runner: &impl DBRunner, ) -> Result<Option<E::Model>, ScopeError>

Execute the query and return at most one result.

§Errors

Returns ScopeError::Db if the database query fails.

Source

pub async fn count(self, runner: &impl DBRunner) -> Result<u64, ScopeError>

Execute the query and return the number of matching results.

§Errors

Returns ScopeError::Db if the database query fails.

Source

pub fn and_id(self, id: Uuid) -> Result<Self, ScopeError>

Add an additional filter for a specific resource ID.

This is useful when you want to further narrow a scoped query to a single resource.

§Example
let user = User::find()
    .secure()
    .scope_with(&scope)?
    .and_id(user_id)
    .one(conn)
    .await?;
§Errors

Returns ScopeError::Invalid if the entity doesn’t have a resource column.

Source§

impl<E> SecureSelect<E, Scoped>
where E: EntityTrait,

Source

pub fn filter(self, filter: Condition) -> Self

Add additional filters to the scoped query. The scope conditions remain in place.

Source

pub fn order_by<C>(self, col: C, order: Order) -> Self
where C: IntoSimpleExpr,

Add ordering to the scoped query.

Source

pub fn limit(self, limit: u64) -> Self

Add a limit to the scoped query.

Source

pub fn offset(self, offset: u64) -> Self

Add an offset to the scoped query.

Source

pub fn and_scope_for<J>(self, scope: &AccessScope) -> Self

Apply scoping for a joined entity.

This delegates to build_scope_condition::<J>() which handles all property types (tenant, resource, owner, custom PEP properties) with proper OR/AND constraint semantics.

§Example
// Select orders, ensuring both Order and Customer match scope
Order::find()
    .secure()
    .scope_with(&scope)?
    .and_scope_for::<customer::Entity>(&scope)
    .all(conn)
    .await?
Source

pub fn scope_via_exists<J>(self, scope: &AccessScope) -> Self

Apply scoping via EXISTS subquery on a related entity.

This is particularly useful when the base entity doesn’t have a tenant column but is related to one that does.

This delegates to build_scope_condition::<J>() for the EXISTS subquery, handling all property types with proper OR/AND constraint semantics.

§Note

This is a simplified EXISTS check (no join predicate linking back to the primary entity). For complex join predicates, use into_inner() and build custom EXISTS clauses.

§Example
// Find settings that exist in a scoped relationship
GlobalSetting::find()
    .secure()
    .scope_with(&AccessScope::for_resources(vec![]))?
    .scope_via_exists::<TenantSetting>(&scope)
    .all(conn)
    .await?
Source

pub fn into_inner(self) -> Select<E>

Unwrap the inner SeaORM Select for advanced use cases.

§Safety

The caller must ensure they don’t remove or bypass the security conditions that were applied during .scope_with().

Source§

impl<E> SecureSelect<E, Scoped>
where E: EntityTrait,

Source

pub fn scope(&self) -> &AccessScope

Get a reference to the stored scope.

This is useful when you need to pass the scope to other secure operations.

Source

pub fn scope_arc(&self) -> Arc<AccessScope>

Get the stored scope as an Arc.

This is useful when you need to share the scope without cloning.

Find related entities using find_also_related with automatic scoping.

This executes a LEFT JOIN to fetch the primary entity along with an optional related entity. The related entity will be None if no matching row exists.

§Automatic Scoping
  • The primary entity E is already scoped by the parent SecureSelect.
  • The related entity R will automatically have tenant filtering applied if it has a tenant column (i.e., R::tenant_col() returns Some).
  • For global entities (those with #[secure(no_tenant)]), no additional filtering is applied — the scoping becomes a no-op automatically.

This unified API handles both tenant-scoped and global entities transparently. The caller does not need to know or care whether the related entity is tenant-scoped or global.

§Entity Requirements

All entities used with this method must derive Scopable. For global entities, use #[secure(no_tenant, no_resource, no_owner, no_type)].

§Example
let scope = AccessScope::for_tenants(vec![tenant_id]);

// Tenant-scoped related entity - scope is auto-applied to Customer
let rows: Vec<(order::Model, Option<customer::Model>)> = Order::find()
    .secure()
    .scope_with(&scope)
    .find_also_related(customer::Entity)
    .all(db)
    .await?;

// Global related entity (no tenant column) - no filtering applied to GlobalConfig
let rows: Vec<(order::Model, Option<global_config::Model>)> = Order::find()
    .secure()
    .scope_with(&scope)
    .find_also_related(global_config::Entity)  // same API, auto no-op!
    .all(db)
    .await?;

Find all related entities using find_with_related with automatic scoping.

This executes a query to fetch the primary entity along with all its related entities (one-to-many relationship).

§Automatic Scoping
  • The primary entity E is already scoped by the parent SecureSelect.
  • The related entity R will automatically have tenant filtering applied if it has a tenant column (i.e., R::tenant_col() returns Some).
  • For global entities (those with #[secure(no_tenant)]), no additional filtering is applied — the scoping becomes a no-op automatically.

This unified API handles both tenant-scoped and global entities transparently. The caller does not need to know or care whether the related entity is tenant-scoped or global.

§Entity Requirements

All entities used with this method must derive Scopable. For global entities, use #[secure(no_tenant, no_resource, no_owner, no_type)].

§Example
let scope = AccessScope::for_tenants(vec![tenant_id]);

// Tenant-scoped related entity - scope is auto-applied to LineItem
let rows: Vec<(order::Model, Vec<line_item::Model>)> = Order::find()
    .secure()
    .scope_with(&scope)
    .find_with_related(line_item::Entity)
    .all(db)
    .await?;

// Global related entity (no tenant column) - no filtering applied to SystemTag
let rows: Vec<(order::Model, Vec<system_tag::Model>)> = Order::find()
    .secure()
    .scope_with(&scope)
    .find_with_related(system_tag::Entity)  // same API, auto no-op!
    .all(db)
    .await?;

Trait Implementations§

Source§

impl<E: Clone + EntityTrait, S: Clone> Clone for SecureSelect<E, S>

Source§

fn clone(&self) -> SecureSelect<E, S>

Returns a duplicate of the value. Read more
1.0.0 · Source§

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

Performs copy-assignment from source. Read more
Source§

impl<E: Debug + EntityTrait, S: Debug> Debug for SecureSelect<E, S>

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<E, S> Freeze for SecureSelect<E, S>
where S: Freeze,

§

impl<E, S> !RefUnwindSafe for SecureSelect<E, S>

§

impl<E, S> Send for SecureSelect<E, S>
where S: Send,

§

impl<E, S> Sync for SecureSelect<E, S>
where S: Sync,

§

impl<E, S> Unpin for SecureSelect<E, S>
where S: Unpin, E: Unpin,

§

impl<E, S> UnsafeUnpin for SecureSelect<E, S>
where S: UnsafeUnpin,

§

impl<E, S> !UnwindSafe for SecureSelect<E, S>

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> Paint for T
where T: ?Sized,

Source§

fn fg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the foreground set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like red() and green(), which have the same functionality but are pithier.

§Example

Set foreground color to white using fg():

use yansi::{Paint, Color};

painted.fg(Color::White);

Set foreground color to white using white().

use yansi::Paint;

painted.white();
Source§

fn primary(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Primary].

§Example
println!("{}", value.primary());
Source§

fn fixed(&self, color: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Fixed].

§Example
println!("{}", value.fixed(color));
Source§

fn rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Rgb].

§Example
println!("{}", value.rgb(r, g, b));
Source§

fn black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Black].

§Example
println!("{}", value.black());
Source§

fn red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Red].

§Example
println!("{}", value.red());
Source§

fn green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Green].

§Example
println!("{}", value.green());
Source§

fn yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Yellow].

§Example
println!("{}", value.yellow());
Source§

fn blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Blue].

§Example
println!("{}", value.blue());
Source§

fn magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Magenta].

§Example
println!("{}", value.magenta());
Source§

fn cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Cyan].

§Example
println!("{}", value.cyan());
Source§

fn white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: White].

§Example
println!("{}", value.white());
Source§

fn bright_black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlack].

§Example
println!("{}", value.bright_black());
Source§

fn bright_red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightRed].

§Example
println!("{}", value.bright_red());
Source§

fn bright_green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightGreen].

§Example
println!("{}", value.bright_green());
Source§

fn bright_yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightYellow].

§Example
println!("{}", value.bright_yellow());
Source§

fn bright_blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlue].

§Example
println!("{}", value.bright_blue());
Source§

fn bright_magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.bright_magenta());
Source§

fn bright_cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightCyan].

§Example
println!("{}", value.bright_cyan());
Source§

fn bright_white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightWhite].

§Example
println!("{}", value.bright_white());
Source§

fn bg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the background set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like on_red() and on_green(), which have the same functionality but are pithier.

§Example

Set background color to red using fg():

use yansi::{Paint, Color};

painted.bg(Color::Red);

Set background color to red using on_red().

use yansi::Paint;

painted.on_red();
Source§

fn on_primary(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Primary].

§Example
println!("{}", value.on_primary());
Source§

fn on_fixed(&self, color: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Fixed].

§Example
println!("{}", value.on_fixed(color));
Source§

fn on_rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Rgb].

§Example
println!("{}", value.on_rgb(r, g, b));
Source§

fn on_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Black].

§Example
println!("{}", value.on_black());
Source§

fn on_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Red].

§Example
println!("{}", value.on_red());
Source§

fn on_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Green].

§Example
println!("{}", value.on_green());
Source§

fn on_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Yellow].

§Example
println!("{}", value.on_yellow());
Source§

fn on_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Blue].

§Example
println!("{}", value.on_blue());
Source§

fn on_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Magenta].

§Example
println!("{}", value.on_magenta());
Source§

fn on_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Cyan].

§Example
println!("{}", value.on_cyan());
Source§

fn on_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: White].

§Example
println!("{}", value.on_white());
Source§

fn on_bright_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlack].

§Example
println!("{}", value.on_bright_black());
Source§

fn on_bright_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightRed].

§Example
println!("{}", value.on_bright_red());
Source§

fn on_bright_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightGreen].

§Example
println!("{}", value.on_bright_green());
Source§

fn on_bright_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightYellow].

§Example
println!("{}", value.on_bright_yellow());
Source§

fn on_bright_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlue].

§Example
println!("{}", value.on_bright_blue());
Source§

fn on_bright_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.on_bright_magenta());
Source§

fn on_bright_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightCyan].

§Example
println!("{}", value.on_bright_cyan());
Source§

fn on_bright_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightWhite].

§Example
println!("{}", value.on_bright_white());
Source§

fn attr(&self, value: Attribute) -> Painted<&T>

Enables the styling Attribute value.

This method should be used rarely. Instead, prefer to use attribute-specific builder methods like bold() and underline(), which have the same functionality but are pithier.

§Example

Make text bold using attr():

use yansi::{Paint, Attribute};

painted.attr(Attribute::Bold);

Make text bold using using bold().

use yansi::Paint;

painted.bold();
Source§

fn bold(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Bold].

§Example
println!("{}", value.bold());
Source§

fn dim(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Dim].

§Example
println!("{}", value.dim());
Source§

fn italic(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Italic].

§Example
println!("{}", value.italic());
Source§

fn underline(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Underline].

§Example
println!("{}", value.underline());

Returns self with the attr() set to [Attribute :: Blink].

§Example
println!("{}", value.blink());

Returns self with the attr() set to [Attribute :: RapidBlink].

§Example
println!("{}", value.rapid_blink());
Source§

fn invert(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Invert].

§Example
println!("{}", value.invert());
Source§

fn conceal(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Conceal].

§Example
println!("{}", value.conceal());
Source§

fn strike(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Strike].

§Example
println!("{}", value.strike());
Source§

fn quirk(&self, value: Quirk) -> Painted<&T>

Enables the yansi Quirk value.

This method should be used rarely. Instead, prefer to use quirk-specific builder methods like mask() and wrap(), which have the same functionality but are pithier.

§Example

Enable wrapping using .quirk():

use yansi::{Paint, Quirk};

painted.quirk(Quirk::Wrap);

Enable wrapping using wrap().

use yansi::Paint;

painted.wrap();
Source§

fn mask(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Mask].

§Example
println!("{}", value.mask());
Source§

fn wrap(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Wrap].

§Example
println!("{}", value.wrap());
Source§

fn linger(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Linger].

§Example
println!("{}", value.linger());
Source§

fn clear(&self) -> Painted<&T>

👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear(). The clear() method will be removed in a future release.

Returns self with the quirk() set to [Quirk :: Clear].

§Example
println!("{}", value.clear());
Source§

fn resetting(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Resetting].

§Example
println!("{}", value.resetting());
Source§

fn bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Bright].

§Example
println!("{}", value.bright());
Source§

fn on_bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: OnBright].

§Example
println!("{}", value.on_bright());
Source§

fn whenever(&self, value: Condition) -> Painted<&T>

Conditionally enable styling based on whether the Condition value applies. Replaces any previous condition.

See the crate level docs for more details.

§Example

Enable styling painted only when both stdout and stderr are TTYs:

use yansi::{Paint, Condition};

painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);
Source§

fn new(self) -> Painted<Self>
where Self: Sized,

Create a new Painted with a default Style. Read more
Source§

fn paint<S>(&self, style: S) -> Painted<&Self>
where S: Into<Style>,

Apply a style wholesale to self. Any previous style is replaced. 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 = 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