Trying to read user roles from the session results in different values when using binding/script

Hi All,

I am attempting to implement a custom permission system in our Ignition app.

I have implemented a script to verify that a user role matches against a permission in our database. The user role is taken from the Ignition users.

I have this working when running the script via a "runScript" expression, and I pass the permission key, and user roles list to the function. No problem. As seen here:

runScript("permission.isAuthorised", 0, "LOUNP", {session.props.auth.user.roles})

This calls the following code:


def roleHasPermission(role, permission):
	
	if role == "Administrator":
		return True
	
	query = "Permissions/SelectUserPermission"
	params = {
		"role": role,
		"permission": permission
	}
	result = system.db.runNamedQuery(query, params)

	return result > 0

def isAuthorised(permission, roles):

	hasPermission = False
	try:
		values = [qv.value for qv in roles]
		for role in values:
			hasPermission = roleHasPermission(role, permission)
	except:
		pass
	
	try:
		if not hasPermission:
			return not system.db.runNamedQuery("Permissions/SelectIsEnforced", {"key": permission})
	except:
		pass
		
	return hasPermission

This works fine, the list of roles passed in is of type:

array(com.inductiveautomation.ignition.common.model.values.QualifiedValue, ...)

My problem is when I try to get the same list of roles from a transform script the type I receive is now different.

def transform(self, value, quality, timestamp):
		
	if not permission.isAuthorised("STSCR", self.session.props.auth.user.roles):
		return False
		
	return value

Using "self.session.props.auth.user.roles" results in a list of type ArrayWrapper:

<ArrayWrapper>: [u'Administrator']

Is this supposed to be the case, or do I need to retrieve the roles from somewhere else? I'd like both arrays to be of type

array(com.inductiveautomation.ignition.common.model.values.QualifiedValue, ...)

I am assuming that it won't be easy to convert an ArrayWrapper to my desired type.

Any guidance is appreciated.

Thanks,
Tom

that desired type is a bit challenging, but if all you need it to do is match the typing schema in isAuthorized, then this should be easier.

per PropertyTreeScriptWrapper.ArrayWrapper
docs, this supports indexing into, so (Pseudocode)


	hasPermission = False
	try:
                if isinstance(roles[0], QualifiedValue):
		            values = [qv.value for qv in roles]
                else:
                    values = [value for value in roles]
		for role in values:
			hasPermission = roleHasPermission(role, permission)
	except:
		pass
	
	try:
		if not hasPermission:
			return not system.db.runNamedQuery("Permissions/SelectIsEnforced", {"key": permission})
	except:
		pass
		
	return hasPermission

should work

1 Like

Ah, perfect, that works great.

Thanks for the quick response!

Tom

1 Like

There's a subtle logical bug here - if you pass a list of [roleThatHasPermission, roleThatDoesn'tHavePermission], you'll get a false return, but if you flip the order of that list you'll get a true return.

For this and other, mostly stylistic reasons, here's how I'd write this code:

def isAuthorised(permission, roles):
	hasPermission = False
	try:
		# using any so that only one role has to be valid to pass -
		# use all instead to require that all provided roles have permissions
		hasPermission = any(
			roleHasPermission(getattr(maybeQv, "value", maybeQv), permission)
			for maybeQv in roles
		)
	except:
		return False

	return hasPermission || not selectIsEnforced(permission)

def selectIsEnforced(permission):
	try:
		return not system.db.runNamedQuery("Permissions/SelectIsEnforced", {"key": permission})
	except:
		return False

However, I'll also mention that the idea of writing your own auth checking routines feels fragile and potentially misguided. You're already in an expression context - why not use isAuthorized?