FilterCheck for read actions.
This check integrates with Ash's policy system to provide permission-based
authorization for read operations. Unlike AshGrant.Check which returns
true/false, this check returns a filter expression that limits query
results to records the actor has permission to access.
For write actions, use AshGrant.Check instead.
Auto-generated Policies
When using default_policies: true in your resource's ash_grant block,
this check is automatically configured for read actions. You don't need
to manually add it to your policies.
When to Use
Use AshGrant.filter_check/1 for:
:readactions- List/index queries
- Any action where you want to filter results based on permissions
Usage in Policies
policies do
# For all read actions
policy action_type(:read) do
authorize_if AshGrant.filter_check()
end
# For specific read actions with action override
policy action(:list_published) do
authorize_if AshGrant.filter_check(action: "read")
end
endOptions
| Option | Type | Description |
|---|---|---|
:action | string | Override action name for permission matching |
:resource | string | Override resource name for permission matching |
How It Works
- Resolve permissions: Calls the configured
PermissionResolverto get the actor's permissions - Get all scopes: Uses
AshGrant.Evaluator.get_all_scopes/3to find all matching scopes (respecting deny-wins semantics) - Check for global access: If scopes include "always", "all", or "global",
returns
true(no filter needed) - Resolve scopes to filters: Uses inline scope DSL or
ScopeResolverto get filter expressions - Combine filters: Combines all filters with OR logic
Multi-Scope Support
When an actor has permissions with multiple scopes, all scopes are combined:
# Actor has both permissions:
# - "post:*:read:own" → filters to author_id == actor.id
# - "post:*:read:published" → filters to status == :published
# Result: author_id == actor.id OR status == :publishedThis allows users to see both their own posts AND all published posts.
Examples
Basic Usage
# Permission: "post:*:read:always"
# Returns: true (no filter)
# Permission: "post:*:read:own"
# Returns: expr(author_id == ^actor(:id))
# Permission: "post:*:read:published"
# Returns: expr(status == :published)With Custom Action Name
# Ash action is :get_by_slug, but we check "read" permission
policy action(:get_by_slug) do
authorize_if AshGrant.filter_check(action: "read")
endFilter Return Values
The check returns one of:
true- No filtering (actor has "always", "all", or "global" scope)false- Block all (no matching permissions or denied)Ash.Expr.t()- Filter expression to apply to the query
Context Injection
Scopes can use ^context(:key) for injectable values. Pass context via
Ash.Query.set_context/2:
# Scope definition:
scope :today, expr(fragment("DATE(inserted_at) = ?", ^context(:reference_date)))
# Query with injected context:
Post
|> Ash.Query.for_read(:read)
|> Ash.Query.set_context(%{reference_date: ~D[2025-01-15]})
|> Ash.read!(actor: actor)This enables deterministic testing of temporal and parameterized scopes.
See Also
AshGrant.Check- For write actionsAshGrant.Evaluator- Permission evaluation logicAshGrant.Info- DSL introspection helpers
Summary
Functions
Callback implementation for Ash.Policy.Check.auto_filter/3.
Callback implementation for Ash.Policy.Check.check/4.
Determines if two check references conflict (are mutually exclusive).
Callback implementation for Ash.Policy.Check.eager_evaluate?/0.
Callback implementation for Ash.Policy.Check.expand_description/3.
Creates a filter check tuple for use in policies.
Determines if one check reference implies another.
Callback implementation for Ash.Policy.Check.init/1.
Callback implementation for Ash.Policy.Check.prefer_expanded_description?/0.
Callback implementation for Ash.Policy.FilterCheck.reject/3.
Callback implementation for Ash.Policy.Check.requires_original_data?/2.
Simplifies a check reference into a SAT expression of simpler check references.
Callback implementation for Ash.Policy.Check.strict_check/3.
Callback implementation for Ash.Policy.Check.type/0.
Functions
Callback implementation for Ash.Policy.Check.auto_filter/3.
Callback implementation for Ash.Policy.Check.check/4.
Determines if two check references conflict (are mutually exclusive).
AshGrant filter checks don't inherently conflict with each other.
Returns false for all cases.
Callback implementation for Ash.Policy.Check.eager_evaluate?/0.
Callback implementation for Ash.Policy.Check.expand_description/3.
Creates a filter check tuple for use in policies.
Determines if one check reference implies another.
Two AshGrant filter checks imply each other if they have identical options (same action and resource overrides). This helps the SAT solver avoid redundant evaluations.
Callback implementation for Ash.Policy.Check.init/1.
Callback implementation for Ash.Policy.Check.prefer_expanded_description?/0.
Callback implementation for Ash.Policy.FilterCheck.reject/3.
Callback implementation for Ash.Policy.Check.requires_original_data?/2.
Simplifies a check reference into a SAT expression of simpler check references.
For AshGrant filter checks, we return the ref unchanged since permissions are resolved dynamically at runtime and cannot be further decomposed statically.
This callback is used by Ash's SAT solver to optimize policy evaluation.
Callback implementation for Ash.Policy.Check.strict_check/3.
Callback implementation for Ash.Policy.Check.type/0.