ash-project / ash

A declarative, extensible framework for building Elixir applications.
https://www.ash-hq.org
MIT License
1.63k stars 216 forks source link

Ash can’t deselect source fields that are required for loading relationships due to policies #1457

Closed sezaru closed 2 months ago

sezaru commented 2 months ago

Describe the bug In some cases, if my field is forbidden, it will make the action crash with

iex(21)> Core.Marketplace.Markets.Property.get_any!(%{id: "0191e21c-130f-7c45-aa9c-461774866df9"}, actor: actor)
[debug] QUERY OK source="properties" db=0.5ms idle=1175.9ms
SELECT p0."id", p0."metadata", p0."comment", p0."acquisition_price", p0."county", p0."country", p0."ap_link", p0."due_diligence_date", p0."processing_status", p0."external_id", p0."external_id_type", p0."view_count", p0."over_under_paid", p0."adj_mao", p0."opened_at", p0."normalized_full_address", p0."normalized_county", p0."normalized_state", (p0."house_number"::text::varchar || ($1::varchar::varchar || (p0."street"::text::varchar || ($2::varchar::varchar || (p0."city"::text::varchar || ($3::varchar::varchar || (p0."state"::text::varchar || ($4::varchar::varchar || p0."zip"::text::varchar)::varchar)::varchar)::varchar)::varchar)::varchar)::varchar)::varchar)::text::text FROM "properties" AS p0 WHERE (p0."id"::uuid = $5::uuid) [" ", " - ", ", ", " - ", "0191e21c-130f-7c45-aa9c-461774866df9"]
** (Ash.Error.Invalid) Invalid Error

* Invalid filter value `id in [#Ash.NotLoaded<:attribute, field: :disposition_agent_id>]`: No matching types. Possible types: [[:any, {:array, :same}]]
    at disposition_agent, filter
  (elixir 1.17.2) lib/process.ex:864: Process.info/2
  (ash 3.4.8) lib/ash/error/query/invalid_filter_value.ex:5: Ash.Error.Query.InvalidFilterValue.exception/1
  (ash 3.4.8) lib/ash/query/operator/operator.ex:184: Ash.Query.Operator.try_cast_with_ref/3
  (ash 3.4.8) lib/ash/filter/filter.ex:3086: Ash.Filter.resolve_call/2
  (ash 3.4.8) lib/ash/filter/filter.ex:2522: Ash.Filter.add_expression_part/3
  (ash 3.4.8) lib/ash/filter/filter.ex:2460: anonymous fn/3 in Ash.Filter.parse_expression/2
  (elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
  (elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
  (ash 3.4.8) lib/ash/filter/filter.ex:334: Ash.Filter.parse/3
  (ash 3.4.8) lib/ash/query/query.ex:2699: Ash.Query.do_filter/3
  (ash 3.4.8) lib/ash/actions/read/relationships.ex:577: anonymous fn/3 in Ash.Actions.Read.Relationships.do_fetch_related_records/4
  (ash 3.4.8) lib/ash/actions/read/relationships.ex:65: Ash.Actions.Read.Relationships.fetch_related_records/3
  (ash 3.4.8) lib/ash/actions/read/relationships.ex:24: Ash.Actions.Read.Relationships.load/3
  (ash 3.4.8) lib/ash/actions/read/read.ex:293: Ash.Actions.Read.do_run/3
  (ash 3.4.8) lib/ash/actions/read/read.ex:81: anonymous fn/3 in Ash.Actions.Read.run/3
  (ash 3.4.8) lib/ash/actions/read/read.ex:80: Ash.Actions.Read.run/3
  (ash 3.4.8) lib/ash.ex:2107: Ash.do_read_one/3
  (ash 3.4.8) lib/ash.ex:2055: Ash.read_one/2
  (ash 3.4.8) lib/ash.ex:2022: Ash.read_one!/2
  (core 1.91.3) deps/ash/lib/ash/code_interface.ex:622: Core.Marketplace.Markets.Property.get_any!/2
  (elixir 1.17.2) src/elixir.erl:386: :elixir.eval_external_handler/3
  (stdlib 6.0.1) erl_eval.erl:904: :erl_eval.do_apply/7
  (elixir 1.17.2) src/elixir.erl:364: :elixir.eval_forms/4
  (elixir 1.17.2) lib/module/parallel_checker.ex:112: Module.ParallelChecker.verify/1
  (iex 1.17.2) lib/iex/evaluator.ex:332: IEx.Evaluator.eval_and_inspect/3
  (iex 1.17.2) lib/iex/evaluator.ex:306: IEx.Evaluator.eval_and_inspect_parsed/3
  (iex 1.17.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
  (iex 1.17.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
  (iex 1.17.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
  (stdlib 6.0.1) proc_lib.erl:329: :proc_lib.init_p_do_apply/3
    (elixir 1.17.2) lib/process.ex:864: Process.info/2
    (ash 3.4.8) lib/ash/error/invalid.ex:3: Ash.Error.Invalid.exception/1
    (ash 3.4.8) /var/home/sezdocs/projects/rebuilt/platform/core/deps/splode/lib/splode.ex:211: Ash.Error.to_class/2
    (ash 3.4.8) lib/ash/error/error.ex:66: Ash.Error.to_error_class/2
    (ash 3.4.8) lib/ash/actions/read/read.ex:29: Ash.Actions.Read.run/3
    (ash 3.4.8) lib/ash/actions/read/relationships.ex:582: anonymous fn/3 in Ash.Actions.Read.Relationships.do_fetch_related_records/4
    (ash 3.4.8) lib/ash/actions/read/relationships.ex:65: Ash.Actions.Read.Relationships.fetch_related_records/3
    (ash 3.4.8) lib/ash/actions/read/relationships.ex:24: Ash.Actions.Read.Relationships.load/3
    (ash 3.4.8) lib/ash/actions/read/read.ex:293: Ash.Actions.Read.do_run/3
    (ash 3.4.8) lib/ash/actions/read/read.ex:81: anonymous fn/3 in Ash.Actions.Read.run/3
    (ash 3.4.8) lib/ash/actions/read/read.ex:80: Ash.Actions.Read.run/3
    (ash 3.4.8) lib/ash.ex:2107: Ash.do_read_one/3
    (ash 3.4.8) lib/ash.ex:2055: Ash.read_one/2
    (ash 3.4.8) lib/ash.ex:2022: Ash.read_one!/2
    (core 1.91.3) deps/ash/lib/ash/code_interface.ex:622: Core.Marketplace.Markets.Property.get_any!/2
    iex:21: (file)

To Reproduce Have an action like this:

  read :get_any do
      get? true

      argument :id, :uuid, allow_nil?: false

      filter expr(id == ^arg(:id))

      prepare build(load: :disposition_agent)
    end

With this relationship that is loaded in the action:

  belongs_to :disposition_agent, User do
      allow_nil? true
      attribute_writable? true
      public? true
    end

Now have these policies in place:

  field_policies do
    field_policy :* do
      forbid_if always()
    end
  end

  policies do
    policy action(:get_any) do
      authorize_if always()
    end
  end

Expected behavior I believe it should return the record with all fields, except id as forbidden.

Runtime

zachdaniel commented 2 months ago

Alright, so this issue might not be exactly what I thought it was. Testing this locally it looks like it's working, but clearly it's not in your case. Can you please make a reproduction I can run to confirm?

zachdaniel commented 2 months ago

nevermind I have it reproduced.