System User
The system user is a system object in ThingWorx designed to make the management of internal service permissions easier, while still allowing the external API layer to be permissioned on a user-by-user basis. The system user is optional. If the system user is not assigned to a service, ThingWorx will check the permissions of the user who made the original request for all subsequent wrapped services.
Normally, when a user calls a service, that user must have permission to the called service as well as permissions to any other services called from within the initial called service. For example, a user executes a service named GetMachineRunTimeHistory on a Thing that internally calls QueryStreamEntriesWithData and GetDataTableEntryByKey. Without using the system user, the user that called GetMachineRunTimeHistory also needs explicit permissions to execute QueryStreamEntriesWithData and GetDataTableEntryByKey.
With the system user, if a custom service is called from within a service or subscription (a wrapped service call), and the system user is permitted to execute the service, the service will be permitted to execute regardless of the user who initially triggered the sequence of events, scripts, or services. However, the user will not be able to directly call the wrapped service itself (from the example above, QueryStreamEntriesWithData or GetDataTableEntryByKey unless the user is explicitly permitted).
This practice allows an administrator to lock down externally-accessible surface APIs using users and groups with explicit access enablement. While it is always recommended to depend on user propagation to wrap service calls, the system user can make management of internal services calls easier from a permission management perspective.
The system user can be added to the Administrators group. Doing this will grant all users full permission on everything nested within a service call – all services invoked inside a custom service will succeed, regardless of the invoking user’s permissions.
The system user does not expand a user's visibility when querying for Things. The system user cannot be used to retrieve Things that a user doesn't have visibility permission to see. Consider the following example that illustrates this:
1. There are five Things, T1, T2, T3, T4, T5, that inherit the Thing Template Template1 and there are two users, User1 and User2.
2. User1 has visibility permission on T1 and T2 and User2 has visibility permission on T4 and T5.
3. Service execution permission is granted to the system user for QueryImplementingThings for Template1 and a custom service named CallQueryImplementingThings is created, which internally calls QueryImplementingThings and provides service execution permission to User1 and User2 to the service CallQueryImplementingThings.
4. When User1 executes the CallQueryImplementingThings service, they will see T1 and T2 in the result.
5. When User2 executes the CallQueryImplementingThings service, they will see T4 and T5 in the result.
|
It is not recommended to use the system user to bypass security or to alleviate the need to carefully design and implement a secure application. Best practice recommends that you should limit the reliance on the system user to specific use cases and not use it as a general workaround to user propagation practices.
|
System User Example
Granting system user permission to run a service means that every user will be granted permission to run that service from within a custom service, but not necessarily granted permission to call that service directly. To use it properly:
1. Give the system user permission to run a service, but restrict that service's permissions, so that other users do not have permission to run that service.
2. Write a custom service that calls that restricted service, then give users permission to run the custom service but not the restricted service.
3. Users will be able to run that custom service, even though they do not have permission to run the restricted service.
To further illustrate this concept using an example, consider the following use case:
You want to write a custom service where users are shown the status of a particular connected device, named CustomService1. To do that, CustomService1 could use the SearchDevices service on the DeviceFunctions resource to look at all devices on the platform, and then filter out the results to return only the particular device.
However, permissions are checked not only for CustomService1, but for every service call made within CustomService1. The user would need permission to run the SearchDevices service to successfully call CustomService1; otherwise, the call to CustomService1 fails. Giving the user permission to call SearchDevices is not ideal because you may not want the user to have access to the status of every device, but only to the particular device.
The system user can resolve this. When a service is executed directly, the permissions of that user are checked to determine if they have permission. This is true for any service, including all custom services. However, when a service is executed from within a custom service, there are actually two permissions checked: the user's permission, and the system user's permission. If either the user or the system user have permission to run the service within the custom service, the within service is executed as if the user has permission to run it. Since SearchDevices is only called inside CustomService1 and is not called directly, you can give system user permission to run SearchDevices. When the user calls CustomService1, the permissions check will occur, seeing that User1 does not have permission, but also seeing that system user does have permission. As a result, SearchDevices will be executed as if the user has permission. If the user ever attempts to call SearchDevices directly without calling it from within CustomService1, then the system user's permission would not apply and the user would be denied permission.