Conversation
|
@zachdaniel Reponed.
Interesting. & scary - force pushing to main 😆 |
|
Yeah it's a long story 😢 I literally force pushed an entire different project to ash main. Was ridiculous (and it wasn't claude, it was just me 🤦) |
|
Okay, I added some tests all over the place. On the last one I found missing tenant propagation. |
|
Will reevaluate in a few days! Thanks for your hard work! 🙇 |
|
I think the only other missing piece here is a verifier that runs to confirm that all relationships along the path exist and match. |
|
This PR doesn't appear to test authorization on intermediate hops for through Here's a minimal test that demonstrates the issue: defmodule Ash.Test.Actions.ThroughAuthorizationTest do
use ExUnit.Case, async: true
alias Ash.Test.Actions.ThroughAuthorizationTest, as: ThisTest
defmodule Domain do
use Ash.Domain
resources do
allow_unregistered? true
end
end
defmodule Post do
use Ash.Resource, domain: ThisTest.Domain, data_layer: Ash.DataLayer.Ets
ets do private?(true) end
actions do
default_accept :*
defaults [:read, :destroy, create: :*, update: :*]
end
attributes do
uuid_primary_key :id
attribute :title, :string, public?: true
end
relationships do
has_many :post_links, ThisTest.PostLink, public?: true,
destination_attribute: :source_id
has_many :linked_posts, __MODULE__, through: [:post_links, :destination]
end
end
defmodule PostLink do
use Ash.Resource,
domain: ThisTest.Domain,
data_layer: Ash.DataLayer.Ets,
authorizers: [Ash.Policy.Authorizer]
ets do private? true end
actions do
default_accept :*
defaults [:read, :destroy, create: :*, update: :*]
end
policies do
policy action_type(:create) do
authorize_if always()
end
policy action_type(:read) do
authorize_if expr(visible == true)
end
end
attributes do
uuid_primary_key :id
attribute :source_id, :uuid, public?: true
attribute :visible, :boolean, public?: true, default: true
end
relationships do
belongs_to :destination, ThisTest.Post, public?: true
end
end
test "loading a through relationship respects policies on the intermediate
resource" do
source = Post |> Ash.Changeset.for_create(:create, %{title: "source"}) |>
Ash.create!()
visible_dest = Post |> Ash.Changeset.for_create(:create, %{title:
"visible"}) |> Ash.create!()
hidden_dest = Post |> Ash.Changeset.for_create(:create, %{title:
"hidden"}) |> Ash.create!()
PostLink
|> Ash.Changeset.for_create(:create, %{source_id: source.id,
destination_id: visible_dest.id, visible: true})
|> Ash.create!()
PostLink
|> Ash.Changeset.for_create(:create, %{source_id: source.id,
destination_id: hidden_dest.id, visible: false})
|> Ash.create!()
# Without auth — both links traversable
assert length(Ash.load!(source, :linked_posts, authorize?:
false).linked_posts) == 2
# With auth — PostLink policy should filter out the hidden link
result = Ash.load!(source, :linked_posts, authorize?: true, actor: %{})
assert length(result.linked_posts) == 1
assert hd(result.linked_posts).title == "visible"
end
endThis fails — result.linked_posts returns 2 records instead of 1, confirming |
|
found an issue, had claude put the above together |
Contributor checklist
#72
#1780
Related to: ash-project/ash_postgres#686 & ash-project/ash_sql#212
Leave anything that you believe does not apply unchecked.