Disabling a component by User Group (Permission)

We have the following challenge :
We need to enable/disable components according to users and groups.
How can we do it ? we are having mainly tables and tabs

You can disable queries by group under Advanced Tab.

If you have to enable/disable permissions, you should consider making each component it's own application and at permissions at that level.

Adding on to @ScottR, you can also use the {{current_user}} object to hide/disable/etc conditionally! That object has the current user’s name and permission groups for you to access. Check it out at the bottom of the left panel :slightly_smiling_face:

@victoria something like this?
Screen Shot 2022-01-06 at 11.08.23 AM

or I suppose you could also use this to hide the component.

{{current_user.groups != "somegrouphere"}}

Thanks @victoria @ScottR ,
Do I have disabled/hidden on all components?

@barry I would think you would have to for each component. You may want to rethink your architecture if this becomes unmanageable.

^ exactly! You'd have to apply the logic to each component individually (you can always throw components into a container and then apply the logic to just that container), or just split things up between apps.

To be fair, it'd be cool if we could get a queryname.isAllowedToRun or something like that for this scenario, rather than having to hard code group names into components or containers. It makes it easier to maintain this in multiple places across an app.

Eg, hypothetically, if there's a button "cancel order", I don't want people to be able to click it anywhere, which means now I need to have in potentially multiple place a {{ current_user.groups.some(group => group.name === "team_lead") }} whereas it'd be much cleaner if I could have a {{ !cancelOrder.isAllowedToRun }} in the disabled section of the button.

It's not my exact use case, but I would also benefit from such a mechanism.

I think you would still need to add in each query the logic {{ !cancelOrder.isAllowedToRun }} or on the cancel button, the same.
Perhaps setting a temp variable to be true or false based on user group and then logic in button to say if true then it is clickable, if false then it is disabled.

Well, yes.

The idea is that you (in the query cancelOrder) set a list of groups (team leads, admins, senior agents), and then anywhere you have a component executing this query you can just check for eligibility (cancelOrder.isAllowedToRun), rather than duplicate the logic everywhere across your app.

The alternative is indeed defining it elsewhere, eg a javascript transformer, but then you can't really use the "groups allowed to run this query" functionality either, unless you want to have it in 2 places again.

I agree - but there's a difference between allowing a query to run and disabling the button that would run it. So you could have it in one place (the button) and that should suffice. That being said there is no "blanket" application of said user group permission level access across an entire app unless you split your application up into separate pages where only certain groups can use them - this would be done at the Retool admin level.

@victoria I would like to see more feature development around the permission system. We have the need described above to permission at the component level (hiding or disabling). I have seen the permissioning of queries that is mentioned above, however that doesn't solve creating a UI that doesn't feel broken. We also need to have the same logic on the components as @b12e has the solution for with "isAllowedToRun".

The bigger issue I am seeing is that referencing the current users' groups and permissioning on the queries is very fragile. We can't tell at the group level where all they might be referenced. Doing something as innocent as renaming a group breaks all group references that may have been created across all apps and queries.

Some potential ideas to solve the problems I am seeing.

  1. Have a way to reference groups and groups the users is apart of in code, in a way that won't break when groups are renamed, in the same way that renaming a component doesn't break all references to it.
  2. A way to see "state" references for groups in the same way we can components to see where a group might be used across apps, in even just to see which apps specifically reference the group.

I feel like we are having to move towards a system with lots of very specifically named groups that also includes the apps they are used on because we fear making any changes to the users included or names of existing groups because it could break access across apps.

I've decided to use group IDs rather than names everywhere and keep a Google sheet with where I've made references.

current_user.groups.map(group => group.id).includes(1839803);

Would only return true if the user has this group ID in its array of groups.

Is it ideal? No. Does it work for me for now? Sure. But ideally groups would get a major overhaul anyway. For example, you can't be group admin without being part of the group. With this approach, group 1839803 is highly restricted (can't do much within the app, a lot of things are not shown or disabled). The team lead of this group is also a team lead of another group with extra permissions (can see more, and do more, than your average user of this app). I can't make him group admin of both groups without adding him to both groups (because one would hide more than the other would allow), and then I'd have to change the above code to

current_user.groups.map(group => group.id).includes(1839803) && 
!current_user.groups.map(group => group.id).includes(172019);

And then it gets very, very messy. Also can say goodbye to readability, as 1839803 and 172019 doesn't mean much to the human eye.

2 Likes

Good idea with the group ids, but you are right about readablity challenges. I started thinking about creating variables for each group id, although I guess that would have to be app specific and only really useful if I had lots of references to different groups within a single app, which I am trying to avoid.