zac
Are `^actor(:role)` checks more performant than custom checks?
I have a use case that, basically:
- Forbids if some role conditions are true for the actor,
- Authorizes if the actor is a team lead of the specific team.
To figure out if the actor is a team lead (of a given sprint), I have to load the team, load the team’s members, and check to see if the actor is current a member and has a “team lead” role on the User. It’s mostly done in a calculation, and a small custom check:
# ActorIsTeamLead
def match?(actor, %{subject: %Ash.Changeset{attributes: %{team_id: team_id}}}, _opts) do
WasteWalk.Teams.get_by_id!(team_id, authorize?: false)
|> Ash.calculate!(:is_team_lead, actor: actor)
end
Technically, to decide if an actor can create a new Sprint, all I need to do is this:
# Sprint.ex
policy action_type([:create]) do
authorize_if WasteWalk.Checks.Sprint.ActorIsTeamLead
end
My question is, would it be potentially more performant to weed out users based on role first, like so?
# Sprint.ex
policy action_type([:create]) do
forbid_if expr(^actor(:role) == :user), name: "team members (non-team leads) cannot create sprints"
forbid_if expr(^actor(:role) != :team_lead), name: "only team leads can create sprints"
authorize_if WasteWalk.Checks.Sprint.ActorIsTeamLead
end
My thinking is that the actor is already in memory – so, potentially a decision could be made without resorting to a query almost all of the time.
But I don’t know if that’s how Ash policies work – will the query be run, no matter what, so having the extra checks is pointless… or, does it optimize to avoid unnecessary queries like this?
(Separately, I was trying to see if the DSL could do this without a custom check, e.g., by using something like relates_to_actor_via(:team, field: :is_team_lead) but I don’t think so… because it’s not an actor relationship, it’s sprint.team_id->team.is_team_lead()).
Marked As Solved
zachdaniel
Yes, those checks above the authorize_if would make the ActorIsTeamLead check not run, but I’d likely just bake that into the check:
def match?(actor, %{subject: %Ash.Changeset{attributes: %{team_id: team_id}}}, _opts) do
actor.role == :team_lead &&
WasteWalk.Teams.get_by_id!(team_id, authorize?: false, load: :is_team_lead, actor: actor).is_team_lead
end
Also Liked
zachdaniel
policy action_type([:create]) do
forbid_if expr(^actor(:role) == :user), name: "team members (non-team leads) cannot create sprints"
forbid_if expr(^actor(:role) != :team_lead), name: "only team leads can create sprints"
authorize_if WasteWalk.Checks.Sprint.ActorIsTeamLead
end
Also there isn’t any current way to have a custom error message from policies right now. Its something we intend to add before long but those name things
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









