CodamAIDocs
Topicdone

Manage groups and members

Create, change, delete, add and remove members, take over existing Keycloak groups.

Variants
createchangedeleteadd memberremove memberimport

What this is about

You manage groups through the admin API under /cias/admin/groups. The user interfaces for this call exactly these endpoints, see The admin interface.

The endpoints

CallDoes
GET /cias/admin/groups?query=…&page=0&size=50one page of groups, sorted by key. query searches key, name and description
GET /cias/admin/groups/{key}one group
POST /cias/admin/groupscreate a group
PUT /cias/admin/groups/{key}change name, description, roles and default group
DELETE /cias/admin/groups/{key}delete a group
GET /cias/admin/groups/{key}/members?page=0&size=50one page of members, as IDs of user records
POST /cias/admin/groups/{key}/membersadd members
POST /cias/admin/groups/{key}/members/removeremove members
GET /cias/admin/groups/of-user/{userId}all groups of one person, not paged
POST /cias/admin/groups/importtake over existing Keycloak groups
POST /cias/admin/groups/reconcilestart a reconciliation with Keycloak, see Reconciliation with Keycloak

Lists always come in pages: items, page, size, total, hasMore. Without a value you get 50 rows, at most 500. If you ask for more, you get 400 instead of silently fewer rows.

The checks

May this call change the group?
  1. CIAS
    Platform administrator
    Is the caller logged in, and does their token carry a role that counts as platform administrator?
    ↳ no 403 cias.authorization.denied, text not permitted
  2. CIAS
    Group
    Does the group exist (for everything except create)? Is the key still free (for create)?
    ↳ no 404 cias.authorization.group-not-found or 409 cias.authorization.group-key-in-use
  3. CIAS
    Roles
    Is every named role in the catalog?
    ↳ no 404 cias.authorization.role-not-found
  4. CIAS
    Keycloak
    When taking: did Keycloak accept the change?
    ↳ no 503, CIAS stays unchanged
  5. Change saved

The 403 looks exactly like every other refusal of the permission check. CIAS writes the exact reason to the log.

Create a group

Request
POST /cias/admin/groups
Authorization: Bearer <token of a platform administrator>
{
  "key": "support",
  "name": "Support 1st Level",
  "description": "Everyone in phone support",
  "roles": [
    { "client": null, "key": "user" },
    { "client": "cdms-backend", "key": "customer-read" },
    { "client": "crms-backend", "key": "ticket-edit" }
  ],
  "defaultGroup": false
}
Response
HTTP 200
{
  "key": "support",
  "name": "Support 1st Level",
  "description": "Everyone in phone support",
  "roles": [
    { "client": null, "key": "user", "displayName": "User",
      "scope": "PLATFORM", "effectiveInEveryTenant": true },
    { "client": "cdms-backend", "key": "customer-read", "displayName": "Read customers",
      "scope": "PLATFORM", "effectiveInEveryTenant": false },
    { "client": "crms-backend", "key": "ticket-edit", "displayName": "Edit tickets",
      "scope": "TENANT", "effectiveInEveryTenant": false }
  ],
  "memberCount": 0,
  "defaultGroup": false,
  "syncState": "SYNCHRONIZED"
}
FieldMeaning
keyrequired. Fixed forever, the name of the group in Keycloak
nameoptional, otherwise the key
rolesthe complete list. client: null is a realm role. If the list is missing, the group carries no roles
defaultGroupif missing, it is false. So a client that does not know the field never creates a default group by accident
effectiveInEveryTenanttrue only for realm roles. false means: this role drops out under a tenant with roles of its own, see Group roles under dynamic tenants
syncStateSYNCHRONIZED when the copy is in Keycloak, otherwise PENDING

A freshly created group has no members yet. So it does not give anyone anything yet.

The six variants

What you can do with a group

When: POST /cias/admin/groups

  1. 1
    CIAS
    checks key and roles, saves the group as PENDING
  2. 2
    CIAS→Keycloak
    creates the group in the realm, marked with cias-managed=true, sets roles and default group
  3. 3
    CIAS
    stores the Keycloak ID, sets SYNCHRONIZED
    Result: 200 with the group. If Keycloak cannot be reached, still 200, but syncState: PENDING

When: PUT /cias/admin/groups/{key}, path and key in the body must match, otherwise 400

  1. 1
    CIAS→Keycloak
    takes away first: roles that are no longer included and, if needed, the default group property
  2. 2
    CIAS
    saves name, description, roles, default group, sets PENDING
  3. 3
    CIAS→Keycloak
    then adds: new roles, default group if needed; sets SYNCHRONIZED
    Result: 200. If the first Keycloak step fails: 503, nothing changed. If the last one fails: 200 with PENDING

When: DELETE /cias/admin/groups/{key}

  1. 1
    CIAS→Keycloak
    deletes the copy of the group
  2. 2
    CIAS
    deletes the group and its memberships
    Result: 204. Members keep their accounts and only lose what the group gave them. If Keycloak fails: 503, the group stays

When: POST …/members with { "userIds": ["5c9e…", "7a1b…"] }

  1. 1
    CIAS
    saves the memberships; anyone who is already a member simply stays one
  2. 2
    CIAS→Keycloak
    adds each person to the copy of the group
    Result: 200 with the group. If Keycloak does not work for all of them: still 200, but PENDING

When: POST …/members/remove with { "userIds": ["5c9e…"] }

  1. 1
    CIAS→Keycloak
    removes each person from the copy of the group
  2. 2
    CIAS
    deletes the memberships
    Result: 200. If Keycloak fails: 503, CIAS stays unchanged

When: POST /cias/admin/groups/import, no body

Takes over the groups that already exist in Keycloak and that CIAS does not manage yet. Details in the next section.

Result: 200 with a report

Changing roles means: send the whole list

There is no “add role” and no “remove role”. With PUT you always send the complete list. You remove a role by sending the list without it. Whatever you leave out goes away, in Keycloak too, even if it was the last role of a module.

The key cannot be changed. If a group should be called something else, change name.

Members are user records

userIds are the IDs of the user records in CIAS, not the Keycloak sub. How to find them is described in The user record. The list must not be empty, otherwise 400. You can add or remove several people at once, just like ticking boxes in an interface.

Take over existing Keycloak groups

A realm that existed before CIAS often contains groups that someone created in the Keycloak console. They carry no cias-managed mark, so CIAS does not touch them. With the import, CIAS takes them over.

What the import does with each group
  1. 1
    CIAS→Keycloak
    reads all top-level groups without the cias-managed mark. Groups that Keycloak manages itself (attributes starting with kc., such as organization groups) are left out
  2. 2
    CIAS
    Does CIAS already manage a group with this key? Then it is left untouched
  3. 3
    CIAS
    Are all roles of the group in the catalog? Does every member have a user record in CIAS?
  4. 4
    CIAS
    no → the group is skipped, with the reason in the report
  5. 5
    CIAS
    saves the group with roles, members and default group, exactly as in Keycloak
  6. 6
    CIAS→Keycloak
    sets the mark cias-managed=true, nothing else
    Result: The group now belongs to CIAS. Roles and members are unchanged
Request
POST /cias/admin/groups/import
Response
HTTP 200
{
  "found": 4,
  "adopted": 2,
  "alreadyKnown": 1,
  "skipped": [
    { "group": "buchhaltung",
      "reason": "it carries 1 role(s) the CIAS catalogue does not know (erp:invoice-approve) — their module has to declare them first, or the next reconciliation would take them off the group" }
  ]
}

A group is taken over completely or not at all. Why so strict? Whatever CIAS manages, the reconciliation later writes back to Keycloak. If CIAS simply left out an unknown role, the next reconciliation would take it away from the group. If CIAS left out an unknown member, that person would be thrown out of the group. So: first have the module declare the missing roles and import the missing accounts, then repeat the import.

The import can safely be repeated. Groups that were taken over carry the mark and no longer show up in the next run. An interrupted run is finished the next time. alreadyKnown counts Keycloak groups without the mark whose name CIAS already uses for a group of its own. They stay untouched. The import does not take over subgroups, only the top level.

Errors at a glance

ResponseWhen
400 cias.authorization.invalid-requestkey missing, userIds empty, path and body name different groups, whitespace in a role name, page too large
403 cias.authorization.deniednot a platform administrator
404 cias.authorization.group-not-foundunknown group. GET /cias/admin/groups/{key} answers 404 without a body
404 cias.authorization.role-not-founda role is not in the catalog
409 cias.authorization.group-key-in-usethe key is already taken
503Keycloak cannot be reached, when taking

Pitfalls

Next

Sources in the code and the knowledge base
  • CIAS/cias-authorization – GroupAdminController (/cias/admin/groups), AuthorizationRestDtos (GroupRequest, GroupRoleRequest, MembersRequest, GroupResponse, GroupRoleResponse, PageResponse), AuthorizationExceptionHandler
  • CIAS/cias-authorization – GroupService (create, update, restrict, delete, addMembers, removeMembers, members, groupsOf), GroupProjection (push, remove, addMember, removeMember), DefineGroupCommand
  • CIAS/cias-authorization – GroupImportService, GroupImportReport
  • CIAS/cias-iam-keycloak – KeycloakGroupAdapter (listAdoptable, adopt)
  • CIAS/cias-kernel – Page (DEFAULT_SIZE 50, MAX_SIZE 500)
  • CIAS/cias-authorization/docs/adr – ADR-017, ADR-034
Search