AE
Ash Elixir•2y ago
xSHYNE

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
authorize_if(relates_to_actor_via([:group, :group_admin]))
authorize_if(relates_to_actor_via([:group, :group_admin]))
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
ZachDaniel
ZachDaniel•2y ago
In your simple check you get access to the changeset
def match?(actor, %{changeset: changeset}, context) do

end
def match?(actor, %{changeset: changeset}, context) do

end
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.
xSHYNE
xSHYNEOP•2y ago
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 ```
ZachDaniel
ZachDaniel•2y ago
Nice 🙂 And yeah the auto_filter option allows for your check to apply a filter for read actions

Did you find this page helpful?