# `AshGrant.FilterCheck`
[🔗](https://github.com/jhlee111/ash_grant/blob/v0.14.1/lib/ash_grant/checks/filter_check.ex#L1)

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 {: .info}
>
> 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:
- `:read` actions
- 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
    end

## Options

| Option | Type | Description |
|--------|------|-------------|
| `:action` | string | Override action name for permission matching |
| `:resource` | string | Override resource name for permission matching |

## How It Works

1. **Resolve permissions**: Calls the configured `PermissionResolver` to get
   the actor's permissions
2. **Get all scopes**: Uses `AshGrant.Evaluator.get_all_scopes/3` to find
   all matching scopes (respecting deny-wins semantics)
3. **Check for global access**: If scopes include "always", "all", or "global",
   returns `true` (no filter needed)
4. **Resolve scopes to filters**: Uses inline scope DSL or `ScopeResolver`
   to get filter expressions
5. **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 == :published

This 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")
    end

## Filter 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 actions
- `AshGrant.Evaluator` - Permission evaluation logic
- `AshGrant.Info` - DSL introspection helpers

# `auto_filter`

# `auto_filter_not`

# `check`

# `conflicts?`

Determines if two check references conflict (are mutually exclusive).

AshGrant filter checks don't inherently conflict with each other.
Returns `false` for all cases.

# `eager_evaluate?`

# `expand_description`

# `filter_check`

Creates a filter check tuple for use in policies.

# `implies?`

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.

# `init`

# `prefer_expanded_description?`

# `reject`

# `requires_original_data?`

# `simplify`

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.

# `strict_check`

# `strict_check_context`

# `type`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
