Skip to main content
Version: v5.6.1

Group

Included from Basic tier

Groups are served on three distinct surfaces. JSON:API type: group_members (one group's membership). The admin local-groups surface is a plain-JSON payload, documented honestly below; the group catalog is the v2 GroupService.

The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.

Two kinds of group exist: provider snapshot groups — each identity provider connection's directory/IdP groups as captured at its users' sign-ins (federated_group_snapshotsretired the old ldap_directory_entries mirror and its GET /api/ui_support/ldap_groups route and refresh) — and local groups, entities OpsChain itself stores (local_groups, with memberships in local_group_members: a local group may exist with zero members, and the listing unions the entity rows with any membership-derived names older replicas wrote). A principal's effective groups are the union of both (snapshot names namespaced <code>:<group> plus whatever the mapping entries grant), and authorisation policy assignments bind to the resulting group names.

group_members — one group's membership (JSON:API)​

GET /api/ui_support/group_members/{group_name} (upstream UISupportController#group_members) — deprecated in the v1 OpenAPI document (no caller; the v2 GroupService.ListGroups serves each group's members), now answering from the provider snapshots:

FieldMeaning
idThe group name (the retired mirror's constructed DN went with the mirror).
attributes.membersThe member usernames whose provider snapshot names the group, sorted ascending; [] when none do.

Local groups — the admin surface (plain JSON, not JSON:API)​

Admin-gated, PostgreSQL-store compositions only:

VerbPathBehaviour
GET/api/admin/groups{"groups": [{"groupname": "...", "source": "local", "members": ["user", …], "member_accounts": [{"user_id": "...", "username": "...", "provider": "local|ldap:<code>|…"}, …]}, …]} — every local group, group then member ordered, empty groups included. members keeps the original username-only shape; member_accounts carries each membership's provider (an open provider-instance string), since membership is per-account and a same-named local/ldap pair holds two distinguishable rows, and the membership's user_id. source names the group's source instance — always local on this surface, the same key vocabulary as the v2 group catalog's source, so badges, filters and grouped views share one naming. Takes the q/limit filter vocabulary.
POST/api/admin/groupsBody {"groupname": "..."} creates an empty local group (members optional at creation; 1–255 characters). 201 with the group document; a duplicate name is a named 422. Emits api:groups:create.
DELETE/api/admin/groups/{groupname}Deletes the group and its memberships (204; 404 when unknown) — refused with a named 422 listing the group-named policy assignments while any reference the group (remove the assignments first). Emits api:groups:delete.
POST/api/admin/groups/{groupname}/membersBody {"username": "..."} (plus optional ?provider=<auth_provider> for a same-named pair) adds the user to the local group, creating the group if needed. 204. Service accounts refuse with a named 422 — machine identities take their authority from trust grants, not groups. Emits api:groups:add_member.
DELETE/api/admin/groups/{groupname}/members/{username}Removes the membership (optional ?provider= selects the account of a pair); 404 ("user … is not a member of local group …") when it does not exist. 204. Emits api:groups:remove_member.

The CLI family is opschain admin groups … (list, create, delete, add-member, remove-member) — riding the v2 GroupService verbs below, with the username arguments resolved to user ids through UserService.GetUser (opschain docs cli/admin-groups).

The v2 group catalog — GroupService​

POST /api/v2/opschain.api.v2.GroupService/ListGroups serves both kinds in one typed, cursor-paginated catalog, snapshot rows first: a snapshot row carries the connection key as source (the catalog names each group's source instance), <code>:<group> as id, the bare group name as cn, and the member usernames/accounts whose snapshot names it; a local row (source: "local") carries the group name and its member usernames. Every row carries member_accounts — each membership's user_id, username and provider (membership is per user id, so a same-named local/ldap pair holds two distinguishable rows and federated shadow accounts are addressable). Any authenticated caller (the v1 ui_support posture); PostgreSQL compositions only — the embedded store answers the typed postgres posture. Directory freshness, import and leaver detection ride IdentityProviderService.SweepIdentityProvider (the retired RefreshDirectory mirror refresh's successor). See opschain docs api-v2.

Local group management — the v2 write verbs​

All four sit behind the /admin updatable determination (permission_denied on deny) and operate on local groups only — snapshot groups are read-only provider facts, managed at the provider. A local group may share a provider group's bare name: the catalog namespaces snapshot ids by connection key, so nothing collides (that is the documented way a mirror account inherits locally).

VerbBehaviour
GroupService.CreateGroupname (1–255 characters) creates an empty local group; a duplicate name is a named invalid_argument. Emits api:groups:create.
GroupService.DeleteGroupid (the group name) deletes the group and its memberships; not_found when unknown — refused with a named invalid_argument listing the group-named policy assignments while any reference the group (remove the assignments first). Emits api:groups:destroy.
GroupService.AddMembergroup_id + user_id adds the member — keyed by user id, provider-agnostic, so ldap and federated shadow accounts need no username/provider disambiguation. The group must exist; a service account refuses a named invalid_argument; adding an existing member is an idempotent no-op. The membership takes effect on the member's next request (the shared group resolver reads local memberships per request). Emits api:groups:member_added on a real add.
GroupService.RemoveMembergroup_id + user_id removes the membership; removing a non-member is an idempotent no-op. Emits api:groups:member_removed on a real removal.

The membership verbs answer the group after the write, member_accounts included.

  • opschain docs identity — how directory and local groups combine into a principal's effective groups.
  • opschain docs resources/user — the member side.