Are Ash Policy FIlter Checks tenant aware?

I am using context multitenancy in my application. I have two resources Inspection and User. The Inspection resource is on a tenant schema, while the User resource is on the public schema. The relationship between the two is many-to-one, meaning an Inspection belongs to a User and a user can have(done) many inspections.

In the User resource

has_many :inspections, Inspection

In the Inspection resource

belongs_to :user, User

I have added a policy in the Inspection resource where in the the user(actor) can only update inspections they are related to like this:

policies do
  policy action(:update) do
    authorize_if relates_to_actor_via(:user)

When I save the form, I get this error in the logs:

[warning] Unhandled error in form submission for MyApp.Inspection.update

This error was unhandled because Ash.Error.Unknown.UnknownError does not implement the `AshPhoenix.FormData.Error` protocol.

** (Ash.Error.Unknown.UnknownError) ** (Postgrex.Error) ERROR 42P01 (undefined_table) relation "inspections" does not exist

    query: SELECT i0."id" FROM "inspections" AS i0 INNER JOIN "public"."users" AS u1 ON i0."user_id" = u1."id" WHERE (u1."id"::uuid = $1::uuid) AND (i0."id"::uuid = $2::uuid)

As you can see, the query for inspections is run against the public schema instead of the tenant schema. I expect the query to be something like this: SELECT i0."id" FROM "<some_tenant_schema>"."inspections" ... instead of SELECT i0."id" FROM "inspections" ...
I have inspected the changesets, and I can confirm the tenant is being set, even when form is loaded in LiveView, I can see from the query in the logs that data for relationship forms(the form has relationships I have not included here for simplicity) while the form was being composed is being queried on tenant schema.

As for the authorization, it seems it is passing, see the log of the policy breakdown:

[error] Successful authorization: MyApp.Inspection.update

Generated Filter: == "fac04600-3797-46cb-a97e-67ac15566368"

Policy Breakdown
A check status of `?` implies that the solver did not need to determine that check.
Some checks may look like they failed when in reality there was no need to check them.
Look for policies with `✘` and `βœ“` in check statuses.

A check with a `⬇` means that it didn't determine if the policy was authorized or forbidden, and so moved on to the next check.
`🌟` and `β›”` mean that the check was responsible for producing an authorized or forbidden (respectively) status.

When viewing successful authorization breakdowns, a `πŸ”Ž` means that the policy or check was enforced via a filter.

If no check results in a status (they all have `⬇`) then the policy is assumed to have failed. In some cases, however, the policy
may have just been ignored, as described above.

  Policy | πŸ”Ž:
    condition: action == :update       
    authorize if: record.user == actor | βœ“ | πŸ”Ž

So what is the issue?

Hmm…my suspicion is that we are missing a call to set_tenant when running the relevant authorization queries. Can you open an issue on the ash repo? I will look into it as soon as I get the chance.

1 Like

fixed in main, thanks for the report!

1 Like

Thank you for the fix here. I pulled from main and it’s working now.

1 Like