Policy checks on related resources
I have been looking for quite some time so this may be a noob ish question. I have a couple answers potentially but I'd like to know kind of the standard way to write a policy for a resource where the relationship to that resource is how I would authorize the actor.
For example
Let's say I have a GroupRequest resource which represents a users request to join a group (contains a group_id and user_id)
I also have a Group that has a group_admin relationship which belongs to a user.
I'd only like actions occuring on a GroupRequest to be performed by the group_admin
(and other similar resources such as GroupUser or GroupMembership)
So the way I'd like to authorize the actor is via
which I believe is giving me what I want.
but I can't seem to figure out how to write a policy for a create_action in the same way.
Lets say for a create action I'd like to "destroy" or "update" a group_request and then create the group_membership, but I'm not sure how to write a policy that does this. A simple check seems correct but how do I pass the group_id from the create action into the check? Did I go about this the wrong way?
I also read I could potentially do a calculations field on any "group" related resource which loads the admin and then I do a check with the actor against that? What's the best scenario here?
3 Replies
In your simple check you get access to the changeset
So you can check what they are changing the
group
to/from and look up if they have access to do so
the changeset
will be nil
unless the policy is running against a create/update/destroy action
I generally end up having a special policy for create that does that, and using expression policies for reads/updates/destroys.Ok I'm going to give that a shot, I for some reason tried this and didn't think I had access to the changeset, maybe it was on a different action? but I just checked and clearly I do.
One more short question, I saw on Ash.Policy.Check there is an auto_filter() option .. I'm taking a stab at guessing here... can I use that to always filter by the argument passed in ...
i.e. always filter a group_request by the groupid arg passed in when doing actions?
Ah it was for reads I get the Query instead. durp.
in case anyone else finds it useful: this seemed to work:
```
defmodule Digsync.Accounts.Policies.IsGroupAdmin do
use Ash.Policy.SimpleCheck
alias Digsync.Accounts
alias Digsync.Accounts.Group
alias Digsync.Accounts.GroupRequest
def describe() do
"Check to see if the actor is a group admin"
end
def match?(actor, %{resource: GroupRequest, changeset: changeset}, _opts) do
group = get_group(changeset)
group.group_admin_id == actor.id
end
def match?(actor, %{resource: GroupMembership, changeset: changeset}, _opts) do
group = get_group(changeset)
group.group_adminid == actor.id
end
def match?(, , ), do: false
defp get_group(changeset) do
changeset
|> Ash.Changeset.get_argument(:group)
|> then(&(Group |> Accounts.get!(&1)))
end
end
```
Nice 🙂
And yeah the auto_filter option allows for your check to apply a filter for read actions