Overview

Row-level security (RLS) permits user access to some rows in a table/view while denying access to the other rows. “User access” in this case could mean viewing data, loading data using custom pipelines, editing data, form-based data entry and/or consuming the OData API.

RLS was designed to allow multiple countries to store data in the same table, while not being able to see or change data of other countries. But it is generic in nature and can be used for any similar scenario (security by major office, security by org unit or team, etc.).

RLS works by matching values assigned to individual user accounts to values in a column (the RLS column) of a table or view. The user is granted access only to the matching rows.

RLS overview

There are 2 main steps to setting up RLS. A walkthrough of each step is provided below.

  1. User accounts are assigned values. This is done on the Admin/Users page.
  2. RLS is applied to 1 or more roles. This is done on the Admin/Roles page. The role determines which users and permissions are affected by RLS and links user account values to columns in tables/views.

Setting up RLS requires MART_MANAGE permission.

Step 1 - Assign values to user accounts

Create a custom user property

Navigate to the Admin/Users page and click Custom Properties.

custom properties button

Click Add Property and enter a name for the new property and click Create.

custom properties form

The property is created and appears in the list.

custom properties list

Assign values to users

Now that the custom user property has been created, we can assign each user a value for the property.

Assignment is currently done by exporting the list of users and making edits in an external spreadsheet application and importing the changes. One day a user interface for these edits may be provided but one advantage to editing in an external application like Excel is that it makes it easier to assign values for long lists of users, ie 100-200 user accounts.

On the Admin/Users page, click the Export button

export users button

Open the exported Excel file - the newly created property should appear as a column in the spreadsheet. Enter values for users in the new column. A user can be assigned multiple values by separating multiple values with a comma.

assign values in spreadsheet

Save and upload the modified users file using the Import button. A normal batch will be created (like when loading data) and the changes to user properties will be summarized in the batch preview. Commmit the batch.

Return back to the Admin/Users page and select one of the users. The assigned properties of the user are visible.

view user properties

Step 2 - Role setup

Create a role in the normal way. Only the users and permissions in this role will be affected by RLS.

Permissions affected by row-level security:

  • Viewing data (DATA_VIEW permission)
  • Loading data using custom pipelines (LOAD_ORIGIN permission)
  • Editing data (DATA_EDIT permission)
  • Form-based data entry (FORM_DATA_ENTRY permission)
  • Consuming the OData API (DATA_VIEW permission)

If a user is in multiple roles with RLS on the same table/view, the access is ORed across the roles. If a user has access to country “ABC” via one role and country “DEF” via the second role, the end-user has access to both ABC and DEF records. See the Use Case Scenarios section in the Troubleshooting section below for more details about behavior when a user is in multiple roles.

To activate RLS on a role, navigate to the RLS tab of the role and select the custom user property created above.

RLS tab

Then select a table/view plus a column (the “RLS column”) in that table/view to associate the user property with. Selecting “Allow null values” means that the end-user also has access to rows where the values are null in the RLS column.

RLS tab filled out

In the image above row-level security has been applied on the DIM_ISO3 column in the DATA table and also on the DIM_ISO3 column in the custom SQL view V_DATA. Rows with NULL values are not accessible in the table but they are in the view.

RLS via fields in FK table (double underscore fields)

If a data table to be secured by RLS has a country column but not a region column, it is still possible to define RLS by region if 1) the country column is a foreign-key field and 2) the foreign-key table (ie a country reference table) contains a region field and 3) the region field is a business primary key field in the foreign-key table.

For example, consider setting up RLS on table DATA which has foreign key field DIM_ISO3 which points to REF_COUNTRY

rls on fk column

REF_COUNTRY has 2 BPK fields, REGION and ISO3. Normally ISO3 would be sufficent as a unique business primary key, but in order to use the REGION field as an RLS column, REGION also has to be added as a business primary key.

FK table with 2 BPK fields

It is now possible to choose REGION in the RLS column dropdown box. It appears as “DIM_ISO3__REGION”: the 2 underscore characters indicate that this field comes from the foreign key table linked by the DIM_ISO3 column.

double underscore fields

Then a custom user property named “REGION” should be created and users should be assigned region codes rather than country codes.

Generically speaking: if the RLS column is a foreign key (FK) field, it is possible to select one of the business primary key (BPK) fields from the FK table. These fields appear as having double underscores in them. This allows setting row-level security based on a column in a related reference table that is not directly stored in the main table.

Troubleshooting

Row-level security requires extra troubleshooting because each user experience can be different from the other.

Simulate with Active Roles

As a mart admin, to repoduce the behavior of an end-user reporting an access issue where RLS is involved:

  1. Put your account into the same role(s) as the end-user
  2. Assign yourself the same user property(ies).
  3. Choose the same roles as the end-user in the Active Roles dropdown.

You will now experience the system as the end-user.

Use Case Scenarios

This section describes various scenarios to better understand system behavior in the context of RLS.

BIG TABLE OF SCENARIOS

Target Table User Role 1 User Role 2 User Property Behavior Why
CASE_DATA null n/a CTRY = null No access Not in any roles
CASE_DATA CONSUMERS
  DATA_VIEW on all objects
n/a CTRY = null View all rows No RLS and DATA_VIEW on all objects.
CASE_DATA COUNTRY_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.CTRY
n/a CTRY = null No access User has no value for CTRY property.
CASE_DATA COUNTRY_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.CTRY
n/a CTRY = ABC Access to ABC rows User’s CTRY value matches RLS column on table.
CASE_DATA COUNTRY_VIEWERS
  No DATA_VIEW on CASE_DATA
  RLS on CASE_DATA.ISO3 = user.CTRY
n/a CTRY = ABC No access. DATA_VIEW permission is unchecked in role.
CASE_DATA COUNTRY_VIEWERS
  DATA_VIEW only on CASE_DATA
  RLS on CASE_DATA.ISO3 = user.CTRY
n/a CTRY = ABC Access to ABC rows DATA_VIEW on specific table and matching RLS values.
CASE_DATA COUNTRY_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.CTRY
n/a CTRY = ABC,DEF Access to ABC and DEF rows. User has multiple matching CTRY values.
CASE_DATA CONSUMERS
  DATA_VIEW on all objects
COUNTRY_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.ctry
CTRY = ABC All rows Role 1 has no RLS.
CASE_DATA COUNTRY_VIEWERS_GRP_A
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.ctry
COUNTRY_VIEWERS_GRP_B
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.ctry
CTRY = ABC Access to ABC rows Matching RLS via 2 roles.
CASE_DATA REGION_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.WHO_RGN = user.region
n/a REGION = AMR Access to AMR rows.  
CASE_DATA REGION_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.WHO_RGN = user.region
COUNTRY_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.CTRY
REGION = AMR
CTRY = ABC
Access to both region AMR and country ABC rows. User matches both region and country.
CASE_DATA REGION_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.WHO_RGN = user.region
COUNTRY_VIEWERS
  DATA_VIEW on all objects
  RLS on CASE_DATA.ISO3 = user.CTRY
REGION = AMR
CTRY = null
Access to AMR rows. User matches only region, even though in COUNTRY_VIEWERS role.