Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion platform/control-plane/query-engines/provision.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The delegation modes:
| **Auto — JWT, else impersonate (recommended)** | The default. Egret uses a per-user JWT when the coordinator can verify it, and otherwise falls back to impersonation. |
| **JWT (per-user)** | Each query carries the caller's own short-lived JWT. No shared credential. OIDC users only. |
| **Impersonate (service user)** | Egret connects as a trusted service account and runs the query as the caller. Works for any signed-in user. |
| **None (open engine)** | Every query runs with the connection's own credential. |
| **None (open engine)** | Every query runs with the connection's own credential. On an engine bound to a policy profile every query currently fails with *Principal … cannot become user …* (KI-333). |

On the demo stack the default mode ran queries as the signed-in person in both cases: an SSO user (`member-sso`) and the local `admin@egret.local` account. `SELECT current_user` returned `member-sso` for the SSO user, and the grants, masks and row filters written in Governance applied to each of them (see [Masks and row filters](../../data-governance/data/masks-and-filters.md)).

Expand Down
2 changes: 1 addition & 1 deletion platform/data-governance/classifications/clearance.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ On StarRocks the NULL masks need the table's columns, which egret reads from its
- A change to the table's columns (add or drop a column), and a create, register or undrop of the table, recompiles the StarRocks profiles at once. The plugin applies the new masks at its next poll (KI-305).
- A rename of the table, or of one of its columns, is held until every StarRocks plugin serves the masks under the new name, so a view over the renamed table reads NULL from its first read. See [Renames and drops](./tagging.md#renames-and-drops).
- A change that would push a clearance over its ceiling is refused at the Iceberg commit with a 409 that names the table, for example *egret refused the table change: compiling the clearance on tag "PII" would expand to 501 policies on StarRocks, over the ceiling of 500: whole-table on demo_warehouse.sales.events (1 deny + 491 column masks) …*. The same check applies to a register and to an undrop from the **Recycle bin** (*egret refused the undrop: …*). A refused register is dropped again and stays in the Recycle bin (KI-306).
- For a table that is not in the catalog yet, only the select deny is compiled, and **Who can read?** says that a view over it is not protected until the table exists (KI-307). The masks are written when the table is created.
- For a table that is not in the catalog yet (an unknown name, a view, or a table not created yet), only the select deny is compiled, so a view over it is not protected until the table exists. Attaching the classification to such a table on a warehouse a StarRocks engine serves succeeds with a warning toast that says so (since platform #818; the API answers it as a notice, see [Tagging](./tagging.md#tag-a-table-or-a-column)), and **Who can read?** says it on the table's columns. The masks are written when the table is created.

The classifications follow a rename of the table or of a column, and a dropped table keeps them for an undrop. See [Renames and drops](./tagging.md#renames-and-drops).

Expand Down
13 changes: 7 additions & 6 deletions platform/data-governance/classifications/tagging.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ A tag does nothing until it is applied. This page covers how to apply a tag to t
Tags attach to tables and columns in egret-managed catalogs (Iceberg warehouses). Tagging needs the permission to update catalogs (**catalog_registry:update**) and **tag:read** to list the tags.

1. Open **Governance → Data** and select the table.
2. To tag the table, press **Add tag** in the header. To tag a column, select the column in **Columns** and press **Add tag** in the column panel.
3. In **Add tag**, open **Tag** and choose a tag. Tags already on the object are not offered; if there are none left the dialog says *Every tag is already on this table* (or *column*). With no tags defined it says *No tags are defined yet.*
4. Press **Add tag**. A toast says *Tag added.*
2. To tag the table, press **Add classification** in the header. To tag a column, select the column in **Columns** and press **Add classification** in the column panel.
3. In **Add classification**, open **Classification** and choose one. Classifications already on the object are not offered; if there are none left the dialog says *Every classification is already on this table.* (or *column*). With none defined it says *No classifications are defined yet.*
4. Press **Add classification**. A toast says *Classification attached.*

When the attach needs your attention, a warning toast takes the place of the success toast. Today there is one such case: a classification with a [clearance](./clearance.md#on-columns-or-on-a-whole-table) attached to a whole table that a StarRocks engine serves but the catalog does not have yet (an unknown name, a view, or a table not created yet). The attach succeeds, and the warning says that a direct read is refused but a view over the table shows the values until the table exists in the catalog, when egret writes the column masks. The tag control under **Browse** shows the same warning.

![The Data page for the sessions table: header, engine docs-trino, and the Columns list with classifications and rules](/img/platform/admin/gov-table-overview.png)

Expand All @@ -25,7 +27,7 @@ The tag then appears in the **Tags** column of the table's **Columns** list, in
A tag applied to a table reaches its columns for policy purposes: engines roll tags up the resource hierarchy. A tag applied to a column does not show up when you explain access for the table. Ask about the column.
:::

When you add a classification to a whole table, the dialog says: *On a whole table, this classification counts for clearance on every column. A rule that masks by classification needs it on columns, not on the table.* Trino and StarRocks both enforce the [clearance](./clearance.md#on-columns-or-on-a-whole-table) and a classification row filter for a table-level classification. A Trino engine does not apply a classification row filter for a column-level one (KI-287).
When you add a classification to a whole table, the dialog says: *On a whole table, this classification counts for clearance on every column. A rule that masks by classification needs it on columns, not on the table.* Trino and StarRocks both enforce the [clearance](./clearance.md#on-columns-or-on-a-whole-table) and a classification row filter for a table-level classification. A row filter by classification also filters a table where the classification is only on columns, on both engines (since platform #818; see [Masks and row filters](../data/masks-and-filters.md#row-filters-by-classification)).

A mask rule that names a classification already on a whole table is refused. The refusal names that table and lists the other tables the classification is on, up to 49; past that it says the list is cut short.

Expand Down Expand Up @@ -147,9 +149,8 @@ These do not follow a rename:

- The tag page counts tagged BI objects only; data tags are visible on the Data page.
- A mask by tag on a column type the mask cannot apply to returns an engine error rather than a masked value.
- On Trino, a row filter by a classification that is only on columns filters nothing (KI-287).
- A classified rename is held for up to 90 seconds, and a silent bound engine makes it fail (KI-310). Run from SQL Lab, a hold longer than 60 seconds shows a timeout, which says that the rename may still complete.
- Adding a classification with a clearance to a table that is not in the catalog yet does not say that a StarRocks view over it is unprotected until the table exists; **Who can read?** does (KI-307).
- A classification with a clearance attached to a table that is not in the catalog yet leaves a StarRocks view over it unprotected until the table exists. The attach warns about it (since platform #818), and **Who can read?** says so on the table's columns. The warning is given at the attach only, not when a clearance is added later to a classification already on such a table.

## Further reading

Expand Down
14 changes: 4 additions & 10 deletions platform/data-governance/data/explain-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The mask word is always the one the engine applies (the served clearance mask),
| No NULL mask, because the table is not in the catalog | member-sso is **refused** `email` (clearance *PII*, on the whole table). No NULL mask for clearance *PII* is served here: egret found no table `sales.orders` in the catalog, so a view over it shows the values |
| No NULL mask, because the column is newer than the compile | … No NULL mask for clearance *PII* is served here: the profile has not been recompiled since this column appeared, so a view over it shows the values |

The last two lines name a gap that closes by itself: the masks are written when the table is created (KI-307) or when the profile is next recompiled (KI-305).
The last two lines name a gap that closes by itself: the masks are written when the table is created or when the profile is next recompiled (KI-305). The first case is also announced when the classification is attached (see [Tagging](../classifications/tagging.md#tag-a-table-or-a-column)).

**A reader with no grant.** StarRocks masks an uncleared reader's column to NULL without checking access on it, but the query still needs SELECT on the other columns it reads. When the person has no read on the table, the sentence says they are refused, not that they read NULL:

Expand Down Expand Up @@ -152,15 +152,9 @@ The members table has a row per member: the **Member** (name and username) and,
On StarRocks a read of a `MASK_NULL` column is not access-checked: it returns NULL, and the query is still refused unless the member may read another selected column. Such a member is counted as masked, not allowed.
:::

## Engine gaps the answer states
## Row filters by classification

One kind of rule compiles to nothing on Trino. Until a later release compiles it, the answer says so where you read it, rather than reporting a protection that is not there:

| Engine | Rule | What the answer says |
|---|---|---|
| Trino | A row filter by classification, where the classification is on a column of the table, not on the table | member-sso sees **every row**: rule *US rows only* does not filter on Trino here, because PII is on a column, not on the table (KI-287) |

The rule dialog warns about this gap before you create such a rule, see [Masks and row filters](./masks-and-filters.md#row-filters-by-classification-on-trino). Since platform #755 StarRocks enforces both a clearance and a row filter for a classification on a whole table, so the answer no longer states a StarRocks gap.
A row filter by classification filters a table on both engines whether the classification is on the table or on one of its columns, so the answer names the rule for either, for example *member-sso sees rows where region = 'us' (rule Filter rows PII P7)*. Before platform #818 Trino did not filter a table where the classification was only on columns, and the answer said *does not filter on Trino here*; that sentence is gone. See [Masks and row filters](./masks-and-filters.md#row-filters-by-classification).

## What explain does not tell you

Expand All @@ -176,7 +170,7 @@ Explain works only for engines whose plugin version egret models: Trino with plu

## In the API

`POST /api/control-plane/perm/explain` (one user) and `POST /api/control-plane/perm/explain/principal` (a group's or role's members) return the same answer. Each profile carries `effect_lines`, the sentences as structured parts (`subject`, `strong`, `code`, `rule`, `clearance`, `principal`, `policy`, `catalog`, `text`) with the joined `text`. It also carries `row_filter` (the table's row-filter decision), `other_columns_access` (on StarRocks, when the column is not access-checked), `clearance_masks_missing` (on StarRocks, each clearance on the column or its table whose NULL mask is not served for the column, with `tag_id`, `tag_name` and a `reason` of `table_not_in_catalog` or `not_compiled`) and `row_filter_not_applied` (the Trino gap). Platform #755 removed `clearance_not_enforced`. A rule attribution carries the rule's `name`, and a tie's cause lists each tied policy's `outcomes`. With no profile, the response carries `effect_line`, the **Not enforced** sentence. All of these fields are optional additions; see the [REST API reference](../../reference/rest-api/perm.md).
`POST /api/control-plane/perm/explain` (one user) and `POST /api/control-plane/perm/explain/principal` (a group's or role's members) return the same answer. Each profile carries `effect_lines`, the sentences as structured parts (`subject`, `strong`, `code`, `rule`, `clearance`, `principal`, `policy`, `catalog`, `text`) with the joined `text`. It also carries `row_filter` (the table's row-filter decision), `other_columns_access` (on StarRocks, when the column is not access-checked), `clearance_masks_missing` (on StarRocks, each clearance on the column or its table whose NULL mask is not served for the column, with `tag_id`, `tag_name` and a `reason` of `table_not_in_catalog` or `not_compiled`). Platform #755 removed `clearance_not_enforced`, and platform #818 removed `row_filter_not_applied`, which reported the Trino gap of a column-level classification. A rule attribution carries the rule's `name`, and a tie's cause lists each tied policy's `outcomes`. With no profile, the response carries `effect_line`, the **Not enforced** sentence. All of these fields are optional additions; see the [REST API reference](../../reference/rest-api/perm.md).

## Troubleshooting

Expand Down
21 changes: 10 additions & 11 deletions platform/data-governance/data/masks-and-filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Each row shows the rule **Name**, the path or tag it selects, its **Kind** (for
| **Mask a column** | Replaces the column's values for every non-exempt reader. |
| **Filter rows** | Keeps only rows for which the expression is true. |

With **Applies to** set to **Classification** and **Filter rows** chosen, a hint under **Kind** reads: *On Trino, a row filter by classification applies only where the classification is on the whole table.* See [Row filters by classification on Trino](#row-filters-by-classification-on-trino).
With **Applies to** set to **Classification** and **Filter rows** chosen, a hint under **Kind** reads: *A row filter by classification filters every table that carries the classification, on the whole table or on any of its columns, on Trino and on StarRocks.* See [Row filters by classification](#row-filters-by-classification).

A new rule's suggested name follows **Kind** until you edit it: a name seeded as *Mask PII Data* becomes *Filter rows PII Data* when you switch to **Filter rows**.

Expand Down Expand Up @@ -93,20 +93,20 @@ a view shows the masked values and no path. A reader who sees the real value in
A change to the masks takes effect on the next compile of the profile. To apply it at once, compile the profile with
`POST /api/control-plane/perm/compile/{profile_id}`.

## Row filters by classification on Trino
## Row filters by classification

A row filter by classification filters a table where the classification is on the table, on both engines. Where it is on a column, StarRocks filters the table and Trino does not yet:
A row filter by classification filters every table that carries the classification, on the whole table or on any of its columns, on Trino and on StarRocks (since platform #818):

- **Trino** filters by a classification only where the classification is on the **whole table**. On a table where it is only on columns, the row filter filters nothing on Trino (KI-287).
- **StarRocks** filters every table that carries the classification, on the table or on any of its columns, with one row-filter policy per table.
- **Trino**: a table that carries the classification on the table is filtered by the rule's tag policy. For each table where the classification is only on columns, egret also writes one row-filter policy on that table, named `perm-rule-<id>-<catalog>.<schema>.<table>`. A table is never filtered twice by one rule.
- **StarRocks**: one row-filter policy per table that carries the classification, at either level.

A later release compiles the Trino case. Until then the preview counts the affected tables and shows a warning, for example:
Attaching or removing the classification, and a rename the classification follows, recompile the Trino and StarRocks profiles, so the per-table policies follow the classification. These tables count toward the rule's expansion ceiling on both engines. An install upgraded from an earlier version recompiles the affected Trino profiles when the server starts.

> On Trino this row filter filters nothing on 3 tables in demo_warehouse: *Personal data (PII)* is on their columns, not on the table. StarRocks filters them. Attach *Personal data (PII)* to the table to filter on Trino.
The preview asks for no tick for this kind of rule. **Who can read?** names the rule that filters the table, for example *member-sso sees rows where region = 'us' (rule Filter rows PII P7)*.

**Create** stays disabled until you tick **Create anyway: I understand this row filter filters nothing on Trino on these tables**; while it waits, its reason reads *Tick 'Create anyway' to create a row filter that filters nothing on Trino on these tables.* When editing a rule the words are **Save anyway** and *save*. Every new preview clears the tick. To filter on both engines, attach the classification to the table.

**Who can read?** states the same gap for a person: *member-sso sees **every row**: rule US rows only does not filter on Trino here, because PII is on a column, not on the table*.
:::note
On Trino, a table that carries classification A on a column and classification B on the whole table, each with a row-filter rule, is filtered by B's rule only: Ranger lets a tag policy decide before a resource policy of the same priority. **Who can read?** answers what Trino does.
:::

## Edit and delete

Expand Down Expand Up @@ -138,7 +138,6 @@ The local platform admin `admin@egret.local` got the same masked values: platfor

- On a Trino profile without a tag service, tag rules compile to nothing. Egret refuses to bind such a profile to an engine.
- A mask on a column whose type the mask cannot apply to can return an error from the engine rather than a masked value.
- A row filter by classification filters nothing on Trino where the classification is only on columns (KI-287).
- A rule that selects by path is copied to the new name when its table or column is renamed, and the original stays on the old name; a rule that selects by tag follows because classifications do. A rule that selects by a pattern the new name escapes refuses the rename. See [Renames and drops](./grants.md#renames-and-drops).
- A row filter or custom mask expression that names a renamed column is not rewritten (KI-321).

Expand Down
Loading
Loading