From 009d4ca445781d00901d7900c0a0fa0fc6e48ec7 Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Wed, 14 Jul 2021 11:46:34 +0200 Subject: [PATCH] KEYCLOAK-18747 Turn MapClientEntity into interface + introduce delegates Given that the Map*Entity is turned into an interface, it makes more sense to use non-primitive types to allow for null values. This enables signalizing that an entity does not define a particular value, and builds a base for definition of instances with defaults: If a value is not present in the queried instance (i.e. is `null`), the value would be obtained from a delegate containing the defaults. --- .../models/map/client/MapClientEntity.java | 611 +++++------------- .../map/client/MapClientEntityDelegate.java | 36 ++ .../map/client/MapClientEntityImpl.java | 559 ++++++++++++++++ .../client/MapClientEntityLazyDelegate.java | 468 ++++++++++++++ .../models/map/client/MapClientProvider.java | 6 +- ...ncurrentHashMapStorageProviderFactory.java | 39 +- .../testsuite/model/MapStorageTest.java | 7 +- 7 files changed, 1272 insertions(+), 454 deletions(-) create mode 100644 model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java create mode 100644 model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java create mode 100644 model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java index b07580d5a8..1ac88c177d 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Red Hat, Inc. and/or its affiliates + * 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"); @@ -19,460 +19,177 @@ package org.keycloak.models.map.client; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.map.common.AbstractEntity; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; /** * * @author hmlnarik */ -public class MapClientEntity implements AbstractEntity { - - private K id; - private String realmId; - - private String clientId; - private String name; - private String description; - private Set redirectUris = new HashSet<>(); - private boolean enabled; - private boolean alwaysDisplayInConsole; - private String clientAuthenticatorType; - private String secret; - private String registrationToken; - private String protocol; - private Map> attributes = new HashMap<>(); - private Map authFlowBindings = new HashMap<>(); - private boolean publicClient; - private boolean fullScopeAllowed; - private boolean frontchannelLogout; - private int notBefore; - private Set scope = new HashSet<>(); - private Set webOrigins = new HashSet<>(); - private Map protocolMappers = new HashMap<>(); - private Map clientScopes = new HashMap<>(); - private Set scopeMappings = new LinkedHashSet<>(); - private boolean surrogateAuthRequired; - private String managementUrl; - private String rootUrl; - private String baseUrl; - private boolean bearerOnly; - private boolean consentRequired; - private boolean standardFlowEnabled; - private boolean implicitFlowEnabled; - private boolean directAccessGrantsEnabled; - private boolean serviceAccountsEnabled; - private int nodeReRegistrationTimeout; - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - protected boolean updated; - - protected MapClientEntity() { - this.id = null; - this.realmId = null; - } - - public MapClientEntity(K id, String realmId) { - Objects.requireNonNull(id, "id"); - Objects.requireNonNull(realmId, "realmId"); - - this.id = id; - this.realmId = realmId; - } - - @Override - public K getId() { - return this.id; - } - - @Override - public boolean isUpdated() { - return this.updated; - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.updated |= ! Objects.equals(this.clientId, clientId); - this.clientId = clientId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.updated |= ! Objects.equals(this.description, description); - this.description = description; - } - - public Set getRedirectUris() { - return redirectUris; - } - - public void setRedirectUris(Set redirectUris) { - this.updated |= ! Objects.equals(this.redirectUris, redirectUris); - this.redirectUris = redirectUris; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.updated |= ! Objects.equals(this.enabled, enabled); - this.enabled = enabled; - } - - public boolean isAlwaysDisplayInConsole() { - return alwaysDisplayInConsole; - } - - public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) { - this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole); - this.alwaysDisplayInConsole = alwaysDisplayInConsole; - } - - public String getClientAuthenticatorType() { - return clientAuthenticatorType; - } - - public void setClientAuthenticatorType(String clientAuthenticatorType) { - this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType); - this.clientAuthenticatorType = clientAuthenticatorType; - } - - public String getSecret() { - return secret; - } - - public void setSecret(String secret) { - this.updated |= ! Objects.equals(this.secret, secret); - this.secret = secret; - } - - public String getRegistrationToken() { - return registrationToken; - } - - public void setRegistrationToken(String registrationToken) { - this.updated |= ! Objects.equals(this.registrationToken, registrationToken); - this.registrationToken = registrationToken; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.updated |= ! Objects.equals(this.protocol, protocol); - this.protocol = protocol; - } - - public Map> getAttributes() { - return attributes; - } - - public void setAttribute(String name, List values) { - this.updated |= ! Objects.equals(this.attributes.put(name, values), values); - } - - public Map getAuthFlowBindings() { - return authFlowBindings; - } - - public void setAuthFlowBindings(Map authFlowBindings) { - this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings); - this.authFlowBindings = authFlowBindings; - } - - public boolean isPublicClient() { - return publicClient; - } - - public void setPublicClient(boolean publicClient) { - this.updated |= ! Objects.equals(this.publicClient, publicClient); - this.publicClient = publicClient; - } - - public boolean isFullScopeAllowed() { - return fullScopeAllowed; - } - - public void setFullScopeAllowed(boolean fullScopeAllowed) { - this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed); - this.fullScopeAllowed = fullScopeAllowed; - } - - public boolean isFrontchannelLogout() { - return frontchannelLogout; - } - - public void setFrontchannelLogout(boolean frontchannelLogout) { - this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout); - this.frontchannelLogout = frontchannelLogout; - } - - public int getNotBefore() { - return notBefore; - } - - public void setNotBefore(int notBefore) { - this.updated |= ! Objects.equals(this.notBefore, notBefore); - this.notBefore = notBefore; - } - - public Set getScope() { - return scope; - } - - public void setScope(Set scope) { - this.updated |= ! Objects.equals(this.scope, scope); - this.scope.clear(); - this.scope.addAll(scope); - } - - public Set getWebOrigins() { - return webOrigins; - } - - public void setWebOrigins(Set webOrigins) { - this.updated |= ! Objects.equals(this.webOrigins, webOrigins); - this.webOrigins.clear(); - this.webOrigins.addAll(webOrigins); - } - - public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { - Objects.requireNonNull(model.getId(), "protocolMapper.id"); - updated = true; - this.protocolMappers.put(model.getId(), model); - return model; - } - - public Collection getProtocolMappers() { - return protocolMappers.values(); - } - - public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { - updated = true; - protocolMappers.put(id, mapping); - } - - public void removeProtocolMapper(String id) { - updated |= protocolMappers.remove(id) != null; - } - - public void setProtocolMappers(Collection protocolMappers) { - this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers); - this.protocolMappers.clear(); - this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity()))); - } - - public ProtocolMapperModel getProtocolMapperById(String id) { - return id == null ? null : protocolMappers.get(id); - } - - public boolean isSurrogateAuthRequired() { - return surrogateAuthRequired; - } - - public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { - this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired); - this.surrogateAuthRequired = surrogateAuthRequired; - } - - public String getManagementUrl() { - return managementUrl; - } - - public void setManagementUrl(String managementUrl) { - this.updated |= ! Objects.equals(this.managementUrl, managementUrl); - this.managementUrl = managementUrl; - } - - public String getRootUrl() { - return rootUrl; - } - - public void setRootUrl(String rootUrl) { - this.updated |= ! Objects.equals(this.rootUrl, rootUrl); - this.rootUrl = rootUrl; - } - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.updated |= ! Objects.equals(this.baseUrl, baseUrl); - this.baseUrl = baseUrl; - } - - public boolean isBearerOnly() { - return bearerOnly; - } - - public void setBearerOnly(boolean bearerOnly) { - this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly); - this.bearerOnly = bearerOnly; - } - - public boolean isConsentRequired() { - return consentRequired; - } - - public void setConsentRequired(boolean consentRequired) { - this.updated |= ! Objects.equals(this.consentRequired, consentRequired); - this.consentRequired = consentRequired; - } - - public boolean isStandardFlowEnabled() { - return standardFlowEnabled; - } - - public void setStandardFlowEnabled(boolean standardFlowEnabled) { - this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled); - this.standardFlowEnabled = standardFlowEnabled; - } - - public boolean isImplicitFlowEnabled() { - return implicitFlowEnabled; - } - - public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { - this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled); - this.implicitFlowEnabled = implicitFlowEnabled; - } - - public boolean isDirectAccessGrantsEnabled() { - return directAccessGrantsEnabled; - } - - public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { - this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled); - this.directAccessGrantsEnabled = directAccessGrantsEnabled; - } - - public boolean isServiceAccountsEnabled() { - return serviceAccountsEnabled; - } - - public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { - this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled); - this.serviceAccountsEnabled = serviceAccountsEnabled; - } - - public int getNodeReRegistrationTimeout() { - return nodeReRegistrationTimeout; - } - - public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) { - this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout); - this.nodeReRegistrationTimeout = nodeReRegistrationTimeout; - } - - public void addWebOrigin(String webOrigin) { - updated = true; - this.webOrigins.add(webOrigin); - } - - public void removeWebOrigin(String webOrigin) { - updated |= this.webOrigins.remove(webOrigin); - } - - public void addRedirectUri(String redirectUri) { - this.updated |= ! this.redirectUris.contains(redirectUri); - this.redirectUris.add(redirectUri); - } - - public void removeRedirectUri(String redirectUri) { - updated |= this.redirectUris.remove(redirectUri); - } - - public void removeAttribute(String name) { - this.updated |= this.attributes.remove(name) != null; - } - - public List getAttribute(String name) { - return attributes.getOrDefault(name, Collections.EMPTY_LIST); - } - - public String getAuthenticationFlowBindingOverride(String binding) { - return this.authFlowBindings.get(binding); - } - - public Map getAuthenticationFlowBindingOverrides() { - return this.authFlowBindings; - } - - public void removeAuthenticationFlowBindingOverride(String binding) { - updated |= this.authFlowBindings.remove(binding) != null; - } - - public void setAuthenticationFlowBindingOverride(String binding, String flowId) { - this.updated = true; - this.authFlowBindings.put(binding, flowId); - } - - public Collection getScopeMappings() { - return scopeMappings; - } - - public void addScopeMapping(String id) { - if (id != null) { - updated = true; - scopeMappings.add(id); - } - } - - public void deleteScopeMapping(String id) { - updated |= scopeMappings.remove(id); - } - - public void addClientScope(String id, boolean defaultScope) { - if (id != null) { - updated = true; - this.clientScopes.put(id, defaultScope); - } - } - - public void removeClientScope(String id) { - if (id != null) { - updated |= clientScopes.remove(id) != null; - } - } - - public Stream getClientScopes(boolean defaultScope) { - return this.clientScopes.entrySet().stream() - .filter(me -> Objects.equals(me.getValue(), defaultScope)) - .map(Entry::getKey); - } - - public String getRealmId() { - return this.realmId; - } +public interface MapClientEntity extends AbstractEntity { + + void addClientScope(String id, Boolean defaultScope); + + ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model); + + void addRedirectUri(String redirectUri); + + void addScopeMapping(String id); + + void addWebOrigin(String webOrigin); + + void deleteScopeMapping(String id); + + List getAttribute(String name); + + Map> getAttributes(); + + Map getAuthFlowBindings(); + + String getAuthenticationFlowBindingOverride(String binding); + + Map getAuthenticationFlowBindingOverrides(); + + String getBaseUrl(); + + String getClientAuthenticatorType(); + + String getClientId(); + + Stream getClientScopes(boolean defaultScope); + + String getDescription(); + + String getManagementUrl(); + + String getName(); + + int getNodeReRegistrationTimeout(); + + int getNotBefore(); + + String getProtocol(); + + ProtocolMapperModel getProtocolMapperById(String id); + + Collection getProtocolMappers(); + + String getRealmId(); + + Set getRedirectUris(); + + String getRegistrationToken(); + + String getRootUrl(); + + Set getScope(); + + Collection getScopeMappings(); + + String getSecret(); + + Set getWebOrigins(); + + Boolean isAlwaysDisplayInConsole(); + + Boolean isBearerOnly(); + + Boolean isConsentRequired(); + + Boolean isDirectAccessGrantsEnabled(); + + Boolean isEnabled(); + + Boolean isFrontchannelLogout(); + + Boolean isFullScopeAllowed(); + + Boolean isImplicitFlowEnabled(); + + Boolean isPublicClient(); + + Boolean isServiceAccountsEnabled(); + + Boolean isStandardFlowEnabled(); + + Boolean isSurrogateAuthRequired(); + + void removeAttribute(String name); + + void removeAuthenticationFlowBindingOverride(String binding); + + void removeClientScope(String id); + + void removeProtocolMapper(String id); + + void removeRedirectUri(String redirectUri); + + void removeWebOrigin(String webOrigin); + + void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole); + + void setAttribute(String name, List values); + + void setAuthFlowBindings(Map authFlowBindings); + + void setAuthenticationFlowBindingOverride(String binding, String flowId); + + void setBaseUrl(String baseUrl); + + void setBearerOnly(Boolean bearerOnly); + + void setClientAuthenticatorType(String clientAuthenticatorType); + + void setClientId(String clientId); + + void setConsentRequired(Boolean consentRequired); + + void setDescription(String description); + + void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled); + + void setEnabled(Boolean enabled); + + void setFrontchannelLogout(Boolean frontchannelLogout); + + void setFullScopeAllowed(Boolean fullScopeAllowed); + + void setImplicitFlowEnabled(Boolean implicitFlowEnabled); + + void setManagementUrl(String managementUrl); + + void setName(String name); + + void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout); + + void setNotBefore(int notBefore); + + void setProtocol(String protocol); + + void setProtocolMappers(Collection protocolMappers); + + void setPublicClient(Boolean publicClient); + + void setRedirectUris(Set redirectUris); + + void setRegistrationToken(String registrationToken); + + void setRootUrl(String rootUrl); + + void setScope(Set scope); + + void setSecret(String secret); + + void setServiceAccountsEnabled(Boolean serviceAccountsEnabled); + + void setStandardFlowEnabled(Boolean standardFlowEnabled); + + void setSurrogateAuthRequired(Boolean surrogateAuthRequired); + + void setWebOrigins(Set webOrigins); + + void updateProtocolMapper(String id, ProtocolMapperModel mapping); } diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java new file mode 100644 index 0000000000..13420e3da0 --- /dev/null +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java @@ -0,0 +1,36 @@ +/* + * 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.client; + +/** + * + * @author hmlnarik + */ +public class MapClientEntityDelegate extends MapClientEntityLazyDelegate { + + private final MapClientEntity delegate; + + public MapClientEntityDelegate(MapClientEntity delegate) { + super(null); + this.delegate = delegate; + } + + @Override + protected MapClientEntity getDelegate() { + return delegate; + } +} diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java new file mode 100644 index 0000000000..2e29c30e3f --- /dev/null +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java @@ -0,0 +1,559 @@ +/* + * 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.models.map.client; + +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.map.common.AbstractEntity; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * + * @author hmlnarik + */ +public class MapClientEntityImpl implements MapClientEntity { + + private K id; + private String realmId; + + private String clientId; + private String name; + private String description; + private Set redirectUris = new HashSet<>(); + private boolean enabled; + private boolean alwaysDisplayInConsole; + private String clientAuthenticatorType; + private String secret; + private String registrationToken; + private String protocol; + private Map> attributes = new HashMap<>(); + private Map authFlowBindings = new HashMap<>(); + private boolean publicClient; + private boolean fullScopeAllowed; + private boolean frontchannelLogout; + private int notBefore; + private Set scope = new HashSet<>(); + private Set webOrigins = new HashSet<>(); + private Map protocolMappers = new HashMap<>(); + private Map clientScopes = new HashMap<>(); + private Set scopeMappings = new LinkedHashSet<>(); + private boolean surrogateAuthRequired; + private String managementUrl; + private String rootUrl; + private String baseUrl; + private boolean bearerOnly; + private boolean consentRequired; + private boolean standardFlowEnabled; + private boolean implicitFlowEnabled; + private boolean directAccessGrantsEnabled; + private boolean serviceAccountsEnabled; + private int nodeReRegistrationTimeout; + + /** + * Flag signalizing that any of the setters has been meaningfully used. + */ + protected boolean updated; + + protected MapClientEntityImpl() { + this.id = null; + this.realmId = null; + } + + public MapClientEntityImpl(K id, String realmId) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(realmId, "realmId"); + + this.id = id; + this.realmId = realmId; + } + + @Override + public K getId() { + return this.id; + } + + @Override + public boolean isUpdated() { + return this.updated; + } + + @Override + public String getClientId() { + return clientId; + } + + @Override + public void setClientId(String clientId) { + this.updated |= ! Objects.equals(this.clientId, clientId); + this.clientId = clientId; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.updated |= ! Objects.equals(this.name, name); + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.updated |= ! Objects.equals(this.description, description); + this.description = description; + } + + @Override + public Set getRedirectUris() { + return redirectUris; + } + + @Override + public void setRedirectUris(Set redirectUris) { + this.updated |= ! Objects.equals(this.redirectUris, redirectUris); + this.redirectUris = redirectUris; + } + + @Override + public Boolean isEnabled() { + return enabled; + } + + @Override + public void setEnabled(Boolean enabled) { + this.updated |= ! Objects.equals(this.enabled, enabled); + this.enabled = enabled; + } + + @Override + public Boolean isAlwaysDisplayInConsole() { + return alwaysDisplayInConsole; + } + + @Override + public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) { + this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole); + this.alwaysDisplayInConsole = alwaysDisplayInConsole; + } + + @Override + public String getClientAuthenticatorType() { + return clientAuthenticatorType; + } + + @Override + public void setClientAuthenticatorType(String clientAuthenticatorType) { + this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType); + this.clientAuthenticatorType = clientAuthenticatorType; + } + + @Override + public String getSecret() { + return secret; + } + + @Override + public void setSecret(String secret) { + this.updated |= ! Objects.equals(this.secret, secret); + this.secret = secret; + } + + @Override + public String getRegistrationToken() { + return registrationToken; + } + + @Override + public void setRegistrationToken(String registrationToken) { + this.updated |= ! Objects.equals(this.registrationToken, registrationToken); + this.registrationToken = registrationToken; + } + + @Override + public String getProtocol() { + return protocol; + } + + @Override + public void setProtocol(String protocol) { + this.updated |= ! Objects.equals(this.protocol, protocol); + this.protocol = protocol; + } + + @Override + public Map> getAttributes() { + return attributes; + } + + @Override + public void setAttribute(String name, List values) { + this.updated |= ! Objects.equals(this.attributes.put(name, values), values); + } + + @Override + public Map getAuthFlowBindings() { + return authFlowBindings; + } + + @Override + public void setAuthFlowBindings(Map authFlowBindings) { + this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings); + this.authFlowBindings = authFlowBindings; + } + + @Override + public Boolean isPublicClient() { + return publicClient; + } + + @Override + public void setPublicClient(Boolean publicClient) { + this.updated |= ! Objects.equals(this.publicClient, publicClient); + this.publicClient = publicClient; + } + + @Override + public Boolean isFullScopeAllowed() { + return fullScopeAllowed; + } + + @Override + public void setFullScopeAllowed(Boolean fullScopeAllowed) { + this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed); + this.fullScopeAllowed = fullScopeAllowed; + } + + @Override + public Boolean isFrontchannelLogout() { + return frontchannelLogout; + } + + @Override + public void setFrontchannelLogout(Boolean frontchannelLogout) { + this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout); + this.frontchannelLogout = frontchannelLogout; + } + + @Override + public int getNotBefore() { + return notBefore; + } + + @Override + public void setNotBefore(int notBefore) { + this.updated |= ! Objects.equals(this.notBefore, notBefore); + this.notBefore = notBefore; + } + + @Override + public Set getScope() { + return scope; + } + + @Override + public void setScope(Set scope) { + this.updated |= ! Objects.equals(this.scope, scope); + this.scope.clear(); + this.scope.addAll(scope); + } + + @Override + public Set getWebOrigins() { + return webOrigins; + } + + @Override + public void setWebOrigins(Set webOrigins) { + this.updated |= ! Objects.equals(this.webOrigins, webOrigins); + this.webOrigins.clear(); + this.webOrigins.addAll(webOrigins); + } + + @Override + public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { + Objects.requireNonNull(model.getId(), "protocolMapper.id"); + updated = true; + this.protocolMappers.put(model.getId(), model); + return model; + } + + @Override + public Collection getProtocolMappers() { + return protocolMappers.values(); + } + + @Override + public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { + updated = true; + protocolMappers.put(id, mapping); + } + + @Override + public void removeProtocolMapper(String id) { + updated |= protocolMappers.remove(id) != null; + } + + @Override + public void setProtocolMappers(Collection protocolMappers) { + this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers); + this.protocolMappers.clear(); + this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity()))); + } + + @Override + public ProtocolMapperModel getProtocolMapperById(String id) { + return id == null ? null : protocolMappers.get(id); + } + + @Override + public Boolean isSurrogateAuthRequired() { + return surrogateAuthRequired; + } + + @Override + public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) { + this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired); + this.surrogateAuthRequired = surrogateAuthRequired; + } + + @Override + public String getManagementUrl() { + return managementUrl; + } + + @Override + public void setManagementUrl(String managementUrl) { + this.updated |= ! Objects.equals(this.managementUrl, managementUrl); + this.managementUrl = managementUrl; + } + + @Override + public String getRootUrl() { + return rootUrl; + } + + @Override + public void setRootUrl(String rootUrl) { + this.updated |= ! Objects.equals(this.rootUrl, rootUrl); + this.rootUrl = rootUrl; + } + + @Override + public String getBaseUrl() { + return baseUrl; + } + + @Override + public void setBaseUrl(String baseUrl) { + this.updated |= ! Objects.equals(this.baseUrl, baseUrl); + this.baseUrl = baseUrl; + } + + @Override + public Boolean isBearerOnly() { + return bearerOnly; + } + + @Override + public void setBearerOnly(Boolean bearerOnly) { + this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly); + this.bearerOnly = bearerOnly; + } + + @Override + public Boolean isConsentRequired() { + return consentRequired; + } + + @Override + public void setConsentRequired(Boolean consentRequired) { + this.updated |= ! Objects.equals(this.consentRequired, consentRequired); + this.consentRequired = consentRequired; + } + + @Override + public Boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + @Override + public void setStandardFlowEnabled(Boolean standardFlowEnabled) { + this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled); + this.standardFlowEnabled = standardFlowEnabled; + } + + @Override + public Boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + @Override + public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) { + this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled); + this.implicitFlowEnabled = implicitFlowEnabled; + } + + @Override + public Boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + @Override + public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) { + this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled); + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + + @Override + public Boolean isServiceAccountsEnabled() { + return serviceAccountsEnabled; + } + + @Override + public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) { + this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled); + this.serviceAccountsEnabled = serviceAccountsEnabled; + } + + @Override + public int getNodeReRegistrationTimeout() { + return nodeReRegistrationTimeout; + } + + @Override + public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) { + this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout); + this.nodeReRegistrationTimeout = nodeReRegistrationTimeout; + } + + @Override + public void addWebOrigin(String webOrigin) { + updated = true; + this.webOrigins.add(webOrigin); + } + + @Override + public void removeWebOrigin(String webOrigin) { + updated |= this.webOrigins.remove(webOrigin); + } + + @Override + public void addRedirectUri(String redirectUri) { + this.updated |= ! this.redirectUris.contains(redirectUri); + this.redirectUris.add(redirectUri); + } + + @Override + public void removeRedirectUri(String redirectUri) { + updated |= this.redirectUris.remove(redirectUri); + } + + @Override + public void removeAttribute(String name) { + this.updated |= this.attributes.remove(name) != null; + } + + @Override + public List getAttribute(String name) { + return attributes.getOrDefault(name, Collections.EMPTY_LIST); + } + + @Override + public String getAuthenticationFlowBindingOverride(String binding) { + return this.authFlowBindings.get(binding); + } + + @Override + public Map getAuthenticationFlowBindingOverrides() { + return this.authFlowBindings; + } + + @Override + public void removeAuthenticationFlowBindingOverride(String binding) { + updated |= this.authFlowBindings.remove(binding) != null; + } + + @Override + public void setAuthenticationFlowBindingOverride(String binding, String flowId) { + this.updated = true; + this.authFlowBindings.put(binding, flowId); + } + + @Override + public Collection getScopeMappings() { + return scopeMappings; + } + + @Override + public void addScopeMapping(String id) { + if (id != null) { + updated = true; + scopeMappings.add(id); + } + } + + @Override + public void deleteScopeMapping(String id) { + updated |= scopeMappings.remove(id); + } + + @Override + public void addClientScope(String id, Boolean defaultScope) { + if (id != null) { + updated = true; + this.clientScopes.put(id, defaultScope); + } + } + + @Override + public void removeClientScope(String id) { + if (id != null) { + updated |= clientScopes.remove(id) != null; + } + } + + @Override + public Stream getClientScopes(boolean defaultScope) { + return this.clientScopes.entrySet().stream() + .filter(me -> Objects.equals(me.getValue(), defaultScope)) + .map(Entry::getKey); + } + + @Override + public String getRealmId() { + return this.realmId; + } + +} diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java new file mode 100644 index 0000000000..a723812d61 --- /dev/null +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java @@ -0,0 +1,468 @@ +/* + * 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.client; + +import org.keycloak.models.ProtocolMapperModel; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicMarkableReference; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * + * @author hmlnarik + */ +public class MapClientEntityLazyDelegate implements MapClientEntity { + + private final Supplier> delegateSupplier; + + private final AtomicMarkableReference> delegate = new AtomicMarkableReference<>(null, false); + + public MapClientEntityLazyDelegate(Supplier> delegateSupplier) { + this.delegateSupplier = delegateSupplier; + } + + protected MapClientEntity getDelegate() { + if (! delegate.isMarked()) { + delegate.compareAndSet(null, delegateSupplier == null ? null : delegateSupplier.get(), false, true); + } + MapClientEntity ref = delegate.getReference(); + if (ref == null) { + throw new IllegalStateException("Invalid delegate obtained"); + } + return ref; + } + + @Override + public void addClientScope(String id, Boolean defaultScope) { + getDelegate().addClientScope(id, defaultScope); + } + + @Override + public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { + return getDelegate().addProtocolMapper(model); + } + + @Override + public void addRedirectUri(String redirectUri) { + getDelegate().addRedirectUri(redirectUri); + } + + @Override + public void addScopeMapping(String id) { + getDelegate().addScopeMapping(id); + } + + @Override + public void addWebOrigin(String webOrigin) { + getDelegate().addWebOrigin(webOrigin); + } + + @Override + public void deleteScopeMapping(String id) { + getDelegate().deleteScopeMapping(id); + } + + @Override + public List getAttribute(String name) { + return getDelegate().getAttribute(name); + } + + @Override + public Map> getAttributes() { + return getDelegate().getAttributes(); + } + + @Override + public Map getAuthFlowBindings() { + return getDelegate().getAuthFlowBindings(); + } + + @Override + public String getAuthenticationFlowBindingOverride(String binding) { + return getDelegate().getAuthenticationFlowBindingOverride(binding); + } + + @Override + public Map getAuthenticationFlowBindingOverrides() { + return getDelegate().getAuthenticationFlowBindingOverrides(); + } + + @Override + public String getBaseUrl() { + return getDelegate().getBaseUrl(); + } + + @Override + public String getClientAuthenticatorType() { + return getDelegate().getClientAuthenticatorType(); + } + + @Override + public String getClientId() { + return getDelegate().getClientId(); + } + + @Override + public Stream getClientScopes(boolean defaultScope) { + return getDelegate().getClientScopes(defaultScope); + } + + @Override + public String getDescription() { + return getDelegate().getDescription(); + } + + @Override + public String getManagementUrl() { + return getDelegate().getManagementUrl(); + } + + @Override + public String getName() { + return getDelegate().getName(); + } + + @Override + public int getNodeReRegistrationTimeout() { + return getDelegate().getNodeReRegistrationTimeout(); + } + + @Override + public int getNotBefore() { + return getDelegate().getNotBefore(); + } + + @Override + public String getProtocol() { + return getDelegate().getProtocol(); + } + + @Override + public ProtocolMapperModel getProtocolMapperById(String id) { + return getDelegate().getProtocolMapperById(id); + } + + @Override + public Collection getProtocolMappers() { + return getDelegate().getProtocolMappers(); + } + + @Override + public String getRealmId() { + return getDelegate().getRealmId(); + } + + @Override + public Set getRedirectUris() { + return getDelegate().getRedirectUris(); + } + + @Override + public String getRegistrationToken() { + return getDelegate().getRegistrationToken(); + } + + @Override + public String getRootUrl() { + return getDelegate().getRootUrl(); + } + + @Override + public Set getScope() { + return getDelegate().getScope(); + } + + @Override + public Collection getScopeMappings() { + return getDelegate().getScopeMappings(); + } + + @Override + public String getSecret() { + return getDelegate().getSecret(); + } + + @Override + public Set getWebOrigins() { + return getDelegate().getWebOrigins(); + } + + @Override + public Boolean isAlwaysDisplayInConsole() { + return getDelegate().isAlwaysDisplayInConsole(); + } + + @Override + public Boolean isBearerOnly() { + return getDelegate().isBearerOnly(); + } + + @Override + public Boolean isConsentRequired() { + return getDelegate().isConsentRequired(); + } + + @Override + public Boolean isDirectAccessGrantsEnabled() { + return getDelegate().isDirectAccessGrantsEnabled(); + } + + @Override + public Boolean isEnabled() { + return getDelegate().isEnabled(); + } + + @Override + public Boolean isFrontchannelLogout() { + return getDelegate().isFrontchannelLogout(); + } + + @Override + public Boolean isFullScopeAllowed() { + return getDelegate().isFullScopeAllowed(); + } + + @Override + public Boolean isImplicitFlowEnabled() { + return getDelegate().isImplicitFlowEnabled(); + } + + @Override + public Boolean isPublicClient() { + return getDelegate().isPublicClient(); + } + + @Override + public Boolean isServiceAccountsEnabled() { + return getDelegate().isServiceAccountsEnabled(); + } + + @Override + public Boolean isStandardFlowEnabled() { + return getDelegate().isStandardFlowEnabled(); + } + + @Override + public Boolean isSurrogateAuthRequired() { + return getDelegate().isSurrogateAuthRequired(); + } + + @Override + public void removeAttribute(String name) { + getDelegate().removeAttribute(name); + } + + @Override + public void removeAuthenticationFlowBindingOverride(String binding) { + getDelegate().removeAuthenticationFlowBindingOverride(binding); + } + + @Override + public void removeClientScope(String id) { + getDelegate().removeClientScope(id); + } + + @Override + public void removeProtocolMapper(String id) { + getDelegate().removeProtocolMapper(id); + } + + @Override + public void removeRedirectUri(String redirectUri) { + getDelegate().removeRedirectUri(redirectUri); + } + + @Override + public void removeWebOrigin(String webOrigin) { + getDelegate().removeWebOrigin(webOrigin); + } + + @Override + public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) { + getDelegate().setAlwaysDisplayInConsole(alwaysDisplayInConsole); + } + + @Override + public void setAttribute(String name, List values) { + getDelegate().setAttribute(name, values); + } + + @Override + public void setAuthFlowBindings(Map authFlowBindings) { + getDelegate().setAuthFlowBindings(authFlowBindings); + } + + @Override + public void setAuthenticationFlowBindingOverride(String binding, String flowId) { + getDelegate().setAuthenticationFlowBindingOverride(binding, flowId); + } + + @Override + public void setBaseUrl(String baseUrl) { + getDelegate().setBaseUrl(baseUrl); + } + + @Override + public void setBearerOnly(Boolean bearerOnly) { + getDelegate().setBearerOnly(bearerOnly); + } + + @Override + public void setClientAuthenticatorType(String clientAuthenticatorType) { + getDelegate().setClientAuthenticatorType(clientAuthenticatorType); + } + + @Override + public void setClientId(String clientId) { + getDelegate().setClientId(clientId); + } + + @Override + public void setConsentRequired(Boolean consentRequired) { + getDelegate().setConsentRequired(consentRequired); + } + + @Override + public void setDescription(String description) { + getDelegate().setDescription(description); + } + + @Override + public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) { + getDelegate().setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + } + + @Override + public void setEnabled(Boolean enabled) { + getDelegate().setEnabled(enabled); + } + + @Override + public void setFrontchannelLogout(Boolean frontchannelLogout) { + getDelegate().setFrontchannelLogout(frontchannelLogout); + } + + @Override + public void setFullScopeAllowed(Boolean fullScopeAllowed) { + getDelegate().setFullScopeAllowed(fullScopeAllowed); + } + + @Override + public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) { + getDelegate().setImplicitFlowEnabled(implicitFlowEnabled); + } + + @Override + public void setManagementUrl(String managementUrl) { + getDelegate().setManagementUrl(managementUrl); + } + + @Override + public void setName(String name) { + getDelegate().setName(name); + } + + @Override + public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) { + getDelegate().setNodeReRegistrationTimeout(nodeReRegistrationTimeout); + } + + @Override + public void setNotBefore(int notBefore) { + getDelegate().setNotBefore(notBefore); + } + + @Override + public void setProtocol(String protocol) { + getDelegate().setProtocol(protocol); + } + + @Override + public void setProtocolMappers(Collection protocolMappers) { + getDelegate().setProtocolMappers(protocolMappers); + } + + @Override + public void setPublicClient(Boolean publicClient) { + getDelegate().setPublicClient(publicClient); + } + + @Override + public void setRedirectUris(Set redirectUris) { + getDelegate().setRedirectUris(redirectUris); + } + + @Override + public void setRegistrationToken(String registrationToken) { + getDelegate().setRegistrationToken(registrationToken); + } + + @Override + public void setRootUrl(String rootUrl) { + getDelegate().setRootUrl(rootUrl); + } + + @Override + public void setScope(Set scope) { + getDelegate().setScope(scope); + } + + @Override + public void setSecret(String secret) { + getDelegate().setSecret(secret); + } + + @Override + public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) { + getDelegate().setServiceAccountsEnabled(serviceAccountsEnabled); + } + + @Override + public void setStandardFlowEnabled(Boolean standardFlowEnabled) { + getDelegate().setStandardFlowEnabled(standardFlowEnabled); + } + + @Override + public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) { + getDelegate().setSurrogateAuthRequired(surrogateAuthRequired); + } + + @Override + public void setWebOrigins(Set webOrigins) { + getDelegate().setWebOrigins(webOrigins); + } + + @Override + public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { + getDelegate().updateProtocolMapper(id, mapping); + } + + @Override + public K getId() { + return getDelegate().getId(); + } + + @Override + public boolean isUpdated() { + return getDelegate().isUpdated(); + } + +} diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java index 7a24c240c5..dc401f5862 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java @@ -81,10 +81,10 @@ public class MapClientProvider implements ClientProvider { }; } - private Function, ClientModel> entityToAdapterFunc(RealmModel realm) { + private > Function entityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller - return origEntity -> new MapClientAdapter(session, realm, registerEntityForChanges(tx, origEntity)) { + return (T origEntity) -> new MapClientAdapter(session, realm, registerEntityForChanges(tx, origEntity)) { @Override public String getId() { return clientStore.getKeyConvertor().keyToString(entity.getId()); @@ -152,7 +152,7 @@ public class MapClientProvider implements ClientProvider { LOG.tracef("addClient(%s, %s, %s)%s", realm, id, clientId, getShortStackTrace()); - MapClientEntity entity = new MapClientEntity<>(entityId, realm.getId()); + MapClientEntity entity = new MapClientEntityImpl<>(entityId, realm.getId()); entity.setClientId(clientId); entity.setEnabled(true); entity.setStandardFlowEnabled(true); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java index e57daa1dab..4a5473ed2c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java @@ -35,8 +35,21 @@ 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.clientscope.MapClientScopeEntity; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.Serialization; +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 com.fasterxml.jackson.databind.JavaType; import java.io.File; import java.io.IOException; @@ -50,6 +63,8 @@ import org.keycloak.models.map.storage.MapStorageProviderFactory; import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity; import org.keycloak.models.map.storage.StringKeyConvertor; +import org.keycloak.models.map.user.MapUserEntity; +import org.keycloak.models.map.userSession.MapUserSessionEntity; import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.sessions.RootAuthenticationSessionModel; @@ -100,6 +115,27 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide MODEL_TO_NAME.put(org.keycloak.authorization.model.Scope.class, "authz-scopes"); } + public static final Map, 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(MapClientEntity.class, MapClientEntityImpl.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 KEY_CONVERTORS = new HashMap<>(); static { KEY_CONVERTORS.put("uuid", StringKeyConvertor.UUIDKey.INSTANCE); @@ -213,7 +249,8 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide if (f != null && f.exists()) { try { LOG.debugf("Restoring contents from %s", f.getCanonicalPath()); - JavaType type = Serialization.MAPPER.getTypeFactory().constructCollectionType(List.class, valueType); + Class valueImplType = INTERFACE_TO_IMPL.getOrDefault(valueType, valueType); + JavaType type = Serialization.MAPPER.getTypeFactory().constructCollectionType(List.class, valueImplType); List values = Serialization.MAPPER.readValue(f, type); values.forEach((V mce) -> store.create(mce)); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java index 54504be0f6..97bc544e04 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/MapStorageTest.java @@ -24,6 +24,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RealmProvider; import org.keycloak.models.map.client.MapClientEntity; +import org.keycloak.models.map.client.MapClientEntityImpl; import org.keycloak.models.map.client.MapClientProviderFactory; import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorageProvider; @@ -114,9 +115,9 @@ public class MapStorageTest extends KeycloakModelTest { assertClientDoesNotExist(storage2, idMain, kcMain, kc2); assertClientDoesNotExist(storage2, id1, kc1, kc2); - MapClientEntity clientMain = new MapClientEntity<>(idMain, realmId); - MapClientEntity client1 = new MapClientEntity<>(id1, realmId); - MapClientEntity client2 = new MapClientEntity<>(id2, realmId); + MapClientEntity clientMain = new MapClientEntityImpl<>(idMain, realmId); + MapClientEntity client1 = new MapClientEntityImpl<>(id1, realmId); + MapClientEntity client2 = new MapClientEntityImpl<>(id2, realmId); storageMain.create(clientMain); storage1.create(client1);