zac
I’m just getting my head around basic AshAuthorization and still struggle with some of the relationship DSL… and I’ve no idea how to tackle this one.
I have two resources, Teams and Users (the later being defined by AshAuthentication). They are related via a many_to_many named :team_members:
# Team
policies do
bypass actor_attribute_equals(:role, :admin) do
authorize_if always()
end
policy action_type(:update) do
authorize_if actor_attribute_equals(:role, :team_lead) && expr(exists(team_memberships, user_id == ^actor(:id)))
end
end
relationships do
has_many :team_memberships, WasteWalk.Teams.Members
has_many :sprints, WasteWalk.Sprints.Sprint
many_to_many :team_members, WasteWalk.Accounts.User do
join_relationship :team_memberships
source_attribute_on_join_resource :team_id
destination_attribute_on_join_resource :user_id
end
end
As you can see, to update a team, you need to be a team lead for that team.
Users are just users, as defined by Ash. I’ve added a many_to_many relationship to the User resource:
# User
relationships do
has_many :team_memberships, WasteWalk.Teams.Members
many_to_many :teams, WasteWalk.Teams.Team do
join_relationship :team_memberships
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :team_id
end
end
Keeping in mind that only someone with a :team_leads role can actually modify their own Team, I want to extend that to team membership. In other words, only a :team_lead can modify (create, destroy) the members of a team.
As it is now (see below) a team lead could modify another team’s membership. (The code API doesn’t really support it, but there’s nothing keeping someone from updating the Members resource directly).
So I’m struggling with the DSL to prevent team leads from modifying relationships unless they are a :team_lead (role) for the specific :team_id they are updating. I’ve no idea how to do that with the policy DSL… (see comment, below – vague idea, but lacking practical knowledge).
# Members
policies do
bypass actor_attribute_equals(:role, :admin) do
authorize_if always()
end
policy action_type(:create) do
authorize_if actor_attribute_equals(:role, :admin)
# TODO restrict to only :team_lead's own teams:
# e.g. kind of like... && expr(exists(team_memberships, user_id == ^actor(:id)))
authorize_if actor_attribute_equals(:role, :team_lead) # &&...??
end
policy action_type(:read) do
authorize_if always()
end
end
actions do
defaults [:read]
create :create do
primary? true
accept [:user_id, :team_id]
end
end
relationships do
belongs_to :user, WasteWalk.Accounts.User, primary_key?: true, allow_nil?: false
belongs_to :team, WasteWalk.Teams.Team, primary_key?: true, allow_nil?: false
actions do
defaults [:read, :destroy, update: :*]
end
end
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex










Showing Posts 1 to 4- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
zachdaniel
Just one note on terminology: policies and AshAuthentication are unrelated to each other.
Authenticationis “who is the user”, andAuthorizationis “what can the user do”.Authorizationis built in to Ash core, butAuthenticationis done with something external to core (most oftenAshAuthentication).One thing to note is to make sure that you’ve read through the policies guide. It can be hard to wrap your head around, but once it clicks you can do a lot of really cool things with them. And most people say they had to read it two or three times
Based on what you’re saying, I actually think there are two problems here.
Data model
If your data model is such that an actor has a role, i.e
:team_leadas an attribute, and then relates to multiple teams, how could you have an actor that is a lead of one team but a member of another? Typically the role would exist in the team memberships. Then you could do something like this:Policy flow
policy checks operate like a “workflow” where you step through each step in order. There are more options than just
authorize_if. So if you are keeping your existing data model, then you are likely looking for something like this:zac
Good points of clarification – thanks for that. I think that was a typo, I should have typed AshAuthorization (not AshAuthentication). (I’ll update that for posterity).
I found the policies guide lacked some specifics, but the corresponding chapter in your book was helpful in clearing up some of the confusion. I do wish there was a more in-depth exploration of how to manipulate many-to-many relationships.
Agreed, but in this business case it’s not a concern. I actually had a
:roleon theMembersresource, but we removed it as unnecessary given the simple business case.That is actually what I’m doing in the latest
Teamand it’s perfect for theTeamAPI. But in this case I’m concerned about direct manipulation of theMember(the relationship)?But it seems that I should prevent directly updating the relationship, e.g.,
The only user that should be allowed to do that is a team lead. So, I’m trying to prevent updates on the relationship itself (here,
Member). Keep in mind that’s the joining resource, not theTeamitself. The join resource only has these relationships available:Is there a way the DSL does something like this, or is this totally custom? (From a DSL perspective, something kind of like this psuedocode):
Otherwise,
Members(the joining resource) is somewhat loosely secured. (Maybe it’s unnecessary? It’s not exposed over an API… I could be taking this too far, but it seems like the business rule should be validated on the joining resource).I’d love to see more complex examples added to the docs / your book… most of the examples are a good start, but they seem to come up short of some real-world concerns, e.g., visibility / security scenarios. For example, being able to follow an artist is nifty, but in a business setting it important to model all the constraints… like, authorize a trade on certain accounts, viewing balances on others, no visibility if it’s a non-client account, plus security to make sure nobody could manipulate the system…
zachdaniel
Yes, you can write:
But, if you actually step through the logic, the above code’s behavior is identical
to this ones:
Now, with all of that said, looking at your code with fresh eyes, we have a problem
You can’t use filter expressions like this on create actions. You’ll get an error if you tried. You’ll need to write a custom check.
An example can be seen here: Ash.Policy.SimpleCheck — ash v3.29.3
Custom checks may also be a nice tool if the policies flow is causing confusion, because they are “plain old Elixir”
zac
Thank you @zachdaniel appreciate you taking the time to understand the question and confirm. I had suspected this would need to be custom… but wasn’t sure, as I really don’t know the policy DSL very well yet.