Just putting a feeler out here for an issue I came across that I think I've found a somewhat elegant solution for, but wanted to get the community's feedback/opinions.
I've got an upcoming project where we'll be creating a standard project and templates that a company will begin using for all new plants going forward (and converting old plants over time). They use a centralized Active Directory with local domain controllers on each site. The problem comes in that they want to use security groups unique to each site, so for example operators at plant 1 will belong to sg_Plant1_Operators and plant 2 they'll belong to sg_Plant2_Operators, and plant 1 supervisors will be part of sg_Plant1_Supervisors. This is all out of my control, and if they were using Perspective with an IdP, I could map these roles over easily to generic security levels. With Vision, typically bindings use the hasRole('RoleName')
function to determine if components are enabled, visible, etc.
To make things easier and adaptable, I've created a dataset memory tag Globals/SecurityRoles with 2 columns RoleName and SecurityGroup. So for the RoleName I'd have something like Supervisor and for SecurityGroup I'd have sg_Plant1_Supervisors. Then in place of hasRole('Supervisor')
I'm using hasRole(lookup({Globals/SecurityRoles}, 'Supervisor', 'Supervisor'))
to lookup the associated AD security group associated with a generic role name.
Does anyone see any red flags that could cause performance issues with possibly 10s to 100s of these running at once on screen changes? Any suggestions of other ways of handling this? I'd like to get this flushed out early on so I'm not going back through every template, faceplate, etc later and reworking all my bindings.
Do that in a Vision Client Tag expression. And reference the client tag wherever you need it. It will be very efficient, and place effectively zero load on the gateway.
1 Like
So are you saying create boolean client tags for each role (something like Role/Operator, Role/Supervisor) that get evaluated essentially once when they login, then just use that tag directly in the binding?
Yes. Tag bindings to Vision Client tags are truly local to the client and are therefore extremely efficient.
1 Like
Just did it, and it works very well (at least in the designer). Bindings are simple for most things since I can directly bind to the client tag, and anything that needs more than just a role I can use an expression with the appropriate tag/tags. Plus it's nice and clean (and readable).
For completeness, here's what the client tags look like for anyone else needing to implement this in the future:
Each tag is simply an expression like hasRole('sg_Plant1_Supervisor')
Then in bindings for a simple permission, you only need to bind directly to the Vision client tag. For more complex bindings, you can use the tag/tags directly in expressions.
For an extra added bonus, in my case, I'm naming my default tag provider the same as the plant name being used by the company in all the security groups, so my expressions can be like this:
hasRole('sg_' + {[System]Client/System/DefaultTagProvider} + '_Supervisor')
1 Like
For more completeness, I was trying to follow along with your setup as it fits a portion of mine. However, I couldn’t get mine to work just leaving my ‘hasRole’ like what you have. Even though i was using Vision, i still had to specify the username and the usersource for my expression to work properly. So, if anyone needs this and you find that the expression isn’t evaluating correctly, and you are using vision with an outside AD, you need to specify the logged in user and the user_source for ‘hasRole’ to work properly like you would have to do if using Perspective. This may be common knowledge for more advanced users but users starting out may have trouble making this connection.
It took me way too long to make this connection than I’m happy to admit.
I’ve since modified mine as it wasn’t working properly/consistently and discovered why.
Your expression needs to have a condition that triggers a change to evaluate properly, so my current expression is more like this if you’re using static strings:
hasRole('Operator') || hasRole('SG-Plant1-Operator') ||
hasRole('Supervisor') || hasRole('SG-Plant1-Supervisor') ||
hasRole('Technician') || hasRole('SG-Plant1-Technician') ||
hasRole('Engineer') || hasRole('SG-Plant1-Engineer') ||
hasRole('Administrator') || hasRole('SG-Plant1-Admin') &&
{[System]Client/User/Username} != ''
That last line is what triggers every time someone logs in and causes the expression to re-evaluate. This is for my operator role tag with other roles having fewer lines in them.
Per the documentation, the username and usersource are optional parameters and default to using the current username and usersource for the logged-in user. So my guess is by you specifying the username, it’s triggering it to evaluate like mine is, but you don’t really have to specify it like you think and can do just like what I did in my example here.
2 Likes
Initially, for testing and training myself, i was using the default user_source in the gateway for my usernames and passwords. Once i tied the AD into it and pointed the project over to that as my default user_source, the evaluations you described would still refer back to the default source i had setup before, even though i have the project now pointing to the external AD. That’s why i wrote what i did. I still have to put the username and the user_source i want when using hasRole because for whatever reason, the project wants to keep pointing back to the old default user_source. Bug? Something i’m doing wrong? I don’t know. But putting all of the information into the hasRole function does the trick in pointing to the proper user_source instead of relying on the project settings.
Not sure…I’m using AD as well and don’t have any issues. The hasRole doesn’t rely on project settings from what it looks like anyway. It uses the user source from the logged in user. In my case, I have a failover user source and both the AD and failover user sources work just fine with this expression even though my default is the AD user source.