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.
This commit is contained in:
Hynek Mlnarik 2021-07-14 11:46:34 +02:00 committed by Hynek Mlnařík
parent 2c8d4ad9b4
commit 009d4ca445
7 changed files with 1272 additions and 454 deletions

View file

@ -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<K> implements AbstractEntity<K> {
private K id;
private String realmId;
private String clientId;
private String name;
private String description;
private Set<String> redirectUris = new HashSet<>();
private boolean enabled;
private boolean alwaysDisplayInConsole;
private String clientAuthenticatorType;
private String secret;
private String registrationToken;
private String protocol;
private Map<String, List<String>> attributes = new HashMap<>();
private Map<String, String> authFlowBindings = new HashMap<>();
private boolean publicClient;
private boolean fullScopeAllowed;
private boolean frontchannelLogout;
private int notBefore;
private Set<String> scope = new HashSet<>();
private Set<String> webOrigins = new HashSet<>();
private Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
private Map<String, Boolean> clientScopes = new HashMap<>();
private Set<String> 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 interface MapClientEntity<K> extends AbstractEntity<K> {
public MapClientEntity(K id, String realmId) {
Objects.requireNonNull(id, "id");
Objects.requireNonNull(realmId, "realmId");
void addClientScope(String id, Boolean defaultScope);
this.id = id;
this.realmId = realmId;
}
ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model);
@Override
public K getId() {
return this.id;
}
void addRedirectUri(String redirectUri);
@Override
public boolean isUpdated() {
return this.updated;
}
void addScopeMapping(String id);
public String getClientId() {
return clientId;
}
void addWebOrigin(String webOrigin);
public void setClientId(String clientId) {
this.updated |= ! Objects.equals(this.clientId, clientId);
this.clientId = clientId;
}
void deleteScopeMapping(String id);
public String getName() {
return name;
}
List<String> getAttribute(String name);
public void setName(String name) {
this.updated |= ! Objects.equals(this.name, name);
this.name = name;
}
Map<String, List<String>> getAttributes();
public String getDescription() {
return description;
}
Map<String, String> getAuthFlowBindings();
public void setDescription(String description) {
this.updated |= ! Objects.equals(this.description, description);
this.description = description;
}
String getAuthenticationFlowBindingOverride(String binding);
public Set<String> getRedirectUris() {
return redirectUris;
}
Map<String, String> getAuthenticationFlowBindingOverrides();
public void setRedirectUris(Set<String> redirectUris) {
this.updated |= ! Objects.equals(this.redirectUris, redirectUris);
this.redirectUris = redirectUris;
}
String getBaseUrl();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.updated |= ! Objects.equals(this.enabled, enabled);
this.enabled = enabled;
}
String getClientAuthenticatorType();
public boolean isAlwaysDisplayInConsole() {
return alwaysDisplayInConsole;
}
public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) {
this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole);
this.alwaysDisplayInConsole = alwaysDisplayInConsole;
}
String getClientId();
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;
}
Stream<String> getClientScopes(boolean defaultScope);
public void setSecret(String secret) {
this.updated |= ! Objects.equals(this.secret, secret);
this.secret = secret;
}
String getDescription();
public String getRegistrationToken() {
return registrationToken;
}
String getManagementUrl();
public void setRegistrationToken(String registrationToken) {
this.updated |= ! Objects.equals(this.registrationToken, registrationToken);
this.registrationToken = registrationToken;
}
String getName();
public String getProtocol() {
return protocol;
}
int getNodeReRegistrationTimeout();
public void setProtocol(String protocol) {
this.updated |= ! Objects.equals(this.protocol, protocol);
this.protocol = protocol;
}
int getNotBefore();
public Map<String, List<String>> getAttributes() {
return attributes;
}
String getProtocol();
public void setAttribute(String name, List<String> values) {
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
}
ProtocolMapperModel getProtocolMapperById(String id);
public Map<String, String> getAuthFlowBindings() {
return authFlowBindings;
}
Collection<ProtocolMapperModel> getProtocolMappers();
public void setAuthFlowBindings(Map<String, String> authFlowBindings) {
this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings);
this.authFlowBindings = authFlowBindings;
}
String getRealmId();
public boolean isPublicClient() {
return publicClient;
}
Set<String> getRedirectUris();
public void setPublicClient(boolean publicClient) {
this.updated |= ! Objects.equals(this.publicClient, publicClient);
this.publicClient = publicClient;
}
String getRegistrationToken();
public boolean isFullScopeAllowed() {
return fullScopeAllowed;
}
String getRootUrl();
public void setFullScopeAllowed(boolean fullScopeAllowed) {
this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed);
this.fullScopeAllowed = fullScopeAllowed;
}
Set<String> getScope();
public boolean isFrontchannelLogout() {
return frontchannelLogout;
}
Collection<String> getScopeMappings();
public void setFrontchannelLogout(boolean frontchannelLogout) {
this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout);
this.frontchannelLogout = frontchannelLogout;
}
String getSecret();
public int getNotBefore() {
return notBefore;
}
Set<String> getWebOrigins();
public void setNotBefore(int notBefore) {
this.updated |= ! Objects.equals(this.notBefore, notBefore);
this.notBefore = notBefore;
}
Boolean isAlwaysDisplayInConsole();
public Set<String> getScope() {
return scope;
}
Boolean isBearerOnly();
public void setScope(Set<String> scope) {
this.updated |= ! Objects.equals(this.scope, scope);
this.scope.clear();
this.scope.addAll(scope);
}
Boolean isConsentRequired();
public Set<String> getWebOrigins() {
return webOrigins;
}
Boolean isDirectAccessGrantsEnabled();
public void setWebOrigins(Set<String> webOrigins) {
this.updated |= ! Objects.equals(this.webOrigins, webOrigins);
this.webOrigins.clear();
this.webOrigins.addAll(webOrigins);
}
Boolean isEnabled();
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
Objects.requireNonNull(model.getId(), "protocolMapper.id");
updated = true;
this.protocolMappers.put(model.getId(), model);
return model;
}
Boolean isFrontchannelLogout();
public Collection<ProtocolMapperModel> getProtocolMappers() {
return protocolMappers.values();
}
Boolean isFullScopeAllowed();
public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
updated = true;
protocolMappers.put(id, mapping);
}
Boolean isImplicitFlowEnabled();
public void removeProtocolMapper(String id) {
updated |= protocolMappers.remove(id) != null;
}
Boolean isPublicClient();
public void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers) {
this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers);
this.protocolMappers.clear();
this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity())));
}
Boolean isServiceAccountsEnabled();
public ProtocolMapperModel getProtocolMapperById(String id) {
return id == null ? null : protocolMappers.get(id);
}
Boolean isStandardFlowEnabled();
public boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}
Boolean isSurrogateAuthRequired();
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired);
this.surrogateAuthRequired = surrogateAuthRequired;
}
void removeAttribute(String name);
public String getManagementUrl() {
return managementUrl;
}
void removeAuthenticationFlowBindingOverride(String binding);
public void setManagementUrl(String managementUrl) {
this.updated |= ! Objects.equals(this.managementUrl, managementUrl);
this.managementUrl = managementUrl;
}
void removeClientScope(String id);
public String getRootUrl() {
return rootUrl;
}
void removeProtocolMapper(String id);
public void setRootUrl(String rootUrl) {
this.updated |= ! Objects.equals(this.rootUrl, rootUrl);
this.rootUrl = rootUrl;
}
void removeRedirectUri(String redirectUri);
public String getBaseUrl() {
return baseUrl;
}
void removeWebOrigin(String webOrigin);
public void setBaseUrl(String baseUrl) {
this.updated |= ! Objects.equals(this.baseUrl, baseUrl);
this.baseUrl = baseUrl;
}
void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole);
public boolean isBearerOnly() {
return bearerOnly;
}
void setAttribute(String name, List<String> values);
public void setBearerOnly(boolean bearerOnly) {
this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly);
this.bearerOnly = bearerOnly;
}
void setAuthFlowBindings(Map<String, String> authFlowBindings);
public boolean isConsentRequired() {
return consentRequired;
}
void setAuthenticationFlowBindingOverride(String binding, String flowId);
public void setConsentRequired(boolean consentRequired) {
this.updated |= ! Objects.equals(this.consentRequired, consentRequired);
this.consentRequired = consentRequired;
}
void setBaseUrl(String baseUrl);
public boolean isStandardFlowEnabled() {
return standardFlowEnabled;
}
void setBearerOnly(Boolean bearerOnly);
public void setStandardFlowEnabled(boolean standardFlowEnabled) {
this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled);
this.standardFlowEnabled = standardFlowEnabled;
}
void setClientAuthenticatorType(String clientAuthenticatorType);
public boolean isImplicitFlowEnabled() {
return implicitFlowEnabled;
}
void setClientId(String clientId);
public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled);
this.implicitFlowEnabled = implicitFlowEnabled;
}
void setConsentRequired(Boolean consentRequired);
public boolean isDirectAccessGrantsEnabled() {
return directAccessGrantsEnabled;
}
void setDescription(String description);
public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled);
this.directAccessGrantsEnabled = directAccessGrantsEnabled;
}
void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled);
public boolean isServiceAccountsEnabled() {
return serviceAccountsEnabled;
}
void setEnabled(Boolean enabled);
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled);
this.serviceAccountsEnabled = serviceAccountsEnabled;
}
void setFrontchannelLogout(Boolean frontchannelLogout);
public int getNodeReRegistrationTimeout() {
return nodeReRegistrationTimeout;
}
void setFullScopeAllowed(Boolean fullScopeAllowed);
public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) {
this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout);
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
}
void setImplicitFlowEnabled(Boolean implicitFlowEnabled);
public void addWebOrigin(String webOrigin) {
updated = true;
this.webOrigins.add(webOrigin);
}
void setManagementUrl(String managementUrl);
public void removeWebOrigin(String webOrigin) {
updated |= this.webOrigins.remove(webOrigin);
}
void setName(String name);
public void addRedirectUri(String redirectUri) {
this.updated |= ! this.redirectUris.contains(redirectUri);
this.redirectUris.add(redirectUri);
}
void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout);
public void removeRedirectUri(String redirectUri) {
updated |= this.redirectUris.remove(redirectUri);
}
void setNotBefore(int notBefore);
public void removeAttribute(String name) {
this.updated |= this.attributes.remove(name) != null;
}
void setProtocol(String protocol);
public List<String> getAttribute(String name) {
return attributes.getOrDefault(name, Collections.EMPTY_LIST);
}
void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers);
public String getAuthenticationFlowBindingOverride(String binding) {
return this.authFlowBindings.get(binding);
}
void setPublicClient(Boolean publicClient);
public Map<String, String> getAuthenticationFlowBindingOverrides() {
return this.authFlowBindings;
}
void setRedirectUris(Set<String> redirectUris);
public void removeAuthenticationFlowBindingOverride(String binding) {
updated |= this.authFlowBindings.remove(binding) != null;
}
void setRegistrationToken(String registrationToken);
public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
this.updated = true;
this.authFlowBindings.put(binding, flowId);
}
void setRootUrl(String rootUrl);
public Collection<String> getScopeMappings() {
return scopeMappings;
}
void setScope(Set<String> scope);
public void addScopeMapping(String id) {
if (id != null) {
updated = true;
scopeMappings.add(id);
}
}
void setSecret(String secret);
public void deleteScopeMapping(String id) {
updated |= scopeMappings.remove(id);
}
void setServiceAccountsEnabled(Boolean serviceAccountsEnabled);
public void addClientScope(String id, boolean defaultScope) {
if (id != null) {
updated = true;
this.clientScopes.put(id, defaultScope);
}
}
void setStandardFlowEnabled(Boolean standardFlowEnabled);
public void removeClientScope(String id) {
if (id != null) {
updated |= clientScopes.remove(id) != null;
}
}
void setSurrogateAuthRequired(Boolean surrogateAuthRequired);
public Stream<String> getClientScopes(boolean defaultScope) {
return this.clientScopes.entrySet().stream()
.filter(me -> Objects.equals(me.getValue(), defaultScope))
.map(Entry::getKey);
}
void setWebOrigins(Set<String> webOrigins);
public String getRealmId() {
return this.realmId;
}
void updateProtocolMapper(String id, ProtocolMapperModel mapping);
}

View file

@ -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<K> extends MapClientEntityLazyDelegate<K> {
private final MapClientEntity<K> delegate;
public MapClientEntityDelegate(MapClientEntity<K> delegate) {
super(null);
this.delegate = delegate;
}
@Override
protected MapClientEntity<K> getDelegate() {
return delegate;
}
}

View file

@ -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<K> implements MapClientEntity<K> {
private K id;
private String realmId;
private String clientId;
private String name;
private String description;
private Set<String> redirectUris = new HashSet<>();
private boolean enabled;
private boolean alwaysDisplayInConsole;
private String clientAuthenticatorType;
private String secret;
private String registrationToken;
private String protocol;
private Map<String, List<String>> attributes = new HashMap<>();
private Map<String, String> authFlowBindings = new HashMap<>();
private boolean publicClient;
private boolean fullScopeAllowed;
private boolean frontchannelLogout;
private int notBefore;
private Set<String> scope = new HashSet<>();
private Set<String> webOrigins = new HashSet<>();
private Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
private Map<String, Boolean> clientScopes = new HashMap<>();
private Set<String> 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<String> getRedirectUris() {
return redirectUris;
}
@Override
public void setRedirectUris(Set<String> 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<String, List<String>> getAttributes() {
return attributes;
}
@Override
public void setAttribute(String name, List<String> values) {
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
}
@Override
public Map<String, String> getAuthFlowBindings() {
return authFlowBindings;
}
@Override
public void setAuthFlowBindings(Map<String, String> 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<String> getScope() {
return scope;
}
@Override
public void setScope(Set<String> scope) {
this.updated |= ! Objects.equals(this.scope, scope);
this.scope.clear();
this.scope.addAll(scope);
}
@Override
public Set<String> getWebOrigins() {
return webOrigins;
}
@Override
public void setWebOrigins(Set<String> 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<ProtocolMapperModel> 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<ProtocolMapperModel> 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<String> getAttribute(String name) {
return attributes.getOrDefault(name, Collections.EMPTY_LIST);
}
@Override
public String getAuthenticationFlowBindingOverride(String binding) {
return this.authFlowBindings.get(binding);
}
@Override
public Map<String, String> 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<String> 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<String> 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;
}
}

View file

@ -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<K> implements MapClientEntity<K> {
private final Supplier<MapClientEntity<K>> delegateSupplier;
private final AtomicMarkableReference<MapClientEntity<K>> delegate = new AtomicMarkableReference<>(null, false);
public MapClientEntityLazyDelegate(Supplier<MapClientEntity<K>> delegateSupplier) {
this.delegateSupplier = delegateSupplier;
}
protected MapClientEntity<K> getDelegate() {
if (! delegate.isMarked()) {
delegate.compareAndSet(null, delegateSupplier == null ? null : delegateSupplier.get(), false, true);
}
MapClientEntity<K> 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<String> getAttribute(String name) {
return getDelegate().getAttribute(name);
}
@Override
public Map<String, List<String>> getAttributes() {
return getDelegate().getAttributes();
}
@Override
public Map<String, String> getAuthFlowBindings() {
return getDelegate().getAuthFlowBindings();
}
@Override
public String getAuthenticationFlowBindingOverride(String binding) {
return getDelegate().getAuthenticationFlowBindingOverride(binding);
}
@Override
public Map<String, String> 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<String> 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<ProtocolMapperModel> getProtocolMappers() {
return getDelegate().getProtocolMappers();
}
@Override
public String getRealmId() {
return getDelegate().getRealmId();
}
@Override
public Set<String> getRedirectUris() {
return getDelegate().getRedirectUris();
}
@Override
public String getRegistrationToken() {
return getDelegate().getRegistrationToken();
}
@Override
public String getRootUrl() {
return getDelegate().getRootUrl();
}
@Override
public Set<String> getScope() {
return getDelegate().getScope();
}
@Override
public Collection<String> getScopeMappings() {
return getDelegate().getScopeMappings();
}
@Override
public String getSecret() {
return getDelegate().getSecret();
}
@Override
public Set<String> 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<String> values) {
getDelegate().setAttribute(name, values);
}
@Override
public void setAuthFlowBindings(Map<String, String> 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<ProtocolMapperModel> protocolMappers) {
getDelegate().setProtocolMappers(protocolMappers);
}
@Override
public void setPublicClient(Boolean publicClient) {
getDelegate().setPublicClient(publicClient);
}
@Override
public void setRedirectUris(Set<String> 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<String> 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<String> 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();
}
}

View file

@ -81,10 +81,10 @@ public class MapClientProvider<K> implements ClientProvider {
};
}
private Function<MapClientEntity<K>, ClientModel> entityToAdapterFunc(RealmModel realm) {
private <T extends MapClientEntity<K>> Function<T, ClientModel> 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<K>(session, realm, registerEntityForChanges(tx, origEntity)) {
return (T origEntity) -> new MapClientAdapter<K>(session, realm, registerEntityForChanges(tx, origEntity)) {
@Override
public String getId() {
return clientStore.getKeyConvertor().keyToString(entity.getId());
@ -152,7 +152,7 @@ public class MapClientProvider<K> implements ClientProvider {
LOG.tracef("addClient(%s, %s, %s)%s", realm, id, clientId, getShortStackTrace());
MapClientEntity<K> entity = new MapClientEntity<>(entityId, realm.getId());
MapClientEntity<K> entity = new MapClientEntityImpl<>(entityId, realm.getId());
entity.setClientId(clientId);
entity.setEnabled(true);
entity.setStandardFlowEnabled(true);

View file

@ -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<?>, 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<String, StringKeyConvertor> 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<V> values = Serialization.MAPPER.readValue(f, type);
values.forEach((V mce) -> store.create(mce));

View file

@ -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<K> clientMain = new MapClientEntity<>(idMain, realmId);
MapClientEntity<K1> client1 = new MapClientEntity<>(id1, realmId);
MapClientEntity<K2> client2 = new MapClientEntity<>(id2, realmId);
MapClientEntity<K> clientMain = new MapClientEntityImpl<>(idMain, realmId);
MapClientEntity<K1> client1 = new MapClientEntityImpl<>(id1, realmId);
MapClientEntity<K2> client2 = new MapClientEntityImpl<>(id2, realmId);
storageMain.create(clientMain);
storage1.create(client1);