Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Role-based access control (RBAC) sets the active identity for a session. Attribute-based access control (ABAC) in Unity Catalog applies row filters and column masks to that identity:
- RBAC: A user assumes a role to act with that role's permissions instead of their own. Use roles to separate access across projects or sensitivity tiers.
- ABAC row filter and column mask policies: Filter rows or mask column values for tables the active identity can already access. Policies are defined on securable objects, such as catalogs or schemas, and use governed tags to identify matching tables and columns. See Core concepts for attribute-based access control (ABAC).
How identity functions behave when assuming a role
The Unity Catalog identity-related SQL functions resolve against the session's active identity, not the underlying authenticating user. When a user assumes a role, the active session identity becomes the role:
| Function | When user acts as their user identity | When user assumes a role |
|---|---|---|
session_user() |
Returns the user's username | Returns the assumed role's name |
is_member(group) |
Returns true if the user is a direct or indirect member of a workspace-local group or an account group assigned to the workspace |
Returns true only if the assumed role is itself a member of group. Returns false for groups the underlying user is a member of but the assumed role is not. |
is_account_group_member(group) |
Returns true if the user is a direct or indirect member of the account group |
Returns true based on the assumed role's direct and indirect account-group memberships, including groups not assigned to the workspace. |
The assumed role is the active identity for ABAC policy evaluation, Unity Catalog grant resolution, and audit attribution. Assuming a role can therefore change the behavior of existing policies and views built around per-user identity.
Note
A role is not a member of itself. When a user assumes role G, session_user() returns G, but is_member('G') and is_account_group_member('G') return false. To match on the assumed role in a policy, compare against session_user() rather than testing membership with is_member or is_account_group_member.
The SQL reference recommends session_user() instead of the deprecated current_user() alias. Use is_account_group_member() to test account-group membership. See session_user function and is_member function.
Common pitfall: row-level security views built on session_user()
A common pattern across ABAC and table-level row filters is to filter rows by joining against a provisioning table (also called a mapping table or access-control list) keyed on the username returned by session_user(). For example:
CREATE OR REPLACE FUNCTION facility_filter(facility_id STRING)
RETURN EXISTS (
SELECT 1
FROM governance.user_facility_provisioning p
WHERE p.user = session_user()
AND p.facility_id = facility_filter.facility_id
);
The facility_filter.facility_id reference identifies the function parameter. Without the function name, facility_id resolves to the provisioning table's column instead. See Name resolution.
When the same user assumes a role, session_user() no longer returns their username. It returns the role's name. Because the role isn't in the provisioning table, the filter returns no rows and the user appears to lose access to data they were granted.
Add the role to the provisioning table
Treat the role as another principal in your provisioning data: insert one row per role with the facilities (or other attributes) that the role should see. The filter then matches whether the active identity is the user or the assumed role.
Combined-use patterns
The following examples combine RBAC with ABAC row filters and column masks.
Requirements
Before you use the examples, complete these steps:
- Use compute that supports ABAC.
- Create the governed tags and grant the permissions needed to apply tags, create user-defined functions (UDFs), and manage policies. See Separation of duties and permissions.
- Grant the querying identities
USE CATALOG,USE SCHEMA, andSELECTon the relevant data. Row filters and column masks do not grant these privileges. See Usage privileges. - Create each example UDF in a schema where you have permission, and use that schema as the current schema when creating its policy, or fully qualify the function name. For a complete walkthrough, see Tutorial: Configure ABAC with SQL.
Per-project row filters keyed on the assumed role
A clinical research organization runs several concurrent trials, each with its own access role. A row filter limits each role to its trial's data. The same pattern applies to other projects that need separate data access. Tag the project_id column in each table with the governed tag key project. The column values identify the project for each row.
Setup:
- Tables under
clinical_trials.*have aproject_idcolumn tagged with the governed tag keyproject. - Each project has a corresponding access role named
role-<project>(for example,role-alpha,role-beta). - Users have the Assume permission only on the roles for projects they work on.
Row filter UDF:
CREATE FUNCTION project_match(project_id STRING) RETURNS BOOLEAN
DETERMINISTIC
RETURN session_user() = CONCAT('role-', project_id);
Policy:
CREATE POLICY per_project_row_filter
ON CATALOG clinical_trials
ROW FILTER project_match
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag('project') AS project
USING COLUMNS (project);
This UDF compares the active identity with each row's project_id column value. MATCH COLUMNS uses the governed tag to find that column. The TO and EXCEPT clauses select the principals a policy applies to. They do not compare row values. As the principal-targeting guidance explains, prefer TO / EXCEPT for simple principal scoping and reserve identity functions inside a UDF for cases like this one, where a single rule depends on both the active identity and the row's content.
A single policy covers every project: USING COLUMNS (project) passes each row's value from the matched column into the UDF. The alias project names the matched column, not a tag value. For an example that uses a lookup table to determine row access, see Use mapping tables for dynamic access control.
Behavior:
- A user acting as their user identity sees no rows in any
clinical_trialstable.session_user()returns their username. Choose role names that cannot match a user's username or a service principal's application ID. - A user assuming
role-alphasees only rows whereproject_idequalsalpha. After switching torole-beta, run the query again to see rows whereproject_idequalsbeta.
Mask personally identifiable information unless a role is assumed
In this example, personally identifiable information (PII), such as Social Security numbers and email addresses, appears masked by default. To see the raw values, a user must assume role-pii-cleared. Audit events identify the role used to access the data and the identity that initiated the event.
Setup:
- Tag sensitive columns with the governed tag key
pii(allowed values such asssn,email,phone). - Create the
role-pii-clearedgroup with no members. Grant the Assume permission on that group.- Users authorized to view raw PII can assume this access role to view unmasked data. Do not add members to the group, because the policy's
EXCEPTclause would allow them to see unmasked data without assuming the role.
- Users authorized to view raw PII can assume this access role to view unmasked data. Do not add members to the group, because the policy's
Create the column mask UDF, using this SQL:
CREATE FUNCTION mask_pii(val STRING) RETURNS STRING
DETERMINISTIC
RETURN '***';
Policy:
CREATE POLICY pii_default_mask
ON CATALOG customer_data
COLUMN MASK mask_pii
TO `account users`
EXCEPT `role-pii-cleared`
FOR TABLES
MATCH COLUMNS has_tag('pii') AS pii_col
ON COLUMN pii_col;
The EXCEPT clause excludes role-pii-cleared from the policy entirely, so the UDF is never invoked when that role is the active identity. See Prefer TO/EXCEPT for principal targeting for the general guidance on principal targeting via TO / EXCEPT.
Behavior:
- A user acting as their user identity sees
***in every PII column. This is the default state for everyone, including users who have the Assume permission onrole-pii-cleared. - After assuming
role-pii-cleared, the policy no longer applies to the session, and the same user sees the raw values. - Audit events for these queries identify the authorizing role in
identity_metadata.run_asand its name inidentity_metadata.run_as_display_name. See Audit attribution.
Sensitivity-tier policies that vary by assumed role
Data is classified into sensitivity tiers (internal, confidential, restricted). Each tier has a corresponding access role, with restricted implying access to confidential and internal as well. A single row filter UDF gates row visibility by comparing each row's tier against the user's assumed role.
Setup:
- Tables have a
sensitivity_levelcolumn tagged with the governed tag keysensitivity(allowed values:internal,confidential,restricted). - Three access roles:
role-sens-internal,role-sens-confidential,role-sens-restricted.
Row filter UDF:
CREATE FUNCTION sensitivity_allowed(row_sensitivity STRING) RETURNS BOOLEAN
DETERMINISTIC
RETURN CASE
WHEN session_user() = 'role-sens-restricted' THEN TRUE
WHEN session_user() = 'role-sens-confidential' THEN row_sensitivity IN ('internal', 'confidential')
WHEN session_user() = 'role-sens-internal' THEN row_sensitivity = 'internal'
ELSE FALSE
END;
Policy:
CREATE POLICY sensitivity_filter
ON CATALOG corp_data
ROW FILTER sensitivity_allowed
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag('sensitivity') AS sens
USING COLUMNS (sens);
Because a role is not a member of itself, this UDF compares session_user() against each role name rather than testing membership with is_account_group_member(). See the note in How identity functions behave when assuming a role for why membership tests don't match the assumed role. See Performance considerations for row filter and column mask policies for performance characteristics of identity functions in UDFs.
Behavior:
- A user acting as their user identity sees no rows. The
ELSE FALSEbranch matches anything that isn't one of the three roles. - Assuming
role-sens-internalreveals onlyinternalrows. - Assuming
role-sens-confidentialrevealsinternalandconfidentialrows. - Assuming
role-sens-restrictedreveals all rows.
Users assume the tier they need for the session. The filter applies to tables with the tagged column and returns the rows allowed by that role.
Audit attribution
In the audit log system table, identity_metadata.run_by identifies who initiated an event, and identity_metadata.run_as identifies the identity used for authorization. The corresponding run_by_display_name and run_as_display_name fields contain human-readable names. For queries made interactively while assuming a role, these identify the user and role respectively. Job workloads can have a different initiator. See Set Run as to a group.
Use audit events to review activity under a role. To inspect which policies apply to a table, use SHOW EFFECTIVE POLICIES as described in Plan for dynamic policy evaluation. For audit access, schema, and availability, see Verify audit attribution.
Additional resources
| Resource | Description |
|---|---|
| Tutorial: Model exclusive access with RBAC | Configure and verify separate data access for two clinical trials using roles. |
| Switch RBAC roles | Assume a role using the role switcher, dedicated access mode clusters, the Databricks CLI, the API, or third-party BI tools. |
| Manage permissions on a group | Grant or revoke the Assume permission on a group so users can assume the corresponding role. |
| Attribute-based access control in Unity Catalog | Learn how governed tags, policies, and policy evaluation work. |