fix: handle atomic_ref templates and changing_attributes/1

This commit is contained in:
Zach Daniel 2024-02-14 13:55:19 -05:00
parent e9d2d8c575
commit f23f0a29fe
5 changed files with 96 additions and 44 deletions

View file

@ -904,7 +904,7 @@ defmodule Ash.Api do
end
defp alter_source({:ok, true, query}, api, actor, %Ash.Changeset{} = subject, opts) do
case alter_source({:ok, true}, api, actor, subject, opts) do
case alter_source({:ok, true}, api, actor, subject, Keyword.put(opts, :base_query, query)) do
{:ok, true, new_subject} -> {:ok, true, new_subject, query}
other -> other
end
@ -939,6 +939,45 @@ defmodule Ash.Api do
case subject do
%Ash.Query{} = query ->
alter_query(query, authorizer, authorizer_state, context)
%Ash.Changeset{} = changeset ->
context = Map.put(context, :changeset, changeset)
with {:ok, changeset, authorizer_state} <-
Ash.Authorizer.add_calculations(
authorizer,
changeset,
authorizer_state,
context
) do
if opts[:base_query] do
case alter_query(opts[:base_query], authorizer, authorizer_state, context) do
{:ok, true, query} ->
{:ok, true, changeset, query}
other ->
other
end
else
{:ok, true, changeset}
end
end
%Ash.ActionInput{} = subject ->
{:ok, true, subject}
end
end
)
end
else
{:ok, true}
end
end
defp alter_source(other, _, _, _, _), do: other
defp alter_query(query, authorizer, authorizer_state, context) do
context = Map.put(context, :query, query)
with {:ok, query, _} <-
@ -969,33 +1008,8 @@ defmodule Ash.Api do
) do
{:ok, true, %{query | filter: hydrated, sort: new_sort}}
end
%Ash.Changeset{} = changeset ->
context = Map.put(context, :changeset, changeset)
with {:ok, changeset, _} <-
Ash.Authorizer.add_calculations(
authorizer,
changeset,
authorizer_state,
context
) do
{:ok, true, changeset}
end
%Ash.ActionInput{} = subject ->
{:ok, true, subject}
end
end
)
end
else
{:ok, true}
end
end
defp alter_source(other, _, _, _, _), do: other
defp run_check(api, actor, subject, opts) do
authorizers =
Ash.Resource.Info.authorizers(subject.resource)

View file

@ -601,6 +601,8 @@ defmodule Ash.Filter do
{:_atomic_ref, field} when is_atom(field) ->
if changeset do
Ash.Changeset.atomic_ref(changeset, field)
else
{:_atomic_ref, field}
end
{:_context, fields} when is_list(fields) ->

View file

@ -720,6 +720,15 @@ defmodule Ash.Filter.Runtime do
end)
end
defp resolve_expr({:_actor, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_arg, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_ref, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_ref, _, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_parent, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_parent, _, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_atomic_ref, _}, _, _, _, _), do: :unknown
defp resolve_expr({:_context, _}, _, _, _, _), do: :unknown
defp resolve_expr(other, _, _, _, _), do: {:ok, other}
defp try_cast_arguments(:var_args, args) do

View file

@ -1051,7 +1051,7 @@ defmodule Ash.Policy.Authorizer do
{{check_module, check_opts}, true} ->
result =
try do
check_module.auto_filter(authorizer.actor, authorizer, check_opts)
nil_to_false(check_module.auto_filter(authorizer.actor, authorizer, check_opts))
rescue
e ->
reraise Ash.Error.to_ash_error(e, __STACKTRACE__,
@ -1071,9 +1071,16 @@ defmodule Ash.Policy.Authorizer do
result =
try do
if :erlang.function_exported(check_module, :auto_filter_not, 3) do
nil_to_false(
check_module.auto_filter_not(authorizer.actor, authorizer, check_opts)
)
else
[not: check_module.auto_filter(authorizer.actor, authorizer, check_opts)]
[
not:
nil_to_false(
check_module.auto_filter(authorizer.actor, authorizer, check_opts)
)
]
end
rescue
e ->
@ -1103,6 +1110,9 @@ defmodule Ash.Policy.Authorizer do
end)
end
defp nil_to_false(nil), do: false
defp nil_to_false(v), do: v
def print_tuple_boolean({op, l, r}) when op in [:and, :or] do
"(#{print_tuple_boolean(l)} #{op} #{print_tuple_boolean(r)})"
end
@ -1148,12 +1158,15 @@ defmodule Ash.Policy.Authorizer do
{{check_module, check_opts}, required_status} ->
additional_filter =
if required_status do
check_module.auto_filter(authorizer.actor, authorizer, check_opts)
nil_to_false(check_module.auto_filter(authorizer.actor, authorizer, check_opts))
else
if :erlang.function_exported(check_module, :auto_filter_not, 3) do
check_module.auto_filter_not(authorizer.actor, authorizer, check_opts)
nil_to_false(check_module.auto_filter_not(authorizer.actor, authorizer, check_opts))
else
[not: check_module.auto_filter(authorizer.actor, authorizer, check_opts)]
[
not:
nil_to_false(check_module.auto_filter(authorizer.actor, authorizer, check_opts))
]
end
end

View file

@ -40,16 +40,30 @@ defmodule Ash.Policy.Check.ChangingAttributes do
{:cont, expr}
{{:ok, from}, {:ok, to}} ->
if expr == true do
{:cont,
Ash.Expr.expr(not (ref(attribute) == ^from and ^atomic_ref(attribute) == ^to))}
else
{:cont,
Ash.Expr.expr(
^expr and not (ref(attribute) == ^from and ^atomic_ref(attribute) == ^to)
)
)}
end
{{:ok, from}, :error} ->
if expr == true do
{:cont, Ash.Expr.expr(ref(attribute) != ^from)}
else
{:cont, Ash.Expr.expr(^expr and ref(attribute) != ^from)}
end
{:error, {:ok, to}} ->
if expr == true do
{:cont, Ash.Expr.expr(^atomic_ref(attribute) != ^to)}
else
{:cont, Ash.Expr.expr(^expr and ^atomic_ref(attribute) != ^to)}
end
end
else
{:cont, expr}
end