My application is configured with config :ash, :policies, log_policy_breakdowns: true. And there is a resource that has field_policies defined. Some fields should only be accessible by an admin user, so the field policies are designed to enforce that restriction.
The problem is that when I run an :update_self action for a non-admin user, there are at least two warning log entries. I don’t want those showing up in the logs for this use case.
The log messages appear to come from the field policies, because they aren’t logged when I allow unrestricted access to all fields.
Here are the field policies:
field_policies do
field_policy_bypass @admin_only_fields, {ActiveMemberPolicy, []} do
authorize_if always()
end
field_policy_bypass @member_self_fields, actor_attribute_equals(:__struct__, __MODULE__) do
authorize_if expr(id == ^actor(:id))
forbid_if always()
end
field_policy @any_member_fields do
authorize_if {ActiveMemberPolicy, []}
end
end
Here’s the update action code:
# :name is a field that can be updated by the actor, if the record _is_ the actor.
attrs = %{name: "New Name"}
member =
member
|> Ash.Changeset.new()
|> Ash.Changeset.for_update(:update_self, attrs, actor: member)
|> GF.Ash.Api.update!()
Seems clearly to be some kind of errant logging. It should either be useful or removed. Can you open an issue on the Ash repo? A cursory glance doesn’t point out to me where the issue is, so I will address early next week when I’m back at a computer.
Something I’m just now noticing is that your code snippet points to you being on 2.x, which is not the code I was looking at earlier. It could be fixed in 3.x. So perhaps looking at the bran 2.0 on ash will point you in the right direction
I’m back, having upgraded to Ash 3. For uncertain reasons, my initial case isn’t logging warnings anymore, but here’s another case where it is.
def Helpdesk.Support.Representative
...
field_policies do
field_policy [:is_admin, :permissions] do
authorize_if actor_attribute_equals(:is_admin, true)
end
field_policy :* do
authorize_if actor_attribute_equals(:__struct__, __MODULE__)
end
end
test "update Representative as self" do
representative =
Representative
|> Ash.Changeset.for_create(:create, %{name: "Jane Doe"}, authorize?: false)
|> Ash.create!()
representative =
Representative
|> Ash.Query.select([:name])
|> Ash.Query.filter(id == ^representative.id)
|> Ash.Query.for_read(:read, %{}, authorize?: false)
|> Ash.read_one!()
representative
|> Ash.Changeset.for_update(:update_self, %{name: "New Name"}, actor: representative)
|> Ash.update!(actor: representative)
end
The update expression (the last expression) in the test produces the following warning log output:
Yes, I want policy breakdowns, but only when there is a policy violation. It has been working that way for every case until this one. In this case, I don’t think this is a policy violation, but there is a logger warning and breakdown.
When you look at the results do the fields have %Ash.ForbiddenField{} as their values? If that is the case, then there are technically policies failing for those fields. Whether or not it’s useful to log those in your case is a different story though. I’d be happy to add a separate option to disable that logging (don’t want to change the behavior of the option for backwards compatibility reasons though).
Now I look at this from a higher level, it turns out to only be an issue in the dev/test environment. I can deal with that, by adding capture_log statements in the tests.
What I meant was the result of the update, not the value prior to update. There I suspect you will see forbidden fields.
Field policies are honored on the results of update/create actions, for example. So if the actor running the action can’t view those fields, then they are getting “failed policy breakdowns” for the read of those fields.
Thats where the option not to log field policy breakdowns can be added.
Yes, there are %Ash.ForbiddenField{} values on the struct that’s returned from the update. But why is there? It’s a field that isn’t part of the update.
It’s not doing a read, it’s authorizing access for the actor doing the update to view the resulting fields. The results of an action always have their fields authorized for viewing. That process doesn’t require a read to be done to be performed.
Yeah, I think thats just confusing log output. Its a “load” operation, i.e Ash.load(..., fields), which is performed “as if it was the primary read action”, even though no read action actually is run.
Again, this is only an issue in the test/dev environment, not production like I originally had assumed. So, resolving this in Ash is not a priority for me. I’ll just use capture_log.