Best option to read a relationship which has policy in Action

Hi, I so much searching today and with AI to be allowed load a relations in action, but i think there is better way! for example in user resource get_by_subject I added this:
read :get_by_subject do
description "Get a user by the subject claim in a JWT"
argument :subject, :string, allow_nil?: false
get? true
prepare AshAuthentication.Preparations.FilterBySubject

prepare fn query, _context ->
query
|> Ash.Query.load([:permissions, :permission_values])
end
end
read :get_by_subject do
description "Get a user by the subject claim in a JWT"
argument :subject, :string, allow_nil?: false
get? true
prepare AshAuthentication.Preparations.FilterBySubject

prepare fn query, _context ->
query
|> Ash.Query.load([:permissions, :permission_values])
end
end
permissions is a many_to_many
many_to_many :permissions, MishkaCms.Accounts.Permission do
through MishkaCms.Accounts.UserPermission
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :permission_id
end
many_to_many :permissions, MishkaCms.Accounts.Permission do
through MishkaCms.Accounts.UserPermission
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :permission_id
end
So i added this Policy in resource b and c
bypass always() do
description "Allow when accessed through relationship"
authorize_if expr(not is_nil(^context(:accessing_from)))
end
bypass always() do
description "Allow when accessed through relationship"
authorize_if expr(not is_nil(^context(:accessing_from)))
end
So it let me to to get the data of permissions before it it just return empty list [] I tried to add authorize?: false To |> Ash.Query.load([:permissions, :permission_values], authorize?: false) But it did not work and return empty list I think this (authorize_if expr(not is_nil(^context(:accessing_from)))) way can be a problem for me base on a mistake leak the data Thank you in advance!
Solution:
Finally i found :)) i created this debugger 😂 😂😂 ```elixir defmodule MishkaCms.Accounts.Checks.DebugAccessingFrom do use Ash.Policy.SimpleCheck ...
Jump to solution
7 Replies
ZachDaniel
ZachDaniel•2mo ago
You would typically do authorize_if accessing_from({Resource, :relationship}) if you want to allow loading from a relationship
Shahryar
ShahryarOP•2mo ago
Thank you, but you mean this? after chasing this i get permissions: [] again! :thinkies: MishkaCms.Accounts.User
read :get_by_subject do
description "Get a user by the subject claim in a JWT"
argument :subject, :string, allow_nil?: false
get? true
prepare AshAuthentication.Preparations.FilterBySubject

prepare fn query, _context ->
query
|> Ash.Query.load([:permissions, :permission_values])
end
end
read :get_by_subject do
description "Get a user by the subject claim in a JWT"
argument :subject, :string, allow_nil?: false
get? true
prepare AshAuthentication.Preparations.FilterBySubject

prepare fn query, _context ->
query
|> Ash.Query.load([:permissions, :permission_values])
end
end
MishkaCms.Accounts.Permission
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :relationship)
end

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

policy always() do
forbid_if always()
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :relationship)
end

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

policy always() do
forbid_if always()
end
end
MishkaCms.Accounts.UserPermission
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :relationship)
end

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

policy always() do
forbid_if always()
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :relationship)
end

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

policy always() do
forbid_if always()
end
end
ZachDaniel
ZachDaniel•2mo ago
That bottom policy always forbids everything
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :relationship)
authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :relationship)
authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
This is probably what you want Remember to thoroughly read the policies guide to understand how they work, even if you have to read it multiple times 😄
Shahryar
ShahryarOP•2mo ago
I have read more than 100 times :))) sorry to waste your time again i get permissions: [] This fire my head :))
ZachDaniel
ZachDaniel•2mo ago
🤔 Did you put in the right relationship name?
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :the_name_of_the_relationship)
authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :the_name_of_the_relationship)
authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
Shahryar
ShahryarOP•2mo ago
Ahhhmmm. it is many to many!! sorry i am so confused where should i put them MishkaCms.Accounts.User we have action that want to read permissions I need to put it inside my MishkaCms.Accounts.Permission resource like this?
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions)

authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions)

authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
and what i should put inside the MishkaCms.Accounts.UserPermission resource because i put the
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions)
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions)
in both MishkaCms.Accounts.Permission and MishkaCms.Accounts.UserPermission resource Gist: https://gist.github.com/shahryarjb/6728cf3b4b1c42c467dd38197dd632a4
Solution
Shahryar
Shahryar•2mo ago
Finally i found :)) i created this debugger 😂 😂😂
defmodule MishkaCms.Accounts.Checks.DebugAccessingFrom do
use Ash.Policy.SimpleCheck

def describe(_opts), do: "Debug accessing_from"

def match?(_actor, %{context: context} = _authorizer, _opts) do
case context[:accessing_from] do
nil ->
IO.puts("accessing_from is nil")
false
accessing_from ->
IO.inspect(accessing_from, label: "accessing_from content", limit: :infinity)
# Check what fields are available
IO.inspect(Map.keys(accessing_from), label: "accessing_from keys")
if Map.has_key?(accessing_from, :source) do
IO.inspect(accessing_from.source, label: "Source resource")
end
if Map.has_key?(accessing_from, :name) do
IO.inspect(accessing_from.name, label: "Relationship name")
end
true
end
end
end
defmodule MishkaCms.Accounts.Checks.DebugAccessingFrom do
use Ash.Policy.SimpleCheck

def describe(_opts), do: "Debug accessing_from"

def match?(_actor, %{context: context} = _authorizer, _opts) do
case context[:accessing_from] do
nil ->
IO.puts("accessing_from is nil")
false
accessing_from ->
IO.inspect(accessing_from, label: "accessing_from content", limit: :infinity)
# Check what fields are available
IO.inspect(Map.keys(accessing_from), label: "accessing_from keys")
if Map.has_key?(accessing_from, :source) do
IO.inspect(accessing_from.source, label: "Source resource")
end
if Map.has_key?(accessing_from, :name) do
IO.inspect(accessing_from.name, label: "Relationship name")
end
true
end
end
end
in my user
many_to_many :permissions, MishkaCms.Accounts.Permission do
through MishkaCms.Accounts.UserPermission
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :permission_id
end
many_to_many :permissions, MishkaCms.Accounts.Permission do
through MishkaCms.Accounts.UserPermission
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :permission_id
end
inside MishkaCms.Accounts.Permission
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions)

authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions)

authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
and MishkaCms.Accounts.UserPermission
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions_join_assoc)

authorize_if {MishkaCms.Accounts.Checks.AccessPermissions,
permissions: ["admin:dashboard:*", "admin:api:*", "*"]}
end
end
policies do
policy always() do
authorize_if accessing_from(MishkaCms.Accounts.User, :permissions_join_assoc)

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

Did you find this page helpful?