Skip to main content
Version: Next

Groups & Permissions

Permissions are never granted to a person directly — only to a group. A person's real access is the union of every grant held by every group they belong to. This page is the task-oriented walkthrough: creating a group, granting a permission, and reading the result back in the Authorization Matrix. For the full permission-string catalog and the resolver's precedence rules, see Security → Permissions Reference; for the access model itself (seat types, groups, grants), the reference page is Security → Users and Groups.

Creating a group​

From the admin navigation, open Groups, then New Group. A group needs only a name and an optional description.

The same action is available over the API:

POST/api/groups🔒 auth

Body: {name: string, description?: string}. Requires org.admin. Returns the created group.

A group can also be LDAP-mapped instead of local, syncing its membership from a directory server rather than the membership endpoints below — see Security → LDAP Configuration for the current status of that path.

Granting a permission​

Open a group's detail page and add a permission, or call the API directly:

POST/api/groups/:id/permissions🔒 auth

Body: {permission_type: string, target_id?: string}. Requires org.admin.

permission_type is a <resource>.<action> string — dashboard.view, project.edit, and so on. The full catalog, with what each string unlocks, lives on the Permissions Reference page.

target_id is what decides org-wide vs. per-resource:

  • Omit it (or send null) — the grant is organization-wide. Every group member gets that permission on every matching resource, present and future.
  • Set it to a specific resource id (e.g. one dashboard_id) — the grant applies to that resource only. A group can hold the same permission_type scoped to several different resources, one row each.

The resolver checks the specific target_id first and falls back to an org-wide (target_id IS NULL) grant, so a narrower and a broader grant for the same permission can coexist without conflicting.

Revoke a grant the same way, in reverse:

DELETE/api/groups/:id/permissions/:permission_id🔒 auth

Removes one specific permission grant from a group.

Membership​

POST/api/groups/:id/members🔒 auth

Body: {user_id: string}. Adds a user to the group.

DELETE/api/groups/:id/members/:user_id🔒 auth

Removes a user from the group. They lose any permission granted only through it.

A user's total access is always the union across every group they're a member of — there's no "deny" grant that subtracts access, so adding someone to a second group can only add capability, never remove it.

The Authorization Matrix​

Open the Authorization Matrix page from the admin navigation for a single grid view of every group against every permission type it holds — rows are groups, columns are permission types, and a check in a cell means that group holds the permission, either organization-wide or on at least one specific target.

A few things worth knowing before you use it:

  • Reading it is open to any org member; only an org admin sees the controls to change anything.
  • Only the org-wide toggle is editable from the matrix itself. Clicking a cell grants or revokes the target_id IS NULL row for that group/permission pair. Per-resource grants (scoped to one specific dashboard, project, or dataset) are added from the group's own detail page, where you pick the target explicitly.
  • New permission-type columns appear automatically. To introduce a permission string the matrix hasn't seen before, grant it once from a group's detail page — it shows up as a column on the matrix's next load.
  • Columns are colour-coded by permission family (org.*, project.*, dashboard.*, dataset.*, feature.*) purely as a reading aid — the colour carries no access-control meaning of its own.

Resolution order, in brief​

When a request needs dashboard.edit on dashboard 7, the resolver checks, in order: superadmin → admin-role shortcut → the requesting user's implicit seat-level grant → a group grant scoped to target_id='7' → a group grant with target_id IS NULL → otherwise, 403. The full walkthrough, including the exact 403 body shape and how legacy roles migrate onto this model, lives on Permissions Reference.