diff --git a/test/policy/field_policies/expr_conditions_test.exs b/test/policy/field_policies/expr_conditions_test.exs new file mode 100644 index 00000000..83dc6bb6 --- /dev/null +++ b/test/policy/field_policies/expr_conditions_test.exs @@ -0,0 +1,178 @@ +defmodule Ash.Test.Policy.FieldPolicy.ExpressionConditionTest do + use ExUnit.Case, async: true + + defmodule Api do + @moduledoc false + use Ash.Api + + resources do + allow_unregistered? true + end + end + + defmodule ResourceWithExprCondition do + use Ash.Resource, + data_layer: Ash.DataLayer.Ets, + authorizers: [Ash.Policy.Authorizer] + + attributes do + uuid_primary_key :id + + attribute :name, :string + end + + field_policies do + field_policy :name, [expr(name == ^actor(:name))] do + authorize_if always() + end + end + + policies do + policy always() do + authorize_if always() + end + end + + code_interface do + define_for Api + + define :create + define :read + end + + actions do + defaults [:create, :read] + end + end + + test "expr condition forbids field if it does not match" do + ResourceWithExprCondition.create!(%{name: "foo"}) + ResourceWithExprCondition.create!(%{name: "bar"}) + + assert [ + %{name: "bar"}, + %{name: %Ash.ForbiddenField{field: :name, type: :attribute}} + ] = + ResourceWithExprCondition.read!( + actor: %{name: "bar"}, + query: ResourceWithExprCondition |> Ash.Query.sort([:name]) + ) + end + + defmodule ResourceWithMultiplePoliciesForOneField do + use Ash.Resource, + data_layer: Ash.DataLayer.Ets, + authorizers: [Ash.Policy.Authorizer] + + attributes do + uuid_primary_key :id + + attribute :name, :string + end + + field_policies do + field_policy :name, [actor_attribute_equals(:admin, true)] do + authorize_if always() + end + + field_policy :name, expr(name == ^actor(:name)) do + authorize_if always() + end + end + + policies do + policy always() do + authorize_if always() + end + end + + code_interface do + define_for Api + + define :create + define :read + end + + actions do + defaults [:create, :read] + end + end + + test "multiple field policies for the same field with different conditions work" do + ResourceWithMultiplePoliciesForOneField.create!(%{name: "foo"}) + ResourceWithMultiplePoliciesForOneField.create!(%{name: "baz"}) + + assert [ + %{name: "baz"}, + %{name: "foo"} + ] = + ResourceWithMultiplePoliciesForOneField.read!( + actor: %{name: "baz", admin: true}, + query: ResourceWithMultiplePoliciesForOneField |> Ash.Query.sort([:name]) + ) + end + + defmodule ResourceWithMultiplePoliciesForOneFieldWithExtraCheckOptions do + use Ash.Resource, + data_layer: Ash.DataLayer.Ets, + authorizers: [Ash.Policy.Authorizer] + + attributes do + uuid_primary_key :id + + attribute :name, :string + end + + field_policies do + field_policy :name, [actor_attribute_equals(:admin, true)] do + authorize_if always() + end + + # I saw in the generated spark_dsl_config, that the extra check options are + # set for expr inside the field policy, but not for conditions I think. + # The behaviour is different if I set them here. + field_policy :name, + {Ash.Policy.Check.Expression, + [ + expr: expr(name == ^actor(:name)), + ash_field_policy?: true, + access_type: :filter + ]} do + authorize_if always() + end + end + + policies do + policy always() do + authorize_if always() + end + end + + code_interface do + define_for Api + + define :create + define :read + end + + actions do + defaults [:create, :read] + end + end + + test "multiple field policies for the same field with different conditions work (extra check options)" do + ResourceWithMultiplePoliciesForOneFieldWithExtraCheckOptions.create!(%{name: "foo"}) + ResourceWithMultiplePoliciesForOneFieldWithExtraCheckOptions.create!(%{name: "baz"}) + + assert [ + %{name: "baz"}, + %{name: "foo"} + ] = + ResourceWithMultiplePoliciesForOneFieldWithExtraCheckOptions.read!( + actor: %{name: "baz", admin: true}, + query: + ResourceWithMultiplePoliciesForOneFieldWithExtraCheckOptions + |> Ash.Query.sort([:name]) + ) + end +end