Problem to load `included` relation data - return `[]` list in AshJson without actor

Hi, i have a problem with load data of relation table in Json API. consider just use includes without paginated_includes (this problem still exist not just about paginated)
json_api do
type "collection"

includes [:posts, :master_posts, :tenant_posts]

paginated_includes([:posts, :master_posts, :tenant_posts])
end
json_api do
type "collection"

includes [:posts, :master_posts, :tenant_posts]

paginated_includes([:posts, :master_posts, :tenant_posts])
end
So i created this for :posts:
many_to_many :posts, MishkaBlog.BlogPost do
public? true
description "Blog posts that belong to this collection"
through MishkaBlog.PostCollection
source_attribute_on_join_resource :collection_id
destination_attribute_on_join_resource :post_id
read_action :public_list
end
many_to_many :posts, MishkaBlog.BlogPost do
public? true
description "Blog posts that belong to this collection"
through MishkaBlog.PostCollection
source_attribute_on_join_resource :collection_id
destination_attribute_on_join_resource :post_id
read_action :public_list
end
As you see i did read_action :public_list and this read_action is inside MishkaBlog.BlogPost like this: Action:
read :public_list do
filter expr(status == :published)
pagination offset?: true, default_limit: 20, max_page_size: 100, countable: true
end
read :public_list do
filter expr(status == :published)
pagination offset?: true, default_limit: 20, max_page_size: 100, countable: true
end
policy:
policies do
bypass action(:public_list) do
authorize_if always()
end

bypass action(:public_get) do
authorize_if always()
end

policy always() do
access_type :strict
authorize_if accessing_from(MishkaBlog.Collection, :posts)

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
bypass action(:public_list) do
authorize_if always()
end

bypass action(:public_get) do
authorize_if always()
end

policy always() do
access_type :strict
authorize_if accessing_from(MishkaBlog.Collection, :posts)

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
Unfortunately it returns posts data [], but if i use these relation and send actor i have no problem
many_to_many :tenant_posts, MishkaBlog.BlogPost do
public? true
...
destination_attribute_on_join_resource :post_id
end

many_to_many :master_posts, MishkaBlog.BlogPost do
public? true
...
read_action :read_any

filter expr(is_nil(type(^actor(:tenant_id), :uuid)))
end
many_to_many :tenant_posts, MishkaBlog.BlogPost do
public? true
...
destination_attribute_on_join_resource :post_id
end

many_to_many :master_posts, MishkaBlog.BlogPost do
public? true
...
read_action :read_any

filter expr(is_nil(type(^actor(:tenant_id), :uuid)))
end
By the way public_list direct json api works as public without actor why it happens? Thank you
Solution:
First I did this and did not work! the :<relationship_name>_join_relationship dose not work for me, idk but
i told ai to create a debug file instead of accessing from https://gist.github.com/shahryarjb/2e2e94288f8edd04bd69e6b76d292f35 ...
Jump to solution
24 Replies
Shahryar
ShahryarOP2mo ago
i even comment the other policies and use
bypass always() do
authorize_if always()
end
bypass always() do
authorize_if always()
end
it is very intersting when i put user token in header it loads data! without any permission, but without actor it does not work and return []. when i set the json_api do authorize? false it works, :)) how can just specific json api authorize? false not all of them? and it is interesting it just prevents data for relation and the main request works! when loading relationships through AshJsonApi, even if the main action is public, the related resources are still being checked for authorization. 🥲 for example Category (its data is returned) but the post of category is [] Could you please help me how to create public api?
barnabasj
barnabasj2mo ago
have you tried enabling policy logging https://hexdocs.pm/ash/policies.html#debugging-and-logging ? I don't see anything that's obviously wrong here
Shahryar
ShahryarOP2mo ago
I added it inside my config but it do not print anything. as you see it dose not print anything about forbidden https://gist.github.com/shahryarjb/9e3a4ce200f17ebbd445c36a5d4c94db
{
"data": {
"attributes": {
"name": "Technology",
"status": "active",
"description": "Latest tech trends, gadgets, and digital innovations",
"language": "en",
],
"site_id": "75b28501-ce4b-430b-9982-b5d1e4062e6d",
"featured": true,
"display_order": 1,
"language_group_id": "4947c42b-0625-40da-a815-8d1a9c2af8df",
"parent_collection_id": null,
"slug": "technology"
},
"id": "4947c42b-0625-40da-a815-8d1a9c2af8df",
"links": {},
"meta": {},
"type": "collection",
"relationships": {
"site": {
"links": {},
"meta": {}
},
"posts": {
"data": [],
"links": {
"first": "http://localhost:4000/api/json/v1/user/mishka-blog/collection/active/4947c42b-0625-40da-a815-8d1a9c2af8df?fields[collection]=id%2Cname%2Cslug%2Cdescription%2Cstatus%2Cschema%2Cseo_tags%2Cfeatured%2Cdisplay_order%2Clanguage%2Clanguage_group_id%2Csite_id%2Cparent_collection_id&include=posts&included_page[posts][limit]=10",
"next": null,
"prev": null
},
"meta": {
"offset": 0,
"limit": 10
}
....
}
{
"data": {
"attributes": {
"name": "Technology",
"status": "active",
"description": "Latest tech trends, gadgets, and digital innovations",
"language": "en",
],
"site_id": "75b28501-ce4b-430b-9982-b5d1e4062e6d",
"featured": true,
"display_order": 1,
"language_group_id": "4947c42b-0625-40da-a815-8d1a9c2af8df",
"parent_collection_id": null,
"slug": "technology"
},
"id": "4947c42b-0625-40da-a815-8d1a9c2af8df",
"links": {},
"meta": {},
"type": "collection",
"relationships": {
"site": {
"links": {},
"meta": {}
},
"posts": {
"data": [],
"links": {
"first": "http://localhost:4000/api/json/v1/user/mishka-blog/collection/active/4947c42b-0625-40da-a815-8d1a9c2af8df?fields[collection]=id%2Cname%2Cslug%2Cdescription%2Cstatus%2Cschema%2Cseo_tags%2Cfeatured%2Cdisplay_order%2Clanguage%2Clanguage_group_id%2Csite_id%2Cparent_collection_id&include=posts&included_page[posts][limit]=10",
"next": null,
"prev": null
},
"meta": {
"offset": 0,
"limit": 10
}
....
}
as you see this still returns
"posts": { "data": [],
"posts": { "data": [],
If i login and request to it, it works, please consider the category loaded but the posts is []
barnabasj
barnabasj2mo ago
There is also a setting for successful logs. You set it to the log level and your logger needs to show that level It should show you the action and checks it looked at to get to that result
Shahryar
ShahryarOP2mo ago
Thank you now it prints for category
[error] Successful authorization: MishkaBlog.Collection.public_get


Policy Breakdown
unknown actor

Bypass: Policy | 🌟:

condition: action == :public_get

authorize if: always true | ✓ | 🌟

Policy | 🔎:

condition: always true

authorize if: User has any permission: ["admin:dashboard:*", "admin:api:*", "*"] | ? | ⬇
[error] Successful authorization: MishkaBlog.Collection.public_get


Policy Breakdown
unknown actor

Bypass: Policy | 🌟:

condition: action == :public_get

authorize if: always true | ✓ | 🌟

Policy | 🔎:

condition: always true

authorize if: User has any permission: ["admin:dashboard:*", "admin:api:*", "*"] | ? | ⬇
for the category relation posts
[error] Successful authorization: MishkaBlog.PostCollection.read


Policy Breakdown
unknown actor

Policy | 🔎:

condition: always true

authorize if: User has any permission: ["admin:dashboard:*", "admin:api:*", "*"] | ✘ | ⬇



[error] Successful authorization: MishkaBlog.BlogPost.public_list


Policy Breakdown
unknown actor

Bypass: Policy | 🌟:

condition: action == :public_list

authorize if: always true | ✓ | 🌟

Bypass: Policy | ?:
condition: action == :public_get

Policy | 🔎:

condition: always true

authorize if: User has any permission: ["admin:dashboard:*", "admin:api:*", "*"] | ? | ⬇
[error] Successful authorization: MishkaBlog.PostCollection.read


Policy Breakdown
unknown actor

Policy | 🔎:

condition: always true

authorize if: User has any permission: ["admin:dashboard:*", "admin:api:*", "*"] | ✘ | ⬇



[error] Successful authorization: MishkaBlog.BlogPost.public_list


Policy Breakdown
unknown actor

Bypass: Policy | 🌟:

condition: action == :public_list

authorize if: always true | ✓ | 🌟

Bypass: Policy | ?:
condition: action == :public_get

Policy | 🔎:

condition: always true

authorize if: User has any permission: ["admin:dashboard:*", "admin:api:*", "*"] | ? | ⬇
many_to_many :posts, MishkaBlog.BlogPost do
public? true
description "Blog posts that belong to this collection"
through MishkaBlog.PostCollection
source_attribute_on_join_resource :collection_id
destination_attribute_on_join_resource :post_id
read_action :public_list
end
many_to_many :posts, MishkaBlog.BlogPost do
public? true
description "Blog posts that belong to this collection"
through MishkaBlog.PostCollection
source_attribute_on_join_resource :collection_id
destination_attribute_on_join_resource :post_id
read_action :public_list
end
the action read it :public_list
barnabasj
barnabasj2mo ago
If you set the log level to debug you should see the generated SQL, so far everything looks right
Shahryar
ShahryarOP2mo ago
I saw this https://gist.github.com/shahryarjb/9e3a4ce200f17ebbd445c36a5d4c94db but idk , where is the problem! but with AI it says me
INNER JOIN "public"."mishka_blog_post_collections" AS sm1
ON FALSE AND (sm1."post_id" = sm0."id")
INNER JOIN "public"."mishka_blog_post_collections" AS sm1
ON FALSE AND (sm1."post_id" = sm0."id")
ON FALSE 🤔🤔🤔 I tested with actor too https://gist.github.com/shahryarjb/9e3a4ce200f17ebbd445c36a5d4c94db it has no on false
INNER JOIN "public"."mishka_blog_post_collections" AS sm1
ON sm1."post_id" = sm0."id"
INNER JOIN "public"."mishka_blog_post_collections" AS sm1
ON sm1."post_id" = sm0."id"
it is a bug in AshJson or Ash?
barnabasj
barnabasj2mo ago
if you try to do the same in iex, just calling the action with a load, does it happen there as well?
Shahryar
ShahryarOP2mo ago
Yes this is the the log https://gist.github.com/shahryarjb/9e3a4ce200f17ebbd445c36a5d4c94db it has
ON FALSE AND
ON FALSE AND
And if i run time with actor work and has not ON FALSE like this:
Ash.get(MishkaBlog.Collection, "4947c42b-0625-40da-a815-8d1a9c2af8df",
action: :public_get,
tenant: "75b28501-ce4b-430b-9982-b5d1e4062e6d",
load: [
posts: MishkaBlog.BlogPost |> Ash.Query.page(limit: 5)
],
actor: actor
)
Ash.get(MishkaBlog.Collection, "4947c42b-0625-40da-a815-8d1a9c2af8df",
action: :public_get,
tenant: "75b28501-ce4b-430b-9982-b5d1e4062e6d",
load: [
posts: MishkaBlog.BlogPost |> Ash.Query.page(limit: 5)
],
actor: actor
)
it is same AshJson Request
ZachDaniel
ZachDaniel2mo ago
@Shahryar do you have any resources with a bypass at the bottom of the list of policies?
Shahryar
ShahryarOP2mo ago
Sorry dear Zach i did not understand but these are my 2 files policies Main table (collection or category)
policies do
# Allow public access to view active collections
bypass action(:public_list) do
authorize_if always()
end

bypass action(:public_get) do
authorize_if always()
end

# Admin access for all other actions
policy always() do
access_type :strict

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
# Allow public access to view active collections
bypass action(:public_list) do
authorize_if always()
end

bypass action(:public_get) do
authorize_if always()
end

# Admin access for all other actions
policy always() do
access_type :strict

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
Relation table (posts)
policies do
# Allow public access to view published posts
bypass action(:public_list) do
authorize_if always()
end

bypass action(:public_get) do
authorize_if always()
end

# Admin access for all other actions
policy always() do
access_type :strict

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
# Allow public access to view published posts
bypass action(:public_list) do
authorize_if always()
end

bypass action(:public_get) do
authorize_if always()
end

# Admin access for all other actions
policy always() do
access_type :strict

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
ZachDaniel
ZachDaniel2mo ago
Does this work if you do it in code? i.e
Resource
|> Ash.Query.load(:relationship)
|> Ash.Query.for_read(:action, %{}, actor: your_user)
|> Ash.read!()
Resource
|> Ash.Query.load(:relationship)
|> Ash.Query.for_read(:action, %{}, actor: your_user)
|> Ash.read!()
oh I see you shared that already
Shahryar
ShahryarOP2mo ago
Yes, the iex one has this problem too with actor it is okey but without actor it is not okey and add ON FALSE
ZachDaniel
ZachDaniel2mo ago
Do you have policies on the join resource? MishkaBlog.PostCollection
Shahryar
ShahryarOP2mo ago
just have this, MishkaBlog.PostCollection, am i missing let it?
# Authorization policies for post-collection associations
policies do
policy always() do
authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
# Authorization policies for post-collection associations
policies do
policy always() do
authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
if this is the problem i should kill my self :))
ZachDaniel
ZachDaniel2mo ago
i should kill my self
don't say that bro But yes taht is probably the problem You will want to set up a join_relationship for this purpose
has_many :post_collections, MishkaBlog.PostCollection do
read_action :public_list
end

many_to_many :posts, MishkaBlog.BlogPost do
public? true
description "Blog posts that belong to this collection"
through MishkaBlog.PostCollection
source_attribute_on_join_resource :collection_id
destination_attribute_on_join_resource :post_id
read_action :public_list
join_relationship :post_collections
end
has_many :post_collections, MishkaBlog.PostCollection do
read_action :public_list
end

many_to_many :posts, MishkaBlog.BlogPost do
public? true
description "Blog posts that belong to this collection"
through MishkaBlog.PostCollection
source_attribute_on_join_resource :collection_id
destination_attribute_on_join_resource :post_id
read_action :public_list
join_relationship :post_collections
end
Now with that said I have something that will probably make your life way easier It seems like what you want to do is make it so that if you can see one thing, you can read its relationship
policies do
bypass accessing_from({SourceResource, :relationship_name}) do
authorize_if action_type(:read)
end

# Admin access for all other actions
policy always() do
access_type :strict

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
bypass accessing_from({SourceResource, :relationship_name}) do
authorize_if action_type(:read)
end

# Admin access for all other actions
policy always() do
access_type :strict

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
Then you don't need public_list for relationships etc.
Shahryar
ShahryarOP2mo ago
i did this but dose not work
policies do
policy always() do
authorize_if accessing_from(MishkaBlog.BlogPost, :post)
authorize_if accessing_from(MishkaBlog.Collection, :collection)

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end

attributes do
create_timestamp :inserted_at
update_timestamp :updated_at
end

relationships do
belongs_to :post, MishkaBlog.BlogPost do
description "The blog post in the collection"
primary_key? true
allow_nil? false
end

belongs_to :collection, MishkaBlog.Collection do
description "The collection the post belongs to"
primary_key? true
allow_nil? false
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaBlog.BlogPost, :post)
authorize_if accessing_from(MishkaBlog.Collection, :collection)

authorize_if {MishkaCmsCoreResources.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end

attributes do
create_timestamp :inserted_at
update_timestamp :updated_at
end

relationships do
belongs_to :post, MishkaBlog.BlogPost do
description "The blog post in the collection"
primary_key? true
allow_nil? false
end

belongs_to :collection, MishkaBlog.Collection do
description "The collection the post belongs to"
primary_key? true
allow_nil? false
end
end
ZachDaniel
ZachDaniel2mo ago
YOu will need it on both resources the join resource and the destination resource oh also that relationship name is wrong The through relationship is autogenerated if you don't set it
authorize_if accessing_from(MishkaBlog.BlogPost, :collection_join_relationship)
authorize_if accessing_from(MishkaBlog.Collection, :posts_join_relationship)
authorize_if accessing_from(MishkaBlog.BlogPost, :collection_join_relationship)
authorize_if accessing_from(MishkaBlog.Collection, :posts_join_relationship)
You want something like that
Shahryar
ShahryarOP2mo ago
Thank you Zach ♥️🙏🏻, i think i miss something here, i try and back to here again , so much thanks for testing i put the
policies do
bypass always() do
authorize_if always()
end
end
policies do
bypass always() do
authorize_if always()
end
end
inside the joiner it works!
ZachDaniel
ZachDaniel2mo ago
Yeah, so the join relationship is by default :<relationship_name>_join_relationship So likely just need to fix that
Solution
Shahryar
Shahryar2mo ago
First I did this and did not work! the :<relationship_name>_join_relationship dose not work for me, idk but
i told ai to create a debug file instead of accessing from https://gist.github.com/shahryarjb/2e2e94288f8edd04bd69e6b76d292f35
authorize_if MishkaBlog.Checks.DebugAccessingFrom
authorize_if MishkaBlog.Checks.DebugAccessingFrom
After finding the name is _join_assoc and code i change this
# When accessing from Collection resource via its posts relationships (join assoc names)
authorize_if accessing_from(MishkaBlog.Collection, :posts_join_assoc)
authorize_if accessing_from(MishkaBlog.Collection, :tenant_posts_join_assoc)
authorize_if accessing_from(MishkaBlog.Collection, :master_posts_join_assoc)

# When accessing from BlogPost resource via its collections relationship (join assoc name)
authorize_if accessing_from(MishkaBlog.BlogPost, :collections_join_assoc)
# When accessing from Collection resource via its posts relationships (join assoc names)
authorize_if accessing_from(MishkaBlog.Collection, :posts_join_assoc)
authorize_if accessing_from(MishkaBlog.Collection, :tenant_posts_join_assoc)
authorize_if accessing_from(MishkaBlog.Collection, :master_posts_join_assoc)

# When accessing from BlogPost resource via its collections relationship (join assoc name)
authorize_if accessing_from(MishkaBlog.BlogPost, :collections_join_assoc)
ZachDaniel
ZachDaniel2mo ago
ah on the destination resource, it is being access from the join resource Oh, or maybe its not from the join resource I forget actualy try both
Shahryar
ShahryarOP2mo ago
For now it works perfectly 😂 Thank you so much, if i will try the thing you tell me where i can send the pr for this doc? Ash policy ?
ZachDaniel
ZachDaniel2mo ago
the policies guide would be a good place yes

Did you find this page helpful?