Skip to content

Commit

Permalink
Introduce model-entity util methods
Browse files Browse the repository at this point in the history
  • Loading branch information
hmlnarik authored and laskasn committed Jan 20, 2022
1 parent f512d70 commit e099517
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,24 @@ public <V> V newInstance(Class<V> clazz) {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,20 @@
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;
Expand All @@ -67,18 +44,17 @@
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;

Expand Down Expand Up @@ -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);
Expand All @@ -195,7 +109,7 @@ public void init(Scope config) {

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)));
}

Expand Down Expand Up @@ -259,7 +173,7 @@ private void storeMap(String mapName, ConcurrentHashMapStorage<?, ?, ?> store) {
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;
Expand All @@ -285,7 +199,10 @@ public String toString() {
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);
Expand All @@ -308,7 +225,7 @@ public String getId() {
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."
Expand Down

0 comments on commit e099517

Please sign in to comment.