Whats wrong with writing the custom check as you mentioned originally? Then you don’t have to do it in the controller. The general goal for building with Ash is to put as much of this stuff in the resource, to encapsulate the domain operation happening. Most people using Ash won’t even have controllers because they’ll use an api extension
Well with this:
policy action_type(:read) do
authorize_if(IsOwnerCheck)
end
and this:
defmodule IsOwnerCheck do
use Ash.Policy.SimpleCheck
def match?(actor, context, opts) do
What.To.Do.here?
end
def describe(x) do
x |> IO.inspect(label: ~S/x/)
"hello"
end
end
I am not sure what I should do in the simple check. The context contains a query but If I run the query I get an infinite loop. I cannot use MyApi.can? because that would be an infinite loop as well.
I am not sure if I want the JSON API extension right now. I’all have two or three resources only but with some complicated logic in the backend, I do not feel like a REST API would be good for this. But I might try.
Ah, I was thinking you would want an Ash.Policy.Check
instead of an Ash.Policy.SimpleCheck
. But even then the check
function has you manually filter down the results, like so:
defmodule IsOwnerCheck do
use Ash.Policy.SimpleCheck
def describe(_x) do
"actor is owner"
end
def strict_check(_, _, _) do
:unknown
end
def check(_, records, _) do
# return only the records that match
Enum.filter(records, fn record ->
...matches?
end)
end
end
But that isn’t what you’d want in this case. I think we’d want to expand the policy options to allow you to do something like this as an “after” check, which doesn’t filter and can only forbid access. Barring that, I would suggest the after action hook that returns the forbidden error. Its hard for me to say looking at it why its not working for you, but I’ve got very similar setups in various places.
The simplest answer in your case to get you over the current hurdle would to just put the same logic you’d put in the controller in the after action hook, and skip the Api.can?
step.
Ok so I got it working like you said by putting the logic in the “after”:
read :by_id do
argument(:id, :uuid, allow_nil?: false)
get?(true)
filter(expr(id == ^arg(:id)))
prepare(fn query, %{actor: actor} ->
Ash.Query.after_action(query, fn
_, [] ->
{:ok, []}
_, [single] ->
case single.owner_id == actor.id do
true -> {:ok, [single]}
false -> {:error, Ash.Error.Forbidden.exception([])}
end
end)
end)
end
Next step will be to look in the “allowances” table if there is a record for that inventory and that actor, to also allow
Thank you for your help @zachdaniel !