Skip to main content

Administering access

This page contains task-oriented recipes for managing users, roles, groups, memberships, and pool grants through the REST API. All endpoints described here are available only when stateStorage=postgres (the default). They require a valid admin credential on every request.

For a description of the underlying data model, the EffectiveSet, pool-access gates, and privilege-escalation rules, see the "Access control model" page.

Authentication

Pass an X-API-Key header on every request. Two ways to obtain a value:

  • Static key: set QOD_API_KEY in the manager environment. Use that same value as the header.
  • Session token: call POST /api/auth/login with {"username":"...","password":"..."} and use the returned token field.
TOKEN=$(curl -sS -X POST http://localhost:20900/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' \
| jq -r .token)

The examples below use $TOKEN as shorthand for whichever value you choose.


1. Create a role and grant a table permission

A role bundles one or more table permissions. First create the role, then attach grants to it.

Create a role

curl -sS -X POST http://localhost:20900/api/role/create \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"tenant":"acme","name":"analyst","description":"Read-only analyst role"}'

Response includes the generated id field. Copy it for the next step.

{"id":"r-7a2b...","tenantId":"...","name":"analyst","description":"Read-only analyst role","createdAt":"..."}

Grant a table permission to the role

Request body fields: roleId (required), catalog (default *), schema (default *), table (default *), verb (required; one of SELECT, INSERT, UPDATE, DELETE, ALL).

curl -sS -X POST http://localhost:20900/api/role/permission/grant \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"roleId": "<role-id>",
"catalog": "tpch",
"schema": "tpch1",
"table": "customer",
"verb": "SELECT"
}'

To grant access to every table in a tenant (wildcard), omit catalog, schema, and table (they default to *):

curl -sS -X POST http://localhost:20900/api/role/permission/grant \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"roleId":"<role-id>","verb":"SELECT"}'

In the admin UI: open the tenant detail page, navigate to the Roles tab, create a role, then use the permission editor to add grants.


2. Create a group and attach a role

Groups collect multiple users under a shared set of role assignments.

Create a group

curl -sS -X POST http://localhost:20900/api/group/create \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"tenant":"acme","name":"data-team","description":"Data analysts group"}'

Attach a role to the group

curl -sS -X POST http://localhost:20900/api/membership/group-role/add \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"groupId":"<group-id>","roleId":"<role-id>"}'

This endpoint is idempotent: calling it multiple times with the same pair produces no error and no duplicate row.

To remove the assignment:

curl -sS -X POST http://localhost:20900/api/membership/group-role/remove \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"groupId":"<group-id>","roleId":"<role-id>"}'

In the admin UI: open the tenant detail page, navigate to the Groups tab, create a group, then use the role assignment controls to link roles.


3. Create a user

Tenant-scoped user

A tenant-scoped user can only connect to pools belonging to their tenant. The role field controls the admin UI role (user or admin); it is not an RBAC role in the access-control sense.

curl -sS -X POST http://localhost:20900/api/user/create \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"tenant": "acme",
"username": "alice",
"password": "s3cr3t",
"role": "user"
}'

Superuser (tenant null)

A superuser has tenant: null. Superusers bypass the pool-access gate and the per-statement ACL gate entirely. Only an existing superuser may create another superuser; a tenant-scoped admin attempting this call receives a 403.

curl -sS -X POST http://localhost:20900/api/user/create \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"username": "ops-admin",
"password": "str0ng!",
"role": "admin"
}'

Omitting tenant is equivalent to passing "tenant": null.

In the admin UI: open the Users page, click "Create user", fill in the form. The superuser option appears only when the logged-in user is themselves a superuser.


4. Add a user to a role and to a group

These endpoints are idempotent: adding an already-existing membership returns 204 with no error.

User to role

curl -sS -X POST http://localhost:20900/api/membership/user-role/add \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"userId":"<user-id>","roleId":"<role-id>"}'

User to group

curl -sS -X POST http://localhost:20900/api/membership/user-group/add \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"userId":"<user-id>","groupId":"<group-id>"}'

To remove either membership, call the corresponding .../remove path with the same body.

In the admin UI: on the user detail row, use the role and group assignment controls.


5. Grant pool access

A pool permission allows a user or a group to open FlightSQL connections to a specific pool. Pass exactly one of userId or groupId. Set poolId to a specific pool's id (the UUID returned in PoolResponse.id) or omit it to grant access to every pool in the tenant.

Grant a specific pool to a user

curl -sS -X POST http://localhost:20900/api/pool/permission/grant \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"tenant": "acme",
"poolId": "<pool-uuid>",
"userId": "<user-id>"
}'

Grant all pools in a tenant to a group

curl -sS -X POST http://localhost:20900/api/pool/permission/grant \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"tenant": "acme",
"groupId": "<group-id>"
}'

Omitting poolId (or passing null) grants access to every current and future pool in the tenant.

Revoke a pool grant

Use the id from the grant response:

curl -sS -X POST http://localhost:20900/api/pool/permission/revoke \
-H "X-API-Key: $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"id":"<grant-id>"}'

List pool grants

All three query parameters are optional; any combination filters the results:

curl -sS "http://localhost:20900/api/pool/permission/list?tenant=acme&userId=<user-id>" \
-H "X-API-Key: $TOKEN"

In the admin UI: on the pool detail page, use the "Access" tab to add or remove user and group grants.


6. Inspect a user's effective permissions

The effective-permissions endpoint returns the full closure of roles, groups, pool grants, and table permissions for a given user without requiring you to trace the graph manually.

curl -sS "http://localhost:20900/api/user/<user-id>/effective" \
-H "X-API-Key: $TOKEN"

Example response shape:

{
"user": {"id":"...","tenant":"acme","username":"alice","role":"user",...},
"roles": [{"id":"...","name":"analyst",...}],
"groups": [{"id":"...","name":"data-team",...}],
"pools": [{"id":"...","tenantId":"...","poolId":"<pool-uuid>",...}],
"tablePerms": [
{"id":"...","roleId":"...","catalogName":"tpch","schemaName":"tpch1","tableName":"customer","verb":"SELECT",...}
]
}

tablePerms lists every permission reachable through the user's direct roles and through roles inherited via group membership. This is the set the ACL gate evaluates at statement time.

In the admin UI: click a user in the Users page to open the effective-permissions panel.


Reference: list endpoints

ResourcePath
UsersGET /api/user/list?tenant=<name> (omit tenant to list all users including superusers)
RolesGET /api/role/list?tenant=<name>
Role permissionsGET /api/role/permission/list?roleId=<id>
GroupsGET /api/group/list?tenant=<name>
Group role membershipsGET /api/membership/group-role/list?groupId=<id>
Pool permissionsGET /api/pool/permission/list?tenant=<name>&userId=<id>&groupId=<id>