ash-project / ash-project/ash

Reenable filter predicate analysis prior to authorization

Open
#1,286 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Elixir
Stars
2.5k
Forks
426
Avg merge
23h 26m
Merged PRs (30d)
46

Description

Something that we used to do, but turned off (for various reasons I won't go into here), is analysis of filter statements, using filter predicate comparison. The code that powers this is still available and in use for functions like:

`Ash.Query.subset_of(query, expr)`

## How does this help with policies?

This can help significantly with the performance around authorizing access to data. For example, lets say you have a policy like this:

```elixir
policy action_type(:read) do
authorize_if expr(owner_id == ^actor(:id))
end
```

And an actor, like this `%User{id: 1}`

Then, you make a request:

```elixir
Resource
|> Ash.Query.filter(owner_id == 2)
|> Ash.read!(actor: %User{id: 1})
```

What we will currently do is produce this query ultimately:

```elixir
Resource
|> Ash.Query.filter(owner_id == 2 and owner_id == 1)
|> Ash.read!()
```

However, we can return to leveraging our filter predicate comparison. Here is an example implementation of comparing to equality filter predicates:

```elixir
@impl Ash.Filter.Predicate
def compare(
%__MODULE__{left: left, right: left_right},
%__MODULE__{left: left, right: right_right}
) do
if Comp.equal?(left_right, right_right) do
:mutually_inclusive
else
:mutually_exclusive
end
end
```

We can use this code to determine that `x == 1` and `x == 2` is *mutually exclusive*, and determine that it is *therefore not possible for policies to pass*.

At this point, we will skip running the query entirely, and instead return an empty data set. This means that forbidden requests can cost practically zero performance penalty against an API 🥳

We have the tools to do this, we just need to reenable it and test it :)

Contributor guide

Open the contributing guide

Research direction

Start by tracing authorization's use of Ash.Query.subset_of/2 and the Ash.Filter.Predicate.compare callbacks. Reproduce the mutually exclusive filters shown in the issue, then inspect the existing authorization tests or add coverage for returning an empty data set without running the query. Done means compatible filters still authorize normally and contradictory policy/request filters are short-circuited.

Written by the indexing model from the issue text.

Assessment

Tech stack
elixir
Domain
authorization, performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.