Skip to content

Add active ACL inspection functions - #37

Merged
glasstiger merged 1 commit into
masterfrom
ia_active_permissions
Oct 21, 2026
Merged

glasstiger merged 1 commit into
masterfrom
ia_active_permissions

Conversation

@glasstiger

Copy link
Copy Markdown
Contributor

Summary

Register the new QuestDB Enterprise ACL inspection functions as table-valued functions:

  • active_permissions() returns effective permissions across persisted users, groups, and service accounts, including group inheritance.
  • active_grants() returns direct grants across those entities.

Both functions are added to the CURSOR section of tableValuedFunctions, in alphabetical order. This makes them available for autocomplete in table-source contexts such as SELECT * FROM active_permissions(), while keeping them out of scalar-expression suggestions.

Autocomplete regression tests cover both the positive FROM context and the negative SELECT expression context.

Dependencies

Verification

  • corepack yarn test: 15,105 tests passed, 1 skipped
  • corepack yarn vitest run tests/autocomplete.test.ts -t "active": 2 tests passed
  • corepack yarn typecheck
  • corepack yarn lint

@glasstiger glasstiger left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

No blocking issues. Ready to merge.

Verified

  1. Correct category. In questdb/questdb-enterprise#1222 (merged), both functions take no arguments and both factories return isCursor() == true (ActiveGrantsFunctionFactory extends ActivePermissionsFunctionFactory). functions() reports cursor factories as CURSOR, so the CURSOR section is right. Alphabetical order holds (active_grants < active_permissions < all_permissions). Both are registered automatically by the package scan in EntCairoEngine.
  2. No grammar changes needed. Parse, toSql round-trip and format all work for FROM active_permissions() and FROM active_grants(), including aliases, JOINs, CTEs and WHERE grant_option.
  3. Autocomplete in the right places only. Suggested after FROM, JOIN (including ASOF and CROSS), a comma join, inside CTEs and subqueries, EXPLAIN, INSERT … SELECT and CREATE TABLE … AS (SELECT …). Not suggested in the SELECT list or WHERE.
  4. The tests are meaningful.
    • The positive test (FROM act|) fails with the functions.ts change reverted.
    • The negative test (SELECT act|) passes either way, but it's a real guard: about 255 function suggestions come back at SELECT act without prefix filtering, so moving these two into the scalar, aggregate or window lists would fail it.
  5. Docs examples work. Every new SQL example in questdb/documentation#566 parses, round-trips and formats idempotently, so adding them to the fixtures later won't break anything. The only exception is the syntax-notation line permissions([entityName]), which isn't SQL.
  6. Local checks. yarn test (15,106 passed), yarn typecheck, yarn lint and prettier --check are clean. The branch is up to date with master.

Optional nits

  • The new tests sit in "Regression scenarios (bugs that motivated this plan)". That matches the neighbouring gen| and long| tests, but these aren't really regressions. Fine as is.
  • Once questdb/documentation#566 merges, consider adding SELECT * FROM active_permissions() and SELECT * FROM active_grants() to tests/fixtures/docs-queries.json, next to all_permissions().
  • The PR description still lists questdb/questdb-enterprise#1222 as a dependency, but it has merged. Only the docs PR is still open, and it doesn't block this one.

Out of scope

reload_tick_calendars() (Enterprise, questdb/questdb-enterprise#883) is missing from scalarFunctions. This gap predates this PR and could be a small follow-up.

@puzpuzpuz
puzpuzpuz self-requested a review October 21, 2026 17:00
@glasstiger
glasstiger merged commit f97aa5b into master Oct 21, 2026
2 checks passed
@glasstiger glasstiger mentioned this pull request Oct 21, 2026
javier pushed a commit to questdb/documentation that referenced this pull request Oct 26, 2026
## Summary

Documents the changes from
[questdb/questdb-enterprise#1222](questdb/questdb-enterprise#1222)
and its OSS tandem
[questdb/questdb#7631](questdb/questdb#7631),
shipped in QuestDB Enterprise 4.1.0:

- New `active_permissions()` and `active_grants()` ACL table functions.
- Enterprise administrative functions are rejected in materialized view
and live view queries.
- A read-only replica's access-list refresh is bounded to 30 seconds and
fails closed.

## Access control functions page

New reference page, [Access control
functions](https://preview-566--questdb-documentation.netlify.app/docs/query/functions/access-control/)
(`query/functions/access-control.md`), added to the Functions sidebar.
It covers all four Enterprise ACL table functions:

| Function | Returns | Required permissions |
| --- | --- | --- |
| `active_grants()` (new in 4.1.0) | Direct grants of every user, group,
and service account | `LIST USERS` and `USER DETAILS` |
| `active_permissions()` (new in 4.1.0) | Effective permissions of every
user, group, and service account | `LIST USERS` and `USER DETAILS` |
| `all_permissions()` | Every permission and the levels it can be
granted at | None |
| `permissions([entityName])` | Effective permissions of the current
user or a named entity | `USER DETAILS` for other entities |

The page documents:

- **Result columns and scope semantics.** Database-level rows have a
`null` `table_name`. Each combination of entity, permission, table, and
column appears once. A permission appears on two scopes when its grant
option is narrower than the permission. Grants on tables or columns that
do not exist yet are included, which `permissions()` omits.
- **What `active_permissions()` adds to `active_grants()`.** It adds
permissions inherited from groups, the implicit permission on the
designated timestamp, and the permissions that `DATABASE ADMIN`
includes.
- **Required permissions.** Both functions need `LIST USERS` and `USER
DETAILS`, even to read your own rows. The page lists the `Access denied`
errors a caller gets without them.
- **Limitations.** The built-in admin and external (SSO/OIDC) users are
not listed, so you audit their mapped groups instead. Group memberships
and `ASSUME SERVICE ACCOUNT` grants are not listed. With
`acl.enabled=false` the functions return no rows. They cannot be used in
materialized or live views, but regular views can call them.
- **Verified examples with result tables**, all built on one set of
entities and grants:
  - direct grants compared with effective permissions;
  - who can read a table, including database-level and column grants;
  - which entities hold the grant option;
  - the levels each permission can be granted at;
  - one entity's permissions, with the `origin` column.

## Materialized views and live views

Eight Enterprise administrative functions are rejected in materialized
view and live view queries: `active_grants()`, `active_permissions()`,
`backup_instance_name()`, `backup_status()`, `backups()`,
`permissions()`, `reload_tick_calendars()`, and `reload_tls()`. They
check the caller's permissions, and a background refresh has no session
to check against. This PR documents:

- the restriction in the query constraints of
`concepts/materialized-views.md`, `concepts/live-views.md`,
`query/sql/create-mat-view.md`, and `query/sql/create-live-view.md`;
- the `administrative function cannot be used in materialized view:
<function>` error and its live view counterpart, in the `CREATE
MATERIALIZED VIEW` and `CREATE LIVE VIEW` error tables.

## Access control on replicas

New [Access control on
replicas](https://preview-566--questdb-documentation.netlify.app/docs/security/rbac/#replicas)
section in `security/rbac.md`:

- Access control is managed on the primary. A read-only replica rejects
statements such as `CREATE USER` and `GRANT`.
- Before it authorizes a statement, the replica reloads its replicated
access lists if new changes have arrived.
- A statement waits up to 30 seconds for another session's reload. If
the lists are still stale after that, the statement fails with `timed
out waiting for the access lists to refresh [timeout=30000ms]` rather
than being authorized against outdated permissions.

## Related changes

- **Enterprise quick start:** corrects the claim that
`all_permissions()` lists applied permissions. It now shows an
`active_grants()` query for checking grants.
- **RBAC:**
- adds an audit snippet using `active_permissions()` and
`active_grants()`;
  - notes that they require both `LIST USERS` and `USER DETAILS`;
- links the permissions reference to `all_permissions()` and says that
it lists the available permissions and their grant levels.
- **`SHOW PERMISSIONS`:** links to `permissions()`, which returns the
same rows as a table function. Also links to `active_permissions()`,
which lists every entity at once.
- **`GRANT`:** points to `active_grants()` for grants on tables or
columns that do not exist yet, since `SHOW PERMISSIONS` does not show
them.
- **Changelog:** entries for the new page and for the view, replica, and
quick start updates.

## Dependencies

All merged and included in QuestDB Enterprise 4.1.0:

-
[questdb/questdb-enterprise#1222](questdb/questdb-enterprise#1222):
ACL functions, view restriction, and replica refresh.
- [questdb/questdb#7631](questdb/questdb#7631):
OSS side of the view restriction.
-
[questdb/sql-parser#37](questdb/sql-parser#37):
Web Console autocomplete and syntax highlighting for
`active_permissions()` and `active_grants()`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants