Perspective changing value of control before authenticating the user

I need to have a specification where the Perspective interface needs to authenticate the user (prompt for username and password) on each user input (toggling buttons and moving sliders, etc).

I currently have that configured in the event configuration for each input as an Auth Challenge for an onClick mouse event.

When I tested it out however, the input would change the tag value, and then authenticate the user. This is the opposite sequence of what I need to happen.

Show the code, man! Show the code!
Don't forget to use the </> code formatting button!

2 Likes

I don't really have any code. I just have these settings. Is there code I'm missing that I need to add?

What you're expecting just isn't how the authentication challenge works.

We don't currently have a way to automatically restrict every component to require an auth challenge.
What you'd have to do is use scripting as a middleman:

  • Add a property change script to the 'value' property on each component. When it's changed on one of your components (which will have to be bound unidirectionally to your tags), you can invoke system.perspective.authenticationChallenge with a well-structured payload dictionary.
  • In the Authentication Challenge Completed event script, you would parse out the payload (which should probably include a tagPath and a value) and then programmatically issue a write with system.tag.writeBlocking.

This may seem overwrought at first glance, but is a requirement because of the asynchronous nature of identity providers.

4 Likes

Apologies in advance for my horrible code. I'm still a beginner with Ignition and this is my first time hearing about payloads. Would the code look something like this?

Property change script on toggle switch:

def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	payload = {'newVal':currentValue, 'tagPath':origin}
	system.perspective.authenticationChallenge(payload)

Session Events:

def onAuthChallengeCompleted(session, payload, result):
	val = payload['newVal']
	tagPath = payload['tagPath']
	system.tag.writeBlocking(tagPath, val)

That code looks perfect, actually, with one hiccup - you can't use origin like that; it's going to be a value like "Browser" or "Binding" or "Session", not a useful tagpath.

You can't dynamically retrieve the tagpath from a binding, but what you can do is add a custom property to your (in this case, toggle switch) that's just the tag path you care about as a string.
Then you can switch your unidirectional binding to be an "Indirect" binding and reference that custom property. Then update your property change script to also refer to that custom property (e.g. self.custom.tagPath or whatever you end up naming it).

Don't forget to check for a successful result before writing....

1 Like

Hah, yeah, good catch.
You'll want to put an if result.isSuccess(): before initiating a write.

Updates to the code:

  • Added a custom property tagPath = "[default]New Tag" (temporary tag for the proof of concept).

Property change script:

def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	payload = {'newVal':currentValue, 'tagPath':self.custom.tagPath}
	system.perspective.authenticationChallenge(payload)

Session Event

def onAuthChallengeCompleted(session, payload, result):
	val = payload['newVal']
	tagPath = payload['tagPath']
	if result.isSuccess():
		system.tag.writeBlocking(tagPath, val)

So that doesn't work. I get the following error:

Error running property change script on ToggleSwitch.props.selected: Traceback (most recent call last): File "function:valueChanged", line 3, in valueChanged at java.base/java.util.UUID.fromString(Unknown Source) at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.getSession(AbstractScriptingFunctions.java:91) at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnSession(AbstractScriptingFunctions.java:118) at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnPage(AbstractScriptingFunctions.java:47) at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.authenticationChallenge(PerspectiveScriptingFunctions.java:418) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: UUID string too large

And there's no other way to do this? It would have to be done for several hundred tags and controls.

Try system.perspective.authenticationChallenge(payload=payload). I think the function is confused about which argument you're passing in.

Fundamentally, not really. One thing you could do is make repeatable views that contain this additional logic for you. You can have an input parameter tagPath, and then the component you want to wrap inside. Then you just drop your wrapping view into your main screen and configure the tag paths accordingly. This does have some performance ramifications, though; embedded views can get expensive on complicated screens.

It may be worth seeing if the specification can be amended to allow you to require an authentication challenge to open a write-able version of your screens, or otherwise enable editing on all the components. If the spec truly requires that every operation pass an auth challenge, with no grace period whatsoever, this is your only option (to my knowledge, anyways).

2 Likes