Python scripting: how to get current user source?

Hi,

I would like to check not only a users role, but also from which user source the currently logged in user is. The user should only be able to interact with a component when he comes from the right usersource and has the required roles.

I have found the expression hasRole(“Administrator”, [user]", [usersource]) and the python function system.security.getRoles() , but they do not solve my problem, since they only check for a users role, or the role of a user from a specific usersource. But I need to be able to know the current users usersource.
Unfortunately I don’t see how I can get this information. The documentation of hasRole() says that if left blank, the current users usersource is used. So I assume that this information should be available somewhere.

Security levels and zones would probably be best suited to handle this, but we are using Vision and they can not be used to restrict access to vision components, so i have to check it in a script.

Welcome, @reto.kunz :slight_smile:

Firstly, I am not sure why this is important:

A project can only have a single user source associated with it.

Please can you explain your thinking behind why this is required? :slight_smile:

If you just need the name of the current project’s usersource, there’s a system tag: [System]Client/System/UserSource

2 Likes

Well, that’s not 100% true. A usersource can then have a fallback usersource.

Imagaine the following scenario:
You have a AD/Hybrid -usersource with a local usersource in soft fallback mode.
AD-usersource is managed by the customer, in the local usersource you have additional users with admin rights. Only those have access to certain components.
When you log in with an admin account which does not exist in the AD usersource, it comes from the local usersource.

To me this seems to be a good solution for managing admin users of a project without the customer having access to them. You could also provide a user management in the vision client which can only manage the AD/Hybrid usersource. However this is a dangerous approach.
When you add a user in the AD-database which has exactly the same name and roles which also exist in the local database, he will also have access to your components.
This could be avoided when you know from which usersource the current user comes.

This is why [System]Client/System/UserSource does not work in this case. It contains the projects usersource, not the current users usersource.
With hasRole(“Administrator”, [user]", [usersource]) there is a similar problem. it will return True when there is a user with this role in the specified usersource - regardles from which usersource the current user comes.

1 Like

That's a fair point, and something I hadn't considered.

However, the purpose of the fallback is not meant to provide the functionality you describe. Although it is theoretically possible, the fallback should only really be used as a backup IMO, not as a secondary user source. The other thing to consider is the supportability of an unconventional security structure. Although it makes sense to you, this may not make sense to someone else in 10 years time.

That being said, if you do wish to pursue this functionality...

This should only be true if the user exists and has that role in both user sources, or are you seeing different behaviour?

If your aim is this, then the user should not exist in both sources, so the hasRole("myRole", currentUser, secondaryUserSource) function should return false.

I believe I understand your issue (which is a valid one :slight_smile:), just trying to brainstorm ways that may be better, or get your current method working in some guise :thinking:

1 Like

True, I am fully aware that I would be 'abusing' this functionality. But it seems to be a very handy workaround to have a second usersource which is not accessible to the customer.
I can not see another way to achieve this functionality. However, if there are better ways to achieve this I would of course be willing to change my approach.

Example:

user in local (fallback) usersource 'default':
name: admin, password: password, role: administrator

newly created user in AD/Hybrid usersource 'remoteSource':
name: admin, password:1234, role: administrator

In this example the 'admin' user from the AD usersource could log in with his own login credentials and imitate the admin user from the local user source. hasRole("administrator", "admin", "default") would return True, even if the current user comes from the 'remoteSource'.
Therefore this workaround would be a bad idea, except if we could know from which usersource the user is.
Unfortunately I have not found any possibility for this so far, not even in the sdk api doc.

Another possibility would be to have predefined roles (no admin roles) in the 'remoteSource' and not giving the customer the rights to change roles, only to allocate roles to users. This is probably the easier and safer way, but limits the customers access rights to 'his' user source.

You're right, this is an issue. However, if we're strictly speaking Microsoft AD and Ignition Internal user sources, you could use characters in the username for the local that are invalid for the AD, e.g. @.

username@suffix is invalid for Microsoft AD but not for Ignition.

This is a bit of a fudge, but would get around the issue currently (this would not be a future-proofed solution). We're well of the beaten path, so... :sweat_smile:

This has the advantage that you would not need to use the fallback functionality. hasRole() specifying the user source would suffice as the user would never exist in the AD.

The disadvantage is supportability. Everyone developing the software would have to conform to the username structure.

Just wanted to voice my strong disagreement with this statement. I use and encourage soft fallback (to local users) specifically to allow contractors admin access on an otherwise AD-controlled system. Many clients do not want their contractors in AD.

4 Likes

That's a fair point :slight_smile: it's only my opinion as I haven't needed to resort to this yet (I'm sure my tune will change if I ever have too :sweat_smile:).

In your experience, have you come across ways of subverting the issues presented in the this thread?

Based on this I guess your use-case is less about preventing the customer from accessing the system, and more about allowing access for yourself and/or others without having to go through customer IT etc.

A couple ideas:
You could decorate all the users in your failover source with a unique role - then you just check for that role on a particular user. Not a guarantee, but better than nothing.

You can check if your User object is an instance of com.inductiveautomation.ignition.common.user.BasicUser, which has a getProfileName() method - though I’m not positive it won’t “lie” in the event of a failover.

I think this has the same issue as:

Your second suggestion could be interesting though :thinking:

Thank you for all your interesting inputs!

This is exactly the use case we try to cover. What approach do you choose to prevent the AD-users from becoming administrators? Or is this not necessary in your case?

I have come across this in the api-documentation, but I did not find a method which returns the required user object of the current user?

This would also be an interesting approach, but as you mention it is a bit of a fudge.
But this brought me to another idea, which I think would be safe to use.

We could check not only for the roles, but also for a specific username which is our administrator.
This username could then be filtered out in the AD-usersource with LDAP-filters, so that it is impossible to have this exact same user in the AD-usersource. This is probably the approach I will take.

I don't ever block my customers from accessing their own systems in any way they desire.

Not necessary. My clients who use AD want their own administrators in AD. And how they govern their own AD is none of my business. (I will help implement any security policy the want, of course.)

2 Likes

I'm not suggesting a particular method - but from experience, virtually all of the methods that return User objects are returning either BasicUser or PyUser (which extends BasicUser) instances. Simply print type(userObject) to confirm what implementation class you have of the User interface.