KEYCLOAK-13818: Addressing performance issues with adding client scopes during realm creation. Removing redundant lookups by passing all scopes that need to be created at once.

This commit is contained in:
Michael Cooney 2020-04-15 15:47:10 -04:00 committed by Marek Posolda
parent 49db2c13a5
commit 3291161954
5 changed files with 55 additions and 11 deletions

View file

@ -102,6 +102,13 @@ public class ClientAdapter implements ClientModel, CachedObject {
updated.addClientScope(clientScope, defaultScope); updated.addClientScope(clientScope, defaultScope);
} }
@Override
public void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope) {
for (ClientScopeModel clientScope : clientScopes) {
addClientScope(clientScope, defaultScope);
}
}
@Override @Override
public void removeClientScope(ClientScopeModel clientScope) { public void removeClientScope(ClientScopeModel clientScope) {
getDelegateForUpdate(); getDelegateForUpdate();

View file

@ -355,6 +355,18 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) { public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
if (getClientScopes(defaultScope, false).containsKey(clientScope.getName())) return; if (getClientScopes(defaultScope, false).containsKey(clientScope.getName())) return;
persist(clientScope, defaultScope);
}
@Override
public void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope) {
Map<String, ClientScopeModel> existingClientScopes = getClientScopes(defaultScope, false);
clientScopes.stream()
.filter(clientScope -> !existingClientScopes.containsKey(clientScope.getName()))
.forEach(clientScope -> persist(clientScope, defaultScope));
}
private void persist(ClientScopeModel clientScope, boolean defaultScope) {
ClientScopeClientMappingEntity entity = new ClientScopeClientMappingEntity(); ClientScopeClientMappingEntity entity = new ClientScopeClientMappingEntity();
entity.setClientScope(ClientScopeAdapter.toClientScopeEntity(clientScope, em)); entity.setClientScope(ClientScopeAdapter.toClientScopeEntity(clientScope, em));
entity.setClient(getEntity()); entity.setClient(getEntity());

View file

@ -25,6 +25,12 @@ import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener; import org.keycloak.provider.ProviderEventListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -56,9 +62,7 @@ public abstract class AbstractLoginProtocolFactory implements LoginProtocolFacto
// Create default client scopes for realm built-in clients too // Create default client scopes for realm built-in clients too
if (addScopesToExistingClients) { if (addScopesToExistingClients) {
for (ClientModel client : newRealm.getClients()) { addDefaultClientScopes(newRealm, newRealm.getClients());
addDefaultClientScopes(newRealm, client);
}
} }
} }
@ -69,15 +73,22 @@ public abstract class AbstractLoginProtocolFactory implements LoginProtocolFacto
protected void addDefaultClientScopes(RealmModel realm, ClientModel newClient) { protected void addDefaultClientScopes(RealmModel realm, ClientModel newClient) {
for (ClientScopeModel clientScope : realm.getDefaultClientScopes(true)) { addDefaultClientScopes(realm, Arrays.asList(newClient));
if (getId().equals(clientScope.getProtocol())) { }
newClient.addClientScope(clientScope, true);
} protected void addDefaultClientScopes(RealmModel realm, List<ClientModel> newClients) {
Set<ClientScopeModel> defaultClientScopes = realm.getDefaultClientScopes(true).stream()
.filter(clientScope -> getId().equals(clientScope.getProtocol()))
.collect(Collectors.toSet());
for (ClientModel newClient : newClients) {
newClient.addClientScopes(defaultClientScopes, true);
} }
for (ClientScopeModel clientScope : realm.getDefaultClientScopes(false)) {
if (getId().equals(clientScope.getProtocol())) { Set<ClientScopeModel> nonDefaultClientScopes = realm.getDefaultClientScopes(false).stream()
newClient.addClientScope(clientScope, false); .filter(clientScope -> getId().equals(clientScope.getProtocol()))
} .collect(Collectors.toSet());
for (ClientModel newClient : newClients) {
newClient.addClientScopes(nonDefaultClientScopes, false);
} }
} }

View file

@ -23,6 +23,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.ReadOnlyException;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
@ -224,6 +225,11 @@ public abstract class AbstractReadOnlyClientStorageAdapter extends AbstractClien
throw new ReadOnlyException("client is read only for this update"); throw new ReadOnlyException("client is read only for this update");
} }
@Override
public void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope) {
throw new ReadOnlyException("client is read only for this update");
}
@Override @Override
public void removeClientScope(ClientScopeModel clientScope) { public void removeClientScope(ClientScopeModel clientScope) {
throw new ReadOnlyException("client is read only for this update"); throw new ReadOnlyException("client is read only for this update");

View file

@ -17,6 +17,7 @@
package org.keycloak.models; package org.keycloak.models;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -170,6 +171,13 @@ public interface ClientModel extends ClientScopeModel, RoleContainerModel, Prot
*/ */
void addClientScope(ClientScopeModel clientScope, boolean defaultScope); void addClientScope(ClientScopeModel clientScope, boolean defaultScope);
/**
* Add clientScopes with this client. Add as default scopes (if parameter 'defaultScope' is true) or optional scopes (if parameter 'defaultScope' is false)
* @param clientScopes
* @param defaultScope
*/
void addClientScopes(Set<ClientScopeModel> clientScopes, boolean defaultScope);
void removeClientScope(ClientScopeModel clientScope); void removeClientScope(ClientScopeModel clientScope);
/** /**