keycloak-uncached

Details

diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.5.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.5.0.xml
index 4aee291..37d0250 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.5.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.5.0.xml
@@ -20,6 +20,8 @@
 
      <changeSet author="bburke@redhat.com" id="2.5.0">
          <customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.MigrateUserFedToComponent"/>
+
+         <addUniqueConstraint columnNames="NAME,PARENT_GROUP,REALM_ID" constraintName="SIBLING_NAMES" tableName="KEYCLOAK_GROUP"/>
      </changeSet>
 
     <changeSet author="hmlnarik@redhat.com" id="2.5.0-unicode-oracle">
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
index ea42335..398e036 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
@@ -48,6 +48,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.keycloak.services.ErrorResponse;
 
 /**
  * @author Bill Burke
@@ -138,6 +139,12 @@ public class GroupResource {
         if (group == null) {
             throw new NotFoundException("Could not find group by id");
         }
+        
+        for (GroupModel group : group.getSubGroups()) {
+            if (group.getName().equals(rep.getName())) {
+                return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'");
+            }
+        }
 
         Response.ResponseBuilder builder = Response.status(204);
         GroupModel child = null;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
index f670f57..9d01f35 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
@@ -39,6 +39,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
 import java.util.List;
+import org.keycloak.services.ErrorResponse;
 
 /**
  * @author Bill Burke
@@ -102,6 +103,12 @@ public class GroupsResource {
     public Response addTopLevelGroup(GroupRepresentation rep) {
         auth.requireManage();
 
+        for (GroupModel group : realm.getGroups()) {
+            if (group.getName().equals(rep.getName())) {
+                return ErrorResponse.exists("Top level group named '" + rep.getName() + "' already exists.");
+            }
+        }
+        
         GroupModel child = null;
         Response.ResponseBuilder builder = Response.status(204);
         if (rep.getId() != null) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
index 91d299f..79908d8 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
@@ -152,6 +152,32 @@ public class GroupTest extends AbstractGroupTest {
     }
 
     @Test
+    public void doNotAllowSameGroupNameAtSameLevel() throws Exception {
+        RealmResource realm = adminClient.realms().realm("test");
+        
+        GroupRepresentation topGroup = new GroupRepresentation();
+        topGroup.setName("top");
+        topGroup = createGroup(realm, topGroup);
+        
+        GroupRepresentation anotherTopGroup = new GroupRepresentation();
+        anotherTopGroup.setName("top");
+        Response response = realm.groups().add(anotherTopGroup);
+        assertEquals(409, response.getStatus()); // conflict status 409 - same name not allowed
+        
+        GroupRepresentation level2Group = new GroupRepresentation();
+        level2Group.setName("level2");
+        response = realm.groups().group(topGroup.getId()).subGroup(level2Group);
+        response.close();
+        assertEquals(201, response.getStatus()); // created status
+
+        GroupRepresentation anotherlevel2Group = new GroupRepresentation();
+        anotherlevel2Group.setName("level2");
+        response = realm.groups().group(topGroup.getId()).subGroup(anotherlevel2Group);
+        response.close();
+        assertEquals(409, response.getStatus()); // conflict status 409 - same name not allowed
+    }
+    
+    @Test
     public void createAndTestGroups() throws Exception {
         RealmResource realm = adminClient.realms().realm("test");
         {
@@ -179,7 +205,7 @@ public class GroupTest extends AbstractGroupTest {
         GroupRepresentation topGroup = new GroupRepresentation();
         topGroup.setName("top");
         topGroup = createGroup(realm, topGroup);
-
+        
         List<RoleRepresentation> roles = new LinkedList<>();
         roles.add(topRole);
         realm.groups().group(topGroup.getId()).roles().realmLevel().add(roles);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
index b23194a..18277d2 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
@@ -1173,7 +1173,9 @@ public class PermissionsTest extends AbstractKeycloakTest {
         }, Resource.USER, false);
         invoke(new InvocationWithResponse() {
             public void invoke(RealmResource realm, AtomicReference<Response> response) {
-                response.set(realm.groups().add(new GroupRepresentation()));
+                GroupRepresentation group = new GroupRepresentation();
+                group.setName("mygroup");
+                response.set(realm.groups().add(group));
             }
         }, Resource.USER, true);