Details
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
index 2a78a41..b4ed669 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
@@ -728,8 +728,8 @@ public class RealmCacheSession implements CacheRealmProvider {
RoleModel model = getDelegate().getRoleById(id, realm);
if (model == null) return null;
if (invalidations.contains(id)) return model;
- if (model.getContainer() instanceof ClientModel) {
- cached = new CachedClientRole(loaded, ((ClientModel) model.getContainer()).getId(), model, realm);
+ if (model.isClientRole()) {
+ cached = new CachedClientRole(loaded, model.getContainerId(), model, realm);
} else {
cached = new CachedRealmRole(loaded, model, realm);
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
index d81f95f..c391f3d 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
@@ -144,6 +144,21 @@ public class RoleAdapter implements RoleModel {
}
@Override
+ public boolean isClientRole() {
+ return cached instanceof CachedClientRole;
+ }
+
+ @Override
+ public String getContainerId() {
+ if (isClientRole()) {
+ CachedClientRole appRole = (CachedClientRole)cached;
+ return appRole.getClientId();
+ } else {
+ return realm.getId();
+ }
+ }
+
+ @Override
public RoleContainerModel getContainer() {
if (cached instanceof CachedRealmRole) {
return realm;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 6c081cb..5b7bc8b 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -222,7 +222,7 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
}
@Override
- public Set<RoleModel> getRealmScopeMappings() {
+ public Set<RoleModel> getRealmScopeMappings() {
Set<RoleModel> roleMappings = getScopeMappings();
Set<RoleModel> appRoles = new HashSet<>();
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
index 61cf70c..135d6f4 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -137,6 +137,17 @@ public class RoleAdapter implements RoleModel, JpaModel<RoleEntity> {
}
@Override
+ public boolean isClientRole() {
+ return role.isClientRole();
+ }
+
+ @Override
+ public String getContainerId() {
+ if (isClientRole()) return role.getClient().getId();
+ else return realm.getId();
+ }
+
+ @Override
public RoleContainerModel getContainer() {
if (role.isClientRole()) {
return realm.getClientById(role.getClient().getId());
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
index 9af9729..ccc8330 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
@@ -134,6 +134,19 @@ public class RoleAdapter extends AbstractMongoAdapter<MongoRoleEntity> implement
}
@Override
+ public boolean isClientRole() {
+ return role.getClientId() != null;
+ }
+
+
+
+ @Override
+ public String getContainerId() {
+ if (isClientRole()) return role.getClientId();
+ else return role.getRealmId();
+ }
+
+ @Override
public RoleContainerModel getContainer() {
if (roleContainer == null) {
// Compute it
diff --git a/server-spi/src/main/java/org/keycloak/models/RoleModel.java b/server-spi/src/main/java/org/keycloak/models/RoleModel.java
index b9c24d0..ac88058 100755
--- a/server-spi/src/main/java/org/keycloak/models/RoleModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RoleModel.java
@@ -46,6 +46,10 @@ public interface RoleModel {
Set<RoleModel> getComposites();
+ boolean isClientRole();
+
+ String getContainerId();
+
RoleContainerModel getContainer();
boolean hasRole(RoleModel role);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
index 4edb420..2af9f2b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
@@ -144,6 +144,23 @@ public class ClientModelTest extends AbstractModelTest {
}
+ @Test
+ public void testCircularClientScopes() throws Exception {
+ ClientModel scoped1 = realm.addClient("scoped");
+ RoleModel role1 = scoped1.addRole("role1");
+ ClientModel scoped2 = realm.addClient("scoped2");
+ RoleModel role2 = scoped2.addRole("role2");
+ scoped1.addScopeMapping(role2);
+ scoped2.addScopeMapping(role1);
+ commit();
+ realm = session.realms().getRealmByName("original");
+
+ // this hit the circular cache and failed with a stack overflow
+ scoped1 = realm.getClientByClientId("scoped");
+
+
+ }
+
@Test
public void persist() {