Ash dropping FilterCheck if there's `authorize_if always()`

Hi, I've got a resource that in it's action has:
authorize_if MyApp.FilterByActorId
authorize_if always()
authorize_if MyApp.FilterByActorId
authorize_if always()
Previously there were other checks instead of always() but we've loosened our constraints. When we've changed it to always() the FilterByActorID stopped applying My question is, is this the intended behaviour or is it a bug?
10 Replies
ZachDaniel
ZachDaniel2y ago
That’s intended. Nothing in that policy will forbid the request anymore. I.e if the actors if matches the filter then the policy passes, and then it always passes Policies logically apply from top to bottom
Myrmyr
MyrmyrOP2y ago
Thanks!
barnabasj
barnabasj2y ago
Just to make sure, if the filter is something like expr(owner_id == ^actor(:id)) that filter would not be applied to reads anymore in this case?
ZachDaniel
ZachDaniel2y ago
Yep. You can rewrite the policies to make it apply still It’s just the way that the policy logically works when it just contains two authorize_if statements and the last one supersedes the first one.
barnabasj
barnabasj2y ago
Maybe something should be added to the checks section https://ash-hq.org/docs/guides/ash/latest/topics/policies#checks, because there it looks like the first authorize_if wins, but the filter is handled differently, because it needs to be run later with the data,i guess? Essentially filter_checks only make sense in their own policy block?
Ash HQ
Guide: Policies
Read the "Policies" guide on Ash HQ
ZachDaniel
ZachDaniel2y ago
well, thats not the issue When reading policies, you want to imagine you're reading instructions from top to bottom, and constructing a statement
authorize_if MyApp.FilterByActorId # a better name would be `ActorIdMatches`
authorize_if always()
authorize_if MyApp.FilterByActorId # a better name would be `ActorIdMatches`
authorize_if always()
And you want to imagine its from the perspective of an individual record, not a group of records. authorize_if cannot produce a forbidden result (but if nothing at the end has produced an :authorized result, then it is forbidden. So the statement in procedural logic for each record might look something like this:
cond do
ActorIdMatches.check(record) ->
:authorized
true ->
# authorize_if always() is like a catch all
:authorized
end
cond do
ActorIdMatches.check(record) ->
:authorized
true ->
# authorize_if always() is like a catch all
:authorized
end
So when we build a filter statement, there is no need to add a filter. Because you can see everything Without authorize_if always() at the end, ActorIdMatches would return :unknown, and :unknown at the end is :forbidden. So for an individual record that would be like:
cond do
ActorIdMatches.check(record) ->
:authorized
true ->
:forbidden
end
cond do
ActorIdMatches.check(record) ->
:authorized
true ->
:forbidden
end
So the only branch that this should return true for is the one that filters by id.
barnabasj
barnabasj2y ago
yeah, ok I'm getting it now I think. either the filter check is ok and it is authorized or if it is not it is still authorized because of the always case
ZachDaniel
ZachDaniel2y ago
yep!
barnabasj
barnabasj2y ago
Thanks again for clearing things up for me
ZachDaniel
ZachDaniel2y ago
any time 😄

Did you find this page helpful?