Skip to content

SCIM: organization groups exposed via the groups attribute on Users #51590

Description

@sventorben

Describe the bug

Organization groups (and the organization's internal backing group) are exposed through the groups attribute of the SCIM Users resource.

This is the same boundary that #50310 / #50311 enforced for the Groups resource, but the user read path was not covered by that change.

The invariant is enforced in three other places:

  • GroupResourceTypeProvider.getModel returns null for any non-REALM group (added by Enforce organization groups not accessible from SCIM API #50311)
  • GroupResourceTypeProvider search filters type = REALM
  • The SCIM user write path throws explicitly in UserCoreModelSchema.checkGroupMembershipPermission:
    if (GroupModel.Type.ORGANIZATION.equals(group.getType()) && group.getOrganization() != null) {
        throw new ModelValidationException("Cannot access organization related group via non Organization API.");
    }

The read path does not enforce it. AbstractUserModelSchema.getAttributeValue:

if ("groups".equals(name)) {
    Permissions permissions = session.getContext().getPermissions();

    if (permissions.hasPermission(model, AdminPermissionsSchema.USERS_RESOURCE_TYPE, AdminPermissionsSchema.VIEW)) {
        return model.getGroupsStream()
                .filter(this::canViewGroup)
                .toList();
    }

    return List.of();
}

model.getGroupsStream() returns every group the user belongs to, including Type.ORGANIZATION groups. The only filter is canViewGroup, which resolves through DefaultPermissions.evaluateGroupPermission to GroupPermissionsV2.canView(group) — and that short-circuits to true for any caller holding view-users or manage-users. There is no group-type check on this path.

Because organization membership is itself membership in the organization's backing group, the internal org group is returned as well. That is the same class of exposure that was fixed for group search in commit 8e1b170 ("Searching for organization groups with populateHierarchy=true exposes internal org group").

Version

main (verified at 39896ae53a). The Groups-side fix in #50311 is present; this path is unchanged by it.

Expected behavior

GET /realms/{realm}/scim/v2/Users/{id}?attributes=groups returns only Type.REALM groups. Organization groups and the organization's internal group are not exposed through the SCIM API, consistent with the Groups resource and with the user write path.

Actual behavior

The response groups array includes the organization's internal backing group and any organization groups the user is a member of.

How to Reproduce?

  1. Enable features organizations and scim-api; enable organizations for the realm.
  2. Create an organization and add a user as a member.
  3. Optionally create an organization group via POST /admin/realms/{realm}/organizations/{orgId}/groups and add the user to it.
  4. Authenticate a confidential client whose service account has view-users.
  5. GET /realms/{realm}/scim/v2/Users/{userId}?attributes=groups

The organization's internal group (and any organization groups) appear in the returned groups array. The corresponding GET /realms/{realm}/scim/v2/Groups/{orgGroupId} correctly returns 404.

Anything else?

Note that groups is declared RETURNED_REQUEST, so it is only returned when explicitly requested via attributes=groups. The caller also already holds admin read rights over users, so the practical impact is limited — but it breaks an invariant that is explicitly asserted elsewhere and leaks an internal implementation object.

Filing publicly rather than through the security process, consistent with how #47536 was handled while SCIM is a preview feature.

Design question for the fix: filter only the organization's internal backing group, or all Type.ORGANIZATION groups? The error message on the write path implies all of them.

Affected files:

  • scim/model/src/main/java/org/keycloak/scim/model/user/AbstractUserModelSchema.java

Related: #50310, #50311, #47536

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions