defmodule Ash.Policy.Check.RelatingToActor do @moduledoc "This check is true when the specified relationship is being changed to the current actor." use Ash.Policy.FilterCheck @impl true def describe(opts) do "relating this.#{opts[:relationship]} to the actor" end @impl true def filter(nil, _, _), do: false def filter(actor, %{changeset: %Ash.Changeset{} = changeset}, opts) do resource = changeset.resource relationship = Ash.Resource.Info.relationship(resource, opts[:relationship]) if is_nil(Map.get(actor, relationship.destination_attribute)) do false else if relationship.type != :belongs_to do raise "Can only use `belongs_to` relationships in relating_to_actor checks" end if Ash.Changeset.changing_attribute?(changeset, relationship.source_attribute) do if changeset.action.type == :create do actor_value = Map.get(actor, relationship.destination_attribute) case Map.fetch(changeset.attributes, relationship.source_attribute) do {:ok, ^actor_value} -> if Keyword.has_key?(changeset.atomics, relationship.source_attribute) do expr( ^atomic_ref(relationship.source_attribute) == ^Map.get(actor, relationship.destination_attribute) ) else true end _ -> false end else if Keyword.has_key?(changeset.atomics, relationship.source_attribute) do expr( ^atomic_ref(relationship.source_attribute) == ^Map.get(actor, relationship.destination_attribute) ) else Ash.Changeset.get_attribute(changeset, relationship.source_attribute) == Map.get(actor, relationship.destination_attribute) end end else case changeset.relationships[relationship.name] do [{[input], opts}] -> primary_key = Ash.Resource.Info.primary_key(relationship.destination) actor_keys = take_keys(actor, primary_key) input_keys = take_keys(input, primary_key) ((opts[:on_lookup] == :relate and Enum.all?(actor_keys, & &1)) && Enum.all?(input_keys, & &1)) and input_keys == actor_keys _ -> false end end end end def filter(_, _, _), do: false defp take_keys(input, primary_key) do Enum.map(primary_key, fn key -> Map.get(input, key) || Map.get(input, to_string(key)) end) end end