Add CustomFieldValueCreateScope check for create actions

Ash cannot apply filters to create; this check enforces :linked/:all scope
via strict_check only (no filter).
This commit is contained in:
Moritz 2026-01-27 13:40:17 +01:00 committed by moritz
parent 8f5f69744c
commit c7c6b318ac

View file

@ -0,0 +1,64 @@
defmodule Mv.Authorization.Checks.CustomFieldValueCreateScope do
@moduledoc """
Policy check for CustomFieldValue create actions only.
Use this for create instead of HasPermission because Ash cannot apply
filters to create actions ("Cannot use a filter to authorize a create").
This check performs the same scope logic as HasPermission for create
(PermissionSets + :linked/:all) but only implements strict_check, so it
never adds a filter.
Used in CustomFieldValue policies:
policy action_type(:create) do
authorize_if Mv.Authorization.Checks.CustomFieldValueCreateScope
end
"""
use Ash.Policy.Check
alias Mv.Authorization.PermissionSets
require Logger
@impl true
def describe(_opts),
do: "CustomFieldValue create allowed by permission set scope (:linked or :all)"
@impl true
def strict_check(actor, authorizer, _opts) do
actor = ensure_role_loaded(actor)
with %{role: %{permission_set_name: ps_name}} when not is_nil(ps_name) <- actor,
{:ok, ps_atom} <- PermissionSets.permission_set_name_to_atom(ps_name),
permissions <- PermissionSets.get_permissions(ps_atom),
perm <- find_custom_field_value_create(permissions.resources) do
case perm do
nil -> {:ok, false}
%{scope: :all} -> {:ok, true}
%{scope: :linked} -> {:ok, member_id_matches?(authorizer, actor)}
end
else
_ -> {:ok, false}
end
end
defp find_custom_field_value_create(resources) do
Enum.find(resources, fn p ->
p.resource == "CustomFieldValue" and p.action == :create and p.granted
end)
end
defp member_id_matches?(authorizer, actor) do
member_id = get_create_member_id(authorizer)
!is_nil(member_id) and member_id == actor.member_id
end
defp get_create_member_id(authorizer) do
changeset = authorizer.changeset || authorizer.subject
if changeset && function_exported?(Ash.Changeset, :get_attribute, 2) do
Ash.Changeset.get_attribute(changeset, :member_id)
else
nil
end
end
defp ensure_role_loaded(actor), do: Mv.Authorization.Actor.ensure_loaded(actor)
end