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
32 changes: 32 additions & 0 deletions platform/control-plane/catalogs/catalog-detail.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,36 @@ The table shows each role's **Consumer**, **Grants** (for example `catalog · ma

A grant on one table stays on the table's old name when the table is renamed: the role loses access to the renamed table and gains it on a new table created under the old name (KI-309). Add the grant on the new name and remove the old one.

### What each operation needs {#what-each-operation-needs}

egret checks every Iceberg request a Catalog Client sends against its role's grants before the catalog sees it. A grant on the catalog covers every namespace and table; a grant on a namespace covers the tables and views directly in it, not those in a child namespace (a grant on `corp` does not cover `corp.fin.ledger`); a grant on a table covers that table or view only. **write** and **manage** both permit writes.

| Operation | What the role needs |
|---|---|
| Load or list a table, view or namespace | Any grant covering it. |
| Create a table or view, or stage a create | **write** or **manage** on the catalog, or on the namespace it is created in. A grant on a table name never creates that table. |
| Commit to one table (`POST …/namespaces/{ns}/tables/{t}`) | **write** or **manage** covering the table the commit changes: the table named by the body's `identifier` when it has one, otherwise the table in the path. A view commit is decided on the view in its path. |
| Commit to several tables (`POST …/transactions/commit`) | **write** or **manage** covering **every** table in the commit. One table that is not covered refuses the whole commit, and nothing is committed. |
| Rename a table or view | On the **source**, what a drop needs: **write** or **manage** covering the source table or view. On the **destination**, what a create needs: **write** or **manage** on the catalog, or on the destination namespace. A grant on the destination table name is not enough. |
| Drop a table or view | **write** or **manage** covering it. |
| Drop a namespace recursively (with everything in it) | **write** or **manage** on the **catalog**. A namespace grant still drops an empty namespace, or the namespace's tables one by one. |
| Remote S3 request signing (`…/aws/s3/sign`) | Not used by egret: refused with 403 whatever the grants. Clients read and write table files with vended credentials. |

An engine's client holds `catalog · manage`, so it can do all of these. For example, a role with **write** on the namespace `sales` and on the namespace `stage` can rename `sales.orders` to `stage.orders`; a role with **write** on the table `sales.orders` and a grant on the table `stage.orders` cannot, because the destination needs a namespace grant.

**Names match the way the catalog resolves them.** The catalog treats table and namespace names case-insensitively (and also equates width forms and invisible characters, but not accents), so a grant does too: a grant on `sales.Secret` covers `sales.secret` and `SALES.SECRET`, but not `sales.secrét`. Two grants that differ only in case are the same grant.

**Refusals.** egret answers in the Iceberg REST error format, `{"error": {"message", "type", "code"}}`, so a client such as PyIceberg raises a typed error (`ForbiddenError` for a 403). A refusal names the side that failed and repeats only the names the client sent, so it does not tell whether a table exists:

| Code | When | Example message |
|---|---|---|
| 403 | The grant does not cover the operation | *role 'etl' may not rename sales.orders: renaming needs write on the source, as dropping it would* |
| 403 | A rename destination without a namespace grant | *role 'etl' may not rename into stage.orders: renaming needs write on namespace stage, as creating a table there would* |
| 403 | A multi-table commit with an uncovered table | *role 'etl' may not commit to sales.orders: a multi-table commit needs write on every table it changes, as updating it alone would; nothing was committed* |
| 403 | A recursive namespace drop without a catalog grant | *role 'etl' may not drop namespace corp recursively: a recursive drop deletes every child namespace and table, so it needs a catalog-level write or manage grant* |
| 400 | A rename or commit body egret cannot read | *malformed rename request: …*, *malformed multi-table commit: …*, *malformed table commit: …* |
| 413 | A rename body over 1 MiB, or a commit body over 16 MiB | *the body of a rename request is larger than 1048576 bytes* |

## Catalog Clients (warehouse)

*Machine credentials (client id + secret) an engine or notebook uses to authenticate.*
Expand Down Expand Up @@ -111,6 +141,8 @@ A dropped table keeps its classifications in the **Recycle bin**, and **Undrop**

- A catalog test before attaching checks only that the host answers on its port; a TPC-H catalog has no host to check.
- Attach and detach on Trino restart the coordinator.
- A Catalog Client with only a **read** grant receives vended S3 credentials that can also write and delete the files of the table it loads, so it can damage that table without any catalog write (KI-311). Give **read** grants only to clients you would trust with write access to those tables until this is fixed.
- Any **write** grant lets a role create and list namespaces, and a grant on one table lets it list every table name in that namespace (KI-312).

## Further reading

Expand Down
2 changes: 1 addition & 1 deletion platform/data-governance/classifications/tagging.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ The compile and recycle-bin refusals come at once. The engine refusal comes afte

- **Drop a table:** the classifications stay on its name. Where used marks the table **Dropped**, and the Governance overview reports it as information. Undrop it from the catalog's **Recycle bin** and it is classified again. On StarRocks a view over it can show real values until the plugin's next poll (KI-305).
- **Drop a table, then create another one under its name:** if you rename the new table away, its classifications move with it and are also kept on the old name, because the dropped table may still be undropped.
- **Drop a namespace with everything in it** (a recursive drop): egret removes every classification under the namespace, on its tables and columns, and the classifications its views carried. Views in other schemas that read its tables lose the classifications they derived from them. **Recent changes** records, for example, *Catalog client engine-docs-trino removed 12 classification assignments with namespace sales (namespace dropped)*. On a catalog with soft delete (one with a **Recycle bin**), the catalog refuses a recursive drop unless it is also forced (*Cannot recursively delete namespace with soft-deletion without force flag*), so the classifications go only with a forced drop, and stay when nothing was dropped.
- **Drop a namespace with everything in it** (a recursive drop): egret removes every classification under the namespace, on its tables and columns, and the classifications its views carried. Views in other schemas that read its tables lose the classifications they derived from them. **Recent changes** records, for example, *Catalog client engine-docs-trino removed 12 classification assignments with namespace sales (namespace dropped)*. On a catalog with soft delete (one with a **Recycle bin**), the catalog refuses a recursive drop unless it is also forced (*Cannot recursively delete namespace with soft-deletion without force flag*), so the classifications go only with a forced drop, and stay when nothing was dropped. Only a Catalog Client with a catalog-level **write** or **manage** grant may drop a namespace recursively, as an engine's client can; see [What each operation needs](../../control-plane/catalogs/catalog-detail.md#what-each-operation-needs).
- **Drop an empty namespace:** nothing changes. The catalog refuses to drop a namespace that still holds a table, dropped or not.

### Classifications left on an old name
Expand Down
7 changes: 4 additions & 3 deletions platform/deploy-operate/storage-and-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,17 @@ s3.path-style-access=true
```

With `vended-credentials-enabled`, engines get credentials scoped to the table they load and the static `s3.aws-*` keys are
not needed. A warehouse is provisioned with STS enabled and remote signing off, because the embedded mount exposes no
signer endpoint.
not needed. A warehouse is provisioned with STS enabled and remote signing off, and egret refuses every S3 signer request
(`…/aws/s3/sign`) with 403, whatever the client's grants.

### Setting it up in the app

1. **Control Plane → Storage → Add connection**: register the S3 store (endpoint, bucket, access key and secret).
2. **Control Plane → Catalogs → New catalog**: create an *egret warehouse* over that storage connection. This creates the
warehouse in the embedded catalog.
3. Open the catalog. On **Roles & grants**, add a role and give it a `write` grant (`write` and `manage` permit writes; any
grant permits reads).
grant permits reads). Creating a table, renaming into a namespace and dropping a namespace recursively need more than a
table grant; see [What each operation needs](../control-plane/catalogs/catalog-detail#what-each-operation-needs).
4. On **Catalog Clients**, add a client bound to that role and copy the client id and secret. The secret is shown once.
5. The **Connect** tab shows the catalog URI, warehouse name and OAuth token URL for this deployment, with ready-made
snippets for PyIceberg, Trino and StarRocks.
Expand Down
14 changes: 14 additions & 0 deletions platform/deploy-operate/troubleshooting/catalog-storage-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,19 @@ of a real failure:
| Symptom | Cause |
|---|---|
| 403 on a table that exists | The client's role has no grant, or a `read` grant where the call writes. `write` and `manage` permit writes; any grant permits reads. |
| 403 on a rename (*may not rename …* or *may not rename into …*) | A rename needs `write` covering the source table and a `catalog` or `namespace` grant on the destination namespace; a grant on the destination table name is not enough. |
| 403 on a commit (*may not commit to …*) | A commit needs `write` covering the table it changes, the table named in the body's `identifier` when there is one. A multi-table commit needs it on every table, and one uncovered table refuses the whole commit. |
| 403 on a recursive namespace drop | A recursive drop needs a `catalog`-level `write` or `manage` grant. |
| 403 *remote signing is not used by egret* | The client asked the S3 request signer. egret does not sign requests; configure the client for vended credentials. |
| 400 *malformed rename request*, *malformed multi-table commit* or *malformed table commit* | The request body is not the JSON the Iceberg REST API defines. |
| 413 *the body of a … is larger than … bytes* | A rename body is limited to 1 MiB, and a commit body to 16 MiB. |
| 403 on the warehouse | The token's warehouse differs from the one requested. |
| Warehouse creation refused | A warehouse without a `key_prefix` claims the whole bucket, and the catalog rejects a location that overlaps another warehouse. Give each its own bucket or prefix. |

egret's refusals use the Iceberg REST error format, `{"error": {"message", "type", "code"}}`, so the client shows the
message above in a typed error (PyIceberg raises `ForbiddenError` for a 403). The full list of what each operation needs is
in [What each operation needs](../../control-plane/catalogs/catalog-detail#what-each-operation-needs).

### The advertised address is wrong

```bash
Expand All @@ -64,6 +74,10 @@ If `issuer` or `token_endpoint` names an address clients cannot reach (`http://l
provisioned**, because their security integration compares the old issuer byte for byte; re-provision them. See
[Configuration](../configuration#address-settings-that-people-confuse).

Behind the demo stack's edge on a port other than 80, `GET /catalog/v1/config` advertises its `uri` without the port (for
example `http://egret.localtest.me/catalog` on port 18780). PyIceberg follows that address and fails with *connection
refused* (KI-314). A client that reaches egret directly on the stack's network is not affected.

## The bucket does not exist

Nothing creates the bucket: not Egret, and not the development compose file. The server starts anyway, because it does not
Expand Down
18 changes: 18 additions & 0 deletions platform/deploy-operate/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ check *classifications on tables that do not exist* and *classifications on colu
classification to the new name and remove it from the old one. See
[Renames and drops](../data-governance/classifications/tagging#renames-and-drops).

### Catalog renames and commits are authorized by their tables (platform #758)

From platform #758 egret's Iceberg catalog decides a rename, a commit and a recursive namespace drop on the tables they
change. There is no migration and no API change. Engines provisioned by egret hold `catalog · manage` and are not affected;
Trino's creates, writes, renames and drops were checked. A Catalog Client with narrower grants may now be refused calls it
could make before:

- a rename needs `write` on the source table and a `catalog` or `namespace` grant on the destination namespace (a grant on
the destination table name no longer suffices);
- a multi-table commit needs `write` on every table in it;
- a recursive namespace drop needs a `catalog`-level `write` or `manage` grant;
- remote S3 signing is refused with 403.

Grants now match names case-insensitively, as the catalog does, so two grants that differ only in case are the same grant.
A role reaches no table it could not already reach by spelling the name as its grant does. Refusals are Iceberg JSON errors, and rename and commit bodies are
limited to 1 MiB and 16 MiB (413). See
[What each operation needs](../control-plane/catalogs/catalog-detail#what-each-operation-needs).

### StarRocks 4.1.6 enforces view privileges

See [Engines and Ranger setup](./engines-and-ranger#supported-versions).
Expand Down
8 changes: 8 additions & 0 deletions platform/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ rename until the engines serve them under the new name, which takes up to 90 sec
A drop keeps them for an undrop, and a recursive namespace drop removes them. Grants, rules and hand-written policies written
on the table's path do not follow (KI-308). See [Renames and drops](./data-governance/classifications/tagging#renames-and-drops).

### Why is a catalog client refused a rename or a commit that its grant seems to allow?

egret authorizes a rename and a commit by the tables they change. A rename needs `write` on the source table, as a drop
would, and a `catalog` or `namespace` grant on the destination namespace, as a create would; a grant on the destination
table name is not enough. A commit needs `write` on the table its body names, and a multi-table commit on every table in it.
A recursive namespace drop needs a `catalog`-level grant. Grants match names case-insensitively, as the catalog does. See
[What each operation needs](./control-plane/catalogs/catalog-detail#what-each-operation-needs).

### Why did my StarRocks policies stop applying after a restart?

The activation `access_control = ranger` lives in the front end's `fe.conf`, which a recreated container regenerates, and
Expand Down
Loading
Loading