Security Level rule based on other Security Levels

Hello,
Is there a way to create a Security Level rule based on another custom Security Level, rather than using Roles or Security Zones?

For example, let’s say I have the following security groups:

And I would like all users with the Manager role in PlantA to automatically have the Intern role in PlantB.

Is it possible to create such a rule?

Thank you.

You could try using the security rules to make your own definitions?

Yes, using Security Rules is exactly what I want to do. However, I cannot figure out how to create a rule based on another custom Security Level rather than on a Role ( {user:roles}) or Security Zone ({security-zones}).

To make my question more clear, let’s use the example from my original post.
I want to create a rule for PlantB/Intern that grants access to:

  • users with the PlantB_Intern role
  • AND users already in the PlantA/Manager security level

So the rule would look something like this:

containsAny({user:roles}, 'PlantB_Intern') || <something I don't know>

For this example, let’s assume the PlantA/Manager security level is defined by some complicated expression and not simply by the PlantA_Manager role.

I just had an idea to use the isAuthorized expression function:

isAuthorized(true, 'Authenticated/Custom Roles/PlantA/Manager')

However, it did not work. The only result was the following error in the log:

com.inductiveautomation.ignition.gateway.auth.security.level.rules.SecurityLevelRuleFactoryException: Unable to create security level rule of type 'expression'

Maybe I'm not understanding correctly but it doesn't make sense what you are trying to do.

You are setting roles for an user. An user can have a role, but can't have a role configured based on another role it makes no sense because you wouldn't use this role, you would use the first one.

For what I can see, you are treating security level rules as roles.

However, I belive that what you are trying to do it is possible. I'm not entirily sure on your problem. If you could detail it a bit would be helpful.

Check the documentation:

@danielps1818 thank you for your reply.

Please let’s be precise with the terminology: Roles, Security Levels, and Security Zones.

Let me describe our situation:

Roles are defined in the Identity Provider.
The Identity Provider in our company is managed by another team, and making changes to roles is complicated for us.

Because of that, we created custom Security Levels under “Groups”. These are much more flexible for our needs, and we use more complicated expressions and manual exceptions to assign users to these groups.

Now I want to create another Security Level branch called “Actions”, with levels such as canPressStartButton. Many instances of my Start button across multiple pages will use this Security Level.

The idea is to define canPressStartButton using an expression based on our Groups (for example: all users in GroupA except users in GroupB)

This would allow us to centrally manage permissions without having to update every individual button whenever requirements change.

Now I understand.


For your situation you have 2 options:

Doing what you did here ↑. However I belive that the error is occuring because that level hasn't loaded yet. Not sure.

Or simply copying and pasting the full code of the original level. I will not recommend doing this.


However, I would like to share what is our solution. We think it's a good aproach and maybe it could help you.

In our case, the users roles are also managed by the AD.
What we have as our security rules are only your "Actions".
Each rule calls the same script

runScript("core.has_role", 0, {attribute-source:idTokenClaims:roles}, "ID-Rule")

This script throws a query to our database. The table looks like this:

SECURITY_ID NAME ROLES
PRESS_BUTTON_ERROR Allow to press the error button ["Administrator","Agent","Security"]
PRESS_BUTTON_SAVE Allow to press the save button ["Administrator","Agent","Operator"]
VISUALIZATION_ALARMS Allowed to enter the alarms view. ["Administrator"]

The script looks something like this:

has_role function
def has_role(role_list, rule):
	rules_ds = system.db.runNamedQuery(
		'GET_SECURITY_LEVEL_RULE',
		{"SECURITY_LEVEL_ID": rule}
	)
	
	if len(rules_ds) != 1:
		return False
	
	rule_roles = system.util.jsonDecode(rules_ds.getValueAt(0, 0))
	for role in role_list:
		if role in rule_roles:
			return True
	return False

It will return true when the role is allowed to do that rule.

This way, you can link rules to independent roles. No need to have multiple roles or having that tree structure. You configure the security level rules (the actions) and you configure in the database what roles are allowed to do.

For your case:

  • Action
    • canPressStartButton

When checking if it can press the button it will launch:

has_role(["PlantA/Manager", ... ], "canPressStartButton")

This will search in the database the specific rule:

SECURITY_ID NAME ROLES
canPressStartButton Allow to press the start button ["PlantA/Manager", "PlantB/Intern", "Manteinance", ... ]

Because the rule allows PlantA/Managers to execute that, it will allow to press the button (it will return true).

A lot of this explanation has been done on the fly, so it will not be exactly like that.
Hope I helped on something and hope you find a solution.

I haven't really played with this feature yet, but I suspect you need to be using Security Level Rules:

Particularly note the use of the expression language to extract information from the IdP's roles and apply that to your custom security levels.

It is really important to use this feature for custom security instead of UI expression bindings, as only true security levels can be attached to tags.

You may find my Integration Toolkit's iterator expressions helpful for complex cases, in lieu of runScript() expressions (like Daniel's).

(Your custom levels need to be under "Authenticated" in the hierarchy, or the user attributes won't be available.)

@danielps1818 and @pturmel Thank you both for your help.
I will use some runScript expression.