You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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) {
thrownewModelValidationException("Cannot access organization related group via non Organization API.");
}
The read path does not enforce it. AbstractUserModelSchema.getAttributeValue:
if ("groups".equals(name)) {
Permissionspermissions = session.getContext().getPermissions();
if (permissions.hasPermission(model, AdminPermissionsSchema.USERS_RESOURCE_TYPE, AdminPermissionsSchema.VIEW)) {
returnmodel.getGroupsStream()
.filter(this::canViewGroup)
.toList();
}
returnList.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?
Enable features organizations and scim-api; enable organizations for the realm.
Create an organization and add a user as a member.
Optionally create an organization group via POST /admin/realms/{realm}/organizations/{orgId}/groups and add the user to it.
Authenticate a confidential client whose service account has view-users.
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.
Describe the bug
Organization groups (and the organization's internal backing group) are exposed through the
groupsattribute of the SCIMUsersresource.This is the same boundary that #50310 / #50311 enforced for the
Groupsresource, but the user read path was not covered by that change.The invariant is enforced in three other places:
GroupResourceTypeProvider.getModelreturnsnullfor any non-REALMgroup (added by Enforce organization groups not accessible from SCIM API #50311)GroupResourceTypeProvidersearch filterstype = REALMUserCoreModelSchema.checkGroupMembershipPermission:The read path does not enforce it.
AbstractUserModelSchema.getAttributeValue:model.getGroupsStream()returns every group the user belongs to, includingType.ORGANIZATIONgroups. The only filter iscanViewGroup, which resolves throughDefaultPermissions.evaluateGroupPermissiontoGroupPermissionsV2.canView(group)— and that short-circuits totruefor any caller holdingview-usersormanage-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=groupsreturns onlyType.REALMgroups. Organization groups and the organization's internal group are not exposed through the SCIM API, consistent with theGroupsresource and with the user write path.Actual behavior
The response
groupsarray includes the organization's internal backing group and any organization groups the user is a member of.How to Reproduce?
organizationsandscim-api; enable organizations for the realm.POST /admin/realms/{realm}/organizations/{orgId}/groupsand add the user to it.view-users.GET /realms/{realm}/scim/v2/Users/{userId}?attributes=groupsThe organization's internal group (and any organization groups) appear in the returned
groupsarray. The correspondingGET /realms/{realm}/scim/v2/Groups/{orgGroupId}correctly returns 404.Anything else?
Note that
groupsis declaredRETURNED_REQUEST, so it is only returned when explicitly requested viaattributes=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.ORGANIZATIONgroups? The error message on the write path implies all of them.Affected files:
scim/model/src/main/java/org/keycloak/scim/model/user/AbstractUserModelSchema.javaRelated: #50310, #50311, #47536