diff --git a/lib/ash_backpex/live_resource/transformers/generate_backpex.ex b/lib/ash_backpex/live_resource/transformers/generate_backpex.ex index 3cc6f82..60f9e3d 100644 --- a/lib/ash_backpex/live_resource/transformers/generate_backpex.ex +++ b/lib/ash_backpex/live_resource/transformers/generate_backpex.ex @@ -408,6 +408,25 @@ defmodule AshBackpex.LiveResource.Transformers.GenerateBackpex do def can?(_assigns, :delete, _item), do: false end + # Fallback for custom item actions and any other actions + # Checks Ash authorization if a matching action exists, otherwise allows by default + def can?(assigns, action, item) do + case Ash.Resource.Info.action(@resource, action) do + nil -> + true + + ash_action -> + target = + if is_struct(item) and item.__struct__ == @resource do + {item, ash_action.name} + else + {@resource, ash_action.name} + end + + Ash.can?(target, Map.get(assigns, :current_user)) + end + end + def maybe_default_options(assigns) do case assigns do %{field: {attribute_name, _field_cfg}} -> diff --git a/test/ash_backpex/authz_test.exs b/test/ash_backpex/authz_test.exs index 8ff3ae6..79c1d05 100644 --- a/test/ash_backpex/authz_test.exs +++ b/test/ash_backpex/authz_test.exs @@ -41,6 +41,20 @@ defmodule AshBackpex.AuthzTest do end end + describe "AshBackpex.LiveResource :: can? with custom item actions" do + test "returns true for unknown actions that don't exist on the resource" do + # :promote is not an Ash action on Item, so fallback returns true + assert TestCustomItemActionLive.can?(%{current_user: nil}, :promote, %{}) + assert TestCustomItemActionLive.can?(%{current_user: nil}, :some_unknown_action, %{}) + end + + test "checks Ash authorization when action exists on resource" do + # :read exists on Item resource, so it checks Ash.can? + # Item has no policies, so it should return true + assert TestCustomItemActionLive.can?(%{current_user: nil}, :read, %{}) + end + end + describe "AshBackpex.Adapter :: it can" do test "list/3" do user = user() diff --git a/test/support/test_live.ex b/test/support/test_live.ex index f21b20a..fac88f1 100644 --- a/test/support/test_live.ex +++ b/test/support/test_live.ex @@ -113,3 +113,46 @@ defmodule TestLayout do """ end end + +# Custom item action for testing can?/3 fallback +defmodule TestPromoteItemAction do + @moduledoc false + use BackpexWeb, :item_action + + @impl Backpex.ItemAction + def icon(assigns, _item) do + ~H""" + + """ + end + + @impl Backpex.ItemAction + def label(_assigns, _item), do: "Promote" + + @impl Backpex.ItemAction + def handle(socket, _items, _data) do + {:ok, socket} + end +end + +# LiveResource with custom item action for testing can?/3 fallback +defmodule TestCustomItemActionLive do + @moduledoc false + use AshBackpex.LiveResource + + backpex do + resource(AshBackpex.TestDomain.Item) + layout({TestLayout, :admin}) + + item_actions do + action :promote, TestPromoteItemAction + end + + fields do + field(:name) + end + end +end