Skip to content

Commit 896a455

Browse files
authored
Merge pull request #13 from udin-io/fix/custom-item-actions-can
fix: add catch-all can?/3 clause for custom item actions
2 parents e4a0a0d + dbf8716 commit 896a455

3 files changed

Lines changed: 76 additions & 0 deletions

File tree

lib/ash_backpex/live_resource/transformers/generate_backpex.ex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,25 @@ defmodule AshBackpex.LiveResource.Transformers.GenerateBackpex do
408408
def can?(_assigns, :delete, _item), do: false
409409
end
410410

411+
# Fallback for custom item actions and any other actions
412+
# Checks Ash authorization if a matching action exists, otherwise allows by default
413+
def can?(assigns, action, item) do
414+
case Ash.Resource.Info.action(@resource, action) do
415+
nil ->
416+
true
417+
418+
ash_action ->
419+
target =
420+
if is_struct(item) and item.__struct__ == @resource do
421+
{item, ash_action.name}
422+
else
423+
{@resource, ash_action.name}
424+
end
425+
426+
Ash.can?(target, Map.get(assigns, :current_user))
427+
end
428+
end
429+
411430
def maybe_default_options(assigns) do
412431
case assigns do
413432
%{field: {attribute_name, _field_cfg}} ->

test/ash_backpex/authz_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@ defmodule AshBackpex.AuthzTest do
4141
end
4242
end
4343

44+
describe "AshBackpex.LiveResource :: can? with custom item actions" do
45+
test "returns true for unknown actions that don't exist on the resource" do
46+
# :promote is not an Ash action on Item, so fallback returns true
47+
assert TestCustomItemActionLive.can?(%{current_user: nil}, :promote, %{})
48+
assert TestCustomItemActionLive.can?(%{current_user: nil}, :some_unknown_action, %{})
49+
end
50+
51+
test "checks Ash authorization when action exists on resource" do
52+
# :read exists on Item resource, so it checks Ash.can?
53+
# Item has no policies, so it should return true
54+
assert TestCustomItemActionLive.can?(%{current_user: nil}, :read, %{})
55+
end
56+
end
57+
4458
describe "AshBackpex.Adapter :: it can" do
4559
test "list/3" do
4660
user = user()

test/support/test_live.ex

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,46 @@ defmodule TestLayout do
113113
"""
114114
end
115115
end
116+
117+
# Custom item action for testing can?/3 fallback
118+
defmodule TestPromoteItemAction do
119+
@moduledoc false
120+
use BackpexWeb, :item_action
121+
122+
@impl Backpex.ItemAction
123+
def icon(assigns, _item) do
124+
~H"""
125+
<Backpex.HTML.CoreComponents.icon
126+
name="hero-arrow-up-circle"
127+
class="h-5 w-5 cursor-pointer transition duration-75 hover:scale-110 hover:text-success"
128+
/>
129+
"""
130+
end
131+
132+
@impl Backpex.ItemAction
133+
def label(_assigns, _item), do: "Promote"
134+
135+
@impl Backpex.ItemAction
136+
def handle(socket, _items, _data) do
137+
{:ok, socket}
138+
end
139+
end
140+
141+
# LiveResource with custom item action for testing can?/3 fallback
142+
defmodule TestCustomItemActionLive do
143+
@moduledoc false
144+
use AshBackpex.LiveResource
145+
146+
backpex do
147+
resource(AshBackpex.TestDomain.Item)
148+
layout({TestLayout, :admin})
149+
150+
item_actions do
151+
action :promote, TestPromoteItemAction
152+
end
153+
154+
fields do
155+
field(:name)
156+
end
157+
end
158+
end

0 commit comments

Comments
 (0)