parent
1f9b2be65a
commit
3602873df2
3 changed files with 172 additions and 93 deletions
|
@ -295,6 +295,24 @@ public class DeepCloner {
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a class type of an instance that would be instantiated by {@link #newInstance(java.lang.Class)} method.
|
||||
* @param <V> Type (class or a {@code @Root} interface) to create a new instance
|
||||
* @param clazz Type (class or a {@code @Root} interface) to create a new instance
|
||||
* @return See description
|
||||
*/
|
||||
public <V> Class<? extends V> newInstanceType(Class<V> valueType) {
|
||||
if (valueType == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
V v = newInstance(valueType);
|
||||
return v == null ? null : (Class<? extends V>) v.getClass();
|
||||
} catch (IllegalStateException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deeply clones properties from the {@code from} instance to the {@code to} instance.
|
||||
* @param <V> Type (class or a {@code @Root} interface) to clone the instance
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.map.storage;
|
||||
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.client.MapClientEntity;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeEntity;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.group.MapGroupEntity;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||
import org.keycloak.models.map.role.MapRoleEntity;
|
||||
import org.keycloak.models.map.user.MapUserEntity;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Utility class covering various aspects of relationship between model and entity classes.
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class ModelEntityUtil {
|
||||
|
||||
private static final Map<Class<?>, String> MODEL_TO_NAME = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_NAME.put(AuthenticatedClientSessionModel.class, "client-sessions");
|
||||
MODEL_TO_NAME.put(ClientScopeModel.class, "client-scopes");
|
||||
MODEL_TO_NAME.put(ClientModel.class, "clients");
|
||||
MODEL_TO_NAME.put(GroupModel.class, "groups");
|
||||
MODEL_TO_NAME.put(RealmModel.class, "realms");
|
||||
MODEL_TO_NAME.put(RoleModel.class, "roles");
|
||||
MODEL_TO_NAME.put(RootAuthenticationSessionModel.class, "auth-sessions");
|
||||
MODEL_TO_NAME.put(UserLoginFailureModel.class, "user-login-failures");
|
||||
MODEL_TO_NAME.put(UserModel.class, "users");
|
||||
MODEL_TO_NAME.put(UserSessionModel.class, "user-sessions");
|
||||
|
||||
// authz
|
||||
MODEL_TO_NAME.put(PermissionTicket.class, "authz-permission-tickets");
|
||||
MODEL_TO_NAME.put(Policy.class, "authz-policies");
|
||||
MODEL_TO_NAME.put(ResourceServer.class, "authz-resource-servers");
|
||||
MODEL_TO_NAME.put(Resource.class, "authz-resources");
|
||||
MODEL_TO_NAME.put(org.keycloak.authorization.model.Scope.class, "authz-scopes");
|
||||
}
|
||||
private static final Map<String, Class<?>> NAME_TO_MODEL = MODEL_TO_NAME.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
|
||||
|
||||
private static final Map<Class<?>, Class<? extends AbstractEntity>> MODEL_TO_ENTITY_TYPE = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_ENTITY_TYPE.put(AuthenticatedClientSessionModel.class, MapAuthenticatedClientSessionEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(ClientScopeModel.class, MapClientScopeEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(ClientModel.class, MapClientEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(GroupModel.class, MapGroupEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(RealmModel.class, MapRealmEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(RoleModel.class, MapRoleEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(RootAuthenticationSessionModel.class, MapRootAuthenticationSessionEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(UserLoginFailureModel.class, MapUserLoginFailureEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(UserModel.class, MapUserEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(UserSessionModel.class, MapUserSessionEntity.class);
|
||||
|
||||
// authz
|
||||
MODEL_TO_ENTITY_TYPE.put(PermissionTicket.class, MapPermissionTicketEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(Policy.class, MapPolicyEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(ResourceServer.class, MapResourceServerEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(Resource.class, MapResourceEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(org.keycloak.authorization.model.Scope.class, MapScopeEntity.class);
|
||||
}
|
||||
private static final Map<Class<?>, Class<?>> ENTITY_TO_MODEL_TYPE = MODEL_TO_ENTITY_TYPE.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V extends AbstractEntity, M> Class<V> getEntityType(Class<M> modelClass) {
|
||||
return (Class<V>) MODEL_TO_ENTITY_TYPE.get(modelClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V extends AbstractEntity, M> Class<V> getEntityType(Class<M> modelClass, Class<? extends AbstractEntity> defaultClass) {
|
||||
return (Class<V>) MODEL_TO_ENTITY_TYPE.getOrDefault(modelClass, defaultClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V extends AbstractEntity, M> Class<M> getModelType(Class<V> entityClass) {
|
||||
return (Class<M>) ENTITY_TO_MODEL_TYPE.get(entityClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V extends AbstractEntity, M> Class<M> getModelType(Class<V> entityClass, Class<M> defaultClass) {
|
||||
return (Class<M>) ENTITY_TO_MODEL_TYPE.getOrDefault(entityClass, defaultClass);
|
||||
}
|
||||
|
||||
public static String getModelName(Class<?> key, String defaultValue) {
|
||||
return MODEL_TO_NAME.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
public static String getModelName(Class<?> key) {
|
||||
return MODEL_TO_NAME.get(key);
|
||||
}
|
||||
|
||||
public static Set<String> getModelNames() {
|
||||
return NAME_TO_MODEL.keySet();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <M> Class<M> getModelClass(String key) {
|
||||
return (Class<M>) NAME_TO_MODEL.get(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -19,43 +19,20 @@ package org.keycloak.models.map.storage.chm;
|
|||
import org.keycloak.models.map.common.StringKeyConvertor;
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.Config.Scope;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.component.ComponentModelScope;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.client.MapClientEntity;
|
||||
import org.keycloak.models.map.client.MapClientEntityImpl;
|
||||
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
||||
import org.keycloak.models.map.client.MapProtocolMapperEntityImpl;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeEntity;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.common.UpdatableEntity;
|
||||
import org.keycloak.models.map.group.MapGroupEntity;
|
||||
import org.keycloak.models.map.group.MapGroupEntityImpl;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||
import org.keycloak.models.map.role.MapRoleEntity;
|
||||
import org.keycloak.models.map.role.MapRoleEntityImpl;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import java.io.File;
|
||||
|
@ -67,18 +44,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.user.MapUserEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||
import org.keycloak.models.map.storage.ModelEntityUtil;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.models.map.storage.ModelEntityUtil.getModelName;
|
||||
import static org.keycloak.models.map.storage.ModelEntityUtil.getModelNames;
|
||||
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
||||
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
||||
|
||||
|
@ -110,68 +86,6 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
|||
.constructor(MapRoleEntityImpl.class, MapRoleEntityImpl::new)
|
||||
.build();
|
||||
|
||||
public static final Map<Class<?>, String> MODEL_TO_NAME = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_NAME.put(AuthenticatedClientSessionModel.class, "client-sessions");
|
||||
MODEL_TO_NAME.put(ClientScopeModel.class, "client-scopes");
|
||||
MODEL_TO_NAME.put(ClientModel.class, "clients");
|
||||
MODEL_TO_NAME.put(GroupModel.class, "groups");
|
||||
MODEL_TO_NAME.put(RealmModel.class, "realms");
|
||||
MODEL_TO_NAME.put(RoleModel.class, "roles");
|
||||
MODEL_TO_NAME.put(RootAuthenticationSessionModel.class, "auth-sessions");
|
||||
MODEL_TO_NAME.put(UserLoginFailureModel.class, "user-login-failures");
|
||||
MODEL_TO_NAME.put(UserModel.class, "users");
|
||||
MODEL_TO_NAME.put(UserSessionModel.class, "user-sessions");
|
||||
|
||||
// authz
|
||||
MODEL_TO_NAME.put(PermissionTicket.class, "authz-permission-tickets");
|
||||
MODEL_TO_NAME.put(Policy.class, "authz-policies");
|
||||
MODEL_TO_NAME.put(ResourceServer.class, "authz-resource-servers");
|
||||
MODEL_TO_NAME.put(Resource.class, "authz-resources");
|
||||
MODEL_TO_NAME.put(org.keycloak.authorization.model.Scope.class, "authz-scopes");
|
||||
}
|
||||
|
||||
public static final Map<Class<?>, Class<? extends AbstractEntity>> MODEL_TO_VALUE_TYPE = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_VALUE_TYPE.put(AuthenticatedClientSessionModel.class, MapAuthenticatedClientSessionEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(ClientScopeModel.class, MapClientScopeEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(ClientModel.class, MapClientEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(GroupModel.class, MapGroupEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(RealmModel.class, MapRealmEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(RoleModel.class, MapRoleEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(RootAuthenticationSessionModel.class, MapRootAuthenticationSessionEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(UserLoginFailureModel.class, MapUserLoginFailureEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(UserModel.class, MapUserEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(UserSessionModel.class, MapUserSessionEntity.class);
|
||||
|
||||
// authz
|
||||
MODEL_TO_VALUE_TYPE.put(PermissionTicket.class, MapPermissionTicketEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(Policy.class, MapPolicyEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(ResourceServer.class, MapResourceServerEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(Resource.class, MapResourceEntity.class);
|
||||
MODEL_TO_VALUE_TYPE.put(org.keycloak.authorization.model.Scope.class, MapScopeEntity.class);
|
||||
}
|
||||
|
||||
public static final Map<Class<?>, Class<?>> INTERFACE_TO_IMPL = new HashMap<>();
|
||||
static {
|
||||
INTERFACE_TO_IMPL.put(MapClientEntity.class, MapClientEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapClientScopeEntity.class, MapClientScopeEntityImpl.class);
|
||||
INTERFACE_TO_IMPL.put(MapGroupEntity.class, MapGroupEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapRealmEntity.class, MapRealmEntityImpl.class);
|
||||
INTERFACE_TO_IMPL.put(MapRoleEntity.class, MapRoleEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapRootAuthenticationSessionEntity.class, MapRootAuthenticationSessionEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapUserLoginFailureEntity.class, MapUserLoginFailureEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapUserEntity.class, MapUserEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapUserSessionEntity.class, MapUserSessionEntityImpl.class);
|
||||
//
|
||||
// // authz
|
||||
// INTERFACE_TO_IMPL.put(MapPermissionTicketEntity.class, MapPermissionTicketEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapPolicyEntity.class, MapPolicyEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapResourceServerEntity.class, MapResourceServerEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapResourceEntity.class, MapResourceEntityImpl.class);
|
||||
// INTERFACE_TO_IMPL.put(MapScopeEntity.class, MapScopeEntityImpl.class);
|
||||
}
|
||||
|
||||
private static final Map<String, StringKeyConvertor> KEY_CONVERTORS = new HashMap<>();
|
||||
static {
|
||||
KEY_CONVERTORS.put("uuid", StringKeyConvertor.UUIDKey.INSTANCE);
|
||||
|
@ -195,7 +109,7 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
|||
|
||||
final String keyType = config.get("keyType", "uuid");
|
||||
defaultKeyConvertor = getKeyConvertor(keyType);
|
||||
for (String name : MODEL_TO_NAME.values()) {
|
||||
for (String name : getModelNames()) {
|
||||
keyConvertors.put(name, getKeyConvertor(config.get("keyType." + name, keyType)));
|
||||
}
|
||||
|
||||
|
@ -259,7 +173,7 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
|||
private <K, V extends AbstractEntity & UpdatableEntity, M> ConcurrentHashMapStorage<K, V, M> loadMap(String mapName,
|
||||
Class<M> modelType, EnumSet<Flag> flags) {
|
||||
final StringKeyConvertor kc = keyConvertors.getOrDefault(mapName, defaultKeyConvertor);
|
||||
Class<?> valueType = MODEL_TO_VALUE_TYPE.get(modelType);
|
||||
Class<?> valueType = ModelEntityUtil.getEntityType(modelType);
|
||||
LOG.debugf("Initializing new map storage: %s", mapName);
|
||||
|
||||
ConcurrentHashMapStorage<K, V, M> store;
|
||||
|
@ -285,7 +199,10 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
|||
if (f != null && f.exists()) {
|
||||
try {
|
||||
LOG.debugf("Restoring contents from %s", f.getCanonicalPath());
|
||||
Class<?> valueImplType = INTERFACE_TO_IMPL.getOrDefault(valueType, valueType);
|
||||
Class<?> valueImplType = CLONER.newInstanceType(valueType);
|
||||
if (valueImplType == null) {
|
||||
valueImplType = valueType;
|
||||
}
|
||||
JavaType type = Serialization.MAPPER.getTypeFactory().constructCollectionType(LinkedList.class, valueImplType);
|
||||
|
||||
List<V> values = Serialization.MAPPER.readValue(f, type);
|
||||
|
@ -308,7 +225,7 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
|||
public <K, V extends AbstractEntity & UpdatableEntity, M> ConcurrentHashMapStorage<K, V, M> getStorage(
|
||||
Class<M> modelType, Flag... flags) {
|
||||
EnumSet<Flag> f = flags == null || flags.length == 0 ? EnumSet.noneOf(Flag.class) : EnumSet.of(flags[0], flags);
|
||||
String name = MODEL_TO_NAME.getOrDefault(modelType, modelType.getSimpleName());
|
||||
String name = getModelName(modelType, modelType.getSimpleName());
|
||||
/* From ConcurrentHashMapStorage.computeIfAbsent javadoc:
|
||||
*
|
||||
* "... the computation [...] must not attempt to update any other mappings of this map."
|
||||
|
|
Loading…
Reference in a new issue