From c5a9f3195d38334524fe26360831d257c00a8679 Mon Sep 17 00:00:00 2001 From: glasstiger Date: Fri, 25 Sep 2026 13:53:52 +0100 Subject: [PATCH 1/6] docs: document Enterprise ACL inspection functions --- documentation/changelog.mdx | 1 + .../query/functions/access-control.md | 139 ++++++++++++++++++ documentation/query/sql/show.md | 5 + documentation/security/rbac.md | 6 +- documentation/sidebars.js | 1 + 5 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 documentation/query/functions/access-control.md diff --git a/documentation/changelog.mdx b/documentation/changelog.mdx index 94f165ad0..baf7ae9ad 100644 --- a/documentation/changelog.mdx +++ b/documentation/changelog.mdx @@ -16,6 +16,7 @@ This page tracks significant updates to the QuestDB documentation. ### New +- [Access control functions](/docs/query/functions/access-control/) - Query `active_permissions()` for effective ACL permissions, including group inheritance, or `active_grants()` for direct grants across users, groups, and service accounts in QuestDB Enterprise - [Memory limits](/docs/configuration/cairo-engine/#memory-limits) - New section covering the per-query, materialized view refresh, WAL apply, and live view refresh memory limits, what counts toward them, and what happens on a breach, plus the previously undocumented [`cairo.mat.view.max.refresh.retries`](/docs/configuration/materialized-views/#cairomatviewmaxrefreshretries), [`cairo.mat.view.refresh.busy.retry.limit`](/docs/configuration/materialized-views/#cairomatviewrefreshbusyretrylimit), [`cairo.mat.view.refresh.busy.retry.timeout`](/docs/configuration/materialized-views/#cairomatviewrefreshbusyretrytimeout), [`cairo.write.back.off.timeout.on.mem.pressure`](/docs/configuration/cairo-engine/#cairowritebackofftimeoutonmempressure), [`ram.usage.limit.bytes`](/docs/configuration/cairo-engine/#ramusagelimitbytes), and [`ram.usage.limit.percent`](/docs/configuration/cairo-engine/#ramusagelimitpercent) keys - [RBAC memory limits](/docs/security/rbac/#memory-limits) - Per-user, per-group, and per-service-account query memory limits in QuestDB Enterprise: `SET MEMORY LIMIT` on `ALTER USER`, `ALTER GROUP`, and `ALTER SERVICE ACCOUNT`, how limits resolve, the `SET MEMORY LIMIT` permission, and the upgrade migration - [ALTER GROUP](/docs/query/sql/acl/alter-group/) - New reference page covering `SET MEMORY LIMIT` and external alias mapping diff --git a/documentation/query/functions/access-control.md b/documentation/query/functions/access-control.md new file mode 100644 index 000000000..541983125 --- /dev/null +++ b/documentation/query/functions/access-control.md @@ -0,0 +1,139 @@ +--- +title: Access control functions - active_permissions() and active_grants() +sidebar_label: Access control +description: + Audit effective permissions and direct grants for QuestDB Enterprise users, + groups, and service accounts with active_permissions() and active_grants(). +--- + +In QuestDB Enterprise, `active_permissions()` and `active_grants()` are SQL +table functions for auditing [role-based access control](/docs/security/rbac/). +Use them to find who has access to a table or who has been granted a permission +across all internal users, groups, and service accounts. `active_permissions()` +reports effective permissions, including those inherited from groups; +`active_grants()` reports direct grants only. + +## Syntax + +Both functions take no arguments and can be filtered like tables: + +```questdb-sql title="Effective permissions" +SELECT * FROM active_permissions(); +``` + +```questdb-sql title="Direct grants" +SELECT * FROM active_grants(); +``` + +## Result columns + +Both functions return the same columns: + +| Column | Type | Description | +| -------------- | ------- | ------------------------------------------------------------------------------ | +| `entity_name` | STRING | Name of the user, group, or service account | +| `entity_type` | STRING | `User`, `Group`, or `Service Account` | +| `permission` | STRING | Permission name, such as `SELECT` or `CREATE TABLE` | +| `table_name` | STRING | Table name for a table or column scope; `NULL` for a database-level permission | +| `column_name` | STRING | Column name for a column scope; `NULL` for a table or database scope | +| `grant_option` | BOOLEAN | Whether the entity can grant this permission at this scope to others | + +A database-level `SELECT` (with `table_name IS NULL`) applies to all tables, +including future tables. When filtering for access to a particular table, +include both its table name and `NULL`. A non-`NULL` `column_name` means access +is limited to that column, not the whole table. + +## Effective permissions and direct grants + +`active_permissions()` includes each user's direct permissions and permissions +inherited from their groups. Groups and service accounts have their own rows. It +also includes implicit access to a table's designated timestamp column when a +principal has `SELECT` or `UPDATE` on another column. For a principal with +`DATABASE ADMIN`, it expands the effective database permissions. + +`active_grants()` lists permissions granted directly to each entity. It does not +repeat a group's grants under its members or include implicit designated +timestamp permissions. Results reflect the **current, normalized ACL scopes**, +not the original `GRANT` statements: for example, revoking access to a single +column can turn a table-wide grant into column-level rows. Use +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions-for-current-user) +when you need to inspect one principal instead of searching the whole ACL. + +:::note + +Both functions require `LIST USERS` and `USER DETAILS` permissions. The built-in +admin can call them without explicit grants unless it has assumed a service +account, in which case the assumed account needs both permissions. They return +an empty result if ACL is disabled. They do not list external SSO/OIDC +identities or the built-in admin, which has no persisted ACL entry. A disabled +user can still appear with its retained permissions, so a row does not +necessarily mean the principal can log in. Neither function can be used in a +materialized or live view. + +::: + +## Examples + +### Find who can read a table + +List users and service accounts with effective `SELECT` permission on `trades`. +The `NULL` case includes database-level access, and the `column_name` field +shows whether access is limited to specific columns. Users who inherit a grant +from a group appear under their own names; group rows are excluded here because +groups cannot log in. + +```questdb-sql +SELECT entity_name, entity_type, table_name, column_name +FROM active_permissions() +WHERE permission = 'SELECT' + AND (table_name = 'trades' OR table_name IS NULL) + AND entity_type IN ('User', 'Service Account') +ORDER BY entity_name, table_name, column_name; +``` + +For a table-wide grant, `column_name` is `NULL`; for a database-wide grant, both +scope columns are `NULL`. Column-level rows show access to those columns only. +Check that an account is enabled and has the required connection permission +(such as `PGWIRE` or `HTTP`) before treating it as able to connect. + +### Find direct recipients of a permission + +Find who was directly granted `CREATE TABLE`, including groups. Members who +inherit a group's permission are **not** repeated as recipients; use +`active_permissions()` if you want to see their effective access. + +```questdb-sql +SELECT entity_name, entity_type, grant_option +FROM active_grants() +WHERE permission = 'CREATE TABLE' +ORDER BY entity_type, entity_name; +``` + +For a table-scoped permission such as `SELECT`, also filter by scope, including +database-wide grants: + +```questdb-sql +SELECT entity_name, entity_type, table_name, column_name, grant_option +FROM active_grants() +WHERE permission = 'SELECT' + AND (table_name = 'trades' OR table_name IS NULL) +ORDER BY entity_type, entity_name, table_name, column_name; +``` + +### Find who can delegate SELECT on a table + +Filter effective permissions by `grant_option` to find principals who can grant +`SELECT` on all or part of `trades` to others: + +```questdb-sql +SELECT entity_name, entity_type, table_name, column_name +FROM active_permissions() +WHERE permission = 'SELECT' + AND grant_option + AND (table_name = 'trades' OR table_name IS NULL) + AND entity_type IN ('User', 'Service Account') +ORDER BY entity_name, table_name, column_name; +``` + +As above, a non-`NULL` `column_name` means the grant option is scoped to that +column, not the whole table. diff --git a/documentation/query/sql/show.md b/documentation/query/sql/show.md index 29256373f..53e183d36 100644 --- a/documentation/query/sql/show.md +++ b/documentation/query/sql/show.md @@ -500,6 +500,11 @@ SHOW PERMISSIONS ilp_ingestion; | INSERT | | | f | G | | UPDATE | | | f | G | +To search across all users, groups, and service accounts instead of inspecting +one entity at a time, use +[`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/). +Both require `LIST USERS` and `USER DETAILS`. + ### SHOW SERVER_VERSION Shows PostgreSQL compatibility version. diff --git a/documentation/security/rbac.md b/documentation/security/rbac.md index 62dd03447..9a0703d80 100644 --- a/documentation/security/rbac.md +++ b/documentation/security/rbac.md @@ -571,7 +571,11 @@ SHOW PERMISSIONS username; -- Show permissions for user ``` `SHOW USERS`, `SHOW GROUPS`, and `SHOW SERVICE ACCOUNTS` also report each -entity's [memory limit](#memory-limits). +entity's [memory limit](#memory-limits). To search permissions across all +principals rather than inspect one with `SHOW PERMISSIONS`, query +[`active_permissions()` or `active_grants()`](/docs/query/functions/access-control/). +These functions show effective access (including group inheritance) or direct +grants, respectively, and require both `LIST USERS` and `USER DETAILS`. Example output from `SHOW USER`: diff --git a/documentation/sidebars.js b/documentation/sidebars.js index e82481098..78eddaa19 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -491,6 +491,7 @@ module.exports = { type: "category", label: "Functions", items: [ + "query/functions/access-control", "query/functions/aggregation", "query/functions/array", "query/functions/binary", From f8150919b22299ddf1d99febf6e28700a13d4582 Mon Sep 17 00:00:00 2001 From: glasstiger Date: Fri, 25 Sep 2026 14:09:17 +0100 Subject: [PATCH 2/6] docs: cover all Enterprise ACL inspection functions --- documentation/changelog.mdx | 2 +- .../query/functions/access-control.md | 97 +++++++++++++++---- documentation/query/sql/show.md | 9 +- documentation/security/rbac.md | 14 +-- 4 files changed, 93 insertions(+), 29 deletions(-) diff --git a/documentation/changelog.mdx b/documentation/changelog.mdx index baf7ae9ad..291965044 100644 --- a/documentation/changelog.mdx +++ b/documentation/changelog.mdx @@ -16,7 +16,7 @@ This page tracks significant updates to the QuestDB documentation. ### New -- [Access control functions](/docs/query/functions/access-control/) - Query `active_permissions()` for effective ACL permissions, including group inheritance, or `active_grants()` for direct grants across users, groups, and service accounts in QuestDB Enterprise +- [Access control functions](/docs/query/functions/access-control/) - QuestDB Enterprise's four ACL table functions: `all_permissions()` lists available permission scopes, `permissions()` inspects one principal, and `active_permissions()` and `active_grants()` search effective permissions and direct grants across principals - [Memory limits](/docs/configuration/cairo-engine/#memory-limits) - New section covering the per-query, materialized view refresh, WAL apply, and live view refresh memory limits, what counts toward them, and what happens on a breach, plus the previously undocumented [`cairo.mat.view.max.refresh.retries`](/docs/configuration/materialized-views/#cairomatviewmaxrefreshretries), [`cairo.mat.view.refresh.busy.retry.limit`](/docs/configuration/materialized-views/#cairomatviewrefreshbusyretrylimit), [`cairo.mat.view.refresh.busy.retry.timeout`](/docs/configuration/materialized-views/#cairomatviewrefreshbusyretrytimeout), [`cairo.write.back.off.timeout.on.mem.pressure`](/docs/configuration/cairo-engine/#cairowritebackofftimeoutonmempressure), [`ram.usage.limit.bytes`](/docs/configuration/cairo-engine/#ramusagelimitbytes), and [`ram.usage.limit.percent`](/docs/configuration/cairo-engine/#ramusagelimitpercent) keys - [RBAC memory limits](/docs/security/rbac/#memory-limits) - Per-user, per-group, and per-service-account query memory limits in QuestDB Enterprise: `SET MEMORY LIMIT` on `ALTER USER`, `ALTER GROUP`, and `ALTER SERVICE ACCOUNT`, how limits resolve, the `SET MEMORY LIMIT` permission, and the upgrade migration - [ALTER GROUP](/docs/query/sql/acl/alter-group/) - New reference page covering `SET MEMORY LIMIT` and external alias mapping diff --git a/documentation/query/functions/access-control.md b/documentation/query/functions/access-control.md index 541983125..fd98dbc98 100644 --- a/documentation/query/functions/access-control.md +++ b/documentation/query/functions/access-control.md @@ -1,33 +1,94 @@ --- -title: Access control functions - active_permissions() and active_grants() +title: Access control functions sidebar_label: Access control description: - Audit effective permissions and direct grants for QuestDB Enterprise users, - groups, and service accounts with active_permissions() and active_grants(). + Query QuestDB Enterprise ACL functions all_permissions(), permissions(), + active_permissions(), and active_grants() to inspect available and assigned + permissions. --- -In QuestDB Enterprise, `active_permissions()` and `active_grants()` are SQL -table functions for auditing [role-based access control](/docs/security/rbac/). -Use them to find who has access to a table or who has been granted a permission -across all internal users, groups, and service accounts. `active_permissions()` -reports effective permissions, including those inherited from groups; -`active_grants()` reports direct grants only. +QuestDB Enterprise provides four SQL table functions for inspecting +[role-based access control](/docs/security/rbac/): `all_permissions()` lists +available permission names and scopes; `permissions()` shows one principal's +access; `active_permissions()` and `active_grants()` let you search effective +permissions or direct grants across all persisted internal users, groups, and +service accounts. Use them to find who can access a table or who has been +granted a particular permission. ## Syntax -Both functions take no arguments and can be filtered like tables: +```questdb-sql title="Available permission names and levels" +SELECT * FROM all_permissions(); +``` + +```questdb-sql title="Current or named principal's permissions" +SELECT * FROM permissions(); +SELECT * FROM permissions('analyst'); +``` -```questdb-sql title="Effective permissions" +```questdb-sql title="Effective permissions across principals" SELECT * FROM active_permissions(); ``` -```questdb-sql title="Direct grants" +```questdb-sql title="Direct grants across principals" SELECT * FROM active_grants(); ``` -## Result columns +All four functions return tables and can be filtered with SQL. The only argument +is the optional principal name for `permissions()`, as a string. + +## all_permissions(): available permissions {#all_permissions} + +`all_permissions()` takes no arguments and returns the permission names +supported by the server and the levels where they can be granted. It lists +permissions, **not** principals or their grants. Its columns are `permission` +(STRING) and `level` (STRING). `level` is `Database`, `Database|Table`, or +`Database|Table|Column` according to the permission's allowed scopes. + +For example, check where `SELECT` can be granted: + +```questdb-sql +SELECT permission, level +FROM all_permissions() +WHERE permission = 'SELECT'; +``` + +See the [permissions reference](/docs/security/rbac/#permissions) for the +available permissions and their uses. + +## permissions(): one principal's effective permissions {#permissions} + +`permissions()` without an argument returns permissions for the current +principal. Pass an existing user, group, or service account name as a string to +inspect that entity instead. It returns the same result as +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions-for-current-user), +but can be composed with `WHERE`, `ORDER BY`, and other SQL clauses: + +```questdb-sql +SELECT permission, table_name, column_name, grant_option, origin +FROM permissions('analyst') +WHERE permission = 'SELECT'; +``` + +| Column | Type | Description | +| -------------- | ------- | ----------------------------------------------------------------------- | +| `permission` | STRING | Permission name | +| `table_name` | STRING | Table scope, or `NULL` for database-level permissions | +| `column_name` | STRING | Column scope, or `NULL` for table- and database-level permissions | +| `grant_option` | BOOLEAN | Whether the principal can grant this permission at this scope to others | +| `origin` | STRING | `G` for granted access, `I` for implicit designated-timestamp access | + +`G` includes both direct and inherited permissions; it does not distinguish +between them. You can inspect your own permissions without `USER DETAILS`. +Inspecting another principal generally requires `USER DETAILS`; users can also +inspect their own groups and service accounts they can assume. Unlike the two +`active_*` functions, `permissions()` does not require `LIST USERS` and does not +show all principals in a single result. It cannot be used in a materialized or +live view. + +## active_permissions() and active_grants(): audit all principals {#active-permissions-and-grants} -Both functions return the same columns: +Both functions take no arguments and return the same columns: | Column | Type | Description | | -------------- | ------- | ------------------------------------------------------------------------------ | @@ -43,7 +104,7 @@ including future tables. When filtering for access to a particular table, include both its table name and `NULL`. A non-`NULL` `column_name` means access is limited to that column, not the whole table. -## Effective permissions and direct grants +### Effective permissions and direct grants `active_permissions()` includes each user's direct permissions and permissions inherited from their groups. Groups and service accounts have their own rows. It @@ -55,9 +116,9 @@ principal has `SELECT` or `UPDATE` on another column. For a principal with repeat a group's grants under its members or include implicit designated timestamp permissions. Results reflect the **current, normalized ACL scopes**, not the original `GRANT` statements: for example, revoking access to a single -column can turn a table-wide grant into column-level rows. Use -[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions-for-current-user) -when you need to inspect one principal instead of searching the whole ACL. +column can turn a table-wide grant into column-level rows. To inspect one +principal instead, use [`permissions()`](#permissions) or +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions-for-current-user). :::note diff --git a/documentation/query/sql/show.md b/documentation/query/sql/show.md index 53e183d36..513735c96 100644 --- a/documentation/query/sql/show.md +++ b/documentation/query/sql/show.md @@ -500,10 +500,11 @@ SHOW PERMISSIONS ilp_ingestion; | INSERT | | | f | G | | UPDATE | | | f | G | -To search across all users, groups, and service accounts instead of inspecting -one entity at a time, use -[`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/). -Both require `LIST USERS` and `USER DETAILS`. +To filter the result for one entity with SQL, use +[`permissions()`](/docs/query/functions/access-control/#permissions). To search +across all users, groups, and service accounts, use +[`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). +The latter two require `LIST USERS` and `USER DETAILS`. ### SHOW SERVER_VERSION diff --git a/documentation/security/rbac.md b/documentation/security/rbac.md index 9a0703d80..466130da8 100644 --- a/documentation/security/rbac.md +++ b/documentation/security/rbac.md @@ -571,11 +571,12 @@ SHOW PERMISSIONS username; -- Show permissions for user ``` `SHOW USERS`, `SHOW GROUPS`, and `SHOW SERVICE ACCOUNTS` also report each -entity's [memory limit](#memory-limits). To search permissions across all -principals rather than inspect one with `SHOW PERMISSIONS`, query -[`active_permissions()` or `active_grants()`](/docs/query/functions/access-control/). -These functions show effective access (including group inheritance) or direct -grants, respectively, and require both `LIST USERS` and `USER DETAILS`. +entity's [memory limit](#memory-limits). To filter the permissions of one +principal, use [`permissions()`](/docs/query/functions/access-control/#permissions) +instead of `SHOW PERMISSIONS`. To search across all principals, use +[`active_permissions()` or `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). +These show effective access (including group inheritance) or direct grants, +respectively, and require both `LIST USERS` and `USER DETAILS`. Example output from `SHOW USER`: @@ -802,7 +803,8 @@ again at startup. ## Permissions reference {#permissions} -Use `all_permissions()` to see all available permissions: +Use [`all_permissions()`](/docs/query/functions/access-control/#all_permissions) +to see all available permissions and where they can be granted: ```questdb-sql SELECT * FROM all_permissions(); From df6410983d6dd926b761dc020d299ded265ab002 Mon Sep 17 00:00:00 2001 From: glasstiger Date: Fri, 25 Sep 2026 14:58:22 +0100 Subject: [PATCH 3/6] docs: consolidate SHOW PERMISSIONS variants --- .../query/functions/access-control.md | 4 +- documentation/query/sql/show.md | 62 +++++++------------ documentation/security/rbac.md | 2 +- 3 files changed, 27 insertions(+), 41 deletions(-) diff --git a/documentation/query/functions/access-control.md b/documentation/query/functions/access-control.md index fd98dbc98..d40fe181a 100644 --- a/documentation/query/functions/access-control.md +++ b/documentation/query/functions/access-control.md @@ -61,7 +61,7 @@ available permissions and their uses. `permissions()` without an argument returns permissions for the current principal. Pass an existing user, group, or service account name as a string to inspect that entity instead. It returns the same result as -[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions-for-current-user), +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions), but can be composed with `WHERE`, `ORDER BY`, and other SQL clauses: ```questdb-sql @@ -118,7 +118,7 @@ timestamp permissions. Results reflect the **current, normalized ACL scopes**, not the original `GRANT` statements: for example, revoking access to a single column can turn a table-wide grant into column-level rows. To inspect one principal instead, use [`permissions()`](#permissions) or -[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions-for-current-user). +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions). :::note diff --git a/documentation/query/sql/show.md b/documentation/query/sql/show.md index 513735c96..45ef8dc9c 100644 --- a/documentation/query/sql/show.md +++ b/documentation/query/sql/show.md @@ -448,57 +448,43 @@ full column list, including `hasParquetGenerated`, `isParquet`, ::: -### SHOW PERMISSIONS FOR CURRENT USER + + -_Enterprise only._ - -```questdb-sql -SHOW PERMISSIONS; -``` - -| permission | table_name | column_name | grant_option | origin | -| ---------- | ---------- | ----------- | ------------ | ------ | -| SELECT | | | t | G | +### SHOW PERMISSIONS -### SHOW PERMISSIONS user +_Enterprise only._ `SHOW PERMISSIONS` displays the effective permissions of one +user, group, or service account. Omitting the name shows the current principal; +providing a name shows that entity's permissions. -_Enterprise only._ +#### Current principal ```questdb-sql -SHOW PERMISSIONS admin; +SHOW PERMISSIONS; ``` -| permission | table_name | column_name | grant_option | origin | -| ---------- | ---------- | ----------- | ------------ | ------ | -| SELECT | | | t | G | -| INSERT | orders | | f | G | -| UPDATE | order_itme | quantity | f | G | - -### SHOW PERMISSIONS - -_Enterprise only._ - -#### For a group +#### Named user, group, or service account ```questdb-sql -SHOW PERMISSIONS admin_group; +SHOW PERMISSIONS analyst; +SHOW PERMISSIONS trading_team; +SHOW PERMISSIONS ingest_app; ``` -| permission | table_name | column_name | grant_option | origin | -| ---------- | ---------- | ----------- | ------------ | ------ | -| INSERT | orders | | f | G | - -#### For a service account +The result has these columns: -```questdb-sql -SHOW PERMISSIONS ilp_ingestion; -``` +| Column | Description | +| -------------- | -------------------------------------------------------------------- | +| `permission` | Permission name | +| `table_name` | Table name, or `NULL` for a database-level permission | +| `column_name` | Column name, or `NULL` for a table- or database-level permission | +| `grant_option` | Boolean: whether the entity can grant this permission at this scope | +| `origin` | `G` for granted access, `I` for implicit designated-timestamp access | -| permission | table_name | column_name | grant_option | origin | -| ---------- | ---------- | ----------- | ------------ | ------ | -| SELECT | | | t | G | -| INSERT | | | f | G | -| UPDATE | | | f | G | +`G` includes direct and inherited group permissions; it does not distinguish +between them. You can view your own permissions without `USER DETAILS`. Viewing +another entity generally requires `USER DETAILS`, but users can also view their +own groups and service accounts they can assume. To filter the result for one entity with SQL, use [`permissions()`](/docs/query/functions/access-control/#permissions). To search diff --git a/documentation/security/rbac.md b/documentation/security/rbac.md index 466130da8..c90cdadc6 100644 --- a/documentation/security/rbac.md +++ b/documentation/security/rbac.md @@ -927,4 +927,4 @@ replication role, is an ordinary grantable permission. - [SHOW GROUPS](/docs/query/sql/show/#show-groups) - [SHOW SERVICE ACCOUNT](/docs/query/sql/show/#show-service-account) - [SHOW SERVICE ACCOUNTS](/docs/query/sql/show/#show-service-accounts) -- [SHOW PERMISSIONS](/docs/query/sql/show/#show-permissions-for-current-user) +- [SHOW PERMISSIONS](/docs/query/sql/show/#show-permissions) From 8513fb5aa2429b1b988cac15c0cbc865e62e86d8 Mon Sep 17 00:00:00 2001 From: glasstiger Date: Fri, 25 Sep 2026 15:22:46 +0100 Subject: [PATCH 4/6] docs: identify SHOW permission requirements --- documentation/query/sql/show.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/query/sql/show.md b/documentation/query/sql/show.md index 45ef8dc9c..69922d766 100644 --- a/documentation/query/sql/show.md +++ b/documentation/query/sql/show.md @@ -490,7 +490,7 @@ To filter the result for one entity with SQL, use [`permissions()`](/docs/query/functions/access-control/#permissions). To search across all users, groups, and service accounts, use [`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). -The latter two require `LIST USERS` and `USER DETAILS`. +The latter two require `LIST USERS` and `USER DETAILS` permissions. ### SHOW SERVER_VERSION From d5293d114b42dc6bc4e8d8d64157b39e67702fcb Mon Sep 17 00:00:00 2001 From: glasstiger Date: Fri, 25 Sep 2026 18:09:26 +0100 Subject: [PATCH 5/6] docs: improve ACL inspection reference --- documentation/changelog.mdx | 2 +- .../getting-started/enterprise-quick-start.md | 6 +- .../query/functions/access-control.md | 248 ++++++++++++++---- documentation/query/sql/acl/grant.md | 17 +- documentation/query/sql/acl/revoke.md | 6 +- documentation/query/sql/show.md | 20 +- documentation/security/rbac.md | 10 +- 7 files changed, 243 insertions(+), 66 deletions(-) diff --git a/documentation/changelog.mdx b/documentation/changelog.mdx index 291965044..78dc135fe 100644 --- a/documentation/changelog.mdx +++ b/documentation/changelog.mdx @@ -16,7 +16,7 @@ This page tracks significant updates to the QuestDB documentation. ### New -- [Access control functions](/docs/query/functions/access-control/) - QuestDB Enterprise's four ACL table functions: `all_permissions()` lists available permission scopes, `permissions()` inspects one principal, and `active_permissions()` and `active_grants()` search effective permissions and direct grants across principals +- [Access control functions](/docs/query/functions/access-control/) - QuestDB Enterprise's four ACL table functions: `all_permissions()` lists available permissions and their grant levels, `permissions()` inspects one principal, and `active_permissions()` and `active_grants()` search effective permissions and direct grants across principals - [Memory limits](/docs/configuration/cairo-engine/#memory-limits) - New section covering the per-query, materialized view refresh, WAL apply, and live view refresh memory limits, what counts toward them, and what happens on a breach, plus the previously undocumented [`cairo.mat.view.max.refresh.retries`](/docs/configuration/materialized-views/#cairomatviewmaxrefreshretries), [`cairo.mat.view.refresh.busy.retry.limit`](/docs/configuration/materialized-views/#cairomatviewrefreshbusyretrylimit), [`cairo.mat.view.refresh.busy.retry.timeout`](/docs/configuration/materialized-views/#cairomatviewrefreshbusyretrytimeout), [`cairo.write.back.off.timeout.on.mem.pressure`](/docs/configuration/cairo-engine/#cairowritebackofftimeoutonmempressure), [`ram.usage.limit.bytes`](/docs/configuration/cairo-engine/#ramusagelimitbytes), and [`ram.usage.limit.percent`](/docs/configuration/cairo-engine/#ramusagelimitpercent) keys - [RBAC memory limits](/docs/security/rbac/#memory-limits) - Per-user, per-group, and per-service-account query memory limits in QuestDB Enterprise: `SET MEMORY LIMIT` on `ALTER USER`, `ALTER GROUP`, and `ALTER SERVICE ACCOUNT`, how limits resolve, the `SET MEMORY LIMIT` permission, and the upgrade migration - [ALTER GROUP](/docs/query/sql/acl/alter-group/) - New reference page covering `SET MEMORY LIMIT` and external alias mapping diff --git a/documentation/getting-started/enterprise-quick-start.md b/documentation/getting-started/enterprise-quick-start.md index 86dbd3e2e..e40b4cc64 100644 --- a/documentation/getting-started/enterprise-quick-start.md +++ b/documentation/getting-started/enterprise-quick-start.md @@ -188,7 +188,11 @@ GRANT ALL ON table2 TO user2 WITH GRANT OPTION; Permission grants can be specific and fine-tuned. -List the full list of applied permissions with `all_permissions()`. +Check what a user can do with `SHOW PERMISSIONS user1;`, or audit grants across +all users, groups, and service accounts with +[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). +[`all_permissions()`](/docs/query/functions/access-control/#all_permissions) +lists the permissions available to grant. - For the full role-based access control docs, including group management, see the [RBAC operations guide](/docs/security/rbac/). diff --git a/documentation/query/functions/access-control.md b/documentation/query/functions/access-control.md index d40fe181a..90078ee10 100644 --- a/documentation/query/functions/access-control.md +++ b/documentation/query/functions/access-control.md @@ -1,12 +1,17 @@ --- title: Access control functions sidebar_label: Access control -description: - Query QuestDB Enterprise ACL functions all_permissions(), permissions(), - active_permissions(), and active_grants() to inspect available and assigned - permissions. +description: >- + Audit QuestDB Enterprise access control in SQL: list direct grants, find who + can read a table, and inspect a user's effective permissions. --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + Access control functions are available in QuestDB Enterprise. + + QuestDB Enterprise provides four SQL table functions for inspecting [role-based access control](/docs/security/rbac/): `all_permissions()` lists available permission names and scopes; `permissions()` shows one principal's @@ -18,24 +23,26 @@ granted a particular permission. ## Syntax ```questdb-sql title="Available permission names and levels" -SELECT * FROM all_permissions(); +all_permissions() ``` -```questdb-sql title="Current or named principal's permissions" -SELECT * FROM permissions(); -SELECT * FROM permissions('analyst'); +```questdb-sql title="Current or named entity's permissions" +permissions([entityName]) ``` -```questdb-sql title="Effective permissions across principals" -SELECT * FROM active_permissions(); +```questdb-sql title="Effective permissions across entities" +active_permissions() ``` -```questdb-sql title="Direct grants across principals" -SELECT * FROM active_grants(); +```questdb-sql title="Direct grants across entities" +active_grants() ``` -All four functions return tables and can be filtered with SQL. The only argument -is the optional principal name for `permissions()`, as a string. +All four are table functions used in the `FROM` clause. Their results can be +filtered, joined, and ordered with SQL. + +- `entityName` (optional, string literal): existing user, group, or service + account to inspect. Omit it to inspect the current entity. ## all_permissions(): available permissions {#all_permissions} @@ -47,7 +54,7 @@ permissions, **not** principals or their grants. Its columns are `permission` For example, check where `SELECT` can be granted: -```questdb-sql +```questdb-sql title="Where SELECT can be granted" SELECT permission, level FROM all_permissions() WHERE permission = 'SELECT'; @@ -64,7 +71,7 @@ inspect that entity instead. It returns the same result as [`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions), but can be composed with `WHERE`, `ORDER BY`, and other SQL clauses: -```questdb-sql +```questdb-sql title="SELECT permissions of one user" SELECT permission, table_name, column_name, grant_option, origin FROM permissions('analyst') WHERE permission = 'SELECT'; @@ -80,13 +87,15 @@ WHERE permission = 'SELECT'; `G` includes both direct and inherited permissions; it does not distinguish between them. You can inspect your own permissions without `USER DETAILS`. -Inspecting another principal generally requires `USER DETAILS`; users can also -inspect their own groups and service accounts they can assume. Unlike the two -`active_*` functions, `permissions()` does not require `LIST USERS` and does not -show all principals in a single result. It cannot be used in a materialized or -live view. +Inspecting another entity generally requires `USER DETAILS`; users can also +inspect their own groups and service accounts they can assume. `permissions()` +does not show all entities in a single result. It cannot be used in a +materialized or live view. + + + -## active_permissions() and active_grants(): audit all principals {#active-permissions-and-grants} +## active_permissions() and active_grants(): audit all entities {#active-permissions-and-grants} Both functions take no arguments and return the same columns: @@ -99,10 +108,19 @@ Both functions take no arguments and return the same columns: | `column_name` | STRING | Column name for a column scope; `NULL` for a table or database scope | | `grant_option` | BOOLEAN | Whether the entity can grant this permission at this scope to others | -A database-level `SELECT` (with `table_name IS NULL`) applies to all tables, -including future tables. When filtering for access to a particular table, -include both its table name and `NULL`. A non-`NULL` `column_name` means access -is limited to that column, not the whole table. +Each row is one scope at which the entity holds a permission: + +- `table_name` is `NULL`: database-wide. A database-level `SELECT` applies to + all tables, including tables created later. +- `table_name` is set and `column_name` is `NULL`: the whole table. +- Both are set: that column only. + +An entity can have rows for the same permission at more than one scope. For +example, a table-wide `SELECT` without the grant option can sit next to column +rows that carry the grant option for specific columns. Access is limited to +specific columns only when the entity has no row for that permission on the +whole table or database. When filtering for access to a particular table, +include both its table name and `NULL`. ### Effective permissions and direct grants @@ -126,54 +144,160 @@ Both functions require `LIST USERS` and `USER DETAILS` permissions. The built-in admin can call them without explicit grants unless it has assumed a service account, in which case the assumed account needs both permissions. They return an empty result if ACL is disabled. They do not list external SSO/OIDC -identities or the built-in admin, which has no persisted ACL entry. A disabled -user can still appear with its retained permissions, so a row does not -necessarily mean the principal can log in. Neither function can be used in a -materialized or live view. +identities, which get their access through the groups they are mapped to, or +the built-in admin, which has no persisted ACL entry. A disabled user can still +appear with its retained permissions, so a row does not necessarily mean the +entity can connect. Check the `enabled` column of +[`SHOW USERS`](/docs/query/sql/show/#show-users) or +[`SHOW SERVICE ACCOUNTS`](/docs/query/sql/show/#show-service-accounts), and the +[endpoint permissions](/docs/security/rbac/#endpoint-permissions) such as +`PGWIRE` or `HTTP`. Neither function can be used in a materialized or live view. ::: ## Examples +The examples use the following entities and grants on the `trades` table, whose +designated timestamp column is `timestamp`. The result tables show the output +for this setup. + +```questdb-sql title="Example setup" +CREATE GROUP trading_team; +CREATE USER analyst WITH PASSWORD 'pwd'; +CREATE USER risk_manager WITH PASSWORD 'pwd'; +CREATE SERVICE ACCOUNT report_svc; +ADD USER analyst TO trading_team; + +GRANT CREATE TABLE TO trading_team; +GRANT SELECT ON trades(symbol, price) TO trading_team WITH GRANT OPTION; +GRANT SELECT ON trades TO analyst; +GRANT SELECT ON ALL TABLES TO risk_manager; +GRANT SELECT ON trades(symbol, price) TO report_svc; +``` + +### Compare effective permissions with direct grants + +`active_permissions()` shows everything `analyst` can do, including what it +inherits from `trading_team`: + +```questdb-sql title="Effective permissions of one user" +SELECT permission, table_name, column_name, grant_option +FROM active_permissions() +WHERE entity_name = 'analyst' +ORDER BY permission, table_name, column_name; +``` + +| permission | table_name | column_name | grant_option | +| ------------ | ---------- | ----------- | ------------ | +| CREATE TABLE | NULL | NULL | false | +| SELECT | trades | NULL | false | +| SELECT | trades | price | true | +| SELECT | trades | symbol | true | + +`analyst` can read the whole `trades` table through its own table-wide grant. +The `price` and `symbol` rows come from the group and carry its grant option, +so `analyst` can grant `SELECT` on those two columns, but not on the whole +table. `CREATE TABLE` is also inherited from the group. + +`active_grants()` shows only what was granted to `analyst` directly: + +```questdb-sql title="Direct grants of one user" +SELECT permission, table_name, column_name, grant_option +FROM active_grants() +WHERE entity_name = 'analyst'; +``` + +| permission | table_name | column_name | grant_option | +| ---------- | ---------- | ----------- | ------------ | +| SELECT | trades | NULL | false | + ### Find who can read a table -List users and service accounts with effective `SELECT` permission on `trades`. -The `NULL` case includes database-level access, and the `column_name` field -shows whether access is limited to specific columns. Users who inherit a grant -from a group appear under their own names; group rows are excluded here because -groups cannot log in. +List every entity with effective `SELECT` permission on `trades`, including +database-wide access: -```questdb-sql +```questdb-sql title="Entities that can read trades" SELECT entity_name, entity_type, table_name, column_name FROM active_permissions() WHERE permission = 'SELECT' AND (table_name = 'trades' OR table_name IS NULL) - AND entity_type IN ('User', 'Service Account') -ORDER BY entity_name, table_name, column_name; +ORDER BY entity_type, entity_name, table_name, column_name; ``` -For a table-wide grant, `column_name` is `NULL`; for a database-wide grant, both -scope columns are `NULL`. Column-level rows show access to those columns only. -Check that an account is enabled and has the required connection permission -(such as `PGWIRE` or `HTTP`) before treating it as able to connect. +| entity_name | entity_type | table_name | column_name | +| ------------ | --------------- | ---------- | ----------- | +| trading_team | Group | trades | price | +| trading_team | Group | trades | symbol | +| trading_team | Group | trades | timestamp | +| report_svc | Service Account | trades | price | +| report_svc | Service Account | trades | symbol | +| report_svc | Service Account | trades | timestamp | +| analyst | User | trades | NULL | +| analyst | User | trades | price | +| analyst | User | trades | symbol | +| risk_manager | User | NULL | NULL | + +- `risk_manager` can read every table, including `trades`. +- `analyst` can read the whole table. Its column rows record the grant option + shown in the previous example. +- `trading_team` and `report_svc` can read `symbol` and `price`, plus the + designated timestamp column, which comes with column-level `SELECT`. +- Keep group rows when auditing: a group cannot log in, but its members can, + including external SSO/OIDC users mapped to it, who are not listed + individually. + +To check access to a single column, also accept table-wide rows, and use +`DISTINCT` to get one row per entity: + +```questdb-sql title="Entities that can read trades.price" +SELECT DISTINCT entity_name, entity_type +FROM active_permissions() +WHERE permission = 'SELECT' + AND (table_name = 'trades' OR table_name IS NULL) + AND (column_name = 'price' OR column_name IS NULL) +ORDER BY entity_type, entity_name; +``` + +| entity_name | entity_type | +| ------------ | --------------- | +| trading_team | Group | +| report_svc | Service Account | +| analyst | User | +| risk_manager | User | + +These queries do not show every way to reach the data: + +- **Assumed service accounts.** A user who can assume a service account gets + its permissions after + [`ASSUME SERVICE ACCOUNT`](/docs/security/rbac/#service-account-assumption), + but those permissions appear only under the service account's name. Use + [`SHOW SERVICE ACCOUNTS userName`](/docs/query/sql/show/#show-service-accounts) + to list the accounts a user or group can assume. +- **Views.** `SELECT` on a view over `trades` lets the grantee read the view's + rows without any grant on `trades`. See + [row-level access with views](/docs/security/rbac/#row-level-access-with-views). ### Find direct recipients of a permission -Find who was directly granted `CREATE TABLE`, including groups. Members who -inherit a group's permission are **not** repeated as recipients; use -`active_permissions()` if you want to see their effective access. +Find who was directly granted `CREATE TABLE`. Members who inherit a group's +permission are not repeated, so `analyst` does not appear. Use +`active_permissions()` to see effective access. -```questdb-sql +```questdb-sql title="Direct recipients of CREATE TABLE" SELECT entity_name, entity_type, grant_option FROM active_grants() WHERE permission = 'CREATE TABLE' ORDER BY entity_type, entity_name; ``` +| entity_name | entity_type | grant_option | +| ------------ | ----------- | ------------ | +| trading_team | Group | false | + For a table-scoped permission such as `SELECT`, also filter by scope, including database-wide grants: -```questdb-sql +```questdb-sql title="Direct SELECT grants on trades" SELECT entity_name, entity_type, table_name, column_name, grant_option FROM active_grants() WHERE permission = 'SELECT' @@ -181,20 +305,38 @@ WHERE permission = 'SELECT' ORDER BY entity_type, entity_name, table_name, column_name; ``` +| entity_name | entity_type | table_name | column_name | grant_option | +| ------------ | --------------- | ---------- | ----------- | ------------ | +| trading_team | Group | trades | price | true | +| trading_team | Group | trades | symbol | true | +| report_svc | Service Account | trades | price | false | +| report_svc | Service Account | trades | symbol | false | +| analyst | User | trades | NULL | false | +| risk_manager | User | NULL | NULL | false | + +Unlike `active_permissions()`, this does not include the implicit designated +timestamp rows. + ### Find who can delegate SELECT on a table -Filter effective permissions by `grant_option` to find principals who can grant +Filter effective permissions by `grant_option` to find entities that can grant `SELECT` on all or part of `trades` to others: -```questdb-sql +```questdb-sql title="Entities that can grant SELECT on trades" SELECT entity_name, entity_type, table_name, column_name FROM active_permissions() WHERE permission = 'SELECT' AND grant_option AND (table_name = 'trades' OR table_name IS NULL) - AND entity_type IN ('User', 'Service Account') -ORDER BY entity_name, table_name, column_name; +ORDER BY entity_type, entity_name, table_name, column_name; ``` -As above, a non-`NULL` `column_name` means the grant option is scoped to that -column, not the whole table. +| entity_name | entity_type | table_name | column_name | +| ------------ | ----------- | ---------- | ----------- | +| trading_team | Group | trades | price | +| trading_team | Group | trades | symbol | +| analyst | User | trades | price | +| analyst | User | trades | symbol | + +Both can grant `SELECT` on `price` and `symbol` only. No one in this setup can +grant `SELECT` on the whole table. diff --git a/documentation/query/sql/acl/grant.md b/documentation/query/sql/acl/grant.md index 3a2e17b45..65b1e7a79 100644 --- a/documentation/query/sql/acl/grant.md +++ b/documentation/query/sql/acl/grant.md @@ -15,7 +15,11 @@ import { EnterpriseNote } from "@site/src/components/EnterpriseNote" `GRANT` - grants permissions to a user, group or service account. For full documentation of the Access Control List and Role-based Access Control, -see the [RBAC operations](/docs/security/rbac) page. +see the [RBAC operations](/docs/security/rbac) page. To inspect the resulting ACL +state, use [`permissions()`](/docs/query/functions/access-control/#permissions) +for one entity or +[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) across +all users, groups, and service accounts. --- @@ -240,7 +244,8 @@ Therefore when a table has a designated timestamp, granting `SELECT` or `UPDATE` permissions on any column will automatically extend those permissions to the timestamp column. These are known as [implicit permissions](/docs/security/rbac/#implicit-permissions), and they're -indicated by an `I` in the `origin` column of the `SHOW PERMISSIONS` output. +indicated by an `I` in the `origin` column of the +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions) output. For example, if you grant `UPDATE` permission on the `id` column of the `products` table, the timestamp column also receives `UPDATE` permission: @@ -258,7 +263,8 @@ GRANT UPDATE ON products(id) TO john; ### Optimization When granting permissions on the table or column level, sometimes it might seem -like there is no effect when cross-checking with the `SHOW permissions` command. +like there is no effect when cross-checking with the +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions) command. If QuestDB detects that the permission is already granted on a higher level, it optimizes and removes any child permissions. Doing so keeps the access list model simple and permission checks faster. @@ -297,7 +303,10 @@ GRANT UPDATE ON countries(id) TO john; GRANT UPDATE ON countries(description) TO john; ``` -Such permissions do not show on `SHOW PERMISSIONS` output. +Such permissions do not show in +[`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions) output, but +[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) lists +them. | permission | table_name | column_name | grant_option | origin | | ---------- | ---------- | ----------- | ------------ | ------ | diff --git a/documentation/query/sql/acl/revoke.md b/documentation/query/sql/acl/revoke.md index c302b6014..6ea4e313b 100644 --- a/documentation/query/sql/acl/revoke.md +++ b/documentation/query/sql/acl/revoke.md @@ -15,7 +15,11 @@ import { EnterpriseNote } from "@site/src/components/EnterpriseNote" `REVOKE` - revoke permission from user, group or service account. For full documentation of the Access Control List and Role-based Access Control, -see the [RBAC operations](/docs/security/rbac) page. +see the [RBAC operations](/docs/security/rbac) page. After revoking a permission, +inspect current direct grants with +[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) +or effective access with +[`active_permissions()`](/docs/query/functions/access-control/#active-permissions-and-grants). --- diff --git a/documentation/query/sql/show.md b/documentation/query/sql/show.md index 69922d766..cb48387fa 100644 --- a/documentation/query/sql/show.md +++ b/documentation/query/sql/show.md @@ -466,9 +466,9 @@ SHOW PERMISSIONS; #### Named user, group, or service account ```questdb-sql -SHOW PERMISSIONS analyst; -SHOW PERMISSIONS trading_team; -SHOW PERMISSIONS ingest_app; +SHOW PERMISSIONS analyst; -- user +SHOW PERMISSIONS trading_team; -- group +SHOW PERMISSIONS ingest_app; -- service account ``` The result has these columns: @@ -481,6 +481,18 @@ The result has these columns: | `grant_option` | Boolean: whether the entity can grant this permission at this scope | | `origin` | `G` for granted access, `I` for implicit designated-timestamp access | +For an existing `trades` table, grant `analyst` table-wide access and inspect +its permissions: + +```questdb-sql title="Inspect a table-wide grant" +GRANT SELECT ON trades TO analyst; +SHOW PERMISSIONS analyst; +``` + +| permission | table_name | column_name | grant_option | origin | +| ---------- | ---------- | ----------- | ------------ | ------ | +| SELECT | trades | | false | G | + `G` includes direct and inherited group permissions; it does not distinguish between them. You can view your own permissions without `USER DETAILS`. Viewing another entity generally requires `USER DETAILS`, but users can also view their @@ -630,6 +642,8 @@ these columns by position rather than by name must account for it. See The following functions allow querying tables and views with filters and using the results as part of a function: +- [`permissions()`](/docs/query/functions/access-control/#permissions) +- [`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) - [table_columns()](/docs/query/functions/meta/#table_columns) - [tables()](/docs/query/functions/meta/#tables) - [table_partitions()](/docs/query/functions/meta/#table_partitions) diff --git a/documentation/security/rbac.md b/documentation/security/rbac.md index c90cdadc6..12ef37008 100644 --- a/documentation/security/rbac.md +++ b/documentation/security/rbac.md @@ -590,9 +590,12 @@ REST Token true :::note -Viewing other users' information requires `LIST USERS` (to list all) or -`USER DETAILS` (to see details) permissions. Users can always view their own -information without these permissions. +Listing all users, groups, or service accounts requires `LIST USERS`. Viewing +another entity's details requires `USER DETAILS`. Because `active_permissions()` +and `active_grants()` perform both operations, they require both permissions. +Users can view their own information without either permission. With +`SHOW PERMISSIONS` or `permissions()`, they can also inspect their own groups +and service accounts they can assume without `USER DETAILS`. ::: @@ -905,6 +908,7 @@ replication role, is an ordinary grantable permission. ## SQL commands reference +- [Access control functions](/docs/query/functions/access-control/) - [ADD USER](/docs/query/sql/acl/add-user/) - [ALTER GROUP](/docs/query/sql/acl/alter-group/) - [ALTER SERVICE ACCOUNT](/docs/query/sql/acl/alter-service-account/) From 25144452190798230ef2e60cd15a993f836231a6 Mon Sep 17 00:00:00 2001 From: glasstiger Date: Sun, 27 Sep 2026 23:20:46 +0100 Subject: [PATCH 6/6] docs: use bare function-name headings for ACL functions Use bare function names as H2 headings so Web Console docs retrieval can match them, split active_permissions and active_grants into separate sections, name all four functions in the description, and point inbound links at the per-function anchors. --- .../getting-started/enterprise-quick-start.md | 2 +- .../query/functions/access-control.md | 43 +++++++++++-------- documentation/query/sql/acl/grant.md | 4 +- documentation/query/sql/acl/revoke.md | 4 +- documentation/query/sql/show.md | 6 ++- documentation/security/rbac.md | 3 +- 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/documentation/getting-started/enterprise-quick-start.md b/documentation/getting-started/enterprise-quick-start.md index e40b4cc64..a0d8d2ee7 100644 --- a/documentation/getting-started/enterprise-quick-start.md +++ b/documentation/getting-started/enterprise-quick-start.md @@ -190,7 +190,7 @@ Permission grants can be specific and fine-tuned. Check what a user can do with `SHOW PERMISSIONS user1;`, or audit grants across all users, groups, and service accounts with -[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). +[`active_grants()`](/docs/query/functions/access-control/#active_grants). [`all_permissions()`](/docs/query/functions/access-control/#all_permissions) lists the permissions available to grant. diff --git a/documentation/query/functions/access-control.md b/documentation/query/functions/access-control.md index 90078ee10..5229087b0 100644 --- a/documentation/query/functions/access-control.md +++ b/documentation/query/functions/access-control.md @@ -2,8 +2,9 @@ title: Access control functions sidebar_label: Access control description: >- - Audit QuestDB Enterprise access control in SQL: list direct grants, find who - can read a table, and inspect a user's effective permissions. + Audit QuestDB Enterprise RBAC in SQL with active_grants(), + active_permissions(), permissions(), and all_permissions(): list grants and + find who can read a table. --- import { EnterpriseNote } from "@site/src/components/EnterpriseNote" @@ -44,7 +45,7 @@ filtered, joined, and ordered with SQL. - `entityName` (optional, string literal): existing user, group, or service account to inspect. Omit it to inspect the current entity. -## all_permissions(): available permissions {#all_permissions} +## all_permissions `all_permissions()` takes no arguments and returns the permission names supported by the server and the levels where they can be granted. It lists @@ -63,9 +64,10 @@ WHERE permission = 'SELECT'; See the [permissions reference](/docs/security/rbac/#permissions) for the available permissions and their uses. -## permissions(): one principal's effective permissions {#permissions} +## permissions -`permissions()` without an argument returns permissions for the current +`permissions()` returns the effective permissions of one user, group, or +service account. Without an argument, it returns permissions for the current principal. Pass an existing user, group, or service account name as a string to inspect that entity instead. It returns the same result as [`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions), @@ -92,12 +94,14 @@ inspect their own groups and service accounts they can assume. `permissions()` does not show all entities in a single result. It cannot be used in a materialized or live view. - - + -## active_permissions() and active_grants(): audit all entities {#active-permissions-and-grants} +## active_permissions -Both functions take no arguments and return the same columns: +`active_permissions()` returns the effective permissions of every persisted +user, group, and service account, including permissions users inherit from +their groups. Use it to find who can access a table or column. It takes no +arguments and returns these columns: | Column | Type | Description | | -------------- | ------- | ------------------------------------------------------------------------------ | @@ -122,17 +126,21 @@ specific columns only when the entity has no row for that permission on the whole table or database. When filtering for access to a particular table, include both its table name and `NULL`. -### Effective permissions and direct grants - -`active_permissions()` includes each user's direct permissions and permissions -inherited from their groups. Groups and service accounts have their own rows. It +Each user's rows combine its direct permissions with permissions inherited +from its groups. Groups and service accounts have their own rows. The result also includes implicit access to a table's designated timestamp column when a principal has `SELECT` or `UPDATE` on another column. For a principal with `DATABASE ADMIN`, it expands the effective database permissions. -`active_grants()` lists permissions granted directly to each entity. It does not -repeat a group's grants under its members or include implicit designated -timestamp permissions. Results reflect the **current, normalized ACL scopes**, +## active_grants + +`active_grants()` returns the permissions granted directly to each persisted +user, group, and service account. Use it to audit who was granted what. It +takes no arguments and returns the same columns and scope rows as +[`active_permissions()`](#active_permissions). + +Unlike `active_permissions()`, it does not repeat a group's grants under its +members or include implicit designated timestamp permissions. Results reflect the **current, normalized ACL scopes**, not the original `GRANT` statements: for example, revoking access to a single column can turn a table-wide grant into column-level rows. To inspect one principal instead, use [`permissions()`](#permissions) or @@ -140,7 +148,8 @@ principal instead, use [`permissions()`](#permissions) or :::note -Both functions require `LIST USERS` and `USER DETAILS` permissions. The built-in +`active_permissions()` and `active_grants()` both require `LIST USERS` and +`USER DETAILS` permissions. The built-in admin can call them without explicit grants unless it has assumed a service account, in which case the assumed account needs both permissions. They return an empty result if ACL is disabled. They do not list external SSO/OIDC diff --git a/documentation/query/sql/acl/grant.md b/documentation/query/sql/acl/grant.md index 65b1e7a79..6c06d9971 100644 --- a/documentation/query/sql/acl/grant.md +++ b/documentation/query/sql/acl/grant.md @@ -18,7 +18,7 @@ For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. To inspect the resulting ACL state, use [`permissions()`](/docs/query/functions/access-control/#permissions) for one entity or -[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) across +[`active_grants()`](/docs/query/functions/access-control/#active_grants) across all users, groups, and service accounts. --- @@ -305,7 +305,7 @@ GRANT UPDATE ON countries(description) TO john; Such permissions do not show in [`SHOW PERMISSIONS`](/docs/query/sql/show/#show-permissions) output, but -[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) lists +[`active_grants()`](/docs/query/functions/access-control/#active_grants) lists them. | permission | table_name | column_name | grant_option | origin | diff --git a/documentation/query/sql/acl/revoke.md b/documentation/query/sql/acl/revoke.md index 6ea4e313b..eb40dd72a 100644 --- a/documentation/query/sql/acl/revoke.md +++ b/documentation/query/sql/acl/revoke.md @@ -17,9 +17,9 @@ import { EnterpriseNote } from "@site/src/components/EnterpriseNote" For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. After revoking a permission, inspect current direct grants with -[`active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) +[`active_grants()`](/docs/query/functions/access-control/#active_grants) or effective access with -[`active_permissions()`](/docs/query/functions/access-control/#active-permissions-and-grants). +[`active_permissions()`](/docs/query/functions/access-control/#active_permissions). --- diff --git a/documentation/query/sql/show.md b/documentation/query/sql/show.md index cb48387fa..2bd765be1 100644 --- a/documentation/query/sql/show.md +++ b/documentation/query/sql/show.md @@ -501,7 +501,8 @@ own groups and service accounts they can assume. To filter the result for one entity with SQL, use [`permissions()`](/docs/query/functions/access-control/#permissions). To search across all users, groups, and service accounts, use -[`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). +[`active_permissions()`](/docs/query/functions/access-control/#active_permissions) and +[`active_grants()`](/docs/query/functions/access-control/#active_grants). The latter two require `LIST USERS` and `USER DETAILS` permissions. ### SHOW SERVER_VERSION @@ -643,7 +644,8 @@ The following functions allow querying tables and views with filters and using the results as part of a function: - [`permissions()`](/docs/query/functions/access-control/#permissions) -- [`active_permissions()` and `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants) +- [`active_grants()`](/docs/query/functions/access-control/#active_grants) +- [`active_permissions()`](/docs/query/functions/access-control/#active_permissions) - [table_columns()](/docs/query/functions/meta/#table_columns) - [tables()](/docs/query/functions/meta/#tables) - [table_partitions()](/docs/query/functions/meta/#table_partitions) diff --git a/documentation/security/rbac.md b/documentation/security/rbac.md index 12ef37008..9aa3d8fe3 100644 --- a/documentation/security/rbac.md +++ b/documentation/security/rbac.md @@ -574,7 +574,8 @@ SHOW PERMISSIONS username; -- Show permissions for user entity's [memory limit](#memory-limits). To filter the permissions of one principal, use [`permissions()`](/docs/query/functions/access-control/#permissions) instead of `SHOW PERMISSIONS`. To search across all principals, use -[`active_permissions()` or `active_grants()`](/docs/query/functions/access-control/#active-permissions-and-grants). +[`active_permissions()`](/docs/query/functions/access-control/#active_permissions) or +[`active_grants()`](/docs/query/functions/access-control/#active_grants). These show effective access (including group inheritance) or direct grants, respectively, and require both `LIST USERS` and `USER DETAILS`.