Advanced Permissions

You can create custom JavaScript expressions to apply permission rules and attribute filters. This is a powerful feature that lets you apply multiple permissions/filters to multiple users/groups at once.

You can construct an expression that accesses details about the data, the user, and/or the request, and then applies permissions accordingly. For example, you might want to configure an expression that applies permissions according to:

When configuring advanced permissions, keep the following in mind:

Order of Evaluation and Inheritance

While advanced permissions are powerful and offer many advantages, it is important to note how they differ from regular permissions.

Because advanced permissions are evaluated in the opposite direction from regular permissions, any advanced permission configured at a higher level overrides more granular permissions. For example, an advanced permission configured at the service level will always override any advanced permission configured at the layer, table, and/or task level.

JavaScript Support

The Access Control Expression Editor supports all JavaScript keywords and functions. You will also find some items that are particularly useful or specific to Access Control.

For example:

The editor provides code completion suggestions as you type, but you can press Ctrl + Space to display code completion suggestions at any time.

Configure an Expression

  1. In the VertiGIS Studio Access Control Designer: select the service, layer, or task on which you want to configure an advanced expression.

    For details, refer to the appropriate section:

  2. Click Advanced.

     

    The Advanced Permissions panel for the selected element displays.

     

  3. In the Advanced Permissions panel, click Add.

  4. Select a user or group:

    In the Select a User or Group dialog box, in the drop-down list on the right, choose whether to search by Group or by User.

    Start typing the name of the group/user into the text box, and then select from the drop-down list that displays.

  5. In the Advanced Permissions table, click Edit Expression.

     

    The Expression Editor opens.

  6. Type your expression into the editor. As you type, the editor provides JavaScript code completion suggestions.

     

    Press Ctrl + Space to display code completion suggestions at any time.

  7. Click OK.

  8. Click Save.

Examples

The following sections show how advanced permissions can be used in some common use cases.

These examples are intended to illustrate the APIs and patterns of usage in advanced permissions. They do not represent complete solutions with proper handling of all errors and boundary conditions.

Grant access to specific layers based on user role

Use case: You have multiple layers that you want to allow multiple groups to access.

Using regular permissions, you would need to add one permission to each layer for each group you want to allow.

Using advanced permissions, you can achieve the same result by configuring a single permission at the service level.

 

Example expression:

if (layer.name === "Cities" || layer.name === "Countries") {
   if (isUserInRole("Role 1", "Role 2")) {
      allowLayer();
   }
}

Notice that we can achieve the same result with a single permission and a small amount of code. Now consider scaling the problem up to 50 layers and 10 groups. The advanced permissions approach is much simpler and easier to maintain.

Deny access to an internal field

Use case: Your service has many layers, and many of them contain an internal field that you do not want your users to access.

You could use regular permissions to achieve this, but you would need to configure a deny permission on every layer containing the field.

Using advanced permissions, you only need to configure one permission at the service level.

Because it is applied at the service level, the following expression evaluates for every layer. If the field does not exist on some of the layers, it is simply ignored.

 

Example expression:

Copy
denyField("INTERNAL_ID");

 

This is a simple and efficient way to apply a single permission to multiple layers, using a small amount of code.

Deny access to a field based on URL of the request

Use case: You want to prevent users from accessing a particular field if the incoming request is based on a query.

You cannot use regular permissions to achieve this. However, you can configure one advanced permission that checks the URL path of the request. If the path ends with '/query', access to the field is denied.

 

Example expression:

Copy
if(request.path.endsWith("/query")) {
  denyField("Field1");
}

 

You can use this approach to deny access to multiple fields, or to an entire layer, table, or task.

Set an attribute filter based on a user lookup

Use case: You want to restrict which features of a layer a user can access based on a "ZONE" attribute of the layer using a lookup of usernames to zones defined in code.

 

Example expression:

Copy
const lookup = {
    bsmith: "Zone 1",
    jdoe: "Zone 2",
    user1: "Zone 1",
    user2: "Zone 3",
};
const zone = lookup[user.name];
if (zone) {
    setAttributeFilter(`ZONE = '${zone}'`);
} else {
    setAttributeFilter("1 = 0");
}

 

You can use setAttributeFilter("1 = 0") to deny access to all features.

Set an attribute filter based on a user lookup that uses a HTTP request

Use case: You want to restrict which features of a layer a user can access based on a "ZONE" attribute of the layer using a lookup of usernames to zones provided by an external service over HTTP.

 

Example expression:

Copy
run(async () => {
    const response = await fetch(`https://server/api/?user=${encodeUriComponent(user.name)}`);
    const json = await response.json();
    const zone = json.zone;
    setAttributeFilter(`ZONE = '${zone}'`);
});

 

The run(async () => {... pattern is required when you need to perform asynchronous operations like fetch that use the await keyword.

Set an attribute filter based on a user lookup that uses a HTTP request with caching

Use case: A cache optimized version of the previous example that caches the result of the HTTP lookup for 30 seconds.

 

Example expression:

Copy
run(async () => {
    const cacheKey = `zone-cache-${user.name}`;
    let zone = getCachedData(cacheKey);
    if (!zone) {
        const response = await fetch(`https://server/api/?user=${encodeUriComponent(user.name)}`);
        const json = await response.json();
        zone = json.zone;
        setCachedData(cacheKey, zone, 30000);
    }
    setAttributeFilter(`ZONE = '${zone}'`);
});

 

Set an attribute filter based on a user lookup that uses a HTTP request to an ArcGIS Server service

Use case: You want to restrict which features of a layer a user can access based on a "ZONE" attribute of the layer using a lookup of usernames to zones provided by an ArcGIS Server service.

 

Example expression:

Copy
run(async () => {
    const where = encodeUriComponent(`USER = '${user.name}'`);
    const response = await fetch(`https://server/arcgis/rest/services/MyService/FeatureServer/0/query?f=json&where=${where}&outFields=ZONE`);
    const json = await response.json();
    const zone = json.features[0].attributes.ZONE;
    setAttributeFilter(`ZONE = '${zone}'`);
});

 

Set a geometry filter based on a user lookup that uses a HTTP request to an ArcGIS Server service

Use case: You want to restrict which features of a layer a user can access based on a geometry using a lookup of usernames to geometries provided by an ArcGIS Server service.

 

Example expression:

Copy
run(async () => {
    const where = encodeUriComponent(`USER = '${user.name}'`);
    const response = await fetch(`https://server/arcgis/rest/services/MyService/FeatureServer/0/query?f=json&where=${where}&returnGeometry=true`);
    const json = await response.json();
    const geometry = json.features[0].geometry;
    setGeometryFilter(geometry);
});