Merge pull request #3573 from glavoie/KEYCLOAK-4003

KEYCLOAK-4003: Slow Infinispan RoleAdapter.hasRole() call.
This commit is contained in:
Marek Posolda 2016-12-06 09:49:42 +01:00 committed by GitHub
commit c8b22e71f0
6 changed files with 79 additions and 42 deletions

View file

@ -38,6 +38,7 @@ public class RoleAdapter implements RoleModel {
protected CachedRole cached;
protected RealmCacheSession cacheSession;
protected RealmModel realm;
protected Set<RoleModel> composites;
public RoleAdapter(CachedRole cached, RealmCacheSession session, RealmModel realm) {
this.cached = cached;
@ -132,15 +133,19 @@ public class RoleAdapter implements RoleModel {
@Override
public Set<RoleModel> getComposites() {
if (isUpdated()) return updated.getComposites();
Set<RoleModel> set = new HashSet<RoleModel>();
for (String id : cached.getComposites()) {
RoleModel role = realm.getRoleById(id);
if (role == null) {
throw new IllegalStateException("Could not find composite in role " + getName() + ": " + id);
if (composites == null) {
composites = new HashSet<RoleModel>();
for (String id : cached.getComposites()) {
RoleModel role = realm.getRoleById(id);
if (role == null) {
throw new IllegalStateException("Could not find composite in role " + getName() + ": " + id);
}
composites.add(role);
}
set.add(role);
}
return set;
return composites;
}
@Override
@ -171,11 +176,7 @@ public class RoleAdapter implements RoleModel {
@Override
public boolean hasRole(RoleModel role) {
if (this.equals(role)) return true;
if (!isComposite()) return false;
Set<RoleModel> visited = new HashSet<RoleModel>();
return KeycloakModelUtils.searchFor(role, this, visited);
return this.equals(role) || KeycloakModelUtils.searchFor(role, this);
}
@Override

View file

@ -128,11 +128,7 @@ public class RoleAdapter implements RoleModel, JpaModel<RoleEntity> {
@Override
public boolean hasRole(RoleModel role) {
if (this.equals(role)) return true;
if (!isComposite()) return false;
Set<RoleModel> visited = new HashSet<RoleModel>();
return KeycloakModelUtils.searchFor(role, this, visited);
return this.equals(role) || KeycloakModelUtils.searchFor(role, this);
}
@Override

View file

@ -172,11 +172,7 @@ public class RoleAdapter extends AbstractMongoAdapter<MongoRoleEntity> implement
@Override
public boolean hasRole(RoleModel role) {
if (this.equals(role)) return true;
if (!isComposite()) return false;
Set<RoleModel> visited = new HashSet<RoleModel>();
return KeycloakModelUtils.searchFor(role, this, visited);
return this.equals(role) || KeycloakModelUtils.searchFor(role, this);
}
public MongoRoleEntity getRole() {

View file

@ -177,16 +177,14 @@ public final class KeycloakModelUtils {
* @param visited set of already visited roles (used for recursion)
* @return true if "role" is descendant of "composite"
*/
public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
if (visited.contains(composite)) return false;
visited.add(composite);
Set<RoleModel> composites = composite.getComposites();
if (composites.contains(role)) return true;
for (RoleModel contained : composites) {
if (!contained.isComposite()) continue;
if (searchFor(role, contained, visited)) return true;
}
return false;
public static boolean searchFor(RoleModel role, RoleModel composite) {
return composite.isComposite() && (
composite.getComposites().contains(role) ||
composite.getComposites().stream()
.filter(x -> x.isComposite() && x.hasRole(role))
.findFirst()
.isPresent()
);
}
/**

View file

@ -66,18 +66,27 @@ public class CompositeRolesModelTest extends AbstractModelTest {
@Test
public void testComposites() {
Set<RoleModel> requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_USER");
Assert.assertEquals(2, requestedRoles.size());
Assert.assertEquals(5, requestedRoles.size());
assertContains("APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_ROLE", requestedRoles);
assertContains("APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_CHILD", requestedRoles);
assertContains("APP_COMPOSITE_APPLICATION", "APP_ROLE_2", requestedRoles);
assertContains("APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
assertContains("realm", "REALM_ROLE_1", requestedRoles);
requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "REALM_APP_COMPOSITE_USER");
Assert.assertEquals(1, requestedRoles.size());
Assert.assertEquals(4, requestedRoles.size());
assertContains("APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
requestedRoles = getRequestedRoles("REALM_COMPOSITE_1_APPLICATION", "REALM_COMPOSITE_1_USER");
Assert.assertEquals(1, requestedRoles.size());
assertContains("realm", "REALM_COMPOSITE_1", requestedRoles);
requestedRoles = getRequestedRoles("REALM_COMPOSITE_2_APPLICATION", "REALM_COMPOSITE_1_USER");
Assert.assertEquals(3, requestedRoles.size());
assertContains("realm", "REALM_COMPOSITE_1", requestedRoles);
assertContains("realm", "REALM_COMPOSITE_CHILD", requestedRoles);
assertContains("realm", "REALM_ROLE_4", requestedRoles);
requestedRoles = getRequestedRoles("REALM_ROLE_1_APPLICATION", "REALM_COMPOSITE_1_USER");
Assert.assertEquals(1, requestedRoles.size());
assertContains("realm", "REALM_ROLE_1", requestedRoles);

View file

@ -21,7 +21,7 @@
"email" : "test-user1@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
"value" : "password" }
],
"realmRoles": [ "REALM_COMPOSITE_1" ]
},
@ -31,7 +31,7 @@
"email" : "test-user2@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
"value" : "password" }
],
"realmRoles": [ "REALM_ROLE_1"]
},
@ -41,7 +41,7 @@
"email" : "test-user3@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
"value" : "password" }
],
"realmRoles": [ "REALM_APP_COMPOSITE_ROLE" ]
},
@ -51,7 +51,7 @@
"email" : "test-user4@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
"value" : "password" }
],
"applicationRoles": {
"APP_ROLE_APPLICATION": [ "APP_ROLE_2" ]
@ -63,7 +63,7 @@
"email" : "test-user5@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
"value" : "password" }
],
"realmRoles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
}
@ -80,6 +80,10 @@
"client": "REALM_COMPOSITE_1_APPLICATION",
"roles": ["REALM_COMPOSITE_1"]
},
{
"client": "REALM_COMPOSITE_2_APPLICATION",
"roles": ["REALM_COMPOSITE_1", "REALM_COMPOSITE_CHILD", "REALM_ROLE_4"]
},
{
"client": "REALM_ROLE_1_APPLICATION",
"roles": ["REALM_ROLE_1"]
@ -93,7 +97,15 @@
"baseUrl": "http://localhost:8081/app",
"adminUrl": "http://localhost:8081/app/logout",
"secret": "password"
},
},
{
"name": "REALM_COMPOSITE_2_APPLICATION",
"fullScopeAllowed": false,
"enabled": true,
"baseUrl": "http://localhost:8081/app",
"adminUrl": "http://localhost:8081/app/logout",
"secret": "password"
},
{
"name": "REALM_ROLE_1_APPLICATION",
"fullScopeAllowed": false,
@ -130,10 +142,19 @@
{
"name": "REALM_ROLE_3"
},
{
"name": "REALM_ROLE_4"
},
{
"name": "REALM_COMPOSITE_1",
"composites": {
"realm": ["REALM_ROLE_1"]
"realm": ["REALM_ROLE_1", "REALM_COMPOSITE_CHILD"]
}
},
{
"name": "REALM_COMPOSITE_CHILD",
"composites": {
"realm": ["REALM_ROLE_4"]
}
},
{
@ -142,6 +163,9 @@
"application": {
"APP_ROLE_APPLICATION" :[
"APP_ROLE_1"
],
"APP_COMPOSITE_APPLICATION" :[
"APP_COMPOSITE_ROLE"
]
}
}
@ -168,6 +192,19 @@
"application": {
"APP_ROLE_APPLICATION" :[
"APP_ROLE_1"
],
"APP_COMPOSITE_APPLICATION" :[
"APP_COMPOSITE_CHILD"
]
}
}
},
{
"name": "APP_COMPOSITE_CHILD",
"composites": {
"application": {
"APP_COMPOSITE_APPLICATION" :[
"APP_ROLE_2"
]
}
}
@ -184,7 +221,7 @@
"APP_ROLE_APPLICATION": [
{
"client": "APP_COMPOSITE_APPLICATION",
"roles": ["APP_ROLE_2"]
"roles": ["APP_ROLE_1"]
}
]
}