Use Template UDF for Row-level Filter¶
With template-based row-level filtering, you write the row-filter logic once as a Template UDF in Ranger, and row-filter policies refer to it. For each table that has a template row-filter policy, PolicySync fills the template with the principals of the policy items, creates one row-filter function for that table, and attaches it as the table's row filter.
This approach provides:
- One template for many tables, even when the tables name their columns differently
- No hand-written SQL in policies: each policy item names a template, one of its conditions, and the table columns to use
- Template edits that reach every table using the template, without saving any policy
- Fail-closed behavior: if a table's filter cannot be generated, PolicySync leaves the previous filter in place and withholds the table's own grants (for access policies that cover a whole schema, see Limitations)
How it works¶
- You create a Template UDF (
udfType: 3) in Ranger. It declares itsparameters(name and type of each column value it receives), itsconditionNames(named branches such asCONDITION_US), and a body (udfText) with placeholders. - Each row-filter policy item references the template, one of its conditions (
condition_name), and the tablecolumnsto bind to the parameters, in order. - PolicySync renders the template for the table: each referenced condition gets the principals of the items that use it, conditions that no item uses are left out, and the default branch gets every principal of the policy.
- PolicySync creates the function
<table>_row_filterin the table's schema and attaches it withSET ROW FILTER, before it grants access to the table.
Which row-filter mode to use¶
| Mode | Use it when | Configuration |
|---|---|---|
| Native row filter | Each policy has its own SQL filter expression | CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_ROW_FILTER |
| Reusable UDF | A stable master UDF reads an entitlement table and dispatches to sub-UDFs | CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_REUSABLE_MASTER_UDF_RLS |
| Template UDF (this page) | The same filter logic, with different principals per condition, applies to many tables | CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_TEMPLATE_BASED_ROW_FILTER |
Template-based row filtering has its own flag and does not need CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_ROW_FILTER. A table uses one mode at a time; a service can use native and template row filters on different tables. Template row filters are native Unity Catalog row filters: view-based (secure view) row filtering does not support template policies, so set CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_VIEW_BASED_ROW_FILTER to "false" (see Prerequisites).
Prerequisites¶
- Column loading. PolicySync checks every bound column against the table's columns, so column metadata must be loaded:
CONNECTOR_DATABRICKS_UNITY_CATALOG_LOAD_COLUMNSmust be"true"(the default). Without it, every template row-filter policy fails withTEMPLATE_COLUMNS_NOT_LOADED. - No view-based row filtering.
CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_VIEW_BASED_ROW_FILTERmust be"false"(its default in Privacera Manager is"true"). Secure views do not recognize template references, so a secure view must not be built from a template row-filter policy. - UDF UI in the Portal. Set
PORTAL_UDF_SUPPORTED_SERVICESand enable the User Defined Functions module as described in Portal: UDF-supported services and step 5 of Enable reusable master UDF RLS.
Enable template-based row filters (connector)¶
These settings are Databricks Unity Catalog connector variables in Privacera Manager.
| Variable | Connector property | Default | Description |
|---|---|---|---|
CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_TEMPLATE_BASED_ROW_FILTER | ranger.policysync.connector.0.enable.template.based.row.filter | false | Set to "true" to enable template-based row filters. |
CONNECTOR_RANGER_ENABLE_IMPLICIT_UDF_ENRICHER | policysync.ranger.enable.implicit.udf.enricher | false | Set to "true". PolicySync receives the Template UDFs from Ranger only when this is enabled. |
CONNECTOR_DATABRICKS_UNITY_CATALOG_LOAD_COLUMNS | ranger.policysync.connector.0.load.columns | true | Keep "true"; bound columns are validated against the table's columns. |
CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_VIEW_BASED_ROW_FILTER | ranger.policysync.connector.0.enable.view.based.row.filter | true | Set to "false"; template row filters are native row filters only. |
Template row-filter policies while the flag is off
If a table has a template row-filter policy while CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_TEMPLATE_BASED_ROW_FILTER is "false", PolicySync does not apply it and withholds the table's own grants (reason code TEMPLATE_RLS_DISABLED), so those grants never apply without the filter. For access policies that cover a whole schema, see Limitations.
On Self-Managed Privacera Manager, use the procedure below.
-
SSH to the instance where Privacera Manager is installed.
-
Run the following command to open the
.ymlfile to be edited.Multiple connector instances
If you have multiple connectors, replace
instance1with the appropriate connector instance name.Bash -
Set the following properties:
-
Once the properties are configured, run the following commands to update your Privacera Manager platform instance:
Step 1 - Setup which generates the helm charts. This step usually takes few minutes.
Step 2 - Apply the Privacera Manager helm charts. Step 3 - (Optional) Post-installation step which generates Plugin tar ball, updates Route 53 DNS and so on. This step is not required if you are updating only connector properties.
Create a template¶
Create the template from the User Defined Functions page in the Privacera Portal (UDF type Template), or with the Ranger UDF API.
| Field | Description |
|---|---|
name | Template name, unique per Ranger service (case-insensitive), at most 512 characters. Policies refer to the template by this name. Use identifier-safe names: the name appears in audits and errors. |
udfType | 3 (Template). |
parameters | Ordered list of { "name": ..., "type": ... }. These are the arguments of the generated function; the body refers to them by name. Write each type the way Unity Catalog reports the column type, for example STRING, INT, DATE, DECIMAL(10,2). Types are compared as written, without synonyms: a foreign (federated) table can report a column differently, for example integer for a Glue int column, so declare INTEGER there. |
conditionNames | The named conditions a policy item can choose from. Each name starts with CONDITION and uses uppercase letters, digits, _ and -, for example CONDITION_US. |
udfText | The template body: a boolean SQL expression, described below. |
Template body¶
The body is the SQL expression the generated function returns. It is not a CREATE FUNCTION statement: PolicySync builds the function around it.
- Wrap each condition's branch in
<CONDITION_X>…</CONDITION_X>tags, one block per name inconditionNames. Blocks that no policy item references are removed from the generated function. - Put exactly one
${PRINCIPAL_LIST}inside each block. PolicySync replaces it with the principals of the items that reference that condition, as a quoted, comma-separated list. - Use
${ALL_PRINCIPAL_LIST}(required) outside the blocks. PolicySync replaces it with every principal of the policy. Use it for the default branch, which decides what a caller who matches none of the template's conditions sees. - Refer to the table's columns only through the parameter names. The body can read other tables (for example a consent table) by their fully qualified names.
Callers who match no condition
The default branch decides what a caller sees when they match none of the template's conditions. The examples below return TRUE there, so such a caller sees all rows; whether they can read the table at all is still decided by access policies. Return FALSE instead if such a caller must see no rows.
Example: filter by country¶
Template with one parameter and two conditions:
Example: match groups and roles¶
current_user() returns the user, never a group. To let a condition match groups and roles as well as users, check membership in the body:
Service principals can be policy item principals: they run as themselves, so current_user() returns their application ID.
Reference a template from a row-filter policy¶
In a row-filter policy on the table, set each policy item's row-filter expression (rowFilterInfo.filterExpr) to a JSON reference instead of SQL:
| JSON | |
|---|---|
| Key | Description |
|---|---|
type | Always template. |
template | Name of the Template UDF. |
condition_name | One of the template's conditionNames. The item's users, groups and roles are filled into that condition's ${PRINCIPAL_LIST}. |
columns | The table's columns, bound to the template's parameters by position. The count must equal the number of parameters, and each column's type must match its parameter's type. |
Example row-filter policy with two items on sales.orders:
Rules for a table's row-filter policy:
- All items reference the same template, and all items use the same
columns. - Template items cannot be mixed with SQL row-filter items on the same table.
- A principal can appear in only one condition. Several items can reference the same condition; their principals are merged.
- Another table can use the same template with its own column names, for example
"columns":["region_code"], as long as the column types match the parameters.
What PolicySync creates in Unity Catalog¶
For the example above, PolicySync runs two statements on cat.sales.orders:
- Name and location. The function is
<table>_row_filterin the table's own schema. For foreign catalogs it is created in the Privacera-managed catalog that holds the connector's functions for that catalog. - Ownership marker. The
COMMENTmarks the function as managed by PolicySync and records a hash of what was applied. PolicySync never replaces or drops a function of that name without the marker; seeTEMPLATE_FUNCTION_NAME_CONFLICT. - Principals. Principal names are the names PolicySync uses for them in Unity Catalog (for users, the name
current_user()returns), sorted, so the same input always produces the same function. - No churn. When the rendered function is unchanged, PolicySync runs no DDL. A body edit inside one condition changes only the tables whose policies use that condition.
- Attach before grant. The row filter is attached before PolicySync grants access to the table, so the table's grants never apply without its filter.
Do not edit the generated function
Changes made to <table>_row_filter outside PolicySync are put back at the next periodic permission sync (CONNECTOR_DATABRICKS_UNITY_CATALOG_PERMISSION_SYNC_INTERVAL), or earlier when a policy or template change re-renders the table's function. If the function was replaced by one without the PolicySync marker, PolicySync does not overwrite it and fails closed instead. This repair requires the JDBC permission load (CONNECTOR_DATABRICKS_UNITY_CATALOG_USE_DATABASE_CONNECTION_API: "false", the default), which is the recommended setting for template row filters.
Lifecycle¶
| Change | What PolicySync does in Unity Catalog |
|---|---|
| Template row-filter policy created | Creates <table>_row_filter and attaches it (SET ROW FILTER), then grants access. |
| Item added or removed, principals changed | Re-renders the function and replaces it (CREATE OR REPLACE FUNCTION). The attached filter stays in place. A condition left with no items is removed from the function. |
| Template body edited | Every table using the template is re-applied, without any policy save, within the Ranger plugin refresh interval. Only tables whose rendered function changed run DDL. |
Template renamed, parameters changed, or a referenced condition removed | Rejected by Ranger while a policy references the template (TEMPLATE_IN_USE). To change the signature, create a new template and move the policies to it. A referenced template cannot be deleted. |
| Policy removed or disabled (no template items left on the table) | Detaches the filter (DROP ROW FILTER) and drops the function (DROP FUNCTION IF EXISTS). |
| Table switched between native and template row filters | The new filter replaces the old one with a single SET ROW FILTER, then the old function is dropped. |
| Table dropped and recreated | Unity Catalog drops the attachment with the table but keeps the function. PolicySync attaches the filter to the new table before it grants access again. |
| Function edited, dropped, or filter detached outside PolicySync | Put back at the next periodic permission sync, or earlier on a policy or template change. |
Errors and fail-closed behavior¶
Errors when saving in Ranger¶
Ranger rejects an invalid template or policy with HTTP 400. The error code is in messageList[0].name and the details in msgDesc.
| Code | When |
|---|---|
TEMPLATE_NOT_FOUND | The policy references a template that does not exist or is not of type Template. |
UNKNOWN_CONDITION | condition_name is not one of the template's conditionNames. |
TEMPLATE_MIXED_ITEMS | Template items are mixed with SQL items, or items reference different templates. |
COLUMN_BINDING_INVALID | columns is missing, its length differs from the number of parameters, it has blank or duplicate entries, or items disagree on columns. |
TEMPLATE_BODY_INVALID | The body's condition tags do not match conditionNames, a block has no or several ${PRINCIPAL_LIST}, ${ALL_PRINCIPAL_LIST} is missing, or ${PRINCIPAL_LIST} is outside a block. |
TEMPLATE_IN_USE | Renaming the template, changing its parameters, or removing a condition while a policy references it. |
Errors from the connector¶
Errors that depend on the table are found when PolicySync applies the policy. In every case PolicySync fails closed: the previous row filter stays in place and PolicySync withholds the table's own grants (for access policies that cover a whole schema, see Limitations). Most errors are found before any DDL runs; UNDECLARED_COLUMN and DDL_FAILED are reported by Databricks when it rejects the DDL. After you fix the cause, the table is applied again on its next policy or template change, or at the latest at the next periodic permission sync (CONNECTOR_DATABRICKS_UNITY_CATALOG_PERMISSION_SYNC_INTERVAL, default 2 days). For TEMPLATE_COLUMNS_NOT_LOADED, the column metadata arrives with the next resource sync.
The reason code is in the PolicySync audits: in extraInfo.errorCode of the failed ACCESS row and of the policy's POLICY_RESPONSE row, with details in the message.
| Code | Cause | Fix |
|---|---|---|
PARAM_NAME_MISMATCH | A bound column is not a column of the table. | Correct columns in the policy items. |
PARAM_TYPE_MISMATCH | A bound column's type differs from its parameter's type. | Bind a column of the declared type, or use a template whose parameter type matches. |
PRINCIPAL_OVERLAP | The same principal is in two conditions. | Keep each principal in one condition. |
TEMPLATE_NOT_FOUND | PolicySync has not received the template (for example, right after it was created). | Wait for the next Ranger refresh; check CONNECTOR_RANGER_ENABLE_IMPLICIT_UDF_ENRICHER. |
UNKNOWN_CONDITION | The condition is not in the template PolicySync has. | Check the condition name and the template's conditionNames. |
TEMPLATE_MIXED_ITEMS | Template items are mixed with SQL items, or items reference different templates. | Use one template for all items of the table's row-filter policy. |
COLUMN_BINDING_INVALID | columns is missing, has the wrong count, a blank or duplicate entry, or items disagree on columns. | Correct columns in the policy items so every item binds the same columns, one per parameter. |
PLACEHOLDER_UNFILLED | A placeholder was left in the rendered body, or a referenced condition has no principals. | Check the template body and the policy items. |
TEMPLATE_RENDER_FAILED | The template could not be rendered, for example an item uses the public group. | Read the message; replace public with named groups. |
TEMPLATE_COLUMNS_NOT_LOADED | The table has no column metadata. | Set CONNECTOR_DATABRICKS_UNITY_CATALOG_LOAD_COLUMNS to "true" and wait for the next resource sync. |
TEMPLATE_FUNCTION_NAME_CONFLICT | A function named <table>_row_filter exists without the PolicySync ownership marker. | Rename or drop that function if it is not needed. PolicySync never replaces or drops it. |
TEMPLATE_RLS_DISABLED | A template row-filter policy exists but the connector flag is off. | Set CONNECTOR_DATABRICKS_UNITY_CATALOG_ENABLE_TEMPLATE_BASED_ROW_FILTER to "true". |
UNDECLARED_COLUMN | Databricks rejected the function: the body references a column that is neither a parameter nor a column of a table it reads. | Use parameter names for the table's columns. |
DDL_FAILED | Databricks rejected CREATE FUNCTION or SET ROW FILTER for another reason. | Read the Databricks error in the audit message. |
Ranger UDF API¶
Replace <ranger-host> with your Ranger admin host and use valid credentials. serviceName must match your Databricks Unity Catalog service in Ranger (commonly privacera_databricks_unity_catalog).
| Operation | HTTP |
|---|---|
| Create or update a template | PUT https://<ranger-host>/service/udf/service/<service-name>/name/<udf-name> |
| Get a template | GET https://<ranger-host>/service/udf/service/<service-name>/name/<udf-name> |
| List its conditions | GET https://<ranger-host>/service/udf/service/<service-name>/name/<udf-name>/conditions |
| Delete a template | DELETE https://<ranger-host>/service/udf/service/<service-name>/name/<udf-name> (rejected while a policy references it) |
Limitations¶
- Principals only. Conditions match the caller with
current_user()(and group membership when the body checks it). Attribute and tag macros are not supported. - No
publicgroup. An item that uses thepublicgroup fails withTEMPLATE_RENDER_FAILED. - One template per table. All template items of a table's row-filter policy reference the same template.
- Fail-closed applies to the table's own grants. If a table's template filter fails, PolicySync withholds the grants on that table. An access policy that covers a whole schema (
table: *) is granted asSELECTon the schema, and that schema-level grant still reaches the table without a filter. For tables protected by a row filter, list the tables explicitly in the access policy. - Reusable UDF to template. A table cannot switch directly from a reusable master UDF filter to a template filter. Remove the master UDF policy from the table and sync, then add the template policy and sync again; restrict access to the table between the two syncs.
- Changes made outside PolicySync are repaired at the next periodic permission sync, not immediately: PolicySync checks the attached filter and the function's marker during that sync, with one query per schema that contains tables with a template filter (no extra queries on other applies). Until then the table is filtered by whatever is in Unity Catalog; if the filter was detached, the table has no row filter. To detect a body edit, PolicySync records the function's definition at the first permission sync after it creates or updates the function, and compares later definitions with that record. An edit made before that first permission sync becomes part of the record and is not detected. Restrict who can modify functions and row filters in the table's schema.
- JDBC permission load only. Repair of changes made outside PolicySync is supported only with the JDBC permission load (
CONNECTOR_DATABRICKS_UNITY_CATALOG_USE_DATABASE_CONNECTION_API: "false", the default), which is recommended for template row filters. With the API permission load ("true"), template filters are still applied on every policy or template change, but changes made outside PolicySync are not repaired until the next such change.
- Prev topic: Use Reusable UDF for Row-level Filter
- Next topic: Troubleshooting