Fixes #9482: A user could be assigned to a parent group if he is already assigned to a subgroup.
This commit is contained in:
parent
c70b4eaade
commit
cc2bb96abc
7 changed files with 93 additions and 6 deletions
|
@ -751,7 +751,7 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMemberOf(GroupModel group) {
|
public boolean isMemberOf(GroupModel group) {
|
||||||
return getGroupsStream().anyMatch(Predicate.isEqual(group));
|
return RoleUtils.isDirectMember(getGroupsStream(),group);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Stream<GroupModel> getLDAPGroupMappingsConverted() {
|
protected Stream<GroupModel> getLDAPGroupMappingsConverted() {
|
||||||
|
|
|
@ -392,7 +392,7 @@ public class UserAdapter implements UserModel.Streams, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void joinGroup(GroupModel group) {
|
public void joinGroup(GroupModel group) {
|
||||||
if (isMemberOf(group)) return;
|
if (RoleUtils.isDirectMember(getGroupsStream(), group)) return;
|
||||||
joinGroupImpl(group);
|
joinGroupImpl(group);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,6 +255,7 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void joinGroup(GroupModel group) {
|
public void joinGroup(GroupModel group) {
|
||||||
|
if (RoleUtils.isDirectMember(getGroupsStream(), group)) return;
|
||||||
entity.addGroupsMembership(group.getId());
|
entity.addGroupsMembership(group.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.stream.Collectors;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.models.utils.UserModelDelegate;
|
import org.keycloak.models.utils.UserModelDelegate;
|
||||||
|
|
||||||
import static org.keycloak.common.util.ObjectUtil.isEqualOrBothNull;
|
import static org.keycloak.common.util.ObjectUtil.isEqualOrBothNull;
|
||||||
|
@ -170,7 +171,7 @@ public class UpdateOnlyChangeUserModelDelegate extends UserModelDelegate {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void joinGroup(GroupModel group) {
|
public void joinGroup(GroupModel group) {
|
||||||
if (!isMemberOf(group)) {
|
if (!RoleUtils.isDirectMember(getGroupsStream(),group)) {
|
||||||
delegate.joinGroup(group);
|
delegate.joinGroup(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ public class UpdateOnlyChangeUserModelDelegate extends UserModelDelegate {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leaveGroup(GroupModel group) {
|
public void leaveGroup(GroupModel group) {
|
||||||
if (isMemberOf(group)) {
|
if (RoleUtils.isDirectMember(getGroupsStream(),group)) {
|
||||||
delegate.leaveGroup(group);
|
delegate.leaveGroup(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,16 @@ public class RoleUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param groups
|
||||||
|
* @param targetGroup
|
||||||
|
* @return true if targetGroup is in groups directly
|
||||||
|
*/
|
||||||
|
public static boolean isDirectMember(Stream<GroupModel> groups, GroupModel targetGroup) {
|
||||||
|
return groups.anyMatch(g -> targetGroup.getId().equals(g.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param roles
|
* @param roles
|
||||||
* @param targetRole
|
* @param targetRole
|
||||||
|
|
|
@ -50,6 +50,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
@ -943,7 +944,7 @@ public class UserResource {
|
||||||
throw new NotFoundException("Group not found");
|
throw new NotFoundException("Group not found");
|
||||||
}
|
}
|
||||||
auth.groups().requireManageMembership(group);
|
auth.groups().requireManageMembership(group);
|
||||||
if (!user.isMemberOf(group)){
|
if (!RoleUtils.isDirectMember(user.getGroupsStream(),group)){
|
||||||
user.joinGroup(group);
|
user.joinGroup(group);
|
||||||
adminEvent.operation(OperationType.CREATE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(session.getContext().getUri()).success();
|
adminEvent.operation(OperationType.CREATE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(session.getContext().getUri()).success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3057,4 +3057,78 @@ public class UserTest extends AbstractAdminTest {
|
||||||
testRealm().roles().get("realm-role").remove();
|
testRealm().roles().get("realm-role").remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Test for #9482
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void joinParentGroupAfterSubGroup() {
|
||||||
|
String username = "user-with-sub-and-parent-group";
|
||||||
|
String parentGroupName = "parent-group";
|
||||||
|
String subGroupName = "sub-group";
|
||||||
|
|
||||||
|
UserRepresentation userRepresentation = UserBuilder.create().username(username).build();
|
||||||
|
|
||||||
|
GroupRepresentation subGroupRep = GroupBuilder.create().name(subGroupName).build();
|
||||||
|
GroupRepresentation parentGroupRep = GroupBuilder.create().name(parentGroupName).subGroups(List.of(subGroupRep)).build();
|
||||||
|
|
||||||
|
try (Creator<UserResource> u = Creator.create(realm, userRepresentation);
|
||||||
|
Creator<GroupResource> subgroup = Creator.create(realm, subGroupRep);
|
||||||
|
Creator<GroupResource> parentGroup = Creator.create(realm, parentGroupRep)) {
|
||||||
|
|
||||||
|
UserResource user = u.resource();
|
||||||
|
|
||||||
|
//when
|
||||||
|
user.joinGroup(subgroup.id());
|
||||||
|
List<GroupRepresentation> obtainedGroups = realm.users().get(u.id()).groups();
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertEquals(1, obtainedGroups.size());
|
||||||
|
assertEquals(subGroupName, obtainedGroups.get(0).getName());
|
||||||
|
|
||||||
|
//when
|
||||||
|
user.joinGroup(parentGroup.id());
|
||||||
|
obtainedGroups = realm.users().get(u.id()).groups();
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertEquals(2, obtainedGroups.size());
|
||||||
|
assertEquals(parentGroupName, obtainedGroups.get(0).getName());
|
||||||
|
assertEquals(subGroupName, obtainedGroups.get(1).getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void joinSubGroupAfterParentGroup() {
|
||||||
|
String username = "user-with-sub-and-parent-group";
|
||||||
|
String parentGroupName = "parent-group";
|
||||||
|
String subGroupName = "sub-group";
|
||||||
|
|
||||||
|
UserRepresentation userRepresentation = UserBuilder.create().username(username).build();
|
||||||
|
GroupRepresentation subGroupRep = GroupBuilder.create().name(subGroupName).build();
|
||||||
|
GroupRepresentation parentGroupRep = GroupBuilder.create().name(parentGroupName).subGroups(List.of(subGroupRep)).build();
|
||||||
|
|
||||||
|
try (Creator<UserResource> u = Creator.create(realm, userRepresentation);
|
||||||
|
Creator<GroupResource> subgroup = Creator.create(realm, subGroupRep);
|
||||||
|
Creator<GroupResource> parentGroup = Creator.create(realm, parentGroupRep)) {
|
||||||
|
|
||||||
|
UserResource user = u.resource();
|
||||||
|
|
||||||
|
//when
|
||||||
|
user.joinGroup(parentGroup.id());
|
||||||
|
List<GroupRepresentation> obtainedGroups = realm.users().get(u.id()).groups();
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertEquals(1, obtainedGroups.size());
|
||||||
|
assertEquals(parentGroupName, obtainedGroups.get(0).getName());
|
||||||
|
|
||||||
|
//when
|
||||||
|
user.joinGroup(subgroup.id());
|
||||||
|
obtainedGroups = realm.users().get(u.id()).groups();
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertEquals(2, obtainedGroups.size());
|
||||||
|
assertEquals(parentGroupName, obtainedGroups.get(0).getName());
|
||||||
|
assertEquals(subGroupName, obtainedGroups.get(1).getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue