Table Access Control

Deephaven makes it simple to share the results of Persistent Queries with other users. You can add groups to your Persistent Query as either "Admins" or "Viewers". If you have not defined any ACLs, then all Admin and Viewer groups can see all the results of the query. This makes it simple to share results with a chosen set of users.

Different users will likely be members of different groups and have different restrictions that must be followed when providing them data. Deephaven provides Edge ACLs for this purpose.

Edge ACLs function the same way Source ACLs do in that, based on the user requesting the data, a set of filters are generated and applied to the table before being returned. The difference is that Edge ACLs are attached directly to derived tables and are applied dynamically when a user fetches the table.

Warning

As soon as you add an ACL to one result, the remainder of the exported objects are blocked by default. You must apply ACLs to each object that you want to make available to Viewers. The owner of a query and users in one of the admin groups do not have ACLs applied, and tables are returned without filtering.

Usage

Edge ACLs are created using the EdgeAclProvider.builder() method, and then are built up using the rowAcl() and columnAcl() methods. For each of these, you provide a group and a Filter Generator to produce the desired ACL when a user requests the table.

Once you have specified the ACLs desired, you create the ACL object using the build() method and can then attach the ACLs using the applyTo() method, as in the example below.

The following example assumes that a table "TickingTable" has already been created. Edge ACLs are created using a builder that contains a few simple methods for building up ACL sets.

Once build() is called, you have an ACL object that can then be used to transform one or more tables using the applyTo() method.

Note that you must overwrite the scope variable with the result of the application, since Table properties are immutable.

import io.deephaven.enterprise.acl.EdgeAclProvider
import io.deephaven.enterprise.acl.AclFilterGenerator

def ACL = EdgeAclProvider.builder()
        .rowAcl("NYSE", AclFilterGenerator.where("Exchange in `NYSE`"))
        .columnAcl("LimitPrice", "*", AclFilterGenerator.fullAccess())
        .columnAcl("LimitPrice", ["Price", "TradeVal"], AclFilterGenerator.group("USym"))
        .build()

TickingTable = ACL.applyTo(TickingTable)
from deephaven_enterprise.edge_acl import EdgeAclProvider
import deephaven_enterprise.acl_generator as acl_generator
ACL = EdgeAclProvider.builder() \
    .row_acl("NYSE", acl_generator.where("Exchange in `NYSE`")) \
    .column_acl("LimitPrice", "*", acl_generator.full_access()) \
    .column_acl("LimitPrice", ["Price", "TradeVal"], acl_generator.group("USym")) \
    .build()

TickingTable = ACL.apply_to(TickingTable)

The AclFilterGenerator class provides some helpful factory methods for commonly used ACL types. See the language API documentation for more details.

There are two types of ACLs: Row Acls and Column ACLs.

Row Acls

Row ACLs control access to the rows of a table based on the user requesting the table. These ACLs control the overall visibility of a table to users. For example, if the ACLs attached to a table do not contain any mappings for any group a user is a member of, that user will be denied access to the table entirely.

Column ACLs

Column ACLs allow you to restrict access to values within a column. You could use these to hide pricing data in a table from a specific group while allowing data in other columns to be present.

Warning

Column ACLs require a Default ACL to be applied for unmentioned columns. If no default is provided, when Column ACLs are applied, values in any columns not explicitly mentioned will be hidden from view.

A simple default ACL would be to allow access to all unmentioned columns. The "*" parameter will only match columns that have not already been matched by a more specific ACL.

aclBuilder.columnAcl("allusers", "*", AclFilterGenerator.fullAccess())
aclBuilder.column_acl("allusers", "*", acl_generator.full_access())

Filter Generators

Filter Generators form the backbone of the ACL system. Since the system cannot apply filters to a table before it knows the user who is requesting access, it encodes the ACL as a set of instructions on how to produce the filters, called Filter Generators.

A Filter Generator will take the user's credentials and produce one or more filters that will be applied disjunctively to the table before providing the result to the user. There are a few built in Filter Generator types that can be easily constructed using the AclFilterGenerator class.

Where

AclFilterGenerator.where(@NotNull String filter)
acl_generator.where(filter: str)

This Filter Generator simply produces the provided where clause. This is useful when you need to apply a blanket filter to a table based on a group. For example:

aclBuilder.rowAcl("MarketUs", AclFilterGenerator.where("Exchange in `NYSE`"))
acl_builder.row_acl("MarketUs", acl_generator.where("Exchange in `NYSE`"))

When the requesting user is a member of the 'MarketUs' group, this will produce a filter that selects only the rows in the 'Exchange' column that match 'NYSE' .

Where In

The Where In filter generator is a bit more complicated than the others. This Filter generator produces a whereIn clause using a "Set" table containing the user's grouping information.

AclFilterGenerator.whereIn(String setNamespace, String setTableName, String setGroupColumn, String[] setFilters, boolean useHistorical, String... matchExpressions)

AclFilterGenerator.whereIn(Table setTable, String setGroupColumn, String[] setFilters, String... matchExpressions)
acl_generator.where_in(set_namespace: str, set_table_name: str, set_group_column: str, set_filters: list[str], use_historical: bool, *match_expressions: str)

acl_generator.where_in_table(set_table: Table, set_group_column: str, set_filters: list[str], *match_expressions: str)

When the generator creates the filter, it will filter the "Set" table down to only groups the user is a member of using the "groupColumn" parameter. Next, it will apply a whereIn to the requested table using the filtered set table and the match expressions provided.

The "Set" table may be provided either as a Namespace.TableName pair, to be fetched from the database, or directly as a Table instance.

Group

This Filter Generator will produce a filter that selects rows from the table where the value in the 'group' column matches a group the user belongs to.

AclFilterGenerator.group(@NotNull String groupColumn)
acl_generator.group(groupColumn: str)

In the next example, let's assume the user 'mark' is a member of 'Restricted', 'ETF', and 'Derivative', and the table has an ACL constructed like this:

aclBuilder.rowAcl("Restricted", AclFilterGenerator.group("SecurityType"))
acl_builder.row_acl("Restricted", acl_generator.group("SecurityType"))

Mark will only be able to see rows in the table where 'SecurityType' is either 'ETF' or 'Derivative'.

Conjunctive

The following filter generator simply combines the results of the parameter filter generators conjunctively; in other words, users can only see rows that match ALL of the provided ACLs.

AclFilterGeneratorconjunctive(AclFilterGenerator... generators)
acl_generator.conjunctive(generators: list[AclFilterGenerator])

Disjunctive

The following filter generator simply combines the results of the parameter filter generators disjunctively; in other words, users can only see rows that match ANY of the provided ACLs.

AclFilterGenerator.disjunctive(AclFilterGenerator... generators)
acl_generator.disjunctive(generators: list[AclFilterGenerator])

Full Access

This filter generator simply gives the group full access to the table.

AclFilterGenerator.fullAccess()
acl_generator.full_access()

No Access

This filter generator denies all access to the table for this group.

AclFilterGenerator.noAccess()
acl_generator.no_access()