diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java index 1c5b39afbd..a069c125be 100644 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java @@ -60,6 +60,7 @@ import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel.RoleRemovedEvent; import org.keycloak.models.RoleModel; import org.keycloak.models.RoleProvider; +import org.keycloak.models.delegate.ClientModelLazyDelegate; import org.keycloak.models.jpa.entities.ClientAttributeEntity; import org.keycloak.models.jpa.entities.ClientEntity; import org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity; @@ -659,7 +660,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc query.setParameter("realm", realm.getId()); Stream clients = paginateQuery(query, firstResult, maxResults).getResultStream(); - return closing(clients.map(c -> session.clients().getClientById(realm, c)).filter(Objects::nonNull)); + return closing(clients.map(id -> (ClientModel) new ClientModelLazyDelegate.WithId(session, realm, id))); } @Override @@ -703,7 +704,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc query.setParameter("realm", realm.getId()); Stream results = paginateQuery(query, firstResult, maxResults).getResultStream(); - return closing(results.map(c -> session.clients().getClientById(realm, c))); + return closing(results.map(id -> (ClientModel) new ClientModelLazyDelegate.WithId(session, realm, id))); } @Override @@ -713,8 +714,9 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); CriteriaBuilder builder = em.getCriteriaBuilder(); - CriteriaQuery queryBuilder = builder.createQuery(ClientEntity.class); + CriteriaQuery queryBuilder = builder.createQuery(String.class); Root root = queryBuilder.from(ClientEntity.class); + queryBuilder.select(root.get("id")); List predicates = new ArrayList<>(); @@ -734,9 +736,9 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc Predicate finalPredicate = builder.and(predicates.toArray(new Predicate[0])); queryBuilder.where(finalPredicate).orderBy(builder.asc(root.get("clientId"))); - TypedQuery query = em.createQuery(queryBuilder); + TypedQuery query = em.createQuery(queryBuilder); return closing(paginateQuery(query, firstResult, maxResults).getResultStream()) - .map(c -> session.clients().getClientById(realm, c.getId())); + .map(id -> session.clients().getClientById(realm, id)); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java index ecfc859eaf..69fb6718d9 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java @@ -20,6 +20,7 @@ package org.keycloak.authorization.store.syncronization; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel.RealmRemovedEvent; @@ -34,17 +35,11 @@ public class RealmSynchronizer implements Synchronizer { ProviderFactory providerFactory = factory.getProviderFactory(AuthorizationProvider.class); AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession()); StoreFactory storeFactory = authorizationProvider.getStoreFactory(); + ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore(); event.getRealm().getClientsStream().forEach(clientModel -> { - ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); - - if (resourceServer != null) { - String id = resourceServer.getId(); - //storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId())); - //storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId())); - //storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId())); - storeFactory.getResourceServerStore().delete(id); - } + String id = clientModel.getId(); + resourceServerStore.delete(id); }); } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/delegate/ClientModelLazyDelegate.java b/server-spi-private/src/main/java/org/keycloak/models/delegate/ClientModelLazyDelegate.java new file mode 100644 index 0000000000..fd050528bf --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/models/delegate/ClientModelLazyDelegate.java @@ -0,0 +1,656 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.models.delegate; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientScopeModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicMarkableReference; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * + * @author hmlnarik + */ +public class ClientModelLazyDelegate implements ClientModel { + + private final Supplier delegateSupplier; + + private final AtomicMarkableReference delegate = new AtomicMarkableReference<>(null, false); + + public static class WithId extends ClientModelLazyDelegate { + + private final String id; + + public WithId(String id, Supplier delegateSupplier) { + super(delegateSupplier); + this.id = id; + } + + public WithId(KeycloakSession session, RealmModel realm, String id) { + super(() -> session.clients().getClientById(realm, id)); + this.id = id; + } + + @Override + public String getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof ClientModel)) return false; + + ClientModel that = (ClientModel) o; + return that.getId().equals(getId()); + } + + @Override + public int hashCode() { + return getId().hashCode(); + } + } + + public ClientModelLazyDelegate(Supplier delegateSupplier) { + this.delegateSupplier = delegateSupplier; + } + + private ClientModel getDelegate() { + if (! delegate.isMarked()) { + delegate.compareAndSet(null, delegateSupplier == null ? null : delegateSupplier.get(), false, true); + } + ClientModel ref = delegate.getReference(); + if (ref == null) { + throw new IllegalStateException("Invalid delegate obtained"); + } + return ref; + } + + @Override + public void updateClient() { + getDelegate().updateClient(); + } + + @Override + public String getId() { + return getDelegate().getId(); + } + + @Override + public String getClientId() { + return getDelegate().getClientId(); + } + + @Override + public void setClientId(String clientId) { + getDelegate().setClientId(clientId); + } + + @Override + public String getName() { + return getDelegate().getName(); + } + + @Override + public void setName(String name) { + getDelegate().setName(name); + } + + @Override + public String getDescription() { + return getDelegate().getDescription(); + } + + @Override + public void setDescription(String description) { + getDelegate().setDescription(description); + } + + @Override + public boolean isEnabled() { + return getDelegate().isEnabled(); + } + + @Override + public void setEnabled(boolean enabled) { + getDelegate().setEnabled(enabled); + } + + @Override + public boolean isAlwaysDisplayInConsole() { + return getDelegate().isAlwaysDisplayInConsole(); + } + + @Override + public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) { + getDelegate().setAlwaysDisplayInConsole(alwaysDisplayInConsole); + } + + @Override + public boolean isSurrogateAuthRequired() { + return getDelegate().isSurrogateAuthRequired(); + } + + @Override + public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { + getDelegate().setSurrogateAuthRequired(surrogateAuthRequired); + } + + @Override + public Set getWebOrigins() { + return getDelegate().getWebOrigins(); + } + + @Override + public void setWebOrigins(Set webOrigins) { + getDelegate().setWebOrigins(webOrigins); + } + + @Override + public void addWebOrigin(String webOrigin) { + getDelegate().addWebOrigin(webOrigin); + } + + @Override + public void removeWebOrigin(String webOrigin) { + getDelegate().removeWebOrigin(webOrigin); + } + + @Override + public Set getRedirectUris() { + return getDelegate().getRedirectUris(); + } + + @Override + public void setRedirectUris(Set redirectUris) { + getDelegate().setRedirectUris(redirectUris); + } + + @Override + public void addRedirectUri(String redirectUri) { + getDelegate().addRedirectUri(redirectUri); + } + + @Override + public void removeRedirectUri(String redirectUri) { + getDelegate().removeRedirectUri(redirectUri); + } + + @Override + public String getManagementUrl() { + return getDelegate().getManagementUrl(); + } + + @Override + public void setManagementUrl(String url) { + getDelegate().setManagementUrl(url); + } + + @Override + public String getRootUrl() { + return getDelegate().getRootUrl(); + } + + @Override + public void setRootUrl(String url) { + getDelegate().setRootUrl(url); + } + + @Override + public String getBaseUrl() { + return getDelegate().getBaseUrl(); + } + + @Override + public void setBaseUrl(String url) { + getDelegate().setBaseUrl(url); + } + + @Override + public boolean isBearerOnly() { + return getDelegate().isBearerOnly(); + } + + @Override + public void setBearerOnly(boolean only) { + getDelegate().setBearerOnly(only); + } + + @Override + public int getNodeReRegistrationTimeout() { + return getDelegate().getNodeReRegistrationTimeout(); + } + + @Override + public void setNodeReRegistrationTimeout(int timeout) { + getDelegate().setNodeReRegistrationTimeout(timeout); + } + + @Override + public String getClientAuthenticatorType() { + return getDelegate().getClientAuthenticatorType(); + } + + @Override + public void setClientAuthenticatorType(String clientAuthenticatorType) { + getDelegate().setClientAuthenticatorType(clientAuthenticatorType); + } + + @Override + public boolean validateSecret(String secret) { + return getDelegate().validateSecret(secret); + } + + @Override + public String getSecret() { + return getDelegate().getSecret(); + } + + @Override + public void setSecret(String secret) { + getDelegate().setSecret(secret); + } + + @Override + public String getRegistrationToken() { + return getDelegate().getRegistrationToken(); + } + + @Override + public void setRegistrationToken(String registrationToken) { + getDelegate().setRegistrationToken(registrationToken); + } + + @Override + public String getProtocol() { + return getDelegate().getProtocol(); + } + + @Override + public void setProtocol(String protocol) { + getDelegate().setProtocol(protocol); + } + + @Override + public void setAttribute(String name, String value) { + getDelegate().setAttribute(name, value); + } + + @Override + public void removeAttribute(String name) { + getDelegate().removeAttribute(name); + } + + @Override + public String getAttribute(String name) { + return getDelegate().getAttribute(name); + } + + @Override + public Map getAttributes() { + return getDelegate().getAttributes(); + } + + @Override + public String getAuthenticationFlowBindingOverride(String binding) { + return getDelegate().getAuthenticationFlowBindingOverride(binding); + } + + @Override + public Map getAuthenticationFlowBindingOverrides() { + return getDelegate().getAuthenticationFlowBindingOverrides(); + } + + @Override + public void removeAuthenticationFlowBindingOverride(String binding) { + getDelegate().removeAuthenticationFlowBindingOverride(binding); + } + + @Override + public void setAuthenticationFlowBindingOverride(String binding, String flowId) { + getDelegate().setAuthenticationFlowBindingOverride(binding, flowId); + } + + @Override + public boolean isFrontchannelLogout() { + return getDelegate().isFrontchannelLogout(); + } + + @Override + public void setFrontchannelLogout(boolean flag) { + getDelegate().setFrontchannelLogout(flag); + } + + @Override + public boolean isFullScopeAllowed() { + return getDelegate().isFullScopeAllowed(); + } + + @Override + public void setFullScopeAllowed(boolean value) { + getDelegate().setFullScopeAllowed(value); + } + + @Override + public boolean isPublicClient() { + return getDelegate().isPublicClient(); + } + + @Override + public void setPublicClient(boolean flag) { + getDelegate().setPublicClient(flag); + } + + @Override + public boolean isConsentRequired() { + return getDelegate().isConsentRequired(); + } + + @Override + public void setConsentRequired(boolean consentRequired) { + getDelegate().setConsentRequired(consentRequired); + } + + @Override + public boolean isStandardFlowEnabled() { + return getDelegate().isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + getDelegate().setStandardFlowEnabled(standardFlowEnabled); + } + + @Override + public boolean isImplicitFlowEnabled() { + return getDelegate().isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + getDelegate().setImplicitFlowEnabled(implicitFlowEnabled); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + return getDelegate().isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + getDelegate().setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + } + + @Override + public boolean isServiceAccountsEnabled() { + return getDelegate().isServiceAccountsEnabled(); + } + + @Override + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + getDelegate().setServiceAccountsEnabled(serviceAccountsEnabled); + } + + @Override + public RealmModel getRealm() { + return getDelegate().getRealm(); + } + + @Override + public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) { + getDelegate().addClientScope(clientScope, defaultScope); + } + + @Override + public void addClientScopes(Set clientScopes, boolean defaultScope) { + getDelegate().addClientScopes(clientScopes, defaultScope); + } + + @Override + public void removeClientScope(ClientScopeModel clientScope) { + getDelegate().removeClientScope(clientScope); + } + + @Override + public Map getClientScopes(boolean defaultScope) { + return getDelegate().getClientScopes(defaultScope); + } + + @Override + public ClientScopeModel getDynamicClientScope(String scope) { + return getDelegate().getDynamicClientScope(scope); + } + + @Override + public int getNotBefore() { + return getDelegate().getNotBefore(); + } + + @Override + public void setNotBefore(int notBefore) { + getDelegate().setNotBefore(notBefore); + } + + @Override + public Map getRegisteredNodes() { + return getDelegate().getRegisteredNodes(); + } + + @Override + public void registerNode(String nodeHost, int registrationTime) { + getDelegate().registerNode(nodeHost, registrationTime); + } + + @Override + public void unregisterNode(String nodeHost) { + getDelegate().unregisterNode(nodeHost); + } + + @Override + public boolean isDisplayOnConsentScreen() { + return getDelegate().isDisplayOnConsentScreen(); + } + + @Override + public String getConsentScreenText() { + return getDelegate().getConsentScreenText(); + } + + @Override + public void setDisplayOnConsentScreen(boolean displayOnConsentScreen) { + getDelegate().setDisplayOnConsentScreen(displayOnConsentScreen); + } + + @Override + public void setConsentScreenText(String consentScreenText) { + getDelegate().setConsentScreenText(consentScreenText); + } + + @Override + public String getGuiOrder() { + return getDelegate().getGuiOrder(); + } + + @Override + public void setGuiOrder(String guiOrder) { + getDelegate().setGuiOrder(guiOrder); + } + + @Override + public boolean isIncludeInTokenScope() { + return getDelegate().isIncludeInTokenScope(); + } + + @Override + public void setIncludeInTokenScope(boolean includeInTokenScope) { + getDelegate().setIncludeInTokenScope(includeInTokenScope); + } + + @Override + public Set getScopeMappings() { + return getDelegate().getScopeMappings(); + } + + @Override + public Stream getScopeMappingsStream() { + return getDelegate().getScopeMappingsStream(); + } + + @Override + public Set getRealmScopeMappings() { + return getDelegate().getRealmScopeMappings(); + } + + @Override + public Stream getRealmScopeMappingsStream() { + return getDelegate().getRealmScopeMappingsStream(); + } + + @Override + public void addScopeMapping(RoleModel role) { + getDelegate().addScopeMapping(role); + } + + @Override + public void deleteScopeMapping(RoleModel role) { + getDelegate().deleteScopeMapping(role); + } + + @Override + public boolean hasScope(RoleModel role) { + return getDelegate().hasScope(role); + } + + @Override + public RoleModel getRole(String name) { + return getDelegate().getRole(name); + } + + @Override + public RoleModel addRole(String name) { + return getDelegate().addRole(name); + } + + @Override + public RoleModel addRole(String id, String name) { + return getDelegate().addRole(id, name); + } + + @Override + public boolean removeRole(RoleModel role) { + return getDelegate().removeRole(role); + } + + @Override + public Set getRoles() { + return getDelegate().getRoles(); + } + + @Override + public Stream getRolesStream() { + return getDelegate().getRolesStream(); + } + + @Override + public Set getRoles(Integer firstResult, Integer maxResults) { + return getDelegate().getRoles(firstResult, maxResults); + } + + @Override + public Stream getRolesStream(Integer firstResult, Integer maxResults) { + return getDelegate().getRolesStream(firstResult, maxResults); + } + + @Override + public Set searchForRoles(String search, Integer first, Integer max) { + return getDelegate().searchForRoles(search, first, max); + } + + @Override + public Stream searchForRolesStream(String search, Integer first, Integer max) { + return getDelegate().searchForRolesStream(search, first, max); + } + + @Override + public List getDefaultRoles() { + return getDelegate().getDefaultRoles(); + } + + @Override + public Stream getDefaultRolesStream() { + return getDelegate().getDefaultRolesStream(); + } + + @Override + public void addDefaultRole(String name) { + getDelegate().addDefaultRole(name); + } + + @Override + public void updateDefaultRoles(String... defaultRoles) { + getDelegate().updateDefaultRoles(defaultRoles); + } + + @Override + public void removeDefaultRoles(String... defaultRoles) { + getDelegate().removeDefaultRoles(defaultRoles); + } + + @Override + public Set getProtocolMappers() { + return getDelegate().getProtocolMappers(); + } + + @Override + public Stream getProtocolMappersStream() { + return getDelegate().getProtocolMappersStream(); + } + + @Override + public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { + return getDelegate().addProtocolMapper(model); + } + + @Override + public void removeProtocolMapper(ProtocolMapperModel mapping) { + getDelegate().removeProtocolMapper(mapping); + } + + @Override + public void updateProtocolMapper(ProtocolMapperModel mapping) { + getDelegate().updateProtocolMapper(mapping); + } + + @Override + public ProtocolMapperModel getProtocolMapperById(String id) { + return getDelegate().getProtocolMapperById(id); + } + + @Override + public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) { + return getDelegate().getProtocolMapperByName(protocol, name); + } + +} diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java index d57056b5cd..f262d4f6da 100755 --- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java +++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java @@ -105,12 +105,12 @@ public class ExportUtils { List clients = Collections.emptyList(); if (options.isClientsIncluded()) { - clients = realm.getClientsStream().collect(Collectors.toList()); - List clientReps = new ArrayList<>(); - for (ClientModel app : clients) { - ClientRepresentation clientRep = exportClient(session, app); - clientReps.add(clientRep); - } + clients = realm.getClientsStream() + .filter(c -> { try { c.getClientId(); return true; } catch (Exception ex) { return false; } } ) + .collect(Collectors.toList()); + List clientReps = clients.stream() + .map(app -> exportClient(session, app)) + .collect(Collectors.toList()); rep.setClients(clientReps); } diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java index c88080ce70..d00ea617e5 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -241,7 +241,8 @@ public class ResourceAdminManager { public GlobalRequestResult logoutAll(RealmModel realm) { realm.setNotBefore(Time.currentTime()); - Stream resources = realm.getClientsStream(); + Stream resources = realm.getClientsStream() + .filter(c -> { try { c.getClientId(); return true; } catch (Exception ex) { return false; } } ); GlobalRequestResult finalResult = new GlobalRequestResult(); AtomicInteger counter = new AtomicInteger(0); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index 4528561760..f0605ddc48 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -131,6 +131,7 @@ public class ClientsResource { } Stream s = clientModels + .filter(c -> { try { c.getClientId(); return true; } catch (Exception ex) { return false; } } ) .map(c -> { ClientRepresentation representation = null; if (canView || auth.clients().canView(c)) {