KEYCLOAK-13369 Not possible to move groups in admin console
This commit is contained in:
parent
61fd66e107
commit
3e82473a90
6 changed files with 86 additions and 3 deletions
|
@ -997,7 +997,8 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
listInvalidations.add(realm.getId());
|
listInvalidations.add(realm.getId());
|
||||||
invalidateGroup(group.getId(), realm.getId(), true);
|
invalidateGroup(group.getId(), realm.getId(), true);
|
||||||
if (toParent != null) invalidateGroup(toParent.getId(), realm.getId(), false); // Queries already invalidated
|
if (toParent != null) invalidateGroup(toParent.getId(), realm.getId(), false); // Queries already invalidated
|
||||||
invalidationEvents.add(GroupAddedEvent.create(group.getId(), realm.getId()));
|
String parentId = toParent == null ? null : toParent.getId();
|
||||||
|
invalidationEvents.add(GroupAddedEvent.create(group.getId(), parentId, realm.getId()));
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,12 @@ public class GroupAddedEvent extends InvalidationEvent implements RealmCacheInva
|
||||||
|
|
||||||
private String groupId;
|
private String groupId;
|
||||||
private String realmId;
|
private String realmId;
|
||||||
|
private String parentId;
|
||||||
|
|
||||||
public static GroupAddedEvent create(String groupId, String realmId) {
|
public static GroupAddedEvent create(String groupId, String parentId, String realmId) {
|
||||||
GroupAddedEvent event = new GroupAddedEvent();
|
GroupAddedEvent event = new GroupAddedEvent();
|
||||||
event.realmId = realmId;
|
event.realmId = realmId;
|
||||||
|
event.parentId = parentId;
|
||||||
event.groupId = groupId;
|
event.groupId = groupId;
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
@ -57,18 +59,23 @@ public class GroupAddedEvent extends InvalidationEvent implements RealmCacheInva
|
||||||
@Override
|
@Override
|
||||||
public void addInvalidations(RealmCacheManager realmCache, Set<String> invalidations) {
|
public void addInvalidations(RealmCacheManager realmCache, Set<String> invalidations) {
|
||||||
realmCache.groupQueriesInvalidations(realmId, invalidations);
|
realmCache.groupQueriesInvalidations(realmId, invalidations);
|
||||||
|
if (parentId != null) {
|
||||||
|
invalidations.add(parentId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ExternalizerImpl implements Externalizer<GroupAddedEvent> {
|
public static class ExternalizerImpl implements Externalizer<GroupAddedEvent> {
|
||||||
|
|
||||||
private static final int VERSION_1 = 1;
|
private static final int VERSION_1 = 1;
|
||||||
|
private static final int VERSION_2 = 2;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeObject(ObjectOutput output, GroupAddedEvent obj) throws IOException {
|
public void writeObject(ObjectOutput output, GroupAddedEvent obj) throws IOException {
|
||||||
output.writeByte(VERSION_1);
|
output.writeByte(VERSION_2);
|
||||||
|
|
||||||
MarshallUtil.marshallString(obj.groupId, output);
|
MarshallUtil.marshallString(obj.groupId, output);
|
||||||
MarshallUtil.marshallString(obj.realmId, output);
|
MarshallUtil.marshallString(obj.realmId, output);
|
||||||
|
MarshallUtil.marshallString(obj.parentId, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -76,6 +83,8 @@ public class GroupAddedEvent extends InvalidationEvent implements RealmCacheInva
|
||||||
switch (input.readByte()) {
|
switch (input.readByte()) {
|
||||||
case VERSION_1:
|
case VERSION_1:
|
||||||
return readObjectVersion1(input);
|
return readObjectVersion1(input);
|
||||||
|
case VERSION_2:
|
||||||
|
return readObjectVersion2(input);
|
||||||
default:
|
default:
|
||||||
throw new IOException("Unknown version");
|
throw new IOException("Unknown version");
|
||||||
}
|
}
|
||||||
|
@ -88,5 +97,14 @@ public class GroupAddedEvent extends InvalidationEvent implements RealmCacheInva
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GroupAddedEvent readObjectVersion2(ObjectInput input) throws IOException, ClassNotFoundException {
|
||||||
|
GroupAddedEvent res = new GroupAddedEvent();
|
||||||
|
res.groupId = MarshallUtil.unmarshallString(input);
|
||||||
|
res.realmId = MarshallUtil.unmarshallString(input);
|
||||||
|
res.parentId = MarshallUtil.unmarshallString(input);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,7 @@ public class GroupResource {
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
throw new NotFoundException("Could not find child by id");
|
throw new NotFoundException("Could not find child by id");
|
||||||
}
|
}
|
||||||
|
realm.moveGroup(child, group);
|
||||||
adminEvent.operation(OperationType.UPDATE);
|
adminEvent.operation(OperationType.UPDATE);
|
||||||
} else {
|
} else {
|
||||||
child = realm.createGroup(rep.getName(), group);
|
child = realm.createGroup(rep.getName(), group);
|
||||||
|
|
|
@ -150,6 +150,7 @@ public class GroupsResource {
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
throw new NotFoundException("Could not find child by id");
|
throw new NotFoundException("Could not find child by id");
|
||||||
}
|
}
|
||||||
|
realm.moveGroup(child, null);
|
||||||
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri());
|
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri());
|
||||||
} else {
|
} else {
|
||||||
child = realm.createGroup(rep.getName());
|
child = realm.createGroup(rep.getName());
|
||||||
|
|
|
@ -485,6 +485,45 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
assertThat(group.getAttributes(), hasEntry(is("attr3"), contains("attrval2")));
|
assertThat(group.getAttributes(), hasEntry(is("attr3"), contains("attrval2")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moveGroups() {
|
||||||
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
|
|
||||||
|
// Create 2 top level groups "mygroup1" and "mygroup2"
|
||||||
|
GroupRepresentation group = GroupBuilder.create()
|
||||||
|
.name("mygroup1")
|
||||||
|
.build();
|
||||||
|
GroupRepresentation group1 = createGroup(realm, group);
|
||||||
|
|
||||||
|
group = GroupBuilder.create()
|
||||||
|
.name("mygroup2")
|
||||||
|
.build();
|
||||||
|
GroupRepresentation group2 = createGroup(realm, group);
|
||||||
|
|
||||||
|
// Move "mygroup2" as child of "mygroup1" . Assert it was moved
|
||||||
|
Response response = realm.groups().group(group1.getId()).subGroup(group2);
|
||||||
|
Assert.assertEquals(204, response.getStatus());
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
// Assert "mygroup2" was moved
|
||||||
|
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
||||||
|
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
||||||
|
assertNames(group1.getSubGroups(), "mygroup2");
|
||||||
|
Assert.assertEquals("/mygroup1/mygroup2", group2.getPath());
|
||||||
|
|
||||||
|
|
||||||
|
// Move "mygroup2" back under parent
|
||||||
|
response = realm.groups().add(group2);
|
||||||
|
Assert.assertEquals(204, response.getStatus());
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
// Assert "mygroup2" was moved
|
||||||
|
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
||||||
|
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
||||||
|
assertTrue(group1.getSubGroups().isEmpty());
|
||||||
|
Assert.assertEquals("/mygroup2", group2.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void groupMembership() {
|
public void groupMembership() {
|
||||||
RealmResource realm = adminClient.realms().realm("test");
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.keycloak.admin.client.resource.GroupsResource;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||||
|
import org.keycloak.testsuite.util.GroupBuilder;
|
||||||
|
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -17,6 +18,7 @@ import java.util.List;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.testsuite.Assert.assertNames;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -131,6 +133,27 @@ public class GroupInvalidationClusterTest extends AbstractInvalidationClusterTes
|
||||||
assertEquals(parentGroup.getPath() + "/" + group.getName(), group.getPath());
|
assertEquals(parentGroup.getPath() + "/" + group.getName(), group.getPath());
|
||||||
|
|
||||||
verifyEntityUpdateDuringFailover(group, backendFailover);
|
verifyEntityUpdateDuringFailover(group, backendFailover);
|
||||||
|
parentGroup = readEntityOnCurrentFailNode(parentGroup);
|
||||||
|
|
||||||
|
// Add new child
|
||||||
|
GroupRepresentation childGroup2 = GroupBuilder.create()
|
||||||
|
.name("childGroup2")
|
||||||
|
.build();
|
||||||
|
r = entityResourceOnCurrentFailNode(parentGroup).subGroup(childGroup2);
|
||||||
|
String childGroup2Id = ApiUtil.getCreatedId(r);
|
||||||
|
childGroup2.setId(childGroup2Id);
|
||||||
|
|
||||||
|
|
||||||
|
parentGroup = readEntityOnCurrentFailNode(parentGroup);
|
||||||
|
verifyEntityUpdateDuringFailover(parentGroup, backendFailover);
|
||||||
|
|
||||||
|
// Verify same child groups on both nodes
|
||||||
|
GroupRepresentation parentGroupOnOtherNode = readEntityOnCurrentFailNode(parentGroup);
|
||||||
|
assertNames(parentGroup.getSubGroups(), group.getName(), "childGroup2");
|
||||||
|
assertNames(parentGroupOnOtherNode.getSubGroups(), group.getName(), "childGroup2");
|
||||||
|
|
||||||
|
// Remove childGroup2
|
||||||
|
deleteEntityOnCurrentFailNode(childGroup2);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue