Perspective - how was it done?

Apologies for the vague title, I’m a bit stumped and not really sure what’s going on to accurately describe it.

I have an application where somebody else has developed a perspective application. This person had never used Ignition in any form before, did not spend a lot of time with any sort of documentation, and has created something…uhm…unique. I’m now tasked with working on it, and doing some modifications, and I can’t work out how they’ve actually made some things work.

The thing I’m most concerned about is that they’ve bypassed the application security and put together their own login screen of sorts. On this screen there are three objects: a text field for username entry, a password field for password entry, and an image that you click to “log in”. No actual login or authentication against any user source actually happens during this process - it’s simply a case that once you enter the correct user name and password, the “log in” button appears which will navigate to a certain “password protected” view.

The username entry field has no bindings on any of its components, no scripts, and no events. Likewise with the password field. However, when text is entered into these fields from a session, a memory tag shows the entered contents (yes, including the password, in plain text). These tags have no scripts associated with them.

During a session, the “log in” button (image) does not appear unless the user types in the correct user name and password (not “a” correct user name and password; one specific user/password combination). The image does not have any bindings for any components, so it’s not a simple visibility/size/position animation. It has no associated scripts, and only one event - an onClick event that runs a one-line script to show another view.

Using preview mode in the designer, none of this works. Text entered into the username/password field is not transferred to the memory tags, and the “log in” image is visible at all times.

There are no scripts or events associated with anything else on the view. There are no gateway event scripts, shared scripts or session scripts.

So how is all of this “working”? How does the text entered in the text fields make its way into the tags? How does the “log in” image component appear and disappear? Have they somehow modified the view’s source code outside of Ignition? How would I go about tracking down how all of this works?

As there’s only one user able to log in, perhaps do a text search on that username or password (inside ignition, and on the filesystem, as all perspective and script files are plain text). There’s a big chance it’s hard-coded somewhere.

Also, if it gets written in a memory tag, does it work with mulitple clients? Or does one client also log in all the other clients?

I’ll do a text search tomorrow when I’m back in front of the PC.

As to working with multiple clients - I’ll have to check. But again, no log in is actually happening. It just makes a button appear which allows you to change the main view. Perhaps I’ll enter the user name and password on one client, and see if the button appears on the other.

Update: if I have two sessions open on the “log in” page, when I type usernames and passwords into one session, they simultaneously appear in the text boxes in the other session. Likewise, if I manually enter a value into the tag from the designer, it appears in the session. It’s really as if they are bound to the memory tag - but it doesn’t work in the designer, and no binding exists.

This would be a lot easier if you supplied the view in question. You could either locate the view.json file for the View within the file system, or you could select the View node in the Project Browser, hold Shift and then right-click the View node and select “Copy JSON” and then paste the contents of the clipboard in this thread between two sets of triple ticks (```) so that it retains its formatting.

Also, I have to ask this because you claimed you weren’t very familiar with Perspective:
You say you have two sessions open to the same screen, but are they in two separate browsers? Having multiple browser tabs open to a single project will actually open multiple Perspective Pages to a single Session. If you open the login page in different browsers and you still see the fields update in both when input is typed, then it’s likely a tag is in use. If you DON’T see both sessions update when input is supplied, then it’s likely that a session property is tracking the input.

Unfortunately there are NDA’s in place preventing me from uploading any files, but I can probably redact the JSON text enough to make it safe for posting.

One session was open in a browser, the other on a smartphone. There is definitely a tag in use because I can enter text in the text field and see the tag update, or enter text in the tag and see the text field update. Just like a bidirectional binding - but there is no binding configured.

Below is the redacted JSON text. Redacted data has been replaced with asterisks, and mainly just consists of redacting partial file paths or component names as they could identify the company.

The one exception is near the bottom, where the username and password in question are shown in plain text (now redacted). The context for this is that on the “log in” button, there is an “onclick” event which runs a script. The first line of the script is a “log in as this user” script, but it’s been commented out. I’m guessing the guy who developed it realised there was no need to actually log in as he was bypassing the security, and instead just left the next line of the script (change view).

  "custom": {},
  "params": {},
  "props": {
    "defaultSize": {
      "height": 1024,
      "width": 1366
    }
  },
  "root": {
    "children": [
      {
        "meta": {
          "name": "Welcome to *******"
        },
        "position": {
          "height": 70.04,
          "width": 371.96,
          "x": 510.06,
          "y": 181.96
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/*********.png",
          "style": {
            "textAlign": "center"
          }
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "imgNeedHelp"
        },
        "position": {
          "height": 19.97,
          "width": 87.01,
          "x": 641.06,
          "y": 721
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/Need Help_.png"
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "img******"
        },
        "position": {
          "height": 40.96,
          "width": 143.98,
          "x": 618.93,
          "y": 880.03
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/*******.png"
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "Line1"
        },
        "position": {
          "height": 15.97,
          "width": 471,
          "x": 455.97,
          "y": 354
        },
        "props": {
          "fit": {
            "mode": "contain"
          },
          "source": "/system/images/PageAssets/2. Login Page/Line.png",
          "style": {
            "borderRadius": "1px",
            "borderStyle": "none"
          }
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "Line2"
        },
        "position": {
          "height": 15.97,
          "width": 471,
          "x": 455.97,
          "y": 419.02
        },
        "props": {
          "fit": {
            "mode": "contain"
          },
          "source": "/system/images/PageAssets/2. Login Page/Line.png",
          "style": {
            "borderRadius": "1px",
            "borderStyle": "none"
          }
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "Line3"
        },
        "position": {
          "height": 15.97,
          "width": 471,
          "x": 455.97,
          "y": 483.02
        },
        "props": {
          "fit": {
            "mode": "contain"
          },
          "source": "/system/images/PageAssets/2. Login Page/Line.png",
          "style": {
            "borderRadius": "1px",
            "borderStyle": "none"
          }
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "lblPosition"
        },
        "position": {
          "height": 19.97,
          "width": 149.03,
          "x": 465.94,
          "y": 449.02
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/Password.png"
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "lblUserName"
        },
        "position": {
          "height": 19.97,
          "width": 149.03,
          "x": 465.94,
          "y": 385.02
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/Username.png"
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "name": "txtPwd"
        },
        "position": {
          "height": 19.97,
          "width": 243.97,
          "x": 691.06,
          "y": 449.02
        },
        "type": "ia.input.password-field"
      },
      {
        "meta": {
          "name": "txtUsername"
        },
        "position": {
          "height": 19.97,
          "width": 215.96,
          "x": 691.06,
          "y": 385.02
        },
        "props": {
          "text": "test"
        },
        "type": "ia.input.text-field"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "params": {},
                "view": "Main/NewHMI/3. Register New User"
              },
              "scope": "C",
              "type": "nav"
            }
          }
        },
        "meta": {
          "name": "imgNeedHelp_0",
          "visible": false
        },
        "position": {
          "height": 19.97,
          "width": 140.01,
          "x": 614.02,
          "y": 677.99
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/Register New User.png"
        },
        "type": "ia.display.image"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "params": {},
                "view": "Main/NewHMI/4. Home Page"
              },
              "scope": "C",
              "type": "nav"
            }
          }
        },
        "meta": {
          "name": "btContinue_0"
        },
        "position": {
          "height": 19.97,
          "width": 41.94,
          "x": 663.06,
          "y": 623
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/Login.png",
          "style": {
            "borderRadius": "1px",
            "borderStyle": "none"
          }
        },
        "type": "ia.display.image"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "script": "\t#system.security.switchUser(\"***usernameredacted***\", \"****Passwordredacted*****\")\n\tsystem.perspective.navigate(view \u003d \u0027Main/NewHMI/4. Home Page\u0027)"
              },
              "scope": "G",
              "type": "script"
            }
          }
        },
        "meta": {
          "name": "btContinue"
        },
        "position": {
          "height": 49.05,
          "width": 165.01,
          "x": 601.04,
          "y": 606
        },
        "props": {
          "source": "/system/images/PageAssets/2. Login Page/Rectangle.png",
          "style": {
            "borderRadius": "1px",
            "borderStyle": "none"
          }
        },
        "type": "ia.display.image"
      }
    ],
    "meta": {
      "name": "root"
    },
    "props": {
      "aspectRatio": "1.333984375:1"
    },
    "type": "ia.container.coord"
  }
}

OK, so I’ve gotten hold of the person who did the project and the answer is as simple as you might expect.

It is in fact using tag bindings - the view I was looking at in the designer was not the same view that appeared during the session. It was an identical looking one, except this one has tag bindings.

There are dozens and dozens of half finished, unused views throughout this project. It seems that when he was unsure, he’d copy and paste a view, try something, and if it worked, just keep the copied view and the original one and never tidied up behind himself.

The more I look, the more I’m reminded of a quote from another automation forum I frequent:
“Some projects are best started with a reciprocating saw and a dumpster on castors”

Apologies for the red herring!

2 Likes

That’s really unfortunate.

2 Likes

That's a much more charitable description than I would have given it :laughing:

It sounded like the only possibility to me. But the difference between the client and the designer threw me off...

I've also seen projects with many duplicated screens or templates. Mostly from importing parts of old projects.

What I usually try is rename a screen, template or entire directory (so it doesn't get linked anymore). Then check what parts in the client stop working, and either fix it, revert the name change, or delete the screen or template if nothing is broken.

Until you are left with something clean.

If the functionality is simple enough to rewrite it (when it isn't a project that has grown over 10 years), a rewrite is probably easier.