{@thread.name}

Jan Ulbrich
2023-01-24

Jan Ulbrich:

Hi, I’m trying to return certain attributes only for some user roles aka implementing row level permissions. Currently I’m doing that with an prepare/after_action block and while that works, it doesn’t feel natural. Filters and checks don’t help and removing attributes with the after_action make them still appear in the output of AshJsonApi and I would prefer them to not appear at all. Are there other approaches I could take?

frankdugan3:

Have you looked into Ash’s Policies? The default mode of operation is to filter, and it allows for some pretty complex expressions to determine authorization. https://ash-hq.org/docs/guides/ash/latest/topics/policies

frankdugan3:

My personal favorite way to handle row-level authorization is to give users a set of roles attribute :roles, {:array, UserRole}, allow_nil?: false and then create a custom policy checker so I can create simple policies, like:

    policy action_type(:read) do
      description "Employees should always be able to view their own records, but only ACTIVE users with appropriate roles can see the records of other employees."

      authorize_if relates_to_actor_via([:employee, :user])
      authorize_if relates_to_actor_via([:employee])

      forbid_unless actor_is_active()

      authorize_if actor_roles_has_any([
                     UserRole._Executive(),
                     UserRole._Human_Resources()
                   ])
    end

Jan Ulbrich:

I’m using policies (not that complex ones, though), but they only allow me to filter which rows to return and not the columns, right? My case is more “yes, that user is allowed to get this record, but only certain attributes”… 😇

frankdugan3:

Ahh, so column-level policies. Missed that.

Jan Ulbrich:

Yes, sorry for the confusion

frankdugan3:

Well… You can do some stuff w/ policies to block a row if certain attributes are selected, but that doesn’t sound like what you want. IMO, probably the easiest thing to do is create different resources that don’t have the restricted attributes at all. Ash is going to return a struct, so you will always have the nil d out columns even if you come up w/ a good way of ensuring the unauthorized columns are scrubbed. Dynamic columns is not really part of the way Ash functions.

Zach may have better suggestions, that’s just my 2c.

Jan Ulbrich:

Thanks, I like the idea of an alternative struct with less fields. Looking into that!

frankdugan3:

The way I’ve handled something similar is to break out UserEmployeeEmployeePrivate into separate resources (and tables). They are related, and I just decide w/ some UI logic whether or not to load the more restricted relationships if the actor has certain roles.

frankdugan3:

In a simpler case, I have another app where users can write public product endorsements. I didn’t want to risk leaking any info, so I made a separate resource that only has public attributes and filters the users to just the ones that wrote endorsements.

frankdugan3:

But both User and Endorsement share the same table, so it’s nice and neat.

Jan Ulbrich:

That sounds like a clean approach: Will do! Thanks! 🙂

frankdugan3:

If you’re happy w/ the answer, would you mind closing the topic by marking it Solved ? 🙂

ZachDaniel:

(You actually have to do both, mark it as solved and close it)

ZachDaniel:

I don’t think there is automation to close things with a given tag, although I could add that to our not 🙂

ZachDaniel:

**bot

Jan Ulbrich:

I’m still new to Discord and grooving in… 😄

ZachDaniel:

Not a problem at all. It’s a brand new process so we’re still figuring it out 😅