Applies to VAST Cluster 5.5. Examples use the VAST DataBase Python SDK 2.1.0.
Your platform team controls access to VAST DataBase with identity policies, and can add row and column security rules to them. This page explains how those policies affect what you see and what you can do from the SDK, Trino or Spark. It doesn't explain how to write them; that's an admin task, covered in the KB.
Who the cluster thinks you are
Every request is signed with an S3 access key. The cluster checks the request against the identity policies attached to that key's user (and its groups), and any bucket policy on the database. Effects to keep in mind:
Policies decide whether a request runs; row and column security decides what it returns.
- The SDK acts as the user who owns the key you pass to
vastdb.connect(). - Trino and Spark act as the user who owns the key in the catalog or connector configuration, not as the person running the query, unless end-user impersonation is set up (see Impersonation). Everyone who uses that catalog gets the same access.
- Policies can deny as well as allow. A statement with
"Effect": "Deny"blocks the listed actions even if another statement allows them (Identity and bucket policy reference). - Policies can have conditions. For example,
aws:SourceIplimits a statement to requests from certain client addresses. A key that works from one host can then be refused from another. If that happens, ask whether the policy has conditions.
Which actions each SDK call needs is on setup prerequisites. That page is the reference; this one doesn't repeat it.
What a refused request looks like
When the policy doesn't allow an action, the cluster answers with HTTP 403 and the SDK raises vastdb.errors.Forbidden at the call that needed the action. Nothing is retried: the SDK sends the refused request once. See errors.
Some refusals show up earlier or in a different place than you'd expect:
- Opening a database.
tx.bucket(name)needss3:ListBucket. Without it you getForbiddenwith no error code, even if the policy allows every Tabular action. - Opening a table.
schema.table(name)loads the table's columns and statistics, so it needss3:TabularListColumnsands3:TabularGetTableStatseven if you only want to insert. Withouts3:TabularListColumns,schema.table()fails on a request whoseurlends in?column. - Updates and deletes.
update()anddelete()usually follow aselect()that finds the rows, so they also needs3:TabularQueryData. - Large inserts. If a batch doesn't fit in one request, the SDK inserts some columns first and writes the rest with update requests, so it needs
s3:TabularUpdateRowsas well ass3:TabularInsertRows. Without it, the insert fails on the update request (methodPUT,urlending in?rows), and the rows it already inserted are rolled back. - Deleting partitions.
delete_partitions()on a partitioned table needss3:TabularDeletePartitions;s3:TabularDeleteRowsisn't enough. - VAST Query Engine. Without
s3:QueryEngine*, every Query Engine statement on the table fails when it's planned, withBinder Error: [req=<id>]: permission checking for table: <bucket>/<schema>/<table> failed - AccessDenied., even if the SDK can read the table with the same key.
A failed call inside a transaction raises out of the with block, and the transaction rolls back. Earlier calls in the same transaction are undone too: an insert into one table that succeeded before a refused insert into another left no rows behind.
To test a key's access, run the operation you need and catch the error:
import vastdb
from vastdb import errors
session = vastdb.connect()
try:
with session.transaction() as tx:
table = tx.bucket("my-db").schema("sales").table("orders")
table.select(columns=["id"], limit_rows=1).read_all()
print("query allowed")
except errors.Forbidden as e:
print("refused:", e.code, e.method, e.url)
Row and column security
On top of allowing or denying actions, an identity policy can hold row and column security statements. They change what a query returns, or which clients may read the table at all. The rules are (see Row and column security):
| Rule | What you see |
|---|---|
Column deny (ColumnDeny) |
The listed columns aren't returned. |
Column allow (ColumnAllow) |
Only the listed columns are returned. If both allow and deny apply, a column that's denied isn't returned even if it's also allowed. |
Column mask (ColumnMask) |
The column is returned, but with each value replaced by a SQL expression, for example regexp_replace(my_col_email, '.*', '***'). The query doesn't fail. |
Row filter (RowFilter) |
Rows that don't match a filter, such as COL1='abc', are left out, as if your query had an extra WHERE clause. Several filters are combined with AND. |
Row filter by S3 path (RowFilterByS3PathAuth) |
Rows are returned only if you may read (get-object) every S3 path listed in the row's vastdb_s3_path_auth column. |
What this means for you:
- Two people can run the same query and get different results, in row counts, values and even column lists. A count that looks wrong may be a row filter. Ask your platform team which rules apply to your user before you debug the data.
- Masked values are not the data. Don't write masked results back to another table, or join on masked columns, and expect the real values.
- Rules apply to top-level columns only, not to fields nested inside a struct.
- Row filters and column masks are applied by the query engine. The database returns the filters and masks to apply, and the engine applies them. Masks use Trino SQL syntax (Managing identity policies).
- Engine restriction. If a policy defines row filters or column masks for you on a table, you can only query that table with one of the engines named in the policy's
QueryEnginefield (for exampleTrino). The Python SDK and the VAST Query Engine can't read such a table at all, even with"QueryEngine": "*"; see below. - Column allow and deny also apply to inserts and renames, not only queries. An insert that includes a hidden column is refused.
What the Python SDK and the VAST Query Engine do
Column allow and deny, and the row filter by S3 path, are enforced by the cluster, so they work from every client. Row filters and column masks need an engine that applies them, and the Python SDK and the Query Engine refuse a table that has them for your user:
| Rule on your user | Python SDK | VAST Query Engine (ADBC) |
|---|---|---|
| Column deny or allow | table.columns() and select() show only the visible columns. Asking for a hidden column raises KeyError: 'Column secret does not exist in schema', and a predicate on it fails before anything is sent. An insert that includes a hidden column fails with Forbidden (column name=secret is not allowed); an insert without it succeeds, and the hidden column is left null. |
SELECT * returns the visible columns. Naming a hidden column fails with Binder Error: Referenced column "secret" not found in FROM clause! |
| Row filter | select() fails with Forbidden, code AccessDenied, message cluster version before 5.4.0 but row_filter=0x1 column_mask=0x0 (the same text on a 5.5 cluster). insert() fails with Forbidden. |
Every statement fails with Permission Error: [req=<id>]: query_engine is not supported for tables with row_filter=0x1 |
| Column mask | select() fails the same way (... row_filter=0x0 column_mask=0x1); insert() fails with cmd=INSERT_ROWS not allowed when column mask defined. |
query_engine is not supported for tables with column_mask=0x1 |
| Row filter by S3 path | select() returns only the rows whose paths you may read. |
The same. |
So read a table with row filters or column masks through Trino, and keep the Python SDK and the Query Engine for tables where your user has none. The 0x… values differ with the engines the policy names.
Which engines support it
- Trino applies row filters, column masks and column allow and deny, with impersonation set up in the catalog. See Trino.
- Spark: row filtering and column masking are supported for Trino only (a known limitation in 5.5), and impersonation isn't supported for Spark. Rely on row filters and column masks only with Trino. See Spark.
- The Python SDK and the VAST Query Engine honour column allow and deny and the row filter by S3 path, and refuse tables with row filters or column masks (above).
The vastdb_s3_path_auth column
Path-based row filtering reads a reserved column called vastdb_s3_path_auth. Each cell holds a comma-separated list of S3 paths, each in double quotes, such as "my_bucket/path1","my_bucket/path2". If you design a table for this:
- Add the column when you create the table.
add_column()refuses it on an existing table (Cannot add column=vastdb_s3_path_auth, it can be added only during create), and it can't be dropped or renamed later. - Every value must be a list of quoted paths. A value without the quotes, or a null, is refused with
BadRequest(InvalidArgument). - A row is returned only if the reader may read every path in its cell. With
s3:GetObjectonmy_bucket/path1only, a row holding"my_bucket/path1"was returned and a row holding"my_bucket/path1","my_bucket/path2"wasn't, from both the Python SDK and the Query Engine.
Impersonation
With impersonation, an engine or driver that holds one service key runs each request as the actual end user. The cluster then applies that person's identity policy, including row and column security.
| Client | How | What the service key's user needs |
|---|---|---|
| Trino | Set up by your platform team | s3:EndUserImpersonation and s3:TabularGetRowColumnSecurity |
| Spark | Not supported | — |
| ADBC and the Python SDK's ADBC calls | vastdb.connect(..., end_user="<name>") or Session(end_user=...); the driver option is vast.db.end_user |
s3:EndUserImpersonation (VAST Query Engine) |
In the SDK, end_user only applies to operations that go through the ADBC driver, such as vector_search(). Ordinary select(), insert(), update() and delete() calls still run as the key's owner: with end_user set to a user whose policy hides a column, select() still returned it.
Over ADBC, an impersonated query gets the end user's rules: a column denied to the end user isn't returned, and a table with a row filter for the end user is refused as described above. What you see when it isn't set up:
- The service key's user lacks
s3:EndUserImpersonation:user login_name=<service user> … action=EndUserImpersonation is not allowed (end_user=<name>). - The end user isn't a VAST user:
login_name=<name> was not found.
Use these exact action names: s3:EndUserImpersonation and s3:TabularGetRowColumnSecurity. Similar names, such as s3:GetRowColumnSecurity or s3:TabularEndUserImpersonation, grant nothing. A policy that contains an action name the cluster doesn't recognise is still accepted; that action just grants nothing. s3:Tabular* covers s3:TabularGetRowColumnSecurity but not s3:EndUserImpersonation. Impersonation works only for Trino and the VAST Query Engine (ADBC).
For Trino, impersonation also needs three flags in the catalog file. Without them, row filters aren't applied, although denied columns are still removed. See Trino.
Protect the Trino server log. Trino connector 478 writes the catalog's secret access key to the Trino server log in plain text, in one INFO line per catalog when the catalog loads, although the configuration summary that follows shows it as [REDACTED]. Failed requests are also logged with their headers, which include the access key ID. Until this changes, restrict who can read the Trino server log, and where it's shipped, as you would the catalog file.
If your platform team uses protocol auditing, note that for impersonated access the audit log records only the original user's UID, not their login name or SID (a known limitation in 5.5).
What to ask your platform team
- Which identity policies apply to your key's user, and whether any statements deny actions or have conditions such as source IP.
- Whether any row and column security rules apply to your user, on which tables, and for which engines.
- For Trino: whether impersonation is set up, so rules apply to each person instead of to the catalog's user.
- For ADBC impersonation:
s3:EndUserImpersonationfor the service key. - For the VAST Query Engine:
s3:QueryEngine*.
Admin task: send to your platform team. See Managing permissions for accessing VAST tabular databases, Managing identity policies and Row and column security.
See also
- Setup prerequisites: the actions each SDK call needs, and example policies.
- Errors: reading a
Forbiddenerror. - Trino and Spark: row and column security from each engine.