Move realms, clients, groups, roles, clientscopes into legacy module

- Introduces Datastore SPI for isolating data store methods
- Introduces implementation of the datastore for legacy storage
- Updates DefaultKeycloakSession to leverage Datastore SPI instead
  of direct creating of area providers by the session
This commit is contained in:
Hynek Mlnarik 2022-02-18 21:10:10 +01:00 committed by Hynek Mlnařík
parent 247ff52187
commit 36f76a37ad
23 changed files with 816 additions and 208 deletions

View file

@ -26,8 +26,10 @@ import org.keycloak.models.cache.CachedRealmModel;
import org.keycloak.models.cache.infinispan.entities.*;
import org.keycloak.models.cache.infinispan.events.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.client.ClientStorageProviderModel;
import org.keycloak.storage.datastore.LegacyDatastoreProvider;
import java.util.*;
import java.util.stream.Collectors;
@ -119,11 +121,13 @@ public class RealmCacheSession implements CacheRealmProvider {
protected boolean clearAll;
protected final long startupRevision;
private final LegacyDatastoreProvider datastoreProvider;
public RealmCacheSession(RealmCacheManager cache, KeycloakSession session) {
this.cache = cache;
this.session = session;
this.startupRevision = cache.getCurrentCounter();
this.datastoreProvider = (LegacyDatastoreProvider) session.getProvider(DatastoreProvider.class);
session.getTransactionManager().enlistPrepare(getPrepareTransaction());
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
}
@ -146,31 +150,31 @@ public class RealmCacheSession implements CacheRealmProvider {
public RealmProvider getRealmDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (realmDelegate != null) return realmDelegate;
realmDelegate = session.realmLocalStorage();
realmDelegate = session.getProvider(RealmProvider.class);
return realmDelegate;
}
public ClientProvider getClientDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (clientDelegate != null) return clientDelegate;
clientDelegate = session.clientStorageManager();
clientDelegate = this.datastoreProvider.clientStorageManager();
return clientDelegate;
}
public ClientScopeProvider getClientScopeDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (clientScopeDelegate != null) return clientScopeDelegate;
clientScopeDelegate = session.clientScopeStorageManager();
clientScopeDelegate = this.datastoreProvider.clientScopeStorageManager();
return clientScopeDelegate;
}
public RoleProvider getRoleDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (roleDelegate != null) return roleDelegate;
roleDelegate = session.roleStorageManager();
roleDelegate = this.datastoreProvider.roleStorageManager();
return roleDelegate;
}
public GroupProvider getGroupDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (groupDelegate != null) return groupDelegate;
groupDelegate = session.groupStorageManager();
groupDelegate = this.datastoreProvider.groupStorageManager();
return groupDelegate;
}

View file

@ -0,0 +1,247 @@
/*
* Copyright 2020 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.storage;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.common.util.reflections.Types;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.utils.ServicesUtils;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
/**
*
* @param <ProviderType> This type will be used for looking for factories that produce instances of desired providers
* @param <StorageProviderModelType> Type of model used for creating provider, it needs to extend
* CacheableStorageProviderModel as it has {@code isEnabled()} method and also extend
* PrioritizedComponentModel which is required for sorting providers based on its
* priorities
*/
public abstract class AbstractStorageManager<ProviderType extends Provider,
StorageProviderModelType extends CacheableStorageProviderModel> {
private static final Logger LOG = Logger.getLogger(AbstractStorageManager.class);
/**
* Timeouts are used as time boundary for obtaining models from an external storage. Default value is set
* to 3000 milliseconds and it's configurable.
*/
private static final Long STORAGE_PROVIDER_DEFAULT_TIMEOUT = 3000L;
protected final KeycloakSession session;
private final Class<ProviderType> providerTypeClass;
private final Class<? extends ProviderFactory> factoryTypeClass;
private final Function<ComponentModel, StorageProviderModelType> toStorageProviderModelTypeFunction;
private final String configScope;
private Long storageProviderTimeout;
public AbstractStorageManager(KeycloakSession session, Class<? extends ProviderFactory> factoryTypeClass, Class<ProviderType> providerTypeClass, Function<ComponentModel, StorageProviderModelType> toStorageProviderModelTypeFunction, String configScope) {
this.session = session;
this.providerTypeClass = providerTypeClass;
this.factoryTypeClass = factoryTypeClass;
this.toStorageProviderModelTypeFunction = toStorageProviderModelTypeFunction;
this.configScope = configScope;
}
protected Long getStorageProviderTimeout() {
if (storageProviderTimeout == null) {
storageProviderTimeout = Config.scope(configScope).getLong("storageProviderTimeout", STORAGE_PROVIDER_DEFAULT_TIMEOUT);
}
return storageProviderTimeout;
}
/**
* Returns a factory with the providerId, which produce instances of type CreatedProviderType
* @param providerId id of factory that produce desired instances
* @return A factory that implements {@code ComponentFactory<CreatedProviderType, ProviderType>}
*/
protected <T extends ProviderType> ComponentFactory<T, ProviderType> getStorageProviderFactory(String providerId) {
return (ComponentFactory<T, ProviderType>) session.getKeycloakSessionFactory()
.getProviderFactory(providerTypeClass, providerId);
}
/**
* Returns stream of all storageProviders within the realm that implements the capabilityInterface.
*
* @param realm realm
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @return enabled storage providers for realm and @{code getProviderTypeClass()}
*/
protected <T> Stream<T> getEnabledStorageProviders(RealmModel realm, Class<T> capabilityInterface) {
return getStorageProviderModels(realm, providerTypeClass)
.map(toStorageProviderModelTypeFunction)
.filter(StorageProviderModelType::isEnabled)
.sorted(StorageProviderModelType.comparator)
.map(storageProviderModelType -> getStorageProviderInstance(storageProviderModelType, capabilityInterface, false))
.filter(Objects::nonNull);
}
/**
* Gets all enabled StorageProviders that implements the capabilityInterface, applies applyFunction on each of
* them and then join the results together.
*
* !! Each StorageProvider has a limited time to respond, if it fails to do it, empty stream is returned !!
*
* @param realm realm
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @param applyFunction function that is applied on StorageProviders
* @param <R> result of applyFunction
* @return a stream with all results from all StorageProviders
*/
protected <R, T> Stream<R> flatMapEnabledStorageProvidersWithTimeout(RealmModel realm, Class<T> capabilityInterface, Function<T, ? extends Stream<R>> applyFunction) {
return getEnabledStorageProviders(realm, capabilityInterface)
.flatMap(ServicesUtils.timeBound(session, getStorageProviderTimeout(), applyFunction));
}
/**
* Gets all enabled StorageProviders that implements the capabilityInterface, applies applyFunction on each of
* them and returns the stream.
*
* !! Each StorageProvider has a limited time to respond, if it fails to do it, null is returned !!
*
* @param realm realm
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @param applyFunction function that is applied on StorageProviders
* @param <R> Result of applyFunction
* @return First result from StorageProviders
*/
protected <R, T> Stream<R> mapEnabledStorageProvidersWithTimeout(RealmModel realm, Class<T> capabilityInterface, Function<T, R> applyFunction) {
return getEnabledStorageProviders(realm, capabilityInterface)
.map(ServicesUtils.timeBoundOne(session, getStorageProviderTimeout(), applyFunction))
.filter(Objects::nonNull);
}
/**
* Gets all enabled StorageProviders that implements the capabilityInterface and call applyFunction on each
*
* !! Each StorageProvider has a limited time for consuming !!
*
* @param realm realm
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @param consumer function that is applied on StorageProviders
*/
protected <T> void consumeEnabledStorageProvidersWithTimeout(RealmModel realm, Class<T> capabilityInterface, Consumer<T> consumer) {
getEnabledStorageProviders(realm, capabilityInterface)
.forEachOrdered(ServicesUtils.consumeWithTimeBound(session, getStorageProviderTimeout(), consumer));
}
protected <T> T getStorageProviderInstance(RealmModel realm, String providerId, Class<T> capabilityInterface) {
return getStorageProviderInstance(realm, providerId, capabilityInterface, false);
}
/**
* Returns an instance of provider with the providerId within the realm or null if storage provider with providerId
* doesn't implement capabilityInterface.
*
* @param realm realm
* @param providerId id of ComponentModel within database/storage
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @return an instance of type CreatedProviderType or null if storage provider with providerId doesn't implement capabilityInterface
*/
protected <T> T getStorageProviderInstance(RealmModel realm, String providerId, Class<T> capabilityInterface, boolean includeDisabled) {
if (providerId == null || capabilityInterface == null) return null;
return getStorageProviderInstance(getStorageProviderModel(realm, providerId), capabilityInterface, includeDisabled);
}
/**
* Returns an instance of StorageProvider model corresponding realm and providerId
* @param realm Realm.
* @param providerId Id of desired provider.
* @return An instance of type StorageProviderModelType
*/
protected StorageProviderModelType getStorageProviderModel(RealmModel realm, String providerId) {
ComponentModel componentModel = realm.getComponent(providerId);
if (componentModel == null) {
return null;
}
return toStorageProviderModelTypeFunction.apply(componentModel);
}
/**
* Returns an instance of provider for the model or null if storage provider based on the model doesn't implement capabilityInterface.
*
* @param model StorageProviderModel obtained from database/storage
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @param <T> Required capability interface type
* @return an instance of type T or null if storage provider based on the model doesn't exist or doesn't implement the capabilityInterface.
*/
protected <T> T getStorageProviderInstance(StorageProviderModelType model, Class<T> capabilityInterface) {
return getStorageProviderInstance(model, capabilityInterface, false);
}
/**
* Returns an instance of provider for the model or null if storage provider based on the model doesn't implement capabilityInterface.
*
* @param model StorageProviderModel obtained from database/storage
* @param capabilityInterface class of desired capabilityInterface.
* For example, {@code GroupLookupProvider} or {@code UserQueryProvider}
* @param includeDisabled If set to true, the method will return also disabled providers.
* @return an instance of type T or null if storage provider based on the model doesn't exist or doesn't implement the capabilityInterface.
*/
protected <T> T getStorageProviderInstance(StorageProviderModelType model, Class<T> capabilityInterface, boolean includeDisabled) {
if (model == null || (!model.isEnabled() && !includeDisabled) || capabilityInterface == null) {
return null;
}
@SuppressWarnings("unchecked")
ProviderType instance = (ProviderType) session.getAttribute(model.getId());
if (instance != null && capabilityInterface.isAssignableFrom(instance.getClass())) return capabilityInterface.cast(instance);
ComponentFactory<? extends ProviderType, ProviderType> factory = getStorageProviderFactory(model.getProviderId());
if (factory == null) {
LOG.warnv("Configured StorageProvider {0} of provider id {1} does not exist", model.getName(), model.getProviderId());
return null;
}
if (!Types.supports(capabilityInterface, factory, factoryTypeClass)) {
return null;
}
instance = factory.create(session, model);
if (instance == null) {
throw new IllegalStateException("StorageProvideFactory (of type " + factory.getClass().getName() + ") produced a null instance");
}
session.enlistForClose(instance);
session.setAttribute(model.getId(), instance);
return capabilityInterface.cast(instance);
}
/**
* Stream of ComponentModels of storageType.
* @param realm Realm.
* @param storageType Type.
* @return Stream of ComponentModels
*/
public static Stream<ComponentModel> getStorageProviderModels(RealmModel realm, Class<? extends Provider> storageType) {
return realm.getStorageProviders(storageType);
}
}

View file

@ -33,13 +33,17 @@ public class ClientScopeStorageManager extends AbstractStorageManager<ClientScop
ClientScopeStorageProviderModel::new, "clientscope");
}
private ClientScopeProvider localStorage() {
return session.getProvider(ClientScopeProvider.class);
}
/* CLIENT SCOPE PROVIDER LOOKUP METHODS - implemented by client scope storage providers */
@Override
public ClientScopeModel getClientScopeById(RealmModel realm, String id) {
StorageId storageId = new StorageId(id);
if (storageId.getProviderId() == null) {
return session.clientScopeLocalStorage().getClientScopeById(realm, id);
return localStorage().getClientScopeById(realm, id);
}
ClientScopeLookupProvider provider = getStorageProviderInstance(realm, storageId.getProviderId(), ClientScopeLookupProvider.class);
@ -52,22 +56,22 @@ public class ClientScopeStorageManager extends AbstractStorageManager<ClientScop
@Override
public Stream<ClientScopeModel> getClientScopesStream(RealmModel realm) {
return session.clientScopeLocalStorage().getClientScopesStream(realm);
return localStorage().getClientScopesStream(realm);
}
@Override
public ClientScopeModel addClientScope(RealmModel realm, String id, String name) {
return session.clientScopeLocalStorage().addClientScope(realm, id, name);
return localStorage().addClientScope(realm, id, name);
}
@Override
public boolean removeClientScope(RealmModel realm, String id) {
return session.clientScopeLocalStorage().removeClientScope(realm, id);
return localStorage().removeClientScope(realm, id);
}
@Override
public void removeClientScopes(RealmModel realm) {
session.clientScopeLocalStorage().removeClientScopes(realm);
localStorage().removeClientScopes(realm);
}
@Override

View file

@ -50,6 +50,10 @@ public class ClientStorageManager implements ClientProvider {
private long clientStorageProviderTimeout;
private ClientProvider localStorage() {
return session.getProvider(ClientProvider.class);
}
public static boolean isStorageProviderEnabled(RealmModel realm, String providerId) {
ClientStorageProviderModel model = getStorageProviderModel(realm, providerId);
return model.isEnabled();
@ -131,7 +135,7 @@ public class ClientStorageManager implements ClientProvider {
public ClientModel getClientById(RealmModel realm, String id) {
StorageId storageId = new StorageId(id);
if (storageId.getProviderId() == null) {
return session.clientLocalStorage().getClientById(realm, id);
return localStorage().getClientById(realm, id);
}
ClientLookupProvider provider = (ClientLookupProvider)getStorageProvider(session, realm, storageId.getProviderId());
if (provider == null) return null;
@ -141,7 +145,7 @@ public class ClientStorageManager implements ClientProvider {
@Override
public ClientModel getClientByClientId(RealmModel realm, String clientId) {
ClientModel client = session.clientLocalStorage().getClientByClientId(realm, clientId);
ClientModel client = localStorage().getClientByClientId(realm, clientId);
if (client != null) {
return client;
}
@ -174,7 +178,7 @@ public class ClientStorageManager implements ClientProvider {
// how many results there will be; i.e. we need to query the clients without paginating them and perform pagination
// later at this level
if (hasEnabledStorageProviders(session, realm, ClientLookupProvider.class)) {
Stream<ClientLookupProvider> providersStream = Stream.concat(Stream.of(session.clientLocalStorage()), getEnabledStorageProviders(session, realm, ClientLookupProvider.class));
Stream<ClientLookupProvider> providersStream = Stream.concat(Stream.of(localStorage()), getEnabledStorageProviders(session, realm, ClientLookupProvider.class));
/*
Obtaining clients from an external client storage is time-bounded. In case the external client storage
@ -196,7 +200,7 @@ public class ClientStorageManager implements ClientProvider {
return paginatedStream(res, firstResult, maxResults);
}
else {
return paginatedQuery.query(session.clientLocalStorage(), firstResult, maxResults);
return paginatedQuery.query(localStorage(), firstResult, maxResults);
}
}
@ -204,7 +208,7 @@ public class ClientStorageManager implements ClientProvider {
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) {
StorageId storageId = new StorageId(client.getId());
if (storageId.getProviderId() == null) {
return session.clientLocalStorage().getClientScopes(realm, client, defaultScopes);
return localStorage().getClientScopes(realm, client, defaultScopes);
}
ClientLookupProvider provider = (ClientLookupProvider)getStorageProvider(session, client.getRealm(), storageId.getProviderId());
if (provider == null) return null;
@ -214,37 +218,37 @@ public class ClientStorageManager implements ClientProvider {
@Override
public ClientModel addClient(RealmModel realm, String clientId) {
return session.clientLocalStorage().addClient(realm, clientId);
return localStorage().addClient(realm, clientId);
}
@Override
public ClientModel addClient(RealmModel realm, String id, String clientId) {
return session.clientLocalStorage().addClient(realm, id, clientId);
return localStorage().addClient(realm, id, clientId);
}
@Override
public Stream<ClientModel> getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults) {
return session.clientLocalStorage().getClientsStream(realm, firstResult, maxResults);
return localStorage().getClientsStream(realm, firstResult, maxResults);
}
@Override
public Stream<ClientModel> getClientsStream(RealmModel realm) {
return session.clientLocalStorage().getClientsStream(realm);
return localStorage().getClientsStream(realm);
}
@Override
public long getClientsCount(RealmModel realm) {
return session.clientLocalStorage().getClientsCount(realm);
return localStorage().getClientsCount(realm);
}
@Override
public Stream<ClientModel> getAlwaysDisplayInConsoleClientsStream(RealmModel realm) {
return session.clientLocalStorage().getAlwaysDisplayInConsoleClientsStream(realm);
return localStorage().getAlwaysDisplayInConsoleClientsStream(realm);
}
@Override
public void removeClients(RealmModel realm) {
session.clientLocalStorage().removeClients(realm);
localStorage().removeClients(realm);
}
@Override
@ -252,7 +256,7 @@ public class ClientStorageManager implements ClientProvider {
if (!StorageId.isLocalStorage(client.getId())) {
throw new RuntimeException("Federated clients do not support this operation");
}
session.clientLocalStorage().addClientScopes(realm, client, clientScopes, defaultScope);
localStorage().addClientScopes(realm, client, clientScopes, defaultScope);
}
@Override
@ -260,12 +264,12 @@ public class ClientStorageManager implements ClientProvider {
if (!StorageId.isLocalStorage(client.getId())) {
throw new RuntimeException("Federated clients do not support this operation");
}
session.clientLocalStorage().removeClientScope(realm, client, clientScope);
localStorage().removeClientScope(realm, client, clientScope);
}
@Override
public Map<ClientModel, Set<String>> getAllRedirectUrisOfEnabledClients(RealmModel realm) {
return session.clientLocalStorage().getAllRedirectUrisOfEnabledClients(realm);
return localStorage().getAllRedirectUrisOfEnabledClients(realm);
}
@Override
@ -278,7 +282,7 @@ public class ClientStorageManager implements ClientProvider {
if (!StorageId.isLocalStorage(id)) {
throw new RuntimeException("Federated clients do not support this operation");
}
return session.clientLocalStorage().removeClient(realm, id);
return localStorage().removeClient(realm, id);
}

View file

@ -37,11 +37,15 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
/* GROUP PROVIDER LOOKUP METHODS - implemented by group storage providers */
private GroupProvider localStorage() {
return session.getProvider(GroupProvider.class);
}
@Override
public GroupModel getGroupById(RealmModel realm, String id) {
StorageId storageId = new StorageId(id);
if (storageId.getProviderId() == null) {
return session.groupLocalStorage().getGroupById(realm, id);
return localStorage().getGroupById(realm, id);
}
GroupLookupProvider provider = getStorageProviderInstance(realm, storageId.getProviderId(), GroupLookupProvider.class);
@ -60,7 +64,7 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
*/
@Override
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
Stream<GroupModel> local = session.groupLocalStorage().searchForGroupByNameStream(realm, search, firstResult, maxResults);
Stream<GroupModel> local = localStorage().searchForGroupByNameStream(realm, search, firstResult, maxResults);
Stream<GroupModel> ext = flatMapEnabledStorageProvidersWithTimeout(realm, GroupLookupProvider.class,
p -> p.searchForGroupByNameStream(realm, search, firstResult, maxResults));
@ -71,57 +75,57 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
@Override
public Stream<GroupModel> getGroupsStream(RealmModel realm) {
return session.groupLocalStorage().getGroupsStream(realm);
return localStorage().getGroupsStream(realm);
}
@Override
public Stream<GroupModel> getGroupsStream(RealmModel realm, Stream<String> ids, String search, Integer first, Integer max) {
return session.groupLocalStorage().getGroupsStream(realm, ids, search, first, max);
return localStorage().getGroupsStream(realm, ids, search, first, max);
}
@Override
public Long getGroupsCount(RealmModel realm, Boolean onlyTopGroups) {
return session.groupLocalStorage().getGroupsCount(realm, onlyTopGroups);
return localStorage().getGroupsCount(realm, onlyTopGroups);
}
@Override
public Long getGroupsCountByNameContaining(RealmModel realm, String search) {
return session.groupLocalStorage().getGroupsCountByNameContaining(realm, search);
return localStorage().getGroupsCountByNameContaining(realm, search);
}
@Override
public Stream<GroupModel> getGroupsByRoleStream(RealmModel realm, RoleModel role, Integer firstResult, Integer maxResults) {
return session.groupLocalStorage().getGroupsByRoleStream(realm, role, firstResult, maxResults);
return localStorage().getGroupsByRoleStream(realm, role, firstResult, maxResults);
}
@Override
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm) {
return session.groupLocalStorage().getTopLevelGroupsStream(realm);
return localStorage().getTopLevelGroupsStream(realm);
}
@Override
public Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm, Integer firstResult, Integer maxResults) {
return session.groupLocalStorage().getTopLevelGroupsStream(realm, firstResult, maxResults);
return localStorage().getTopLevelGroupsStream(realm, firstResult, maxResults);
}
@Override
public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) {
return session.groupLocalStorage().createGroup(realm, id, name, toParent);
return localStorage().createGroup(realm, id, name, toParent);
}
@Override
public boolean removeGroup(RealmModel realm, GroupModel group) {
return session.groupLocalStorage().removeGroup(realm, group);
return localStorage().removeGroup(realm, group);
}
@Override
public void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent) {
session.groupLocalStorage().moveGroup(realm, group, toParent);
localStorage().moveGroup(realm, group, toParent);
}
@Override
public void addTopLevelGroup(RealmModel realm, GroupModel subGroup) {
session.groupLocalStorage().addTopLevelGroup(realm, subGroup);
localStorage().addTopLevelGroup(realm, subGroup);
}
@Override

View file

@ -45,6 +45,10 @@ public class RoleStorageManager implements RoleProvider {
this.roleStorageProviderTimeout = roleStorageProviderTimeout;
}
private RoleProvider localStorage() {
return session.getProvider(RoleProvider.class);
}
public static boolean isStorageProviderEnabled(RealmModel realm, String providerId) {
RoleStorageProviderModel model = getStorageProviderModel(realm, providerId);
return model.isEnabled();
@ -114,17 +118,17 @@ public class RoleStorageManager implements RoleProvider {
@Override
public RoleModel addRealmRole(RealmModel realm, String name) {
return session.roleLocalStorage().addRealmRole(realm, name);
return localStorage().addRealmRole(realm, name);
}
@Override
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
return session.roleLocalStorage().addRealmRole(realm, id, name);
return localStorage().addRealmRole(realm, id, name);
}
@Override
public RoleModel getRealmRole(RealmModel realm, String name) {
RoleModel realmRole = session.roleLocalStorage().getRealmRole(realm, name);
RoleModel realmRole = localStorage().getRealmRole(realm, name);
if (realmRole != null) return realmRole;
return getEnabledStorageProviders(session, realm, RoleLookupProvider.class)
.map(provider -> provider.getRealmRole(realm, name))
@ -137,7 +141,7 @@ public class RoleStorageManager implements RoleProvider {
public RoleModel getRoleById(RealmModel realm, String id) {
StorageId storageId = new StorageId(id);
if (storageId.getProviderId() == null) {
return session.roleLocalStorage().getRoleById(realm, id);
return localStorage().getRoleById(realm, id);
}
RoleLookupProvider provider = (RoleLookupProvider)getStorageProvider(session, realm, storageId.getProviderId());
if (provider == null) return null;
@ -147,12 +151,12 @@ public class RoleStorageManager implements RoleProvider {
@Override
public Stream<RoleModel> getRealmRolesStream(RealmModel realm, Integer first, Integer max) {
return session.roleLocalStorage().getRealmRolesStream(realm, first, max);
return localStorage().getRealmRolesStream(realm, first, max);
}
@Override
public Stream<RoleModel> getRolesStream(RealmModel realm, Stream<String> ids, String search, Integer first, Integer max) {
return session.roleLocalStorage().getRolesStream(realm, ids, search, first, max);
return localStorage().getRolesStream(realm, ids, search, first, max);
}
/**
@ -164,7 +168,7 @@ public class RoleStorageManager implements RoleProvider {
*/
@Override
public Stream<RoleModel> searchForRolesStream(RealmModel realm, String search, Integer first, Integer max) {
Stream<RoleModel> local = session.roleLocalStorage().searchForRolesStream(realm, search, first, max);
Stream<RoleModel> local = localStorage().searchForRolesStream(realm, search, first, max);
Stream<RoleModel> ext = getEnabledStorageProviders(session, realm, RoleLookupProvider.class)
.flatMap(ServicesUtils.timeBound(session,
roleStorageProviderTimeout,
@ -178,32 +182,32 @@ public class RoleStorageManager implements RoleProvider {
if (!StorageId.isLocalStorage(role.getId())) {
throw new RuntimeException("Federated roles do not support this operation");
}
return session.roleLocalStorage().removeRole(role);
return localStorage().removeRole(role);
}
@Override
public void removeRoles(RealmModel realm) {
session.roleLocalStorage().removeRoles(realm);
localStorage().removeRoles(realm);
}
@Override
public void removeRoles(ClientModel client) {
session.roleLocalStorage().removeRoles(client);
localStorage().removeRoles(client);
}
@Override
public RoleModel addClientRole(ClientModel client, String name) {
return session.roleLocalStorage().addClientRole(client, name);
return localStorage().addClientRole(client, name);
}
@Override
public RoleModel addClientRole(ClientModel client, String id, String name) {
return session.roleLocalStorage().addClientRole(client, id, name);
return localStorage().addClientRole(client, id, name);
}
@Override
public RoleModel getClientRole(ClientModel client, String name) {
RoleModel clientRole = session.roleLocalStorage().getClientRole(client, name);
RoleModel clientRole = localStorage().getClientRole(client, name);
if (clientRole != null) return clientRole;
return getEnabledStorageProviders(session, client.getRealm(), RoleLookupProvider.class)
.map(provider -> provider.getClientRole(client, name))
@ -214,12 +218,12 @@ public class RoleStorageManager implements RoleProvider {
@Override
public Stream<RoleModel> getClientRolesStream(ClientModel client) {
return session.roleLocalStorage().getClientRolesStream(client);
return localStorage().getClientRolesStream(client);
}
@Override
public Stream<RoleModel> getClientRolesStream(ClientModel client, Integer first, Integer max) {
return session.roleLocalStorage().getClientRolesStream(client, first, max);
return localStorage().getClientRolesStream(client, first, max);
}
/**
@ -231,7 +235,7 @@ public class RoleStorageManager implements RoleProvider {
*/
@Override
public Stream<RoleModel> searchForClientRolesStream(ClientModel client, String search, Integer first, Integer max) {
Stream<RoleModel> local = session.roleLocalStorage().searchForClientRolesStream(client, search, first, max);
Stream<RoleModel> local = localStorage().searchForClientRolesStream(client, search, first, max);
Stream<RoleModel> ext = getEnabledStorageProviders(session, client.getRealm(), RoleLookupProvider.class)
.flatMap(ServicesUtils.timeBound(session,
roleStorageProviderTimeout,

View file

@ -0,0 +1,18 @@
#
# Copyright 2022 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.
#
org.keycloak.storage.datastore.LegacyDatastoreProviderFactory

View file

@ -0,0 +1,157 @@
package org.keycloak.storage.datastore;
import org.keycloak.models.ClientProvider;
import org.keycloak.models.ClientScopeProvider;
import org.keycloak.models.GroupProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.storage.ClientScopeStorageManager;
import org.keycloak.storage.ClientStorageManager;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.GroupStorageManager;
import org.keycloak.storage.RoleStorageManager;
public class LegacyDatastoreProvider implements DatastoreProvider {
private final LegacyDatastoreProviderFactory factory;
private final KeycloakSession session;
private ClientProvider clientProvider;
private ClientScopeProvider clientScopeProvider;
private GroupProvider groupProvider;
private RealmProvider realmProvider;
private RoleProvider roleProvider;
private ClientScopeStorageManager clientScopeStorageManager;
private RoleStorageManager roleStorageManager;
private GroupStorageManager groupStorageManager;
private ClientStorageManager clientStorageManager;
public LegacyDatastoreProvider(LegacyDatastoreProviderFactory factory, KeycloakSession session) {
this.factory = factory;
this.session = session;
}
@Override
public void close() {
}
public ClientProvider clientStorageManager() {
if (clientStorageManager == null) {
clientStorageManager = new ClientStorageManager(session, factory.getClientStorageProviderTimeout());
}
return clientStorageManager;
}
public ClientScopeProvider clientScopeStorageManager() {
if (clientScopeStorageManager == null) {
clientScopeStorageManager = new ClientScopeStorageManager(session);
}
return clientScopeStorageManager;
}
public RoleProvider roleStorageManager() {
if (roleStorageManager == null) {
roleStorageManager = new RoleStorageManager(session, factory.getRoleStorageProviderTimeout());
}
return roleStorageManager;
}
public GroupProvider groupStorageManager() {
if (groupStorageManager == null) {
groupStorageManager = new GroupStorageManager(session);
}
return groupStorageManager;
}
private ClientProvider getClientProvider() {
// TODO: Extract ClientProvider from CacheRealmProvider and use that instead
ClientProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return clientStorageManager();
}
}
private ClientScopeProvider getClientScopeProvider() {
// TODO: Extract ClientScopeProvider from CacheRealmProvider and use that instead
ClientScopeProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return clientScopeStorageManager();
}
}
private GroupProvider getGroupProvider() {
// TODO: Extract GroupProvider from CacheRealmProvider and use that instead
GroupProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return groupStorageManager();
}
}
private RealmProvider getRealmProvider() {
CacheRealmProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return session.getProvider(RealmProvider.class);
}
}
private RoleProvider getRoleProvider() {
// TODO: Extract RoleProvider from CacheRealmProvider and use that instead
RoleProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return roleStorageManager();
}
}
@Override
public ClientProvider clients() {
if (clientProvider == null) {
clientProvider = getClientProvider();
}
return clientProvider;
}
@Override
public ClientScopeProvider clientScopes() {
if (clientScopeProvider == null) {
clientScopeProvider = getClientScopeProvider();
}
return clientScopeProvider;
}
@Override
public GroupProvider groups() {
if (groupProvider == null) {
groupProvider = getGroupProvider();
}
return groupProvider;
}
@Override
public RealmProvider realms() {
if (realmProvider == null) {
realmProvider = getRealmProvider();
}
return realmProvider;
}
@Override
public RoleProvider roles() {
if (roleProvider == null) {
roleProvider = getRoleProvider();
}
return roleProvider;
}
}

View file

@ -0,0 +1,48 @@
package org.keycloak.storage.datastore;
import org.keycloak.Config;
import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.DatastoreProviderFactory;
public class LegacyDatastoreProviderFactory implements DatastoreProviderFactory {
private static final String PROVIDER_ID = "legacy";
private long clientStorageProviderTimeout;
private long roleStorageProviderTimeout;
@Override
public DatastoreProvider create(KeycloakSession session) {
return new LegacyDatastoreProvider(this, session);
}
@Override
public void init(Scope config) {
clientStorageProviderTimeout = Config.scope("client").getLong("storageProviderTimeout", 3000L);
roleStorageProviderTimeout = Config.scope("role").getLong("storageProviderTimeout", 3000L);
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
}
public long getClientStorageProviderTimeout() {
return clientStorageProviderTimeout;
}
public long getRoleStorageProviderTimeout() {
return roleStorageProviderTimeout;
}
}

View file

@ -0,0 +1,134 @@
/*
* Copyright 2020 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.utils;
import org.jboss.logging.Logger;
import org.keycloak.executors.ExecutorsProvider;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
/**
* Utility class for general helper methods used across the keycloak-services.
*/
public class ServicesUtils {
private static final Logger logger = Logger.getLogger(ServicesUtils.class);
public static <T, R> Function<? super T,? extends Stream<? extends R>> timeBound(KeycloakSession session,
long timeout,
Function<T, ? extends Stream<R>> func) {
ExecutorService executor = session.getProvider(ExecutorsProvider.class).getExecutor("storage-provider-threads");
return p -> {
// We are running another thread here, which serves as a time checking thread. When timeout is hit, the time
// checking thread will send interrupted flag to main thread, which can cause interruption of func execution.
// To support interruption func implementation should react to interrupt flag.
// If func doesn't check the interrupted flag, the execution won't be interrupted and can take more time
// than the threshold given by timeout variable
Future<?> timeCheckingThread = executor.submit(timeWarningRunnable(timeout, Thread.currentThread()));
try {
// We cannot run func in different than main thread, because main thread have, for example, EntityManager
// transaction context. If we run any operation on EntityManager in a different thread, it will fail
// with a transaction doesn't exist error
return func.apply(p);
} finally {
timeCheckingThread.cancel(true);
if (Thread.interrupted()) {
logger.warnf("Execution with object [%s] exceeded specified time limit %d. %s", p, timeout, getShortStackTrace());
}
}
};
}
public static <T, R> Function<? super T, R> timeBoundOne(KeycloakSession session,
long timeout,
Function<T, R> func) {
ExecutorService executor = session.getProvider(ExecutorsProvider.class).getExecutor("storage-provider-threads");
return p -> {
// We are running another thread here, which serves as a time checking thread. When timeout is hit, the time
// checking thread will send interrupted flag to main thread, which can cause interruption of func execution.
// To support interruption func implementation should react to interrupt flag.
// If func doesn't check the interrupted flag, the execution won't be interrupted and can take more time
// than the threshold given by timeout variable
Future<?> warningThreadFuture = executor.submit(timeWarningRunnable(timeout, Thread.currentThread()));
try {
// We cannot run func in different than main thread, because main thread have, for example, EntityManager
// transaction context. If we run any operation on EntityManager in a different thread, it will fail
// with a transaction doesn't exist error
return func.apply(p);
} finally {
warningThreadFuture.cancel(true);
if (Thread.interrupted()) {
logger.warnf("Execution with object [%s] exceeded specified time limit %d. %s", p, timeout, getShortStackTrace());
}
}
};
}
public static <T> Consumer<? super T> consumeWithTimeBound(KeycloakSession session,
long timeout,
Consumer<T> func) {
ExecutorService executor = session.getProvider(ExecutorsProvider.class).getExecutor("storage-provider-threads");
return p -> {
// We are running another thread here, which serves as a time checking thread. When timeout is hit, the time
// checking thread will send interrupted flag to main thread, which can cause interruption of func execution.
// To support interruption func implementation should react to interrupt flag.
// If func doesn't check the interrupted flag, the execution won't be interrupted and can take more time
// than the threshold given by timeout variable
Future<?> warningThreadFuture = executor.submit(timeWarningRunnable(timeout, Thread.currentThread()));
try {
// We cannot run func in different than main thread, because main thread have, for example, EntityManager
// transaction context. If we run any operation on EntityManager in a different thread, it will fail
// with a transaction doesn't exist error
func.accept(p);
} finally {
warningThreadFuture.cancel(true);
if (Thread.interrupted()) {
logger.warnf("Execution with object [%s] exceeded specified time limit %d. %s", p, timeout, getShortStackTrace());
}
}
};
}
private static Runnable timeWarningRunnable(long timeout, Thread mainThread) {
return new Runnable() {
@Override
public void run() {
try {
Thread.sleep(timeout);
} catch (InterruptedException exception) {
return; // Do not interrupt if warning thread was interrupted (== main thread finished execution in time)
}
mainThread.interrupt();
}
};
}
}

View file

@ -124,6 +124,10 @@ public class ModelToRepresentation {
}
public static GroupRepresentation groupToBriefRepresentation(GroupModel g) {
return toRepresentation(g, false);
}
public static GroupRepresentation toRepresentation(GroupModel group, boolean full) {
GroupRepresentation rep = new GroupRepresentation();
rep.setId(group.getId());

View file

@ -0,0 +1,22 @@
package org.keycloak.storage;
import org.keycloak.models.ClientProvider;
import org.keycloak.models.ClientScopeProvider;
import org.keycloak.models.GroupProvider;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleProvider;
import org.keycloak.provider.Provider;
public interface DatastoreProvider extends Provider {
public ClientScopeProvider clientScopes();
public ClientProvider clients();
public GroupProvider groups();
public RealmProvider realms();
public RoleProvider roles();
}

View file

@ -0,0 +1,7 @@
package org.keycloak.storage;
import org.keycloak.provider.ProviderFactory;
public interface DatastoreProviderFactory extends ProviderFactory<DatastoreProvider> {
}

View file

@ -0,0 +1,27 @@
package org.keycloak.storage;
import org.keycloak.provider.Spi;
public class DatastoreSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "datastore";
}
@Override
public Class<DatastoreProvider> getProviderClass() {
return DatastoreProvider.class;
}
@Override
public Class<DatastoreProviderFactory> getProviderFactoryClass() {
return DatastoreProviderFactory.class;
}
}

View file

@ -80,6 +80,7 @@ org.keycloak.credential.hash.PasswordHashSpi
org.keycloak.credential.CredentialSpi
org.keycloak.keys.PublicKeyStorageSpi
org.keycloak.keys.KeySpi
org.keycloak.storage.DatastoreSpi
org.keycloak.storage.client.ClientStorageProviderSpi
org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi
org.keycloak.storage.role.RoleStorageProviderSpi

View file

@ -225,6 +225,7 @@ public interface KeycloakSession {
/**
* @return ClientScopeStorageManager instance
* @deprecated Use {@link #clientScopes()} instead
*/
ClientScopeProvider clientScopeStorageManager();
@ -271,6 +272,7 @@ public interface KeycloakSession {
/**
* Keycloak specific local storage for client scopes. No cache in front, this api talks directly to database configured for Keycloak
*
* @deprecated Use {@link #clientScopes()} instead
* @return
*/
ClientScopeProvider clientScopeLocalStorage();

View file

@ -80,7 +80,7 @@ public class RedirectUtils {
@Deprecated
private static Set<String> getValidateRedirectUris(KeycloakSession session) {
RealmModel realm = session.getContext().getRealm();
return session.clientStorageManager().getAllRedirectUrisOfEnabledClients(realm).entrySet().stream()
return session.clients().getAllRedirectUrisOfEnabledClients(realm).entrySet().stream()
.filter(me -> me.getKey().isEnabled() && OIDCLoginProtocol.LOGIN_PROTOCOL.equals(me.getKey().getProtocol()) && !me.getKey().isBearerOnly() && (me.getKey().isStandardFlowEnabled() || me.getKey().isImplicitFlowEnabled()))
.map(me -> resolveValidRedirects(session, me.getKey().getRootUrl(), me.getValue()))
.flatMap(Collection::stream)

View file

@ -24,33 +24,29 @@ import org.keycloak.keys.DefaultKeyManager;
import org.keycloak.models.ClientProvider;
import org.keycloak.models.ClientScopeProvider;
import org.keycloak.models.GroupProvider;
import org.keycloak.models.UserLoginFailureProvider;
import org.keycloak.models.TokenManager;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransactionManager;
import org.keycloak.models.KeyManager;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.ThemeManager;
import org.keycloak.models.TokenManager;
import org.keycloak.models.UserCredentialManager;
import org.keycloak.models.UserLoginFailureProvider;
import org.keycloak.models.UserProvider;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.UserCache;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.InvalidationHandler.InvalidableObjectType;
import org.keycloak.provider.InvalidationHandler.ObjectType;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.InvalidationHandler.InvalidableObjectType;
import org.keycloak.provider.InvalidationHandler.ObjectType;
import org.keycloak.services.clientpolicy.ClientPolicyManager;
import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.storage.ClientStorageManager;
import org.keycloak.storage.ClientScopeStorageManager;
import org.keycloak.storage.GroupStorageManager;
import org.keycloak.storage.RoleStorageManager;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.UserStorageManager;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.vault.DefaultVaultTranscriber;
@ -80,16 +76,8 @@ public class DefaultKeycloakSession implements KeycloakSession {
private final DefaultKeycloakTransactionManager transactionManager;
private final Map<String, Object> attributes = new HashMap<>();
private final Map<InvalidableObjectType, Set<Object>> invalidationMap = new HashMap<>();
private RealmProvider model;
private ClientProvider clientProvider;
private ClientScopeProvider clientScopeProvider;
private GroupProvider groupProvider;
private RoleProvider roleProvider;
private DatastoreProvider datastoreProvider;
private UserStorageManager userStorageManager;
private ClientStorageManager clientStorageManager;
private ClientScopeStorageManager clientScopeStorageManager;
private RoleStorageManager roleStorageManager;
private GroupStorageManager groupStorageManager;
private UserCredentialStoreManager userCredentialStorageManager;
private UserSessionProvider sessionProvider;
private UserLoginFailureProvider userLoginFailureProvider;
@ -113,53 +101,11 @@ public class DefaultKeycloakSession implements KeycloakSession {
return context;
}
private RealmProvider getRealmProvider() {
CacheRealmProvider cache = getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return getProvider(RealmProvider.class);
}
}
private ClientProvider getClientProvider() {
// TODO: Extract ClientProvider from CacheRealmProvider and use that instead
ClientProvider cache = getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return clientStorageManager();
}
}
private ClientScopeProvider getClientScopeProvider() {
// TODO: Extract ClientScopeProvider from CacheRealmProvider and use that instead
ClientScopeProvider cache = getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return clientScopeStorageManager();
}
}
private GroupProvider getGroupProvider() {
// TODO: Extract GroupProvider from CacheRealmProvider and use that instead
GroupProvider cache = getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return groupStorageManager();
}
}
private RoleProvider getRoleProvider() {
// TODO: Extract RoleProvider from CacheRealmProvider and use that instead
RoleProvider cache = getProvider(CacheRealmProvider.class);
if (cache != null) {
return cache;
} else {
return roleStorageManager();
private DatastoreProvider getDatastoreProvider() {
if (this.datastoreProvider == null) {
this.datastoreProvider = getProvider(DatastoreProvider.class);
}
return this.datastoreProvider;
}
@Override
@ -228,59 +174,47 @@ public class DefaultKeycloakSession implements KeycloakSession {
@Override
public RealmProvider realmLocalStorage() {
return getProvider(RealmProvider.class);
return realms();
}
@Override
public ClientProvider clientLocalStorage() {
return getProvider(ClientProvider.class);
return clients();
}
@Override
public ClientScopeProvider clientScopeLocalStorage() {
return getProvider(ClientScopeProvider.class);
return clientScopes();
}
@Override
public GroupProvider groupLocalStorage() {
return getProvider(GroupProvider.class);
return groups();
}
@Override
public ClientProvider clientStorageManager() {
if (clientStorageManager == null) {
clientStorageManager = new ClientStorageManager(this, factory.getClientStorageProviderTimeout());
}
return clientStorageManager;
return clients();
}
@Override
public ClientScopeProvider clientScopeStorageManager() {
if (clientScopeStorageManager == null) {
clientScopeStorageManager = new ClientScopeStorageManager(this);
}
return clientScopeStorageManager;
return clientScopes();
}
@Override
public RoleProvider roleLocalStorage() {
return getProvider(RoleProvider.class);
return roles();
}
@Override
public RoleProvider roleStorageManager() {
if (roleStorageManager == null) {
roleStorageManager = new RoleStorageManager(this, factory.getRoleStorageProviderTimeout());
}
return roleStorageManager;
return roles();
}
@Override
public GroupProvider groupStorageManager() {
if (groupStorageManager == null) {
groupStorageManager = new GroupStorageManager(this);
}
return groupStorageManager;
return groups();
}
@ -413,42 +347,27 @@ public class DefaultKeycloakSession implements KeycloakSession {
@Override
public RealmProvider realms() {
if (model == null) {
model = getRealmProvider();
}
return model;
return getDatastoreProvider().realms();
}
@Override
public ClientProvider clients() {
if (clientProvider == null) {
clientProvider = getClientProvider();
}
return clientProvider;
return getDatastoreProvider().clients();
}
@Override
public ClientScopeProvider clientScopes() {
if (clientScopeProvider == null) {
clientScopeProvider = getClientScopeProvider();
}
return clientScopeProvider;
return getDatastoreProvider().clientScopes();
}
@Override
public GroupProvider groups() {
if (groupProvider == null) {
groupProvider = getGroupProvider();
}
return groupProvider;
return getDatastoreProvider().groups();
}
@Override
public RoleProvider roles() {
if (roleProvider == null) {
roleProvider = getRoleProvider();
}
return roleProvider;
return getDatastoreProvider().roles();
}

View file

@ -49,6 +49,8 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.fasterxml.jackson.core.type.TypeReference;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
@ -99,6 +101,7 @@ import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.PartialImportRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
@ -113,13 +116,9 @@ import org.keycloak.services.managers.UserStorageSyncManager;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.representations.idm.LDAPCapabilityRepresentation;
import org.keycloak.utils.ProfileHelper;
import org.keycloak.utils.ReservedCharValidator;
import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.utils.ServicesUtils;
/**
* Base resource class for the admin REST api of one realm
*
@ -1037,7 +1036,7 @@ public class RealmAdminResource {
public Stream<GroupRepresentation> getDefaultGroups() {
auth.realm().requireViewRealm();
return realm.getDefaultGroupsStream().map(ServicesUtils::groupToBriefRepresentation);
return realm.getDefaultGroupsStream().map(ModelToRepresentation::groupToBriefRepresentation);
}
@PUT
@NoCache

View file

@ -34,6 +34,7 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
/**
* Utility class for general helper methods used across the keycloak-services.
* @deprecated - DELETE once only used from within legacy datastore module
*/
public class ServicesUtils {
@ -131,8 +132,4 @@ public class ServicesUtils {
}
};
}
public static GroupRepresentation groupToBriefRepresentation(GroupModel g) {
return ModelToRepresentation.toRepresentation(g, false);
}
}

View file

@ -193,102 +193,102 @@
<profiles>
<profile>
<id>jpa</id>
<id>legacy-jpa</id>
<properties>
<keycloak.model.parameters>Jpa</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpa</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa+infinispan</id>
<id>legacy-jpa+infinispan</id>
<properties>
<keycloak.model.parameters>Infinispan,Jpa</keycloak.model.parameters>
<keycloak.model.parameters>Infinispan,LegacyJpa</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa+infinispan+client-storage</id>
<id>legacy-jpa+infinispan+client-storage</id>
<properties>
<keycloak.model.parameters>Jpa,Infinispan,HardcodedClientStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpa,Infinispan,HardcodedClientStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa+cross-dc-infinispan</id>
<id>legacy-jpa+cross-dc-infinispan</id>
<properties>
<keycloak.model.parameters>CrossDCInfinispan,Jpa</keycloak.model.parameters>
<keycloak.model.parameters>CrossDCInfinispan,LegacyJpa</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa+cross-dc-infinispan-offline-sessions-preloading</id>
<id>legacy-jpa+cross-dc-infinispan-offline-sessions-preloading</id>
<properties>
<keycloak.model.parameters>CrossDCInfinispan,Jpa</keycloak.model.parameters>
<keycloak.model.parameters>CrossDCInfinispan,LegacyJpa</keycloak.model.parameters>
<keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>true</keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>
</properties>
</profile>
<profile>
<id>jpa+infinispan-offline-sessions-preloading</id>
<id>legacy-jpa+infinispan-offline-sessions-preloading</id>
<properties>
<keycloak.model.parameters>Infinispan,Jpa</keycloak.model.parameters>
<keycloak.model.parameters>Infinispan,LegacyJpa</keycloak.model.parameters>
<keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>true</keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>
</properties>
</profile>
<profile>
<id>jpa-federation+infinispan</id>
<id>legacy-jpa-federation+infinispan</id>
<properties>
<keycloak.model.parameters>Infinispan,JpaFederation,TestsuiteUserMapStorage</keycloak.model.parameters>
<keycloak.model.parameters>Infinispan,LegacyJpaFederation,TestsuiteUserMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation-backward+infinispan</id>
<id>legacy-jpa-federation-backward+infinispan</id>
<properties>
<keycloak.model.parameters>Infinispan,JpaFederation,BackwardsCompatibilityUserStorage</keycloak.model.parameters>
<keycloak.model.parameters>Infinispan,LegacyJpaFederation,BackwardsCompatibilityUserStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation</id>
<id>legacy-jpa-federation</id>
<properties>
<keycloak.model.parameters>JpaFederation,TestsuiteUserMapStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpaFederation,TestsuiteUserMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation-backward</id>
<id>legacy-jpa-federation-backward</id>
<properties>
<keycloak.model.parameters>JpaFederation,BackwardsCompatibilityUserStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpaFederation,BackwardsCompatibilityUserStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation-file-storage</id>
<id>legacy-jpa-federation-file-storage</id>
<properties>
<keycloak.model.parameters>JpaFederation,TestsuiteUserFileStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpaFederation,TestsuiteUserFileStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation-file-storage+infinispan</id>
<id>legacy-jpa-federation-file-storage+infinispan</id>
<properties>
<keycloak.model.parameters>JpaFederation,TestsuiteUserFileStorage,Infinispan</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpaFederation,TestsuiteUserFileStorage,Infinispan</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation+ldap</id>
<id>legacy-jpa-federation+ldap</id>
<properties>
<keycloak.model.parameters>JpaFederation,LdapUserStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpaFederation,LdapUserStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>jpa-federation+ldap+infinispan</id>
<id>legacy-jpa-federation+ldap+infinispan</id>
<properties>
<keycloak.model.parameters>JpaFederation,LdapUserStorage,Infinispan</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpaFederation,LdapUserStorage,Infinispan</keycloak.model.parameters>
</properties>
</profile>
@ -296,7 +296,7 @@
<id>map</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Jpa,Map,ConcurrentHashMapStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpa,Map,ConcurrentHashMapStorage</keycloak.model.parameters>
</properties>
</profile>
@ -304,7 +304,7 @@
<id>hot-rod</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Jpa,Map,HotRodMapStorage</keycloak.model.parameters>
<keycloak.model.parameters>LegacyJpa,Map,HotRodMapStorage</keycloak.model.parameters>
</properties>
</profile>

View file

@ -38,6 +38,8 @@ import org.keycloak.models.jpa.JpaRoleProviderFactory;
import org.keycloak.models.jpa.JpaUserProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.storage.DatastoreSpi;
import org.keycloak.storage.datastore.LegacyDatastoreProviderFactory;
import org.keycloak.testsuite.model.Config;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
@ -48,7 +50,7 @@ import org.keycloak.protocol.LoginProtocolSpi;
*
* @author hmlnarik
*/
public class Jpa extends KeycloakModelParameters {
public class LegacyJpa extends KeycloakModelParameters {
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
// jpa-specific
@ -57,6 +59,8 @@ public class Jpa extends KeycloakModelParameters {
.add(LiquibaseConnectionSpi.class)
.add(UserSessionPersisterSpi.class)
.add(DatastoreSpi.class)
//required for migrateModel
.add(MigrationSpi.class)
.add(LoginProtocolSpi.class)
@ -65,6 +69,8 @@ public class Jpa extends KeycloakModelParameters {
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
// jpa-specific
.add(LegacyDatastoreProviderFactory.class)
.add(DefaultJpaConnectionProviderFactory.class)
.add(JPAAuthorizationStoreFactory.class)
.add(JpaClientProviderFactory.class)
@ -85,7 +91,7 @@ public class Jpa extends KeycloakModelParameters {
.build();
public Jpa() {
public LegacyJpa() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}

View file

@ -37,12 +37,12 @@ import org.keycloak.testsuite.model.Config;
*
* @author hmlnarik
*/
public class JpaFederation extends KeycloakModelParameters {
public class LegacyJpaFederation extends KeycloakModelParameters {
private final AtomicInteger counter = new AtomicInteger();
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.addAll(Jpa.ALLOWED_SPIS)
.addAll(LegacyJpa.ALLOWED_SPIS)
.add(UserStorageProviderSpi.class)
.add(UserFederatedStorageProviderSpi.class)
.add(ClientScopeStorageProviderSpi.class)
@ -50,12 +50,12 @@ public class JpaFederation extends KeycloakModelParameters {
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.addAll(Jpa.ALLOWED_FACTORIES)
.addAll(LegacyJpa.ALLOWED_FACTORIES)
.add(JpaUserFederatedStorageProviderFactory.class)
.add(ClientScopeStorageProviderFactory.class)
.build();
public JpaFederation() {
public LegacyJpaFederation() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
@ -74,6 +74,6 @@ public class JpaFederation extends KeycloakModelParameters {
@Override
public void updateConfig(Config cf) {
Jpa.updateConfigForJpa(cf);
LegacyJpa.updateConfigForJpa(cf);
}
}