From b4281468d059b15b5e6cbdc1252c42ab307861f6 Mon Sep 17 00:00:00 2001 From: Michal Hajas Date: Thu, 10 Feb 2022 11:13:53 +0100 Subject: [PATCH] Convert Map Realm Entities into interfaces Closes #9736 --- .../keycloak/models/map/processor/Util.java | 16 +- .../models/map/realm/MapRealmAdapter.java | 483 ++++-- .../models/map/realm/MapRealmEntity.java | 1353 +++++------------ .../models/map/realm/MapRealmProvider.java | 17 +- .../MapAuthenticationExecutionEntity.java | 134 +- .../entity/MapAuthenticationFlowEntity.java | 107 +- .../entity/MapAuthenticatorConfigEntity.java | 79 +- .../entity/MapClientInitialAccessEntity.java | 100 +- .../map/realm/entity/MapComponentEntity.java | 118 +- .../entity/MapIdentityProviderEntity.java | 197 +-- .../MapIdentityProviderMapperEntity.java | 91 +- .../map/realm/entity/MapOTPPolicyEntity.java | 124 +- .../MapRequiredActionProviderEntity.java | 137 +- .../entity/MapRequiredCredentialEntity.java | 83 +- .../realm/entity/MapWebAuthnPolicyEntity.java | 157 +- ...ncurrentHashMapStorageProviderFactory.java | 15 + .../map/storage/chm/MapFieldPredicates.java | 2 +- .../testsuite/forms/FlowOverrideTest.java | 2 +- .../testsuite/model/RealmModelTest.java | 98 ++ 19 files changed, 1178 insertions(+), 2135 deletions(-) create mode 100644 testsuite/model/src/test/java/org/keycloak/testsuite/model/RealmModelTest.java diff --git a/model/build-processor/src/main/java/org/keycloak/models/map/processor/Util.java b/model/build-processor/src/main/java/org/keycloak/models/map/processor/Util.java index ade5a0bfed..6cd1a3aead 100644 --- a/model/build-processor/src/main/java/org/keycloak/models/map/processor/Util.java +++ b/model/build-processor/src/main/java/org/keycloak/models/map/processor/Util.java @@ -100,10 +100,22 @@ public class Util { } public static String singularToPlural(String word) { - return word.endsWith("y") ? word.substring(0, word.length() -1) + "ies" : word + "s"; + if (word.endsWith("y")) { + return word.substring(0, word.length() -1) + "ies"; + } else if (word.endsWith("ss")) { + return word + "es"; + } else { + return word + "s"; + } } public static String pluralToSingular(String word) { - return word.endsWith("ies") ? word.substring(0, word.length() - 3) + "y" : word.endsWith("s") ? word.substring(0, word.length() - 1) : word; + if (word.endsWith("ies")) { + return word.substring(0, word.length() - 3) + "y"; + } else if (word.endsWith("sses")) { + return word.substring(0, word.length() - 2); + } else { + return word.endsWith("s") ? word.substring(0, word.length() - 1) : word; + } } } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java index a4f8c18c15..f64dc68900 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java @@ -17,11 +17,15 @@ package org.keycloak.models.map.realm; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import static java.util.Objects.nonNull; + +import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,10 +46,11 @@ import org.keycloak.models.GroupModel; import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.OAuth2DeviceConfig; import org.keycloak.models.OTPPolicy; -import org.keycloak.models.PasswordPolicy; import org.keycloak.models.ParConfig; +import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredActionProviderModel; import org.keycloak.models.RequiredCredentialModel; @@ -121,7 +126,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isEnabled() { - return entity.isEnabled(); + Boolean enabled = entity.isEnabled(); + return enabled == null ? false : enabled; } @Override @@ -131,7 +137,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public SslRequired getSslRequired() { - return entity.getSslRequired() == null ? null : SslRequired.valueOf(entity.getSslRequired()); + String sslRequired = entity.getSslRequired(); + return sslRequired == null ? null : SslRequired.valueOf(sslRequired); } @Override @@ -141,7 +148,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isRegistrationAllowed() { - return entity.isRegistrationAllowed(); + Boolean is = entity.isRegistrationAllowed(); + return is == null ? false : is; } @Override @@ -151,7 +159,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isRegistrationEmailAsUsername() { - return entity.isRegistrationEmailAsUsername(); + Boolean is = entity.isRegistrationEmailAsUsername(); + return is == null ? false : is; } @Override @@ -161,7 +170,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isRememberMe() { - return entity.isRememberMe(); + Boolean is = entity.isRememberMe(); + return is == null ? false : is; } @Override @@ -171,7 +181,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isEditUsernameAllowed() { - return entity.isEditUsernameAllowed(); + Boolean is = entity.isEditUsernameAllowed(); + return is == null ? false : is; } @Override @@ -181,7 +192,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isUserManagedAccessAllowed() { - return entity.isAllowUserManagedAccess(); + Boolean is = entity.isAllowUserManagedAccess(); + return is == null ? false : is; } @Override @@ -202,31 +214,35 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public String getAttribute(String name) { List attribute = entity.getAttribute(name); - if (attribute.isEmpty()) return null; + if (attribute == null || attribute.isEmpty()) return null; return attribute.get(0); } @Override public Map getAttributes() { - return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, - entry -> { - if (entry.getValue().isEmpty()) { - return null; - } else if (entry.getValue().size() > 1) { - // This could be caused by an inconsistency in the storage, a programming error, - // or a downgrade from a future version of Keycloak that already supports multi-valued attributes. - // The caller will not see the other values, and when this entity is later updated, the additional values be will lost. - LOG.warnf("Realm '%s' has attribute '%s' with %d values, retrieving only the first", getId(), entry.getKey(), - entry.getValue().size()); - } - return entry.getValue().get(0); - }) + Map> attrs = entity.getAttributes(); + + return attrs == null || attrs.isEmpty() ? Collections.emptyMap() : attrs.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + entry -> { + if (entry.getValue().isEmpty()) { + return null; + } else if (entry.getValue().size() > 1) { + // This could be caused by an inconsistency in the storage, a programming error, + // or a downgrade from a future version of Keycloak that already supports multi-valued attributes. + // The caller will not see the other values, and when this entity is later updated, the additional values be will lost. + LOG.warnf("Realm '%s' has attribute '%s' with %d values, retrieving only the first", getId(), entry.getKey(), + entry.getValue().size()); + } + return entry.getValue().get(0); + }) ); } @Override public boolean isVerifyEmail() { - return entity.isVerifyEmail(); + Boolean is = entity.isVerifyEmail(); + return is == null ? false : is; } @Override @@ -236,7 +252,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isLoginWithEmailAllowed() { - return entity.isLoginWithEmailAllowed(); + Boolean is = entity.isLoginWithEmailAllowed(); + return is == null ? false : is; } @Override @@ -246,7 +263,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isDuplicateEmailsAllowed() { - return entity.isDuplicateEmailsAllowed(); + Boolean is = entity.isDuplicateEmailsAllowed(); + return is == null ? false : is; } @Override @@ -256,7 +274,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isResetPasswordAllowed() { - return entity.isResetPasswordAllowed(); + Boolean is = entity.isResetPasswordAllowed(); + return is == null ? false : is; } @Override @@ -266,7 +285,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isRevokeRefreshToken() { - return entity.isRevokeRefreshToken(); + Boolean is = entity.isRevokeRefreshToken(); + return is == null ? false : is; } @Override @@ -276,7 +296,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getRefreshTokenMaxReuse() { - return entity.getRefreshTokenMaxReuse(); + Integer i = entity.getRefreshTokenMaxReuse(); + return i == null ? 0 : i; } @Override @@ -286,7 +307,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getSsoSessionIdleTimeout() { - return entity.getSsoSessionIdleTimeout(); + Integer i = entity.getSsoSessionIdleTimeout(); + return i == null ? 0 : i; } @Override @@ -296,7 +318,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getSsoSessionMaxLifespan() { - return entity.getSsoSessionMaxLifespan(); + Integer i = entity.getSsoSessionMaxLifespan(); + return i == null ? 0 : i; } @Override @@ -306,7 +329,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getSsoSessionIdleTimeoutRememberMe() { - return entity.getSsoSessionIdleTimeoutRememberMe(); + Integer i = entity.getSsoSessionIdleTimeoutRememberMe(); + return i == null ? 0 : i; } @Override @@ -316,7 +340,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getSsoSessionMaxLifespanRememberMe() { - return entity.getSsoSessionMaxLifespanRememberMe(); + Integer i = entity.getSsoSessionMaxLifespanRememberMe(); + return i == null ? 0 : i; } @Override @@ -326,7 +351,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getOfflineSessionIdleTimeout() { - return entity.getOfflineSessionIdleTimeout(); + Integer i = entity.getOfflineSessionIdleTimeout(); + return i == null ? 0 : i; } @Override @@ -336,12 +362,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getAccessTokenLifespan() { - return entity.getAccessTokenLifespan(); + Integer i = entity.getAccessTokenLifespan(); + return i == null ? 0 : i; } @Override public int getClientSessionIdleTimeout() { - return entity.getClientSessionIdleTimeout(); + Integer i = entity.getClientSessionIdleTimeout(); + return i == null ? 0 : i; } @Override @@ -351,7 +379,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getClientSessionMaxLifespan() { - return entity.getClientSessionMaxLifespan(); + Integer i = entity.getClientSessionMaxLifespan(); + return i == null ? 0 : i; } @Override @@ -361,7 +390,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getClientOfflineSessionIdleTimeout() { - return entity.getClientOfflineSessionIdleTimeout(); + Integer i = entity.getClientOfflineSessionIdleTimeout(); + return i == null ? 0 : i; } @Override @@ -371,7 +401,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getClientOfflineSessionMaxLifespan() { - return entity.getClientOfflineSessionMaxLifespan(); + Integer i = entity.getClientOfflineSessionMaxLifespan(); + return i == null ? 0 : i; } @Override @@ -386,7 +417,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getAccessTokenLifespanForImplicitFlow() { - return entity.getAccessTokenLifespanForImplicitFlow(); + Integer i = entity.getAccessTokenLifespanForImplicitFlow(); + return i == null ? 0 : i; } @Override @@ -396,7 +428,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getAccessCodeLifespan() { - return entity.getAccessCodeLifespan(); + Integer i = entity.getAccessCodeLifespan(); + return i == null ? 0 : i; } @Override @@ -406,7 +439,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getAccessCodeLifespanUserAction() { - return entity.getAccessCodeLifespanUserAction(); + Integer i = entity.getAccessCodeLifespanUserAction(); + return i == null ? 0 : i; } @Override @@ -416,7 +450,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getAccessCodeLifespanLogin() { - return entity.getAccessCodeLifespanLogin(); + Integer i = entity.getAccessCodeLifespanLogin(); + return i == null ? 0 : i; } @Override @@ -426,7 +461,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getActionTokenGeneratedByAdminLifespan() { - return entity.getActionTokenGeneratedByAdminLifespan(); + Integer i = entity.getActionTokenGeneratedByAdminLifespan(); + return i == null ? 0 : i; } @Override @@ -458,7 +494,10 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Map getUserActionTokenLifespans() { - Map tokenLifespans = entity.getAttributes().entrySet().stream() + Map> attrs = entity.getAttributes(); + if (attrs == null || attrs.isEmpty()) return Collections.emptyMap(); + + Map tokenLifespans = attrs.entrySet().stream() .filter(Objects::nonNull) .filter(entry -> nonNull(entry.getValue()) && ! entry.getValue().isEmpty()) .filter(entry -> entry.getKey().startsWith(ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN + ".")) @@ -471,7 +510,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getRequiredCredentialsStream() { - return entity.getRequiredCredentials().map(MapRequiredCredentialEntity::toModel); + Set rCEs = entity.getRequiredCredentials(); + return rCEs == null ? Stream.empty() : rCEs.stream().map(MapRequiredCredentialEntity::toModel); } @Override @@ -485,15 +525,30 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void updateRequiredCredentials(Set credentials) { + Set requiredCredentialEntities = entity.getRequiredCredentials(); + Consumer updateCredentialFnc = e -> { + Optional existingEntity = requiredCredentialEntities.stream() + .filter(existing -> Objects.equals(e.getType(), existing.getType())) + .findFirst(); + + if (existingEntity.isPresent()) { + updateRequiredCredential(existingEntity.get(), e); + } else { + entity.addRequiredCredential(e); + } + }; + credentials.stream() .map(RequiredCredentialModel.BUILT_IN::get) .peek(c -> { if (c == null) throw new RuntimeException("Unknown credential type " + c.getType()); }) .map(MapRequiredCredentialEntity::fromModel) - .forEach(this::updateRequiredCredential); + .forEach(updateCredentialFnc); } - private void updateRequiredCredential(MapRequiredCredentialEntity requiredCredential) { - entity.updateRequiredCredential(requiredCredential); + private void updateRequiredCredential(MapRequiredCredentialEntity existing, MapRequiredCredentialEntity newValue) { + existing.setFormLabel(newValue.getFormLabel()); + existing.setInput(newValue.isInput()); + existing.setSecret(newValue.isSecret()); } @Override @@ -512,7 +567,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public OTPPolicy getOTPPolicy() { - return MapOTPPolicyEntity.toModel(entity.getOTPPolicy()); + MapOTPPolicyEntity policy = entity.getOTPPolicy(); + return policy == null ? OTPPolicy.DEFAULT_POLICY : MapOTPPolicyEntity.toModel(policy); } @Override @@ -527,17 +583,18 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getDefaultGroupsStream() { - return entity.getDefaultGroupIds().map(this::getGroupById); + Set gIds = entity.getDefaultGroupIds(); + return gIds == null ? Stream.empty() : gIds.stream().map(this::getGroupById); } @Override public void addDefaultGroup(GroupModel group) { - entity.addDefaultGroup(group.getId()); + entity.addDefaultGroupId(group.getId()); } @Override public void removeDefaultGroup(GroupModel group) { - entity.removeDefaultGroup(group.getId()); + entity.removeDefaultGroupId(group.getId()); } @Override @@ -597,7 +654,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Map getSmtpConfig() { - return Collections.unmodifiableMap(entity.getSmtpConfig()); + Map sC = entity.getSmtpConfig(); + return sC == null ? Collections.emptyMap() : Collections.unmodifiableMap(sC); } @Override @@ -667,12 +725,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getAuthenticationFlowsStream() { - return entity.getAuthenticationFlows().map(MapAuthenticationFlowEntity::toModel); + Set afs = entity.getAuthenticationFlows(); + return afs == null ? Stream.empty() : afs.stream().map(MapAuthenticationFlowEntity::toModel); } @Override public AuthenticationFlowModel getFlowByAlias(String alias) { - return entity.getAuthenticationFlows() + Set afs = entity.getAuthenticationFlows(); + return afs == null ? null : afs.stream() .filter(flow -> Objects.equals(flow.getAlias(), alias)) .findFirst() .map(MapAuthenticationFlowEntity::toModel) @@ -681,16 +741,20 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) { + if (entity.getAuthenticationFlow(model.getId()).isPresent()) { + throw new ModelDuplicateException("An AuthenticationFlow with given id already exists"); + } + MapAuthenticationFlowEntity authenticationFlowEntity = MapAuthenticationFlowEntity.fromModel(model); entity.addAuthenticationFlow(authenticationFlowEntity); - model.setId(authenticationFlowEntity.getId()); - return model; + + return MapAuthenticationFlowEntity.toModel(authenticationFlowEntity); } @Override public AuthenticationFlowModel getAuthenticationFlowById(String flowId) { if (flowId == null) return null; - return MapAuthenticationFlowEntity.toModel(entity.getAuthenticationFlow(flowId)); + return entity.getAuthenticationFlow(flowId).map(MapAuthenticationFlowEntity::toModel).orElse(null); } @Override @@ -700,12 +764,20 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void updateAuthenticationFlow(AuthenticationFlowModel model) { - entity.updateAuthenticationFlow(MapAuthenticationFlowEntity.fromModel(model)); + entity.getAuthenticationFlow(model.getId()) + .ifPresent(existing -> { + existing.setAlias(model.getAlias()); + existing.setDescription(model.getDescription()); + existing.setProviderId(model.getProviderId()); + existing.setBuiltIn(model.isBuiltIn()); + existing.setTopLevel(model.isTopLevel()); + }); } @Override public Stream getAuthenticationExecutionsStream(String flowId) { - return entity.getAuthenticationExecutions() + Set aee = entity.getAuthenticationExecutions(); + return aee == null ? Stream.empty() : aee.stream() .filter(execution -> Objects.equals(flowId, execution.getParentFlowId())) .map(MapAuthenticationExecutionEntity::toModel) .sorted(AuthenticationExecutionModel.ExecutionComparator.SINGLETON); @@ -714,12 +786,13 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public AuthenticationExecutionModel getAuthenticationExecutionById(String id) { if (id == null) return null; - return MapAuthenticationExecutionEntity.toModel(entity.getAuthenticationExecution(id)); + return entity.getAuthenticationExecution(id).map(MapAuthenticationExecutionEntity::toModel).orElse(null); } @Override public AuthenticationExecutionModel getAuthenticationExecutionByFlowId(String flowId) { - return entity.getAuthenticationExecutions() + Set aee = entity.getAuthenticationExecutions(); + return aee == null ? null : aee.stream() .filter(execution -> Objects.equals(flowId, execution.getFlowId())) .findAny() .map(MapAuthenticationExecutionEntity::toModel) @@ -728,25 +801,37 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) { + if (entity.getAuthenticationExecution(model.getId()).isPresent()) { + throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); + } MapAuthenticationExecutionEntity executionEntity = MapAuthenticationExecutionEntity.fromModel(model); - entity.addAuthenticatonExecution(executionEntity); - model.setId(executionEntity.getId()); - return model; + entity.addAuthenticationExecution(executionEntity); + return MapAuthenticationExecutionEntity.toModel(executionEntity); } @Override public void updateAuthenticatorExecution(AuthenticationExecutionModel model) { - entity.updateAuthenticatonExecution(MapAuthenticationExecutionEntity.fromModel(model)); + entity.getAuthenticationExecution(model.getId()) + .ifPresent(existing -> { + existing.setAuthenticator(model.getAuthenticator()); + existing.setAuthenticatorConfig(model.getAuthenticatorConfig()); + existing.setFlowId(model.getFlowId()); + existing.setParentFlowId(model.getParentFlow()); + existing.setRequirement(model.getRequirement()); + existing.setAutheticatorFlow(model.isAuthenticatorFlow()); + existing.setPriority(model.getPriority()); + }); } @Override public void removeAuthenticatorExecution(AuthenticationExecutionModel model) { - entity.removeAuthenticatonExecution(model.getId()); + entity.removeAuthenticationExecution(model.getId()); } @Override public Stream getAuthenticatorConfigsStream() { - return entity.getAuthenticatorConfigs().map(MapAuthenticatorConfigEntity::toModel); + Set acs = entity.getAuthenticatorConfigs(); + return acs == null ? Stream.empty() : acs.stream().map(MapAuthenticatorConfigEntity::toModel); } @Override @@ -759,7 +844,11 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void updateAuthenticatorConfig(AuthenticatorConfigModel model) { - entity.updateAuthenticatorConfig(MapAuthenticatorConfigEntity.fromModel(model)); + entity.getAuthenticatorConfig(model.getId()) + .ifPresent(oldAC -> { + oldAC.setAlias(model.getAlias()); + oldAC.setConfig(model.getConfig()); + }); } @Override @@ -770,12 +859,13 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public AuthenticatorConfigModel getAuthenticatorConfigById(String id) { if (id == null) return null; - return MapAuthenticatorConfigEntity.toModel(entity.getAuthenticatorConfig(id)); + return entity.getAuthenticatorConfig(id).map(MapAuthenticatorConfigEntity::toModel).orElse(null); } @Override public AuthenticatorConfigModel getAuthenticatorConfigByAlias(String alias) { - return entity.getAuthenticatorConfigs() + Set acs = entity.getAuthenticatorConfigs(); + return acs == null ? null : acs.stream() .filter(config -> Objects.equals(config.getAlias(), alias)) .findFirst() .map(MapAuthenticatorConfigEntity::toModel) @@ -784,7 +874,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getRequiredActionProvidersStream() { - return entity.getRequiredActionProviders() + Set raps = entity.getRequiredActionProviders(); + return raps == null ? Stream.empty() : raps.stream() .map(MapRequiredActionProviderEntity::toModel) .sorted(RequiredActionProviderModel.RequiredActionComparator.SINGLETON); } @@ -793,13 +884,22 @@ public class MapRealmAdapter extends AbstractRealmModel implemen public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) { MapRequiredActionProviderEntity requiredActionProvider = MapRequiredActionProviderEntity.fromModel(model); entity.addRequiredActionProvider(requiredActionProvider); - model.setId(requiredActionProvider.getId()); - return model; + + return MapRequiredActionProviderEntity.toModel(requiredActionProvider); } @Override public void updateRequiredActionProvider(RequiredActionProviderModel model) { - entity.updateRequiredActionProvider(MapRequiredActionProviderEntity.fromModel(model)); + entity.getRequiredActionProvider(model.getId()) + .ifPresent(oldRAP -> { + oldRAP.setAlias(model.getAlias()); + oldRAP.setName(model.getName()); + oldRAP.setProviderId(model.getProviderId()); + oldRAP.setPriority(model.getPriority()); + oldRAP.setEnabled(model.isEnabled()); + oldRAP.setDefaultAction(model.isDefaultAction()); + oldRAP.setConfig(model.getConfig()); + }); } @Override @@ -810,12 +910,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public RequiredActionProviderModel getRequiredActionProviderById(String id) { if (id == null) return null; - return MapRequiredActionProviderEntity.toModel(entity.getRequiredActionProvider(id)); + + return entity.getRequiredActionProvider(id).map(MapRequiredActionProviderEntity::toModel).orElse(null); } @Override public RequiredActionProviderModel getRequiredActionProviderByAlias(String alias) { - return entity.getRequiredActionProviders() + Set raps = entity.getRequiredActionProviders(); + return raps == null ? null : raps.stream() .filter(actionProvider -> Objects.equals(actionProvider.getAlias(), alias)) .findFirst() .map(MapRequiredActionProviderEntity::toModel) @@ -824,12 +926,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getIdentityProvidersStream() { - return entity.getIdentityProviders().map(MapIdentityProviderEntity::toModel); + Set ips = entity.getIdentityProviders(); + return ips == null ? Stream.empty() : ips.stream().map(MapIdentityProviderEntity::toModel); } @Override public IdentityProviderModel getIdentityProviderByAlias(String alias) { - return entity.getIdentityProviders() + Set ips = entity.getIdentityProviders(); + return ips == null ? null : ips.stream() .filter(identityProvider -> Objects.equals(identityProvider.getAlias(), alias)) .findFirst() .map(MapIdentityProviderEntity::toModel) @@ -869,37 +973,58 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void updateIdentityProvider(IdentityProviderModel identityProvider) { - entity.updateIdentityProvider(MapIdentityProviderEntity.fromModel(identityProvider)); + Set ips = entity.getIdentityProviders(); + if (ips != null) { + ips.stream() + .filter(ip -> Objects.equals(ip.getId(), identityProvider.getInternalId())) + .findFirst() + .ifPresent(oldPS -> { + oldPS.setAlias(identityProvider.getAlias()); + oldPS.setDisplayName(identityProvider.getDisplayName()); + oldPS.setProviderId(identityProvider.getProviderId()); + oldPS.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId()); + oldPS.setPostBrokerLoginFlowId(identityProvider.getPostBrokerLoginFlowId()); + oldPS.setEnabled(identityProvider.isEnabled()); + oldPS.setTrustEmail(identityProvider.isTrustEmail()); + oldPS.setStoreToken(identityProvider.isStoreToken()); + oldPS.setLinkOnly(identityProvider.isLinkOnly()); + oldPS.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate()); + oldPS.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault()); + oldPS.setConfig(identityProvider.getConfig() == null ? null : new HashMap<>(identityProvider.getConfig())); + }); - // TODO: Sending an event should be extracted to store layer - session.getKeycloakSessionFactory().publish(new RealmModel.IdentityProviderUpdatedEvent() { + // TODO: Sending an event should be extracted to store layer + session.getKeycloakSessionFactory().publish(new RealmModel.IdentityProviderUpdatedEvent() { - @Override - public RealmModel getRealm() { - return MapRealmAdapter.this; - } + @Override + public RealmModel getRealm() { + return MapRealmAdapter.this; + } - @Override - public IdentityProviderModel getUpdatedIdentityProvider() { - return identityProvider; - } + @Override + public IdentityProviderModel getUpdatedIdentityProvider() { + return identityProvider; + } - @Override - public KeycloakSession getKeycloakSession() { - return session; - } - }); - // TODO: ^^^^^^^ Up to here + @Override + public KeycloakSession getKeycloakSession() { + return session; + } + }); + // TODO: ^^^^^^^ Up to here + } } @Override public Stream getIdentityProviderMappersStream() { - return entity.getIdentityProviderMappers().map(MapIdentityProviderMapperEntity::toModel); + Set ipms = entity.getIdentityProviderMappers(); + return ipms == null ? Stream.empty() : ipms.stream().map(MapIdentityProviderMapperEntity::toModel); } @Override public Stream getIdentityProviderMappersByAliasStream(String brokerAlias) { - return entity.getIdentityProviderMappers() + Set ipms = entity.getIdentityProviderMappers(); + return ipms == null ? Stream.empty() : ipms.stream() .filter(mapper -> Objects.equals(mapper.getIdentityProviderAlias(), brokerAlias)) .map(MapIdentityProviderMapperEntity::toModel); } @@ -907,9 +1032,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public IdentityProviderMapperModel addIdentityProviderMapper(IdentityProviderMapperModel model) { MapIdentityProviderMapperEntity identityProviderMapper = MapIdentityProviderMapperEntity.fromModel(model); + + if (entity.getIdentityProviderMapper(model.getId()).isPresent()) { + throw new ModelDuplicateException("An IdentityProviderMapper with given id already exists"); + } + entity.addIdentityProviderMapper(identityProviderMapper); - model.setId(identityProviderMapper.getId()); - return model; + + return MapIdentityProviderMapperEntity.toModel(identityProviderMapper); } @Override @@ -919,19 +1049,26 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void updateIdentityProviderMapper(IdentityProviderMapperModel model) { - entity.updateIdentityProviderMapper(MapIdentityProviderMapperEntity.fromModel(model)); + entity.getIdentityProviderMapper(model.getId()) + .ifPresent(oldIPM -> { + oldIPM.setName(model.getName()); + oldIPM.setIdentityProviderAlias(model.getIdentityProviderAlias()); + oldIPM.setIdentityProviderMapper(model.getIdentityProviderMapper()); + oldIPM.setConfig(model.getConfig()); + }); } @Override public IdentityProviderMapperModel getIdentityProviderMapperById(String id) { if (id == null) return null; - return MapIdentityProviderMapperEntity.toModel(entity.getIdentityProviderMapper(id)); + return entity.getIdentityProviderMapper(id).map(MapIdentityProviderMapperEntity::toModel).orElse(null); } @Override public IdentityProviderMapperModel getIdentityProviderMapperByName(String brokerAlias, String name) { - return entity.getIdentityProviderMappers() - .filter(identityProviderMapper -> Objects.equals(identityProviderMapper.getIdentityProviderAlias(), brokerAlias) + Set ipms = entity.getIdentityProviderMappers(); + return ipms == null ? null : ipms.stream() + .filter(identityProviderMapper -> Objects.equals(identityProviderMapper.getIdentityProviderAlias(), brokerAlias) && Objects.equals(identityProviderMapper.getName(), name)) .findFirst() .map(MapIdentityProviderMapperEntity::toModel) @@ -964,30 +1101,42 @@ public class MapRealmAdapter extends AbstractRealmModel implemen } } + if (entity.getComponent(model.getId()).isPresent()) { + throw new ModelDuplicateException("A Component with given id already exists"); + } + MapComponentEntity component = MapComponentEntity.fromModel(model); if (model.getParentId() == null) { component.setParentId(getId()); - model.setParentId(getId()); } entity.addComponent(component); - model.setId(component.getId()); - return model; + + return MapComponentEntity.toModel(component); } @Override public void updateComponent(ComponentModel component) { ComponentUtil.getComponentFactory(session, component).validateConfiguration(session, this, component); + entity.getComponent(component.getId()) + .ifPresent(existing -> { + ComponentModel oldModel = MapComponentEntity.toModel(existing); + updateComponent(existing, component); + ComponentUtil.notifyUpdated(session, this, oldModel, component); + }); + } - MapComponentEntity old = entity.getComponent(component.getId()); - if (old == null) return; - - entity.updateComponent(MapComponentEntity.fromModel(component)); - ComponentUtil.notifyUpdated(session, this, MapComponentEntity.toModel(old), component); + private static void updateComponent(MapComponentEntity oldValue, ComponentModel newValue) { + oldValue.setName(newValue.getName()); + oldValue.setProviderId(newValue.getProviderId()); + oldValue.setProviderType(newValue.getProviderType()); + oldValue.setSubType(newValue.getSubType()); + oldValue.setParentId(newValue.getParentId()); + oldValue.setConfig(newValue.getConfig()); } @Override public void removeComponent(ComponentModel component) { - if (entity.getComponent(component.getId()) == null) return; + if (!entity.getComponent(component.getId()).isPresent()) return; session.users().preRemove(this, component); ComponentUtil.notifyPreRemove(session, this, component); @@ -997,7 +1146,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void removeComponents(String parentId) { - entity.getComponents() + Set components = entity.getComponents(); + if (components == null || components.isEmpty()) return; + components.stream() .filter(c -> Objects.equals(parentId, c.getParentId())) .map(MapComponentEntity::toModel) .collect(Collectors.toSet()) // This is necessary to read out all the components before removing them @@ -1010,19 +1161,22 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getComponentsStream() { - return entity.getComponents().map(MapComponentEntity::toModel); + Set components = entity.getComponents(); + return components == null ? Stream.empty() : components.stream().map(MapComponentEntity::toModel); } @Override public Stream getComponentsStream(String parentId) { - return entity.getComponents() - .filter(c -> Objects.equals(parentId, c.getParentId())) - .map(MapComponentEntity::toModel); + Set components = entity.getComponents(); + return components == null ? Stream.empty() : components.stream() + .filter(c -> Objects.equals(parentId, c.getParentId())) + .map(MapComponentEntity::toModel); } @Override public Stream getComponentsStream(String parentId, String providerType) { - return entity.getComponents() + Set components = entity.getComponents(); + return components == null ? Stream.empty() : components.stream() .filter(c -> Objects.equals(parentId, c.getParentId())) .filter(c -> Objects.equals(providerType, c.getProviderType())) .map(MapComponentEntity::toModel); @@ -1030,7 +1184,7 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public ComponentModel getComponent(String id) { - return MapComponentEntity.toModel(entity.getComponent(id)); + return entity.getComponent(id).map(MapComponentEntity::toModel).orElse(null); } @Override @@ -1075,7 +1229,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getNotBefore() { - return entity.getNotBefore(); + Integer i = entity.getNotBefore(); + return i == null ? 0 : i; } @Override @@ -1085,7 +1240,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isEventsEnabled() { - return entity.isEventsEnabled(); + Boolean is = entity.isEventsEnabled(); + return is == null ? false : is; } @Override @@ -1095,7 +1251,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public long getEventsExpiration() { - return entity.getEventsExpiration(); + Long i = entity.getEventsExpiration(); + return i == null ? 0 : i; } @Override @@ -1105,7 +1262,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getEventsListenersStream() { - return entity.getEventsListeners().stream(); + Set eLs = entity.getEventsListeners(); + return eLs == null ? Stream.empty() : eLs.stream(); } @Override @@ -1115,7 +1273,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getEnabledEventTypesStream() { - return entity.getEnabledEventTypes().stream(); + Set eETs = entity.getEnabledEventTypes(); + return eETs == null ? Stream.empty() : eETs.stream(); } @Override @@ -1125,7 +1284,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isAdminEventsEnabled() { - return entity.isAdminEventsEnabled(); + Boolean is = entity.isAdminEventsEnabled(); + return is == null ? false : is; } @Override @@ -1135,7 +1295,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isAdminEventsDetailsEnabled() { - return entity.isAdminEventsDetailsEnabled(); + Boolean is = entity.isAdminEventsDetailsEnabled(); + return is == null ? false : is; } @Override @@ -1173,12 +1334,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isIdentityFederationEnabled() { - return entity.getIdentityProviders().findFirst().isPresent(); + Set ips = entity.getIdentityProviders(); + return ips != null && ips.stream().findAny().isPresent(); } @Override public boolean isInternationalizationEnabled() { - return entity.isInternationalizationEnabled(); + Boolean is = entity.isInternationalizationEnabled(); + return is == null ? false : is; } @Override @@ -1188,7 +1351,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getSupportedLocalesStream() { - return entity.getSupportedLocales().stream(); + Set sLs = entity.getSupportedLocales(); + return sLs == null ? Stream.empty() : sLs.stream(); } @Override @@ -1284,53 +1448,55 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void addDefaultClientScope(ClientScopeModel clientScope, boolean defaultScope) { if (defaultScope) { - entity.addDefaultClientScope(clientScope.getId()); + entity.addDefaultClientScopeId(clientScope.getId()); } else { - entity.addOptionalClientScope(clientScope.getId()); + entity.addOptionalClientScopeId(clientScope.getId()); } } @Override public void removeDefaultClientScope(ClientScopeModel clientScope) { - entity.removeDefaultOrOptionalClientScope(clientScope.getId()); + Boolean removedDefault = entity.removeDefaultClientScopeId(clientScope.getId()); + if (removedDefault == null || !removedDefault) { + entity.removeOptionalClientScopeId(clientScope.getId()); + } } @Override public Stream getDefaultClientScopesStream(boolean defaultScope) { - if (defaultScope) { - return entity.getDefaultClientScopeIds().map(this::getClientScopeById); - } else { - return entity.getOptionalClientScopeIds().map(this::getClientScopeById); - } + Set csIds = defaultScope ? entity.getDefaultClientScopeIds() : entity.getOptionalClientScopeIds(); + return csIds == null ? Stream.empty() : csIds.stream().map(this::getClientScopeById); } @Override public void createOrUpdateRealmLocalizationTexts(String locale, Map localizationTexts) { Map> realmLocalizationTexts = entity.getLocalizationTexts(); - if (realmLocalizationTexts.containsKey(locale)) { - Map currentTexts = realmLocalizationTexts.get(locale); + if (realmLocalizationTexts != null && realmLocalizationTexts.containsKey(locale)) { + Map currentTexts = new HashMap<>(realmLocalizationTexts.get(locale)); currentTexts.putAll(localizationTexts); - entity.updateLocalizationTexts(locale, currentTexts); + entity.setLocalizationText(locale, currentTexts); } else { - entity.addLocalizationTexts(locale, localizationTexts); + entity.setLocalizationText(locale, localizationTexts); } } @Override public boolean removeRealmLocalizationTexts(String locale) { if (locale == null) return false; - return entity.removeLocalizationTexts(locale); + return entity.removeLocalizationText(locale); } @Override public Map> getRealmLocalizationTexts() { - return entity.getLocalizationTexts(); + Map> localizationTexts = entity.getLocalizationTexts(); + return localizationTexts == null ? Collections.emptyMap() : localizationTexts; } @Override public Map getRealmLocalizationTextsByLocale(String locale) { - return entity.getLocalizationText(locale); + Map lT = entity.getLocalizationText(locale); + return lT == null ? Collections.emptyMap() : lT; } @Override @@ -1492,7 +1658,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public boolean isOfflineSessionMaxLifespanEnabled() { - return entity.isOfflineSessionMaxLifespanEnabled(); + Boolean is = entity.isOfflineSessionMaxLifespanEnabled(); + return is == null ? false : is; } @Override @@ -1502,7 +1669,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getOfflineSessionMaxLifespan() { - return entity.getOfflineSessionMaxLifespan(); + Integer i = entity.getOfflineSessionMaxLifespan(); + return i == null ? 0 : i; } @Override @@ -1512,7 +1680,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public WebAuthnPolicy getWebAuthnPolicy() { - return MapWebAuthnPolicyEntity.toModel(entity.getWebAuthnPolicy()); + MapWebAuthnPolicyEntity policy = entity.getWebAuthnPolicy(); + if (policy == null) policy = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy(); + return MapWebAuthnPolicyEntity.toModel(policy); } @Override @@ -1522,7 +1692,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public WebAuthnPolicy getWebAuthnPolicyPasswordless() { - return MapWebAuthnPolicyEntity.toModel(entity.getWebAuthnPolicyPasswordless()); + MapWebAuthnPolicyEntity policy = entity.getWebAuthnPolicyPasswordless(); + if (policy == null) policy = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy(); + return MapWebAuthnPolicyEntity.toModel(policy); } @Override @@ -1532,7 +1704,8 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Map getBrowserSecurityHeaders() { - return Collections.unmodifiableMap(entity.getBrowserSecurityHeaders()); + Map bSH = entity.getBrowserSecurityHeaders(); + return bSH == null ? Collections.emptyMap() : Collections.unmodifiableMap(bSH); } @Override @@ -1549,7 +1722,7 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public ClientInitialAccessModel getClientInitialAccessModel(String id) { - return MapClientInitialAccessEntity.toModel(entity.getClientInitialAccess(id)); + return entity.getClientInitialAccess(id).map(MapClientInitialAccessEntity::toModel).orElse(null); } @Override @@ -1559,14 +1732,14 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public Stream getClientInitialAccesses() { - return entity.getClientInitialAccesses().stream().map(MapClientInitialAccessEntity::toModel); + Set cias = entity.getClientInitialAccesses(); + return cias == null ? Stream.empty() : cias.stream().map(MapClientInitialAccessEntity::toModel); } @Override public void decreaseRemainingCount(ClientInitialAccessModel model) { - MapClientInitialAccessEntity clientInitialAccess = entity.getClientInitialAccess(model.getId()); - clientInitialAccess.setRemainingCount(model.getRemainingCount() - 1); - entity.updateClientInitialAccess(clientInitialAccess); + entity.getClientInitialAccess(model.getId()) + .ifPresent(cia -> cia.setRemainingCount(model.getRemainingCount() - 1)); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java index bfa4066d1b..cac724c4a6 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java @@ -1,35 +1,27 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + package org.keycloak.models.map.realm; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.keycloak.common.util.Time; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.OTPPolicy; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.annotations.IgnoreForEntityImplementationGenerator; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.map.realm.entity.MapAuthenticationExecutionEntity; @@ -44,1004 +36,455 @@ import org.keycloak.models.map.realm.entity.MapRequiredActionProviderEntity; import org.keycloak.models.map.realm.entity.MapRequiredCredentialEntity; import org.keycloak.models.map.realm.entity.MapWebAuthnPolicyEntity; -public class MapRealmEntity extends UpdatableEntity.Impl implements AbstractEntity, EntityWithAttributes { - - private String id; - private String name; - - private Boolean enabled = false; - private Boolean registrationAllowed = false; - private Boolean registrationEmailAsUsername = false; - private Boolean verifyEmail = false; - private Boolean resetPasswordAllowed = false; - private Boolean loginWithEmailAllowed = false; - private Boolean duplicateEmailsAllowed = false; - private Boolean rememberMe = false; - private Boolean editUsernameAllowed = false; - private Boolean revokeRefreshToken = false; - private Boolean adminEventsEnabled = false; - private Boolean adminEventsDetailsEnabled = false; - private Boolean internationalizationEnabled = false; - private Boolean allowUserManagedAccess = false; - private Boolean offlineSessionMaxLifespanEnabled = false; - private Boolean eventsEnabled = false; - private Integer refreshTokenMaxReuse = 0; - private Integer ssoSessionIdleTimeout = 0; - private Integer ssoSessionMaxLifespan = 0; - private Integer ssoSessionIdleTimeoutRememberMe = 0; - private Integer ssoSessionMaxLifespanRememberMe = 0; - private Integer offlineSessionIdleTimeout = 0; - private Integer accessTokenLifespan = 0; - private Integer accessTokenLifespanForImplicitFlow = 0; - private Integer accessCodeLifespan = 0; - private Integer accessCodeLifespanUserAction = 0; - private Integer accessCodeLifespanLogin = 0; - private Integer notBefore = 0; - private Integer clientSessionIdleTimeout = 0; - private Integer clientSessionMaxLifespan = 0; - private Integer clientOfflineSessionIdleTimeout = 0; - private Integer clientOfflineSessionMaxLifespan = 0; - private Integer actionTokenGeneratedByAdminLifespan = 0; - private Integer offlineSessionMaxLifespan = 0; - private Long eventsExpiration = 0l; - private String displayName; - private String displayNameHtml; - private String passwordPolicy; - private String sslRequired; - private String loginTheme; - private String accountTheme; - private String adminTheme; - private String emailTheme; - private String masterAdminClient; - private String defaultRoleId; - private String defaultLocale; - private String browserFlow; - private String registrationFlow; - private String directGrantFlow; - private String resetCredentialsFlow; - private String clientAuthenticationFlow; - private String dockerAuthenticationFlow; - private MapOTPPolicyEntity otpPolicy = MapOTPPolicyEntity.fromModel(OTPPolicy.DEFAULT_POLICY);; - private MapWebAuthnPolicyEntity webAuthnPolicy = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy();; - private MapWebAuthnPolicyEntity webAuthnPolicyPasswordless = MapWebAuthnPolicyEntity.defaultWebAuthnPolicy();; - - private Set eventsListeners = new HashSet<>(); - private Set enabledEventTypes = new HashSet<>(); - private Set supportedLocales = new HashSet<>(); - private Map browserSecurityHeaders = new HashMap<>(); - private Map smtpConfig = new HashMap<>(); - - private final Set defaultGroupIds = new HashSet<>(); - private final Set defaultClientScopes = new HashSet<>(); - private final Set optionalClientScopes = new HashSet<>(); - private final Map> attributes = new HashMap<>(); - private final Map> localizationTexts = new HashMap<>(); - private final Map clientInitialAccesses = new HashMap<>(); - private final Map components = new HashMap<>(); - private final Map authenticationFlows = new HashMap<>(); - private final Map authenticationExecutions = new HashMap<>(); - private final Map requiredCredentials = new HashMap<>(); - private final Map authenticatorConfigs = new HashMap<>(); - private final Map identityProviders = new HashMap<>(); - private final Map identityProviderMappers = new HashMap<>(); - private final Map requiredActionProviders = new HashMap<>(); - - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ - - public MapRealmEntity() {} - - public MapRealmEntity(String id) { - this.id = id; - } - - @Override - public String getId() { - return this.id; - } - - @Override - public void setId(String id) { - if (this.id != null) throw new IllegalStateException("Id cannot be changed"); - this.id = id; - this.updated |= id != null; - } - - @Override - public boolean isUpdated() { - return this.updated - || authenticationExecutions.values().stream().anyMatch(MapAuthenticationExecutionEntity::isUpdated) - || authenticationFlows.values().stream().anyMatch(MapAuthenticationFlowEntity::isUpdated) - || authenticatorConfigs.values().stream().anyMatch(MapAuthenticatorConfigEntity::isUpdated) - || clientInitialAccesses.values().stream().anyMatch(MapClientInitialAccessEntity::isUpdated) - || components.values().stream().anyMatch(MapComponentEntity::isUpdated) - || identityProviders.values().stream().anyMatch(MapIdentityProviderEntity::isUpdated) - || identityProviderMappers.values().stream().anyMatch(MapIdentityProviderMapperEntity::isUpdated) - || requiredActionProviders.values().stream().anyMatch(MapRequiredActionProviderEntity::isUpdated) - || requiredCredentials.values().stream().anyMatch(MapRequiredCredentialEntity::isUpdated) - || otpPolicy.isUpdated() - || webAuthnPolicy.isUpdated() - || webAuthnPolicyPasswordless.isUpdated(); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.updated |= ! Objects.equals(this.name, name); - this.name = name; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.updated |= ! Objects.equals(this.displayName, displayName); - this.displayName = displayName; - } - - public String getDisplayNameHtml() { - return displayNameHtml; - } - - public void setDisplayNameHtml(String displayNameHtml) { - this.updated |= ! Objects.equals(this.displayNameHtml, displayNameHtml); - this.displayNameHtml = displayNameHtml; - } - - public Boolean isEnabled() { - return enabled; - } - - public void setEnabled(Boolean enabled) { - this.updated |= ! Objects.equals(this.enabled, enabled); - this.enabled = enabled; - } - - public Boolean isRegistrationAllowed() { - return registrationAllowed; - } - - public void setRegistrationAllowed(Boolean registrationAllowed) { - this.updated |= ! Objects.equals(this.registrationAllowed, registrationAllowed); - this.registrationAllowed = registrationAllowed; - } - - public Boolean isRegistrationEmailAsUsername() { - return registrationEmailAsUsername; - } - - public void setRegistrationEmailAsUsername(Boolean registrationEmailAsUsername) { - this.updated |= ! Objects.equals(this.registrationEmailAsUsername, registrationEmailAsUsername); - this.registrationEmailAsUsername = registrationEmailAsUsername; - } - - public Boolean isVerifyEmail() { - return verifyEmail; - } - - public void setVerifyEmail(Boolean verifyEmail) { - this.updated |= ! Objects.equals(this.verifyEmail, verifyEmail); - this.verifyEmail = verifyEmail; - } - - - public Boolean isResetPasswordAllowed() { - return resetPasswordAllowed; - } - - public void setResetPasswordAllowed(Boolean resetPasswordAllowed) { - this.updated |= ! Objects.equals(this.resetPasswordAllowed, resetPasswordAllowed); - this.resetPasswordAllowed = resetPasswordAllowed; - } - - public Boolean isLoginWithEmailAllowed() { - return loginWithEmailAllowed; - } - - public void setLoginWithEmailAllowed(Boolean loginWithEmailAllowed) { - this.updated |= ! Objects.equals(this.loginWithEmailAllowed, loginWithEmailAllowed); - this.loginWithEmailAllowed = loginWithEmailAllowed; - } - - public Boolean isDuplicateEmailsAllowed() { - return duplicateEmailsAllowed; - } - - public void setDuplicateEmailsAllowed(Boolean duplicateEmailsAllowed) { - this.updated |= ! Objects.equals(this.duplicateEmailsAllowed, duplicateEmailsAllowed); - this.duplicateEmailsAllowed = duplicateEmailsAllowed; - } - - public Boolean isRememberMe() { - return rememberMe; - } - - public void setRememberMe(Boolean rememberMe) { - this.updated |= ! Objects.equals(this.rememberMe, rememberMe); - this.rememberMe = rememberMe; - } - - public Boolean isEditUsernameAllowed() { - return editUsernameAllowed; - } - - public void setEditUsernameAllowed(Boolean editUsernameAllowed) { - this.updated |= ! Objects.equals(this.editUsernameAllowed, editUsernameAllowed); - this.editUsernameAllowed = editUsernameAllowed; - } - - public Boolean isRevokeRefreshToken() { - return revokeRefreshToken; - } - - public void setRevokeRefreshToken(Boolean revokeRefreshToken) { - this.updated |= ! Objects.equals(this.revokeRefreshToken, revokeRefreshToken); - this.revokeRefreshToken = revokeRefreshToken; - } - - public Boolean isAdminEventsEnabled() { - return adminEventsEnabled; - } - - public void setAdminEventsEnabled(Boolean adminEventsEnabled) { - this.updated |= ! Objects.equals(this.adminEventsEnabled, adminEventsEnabled); - this.adminEventsEnabled = adminEventsEnabled; - } - - public Boolean isAdminEventsDetailsEnabled() { - return adminEventsDetailsEnabled; - } - - public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) { - this.updated |= ! Objects.equals(this.adminEventsDetailsEnabled, adminEventsDetailsEnabled); - this.adminEventsDetailsEnabled = adminEventsDetailsEnabled; - } - - public Boolean isInternationalizationEnabled() { - return internationalizationEnabled; - } - - public void setInternationalizationEnabled(Boolean internationalizationEnabled) { - this.updated |= ! Objects.equals(this.internationalizationEnabled, internationalizationEnabled); - this.internationalizationEnabled = internationalizationEnabled; - } - - public Boolean isAllowUserManagedAccess() { - return allowUserManagedAccess; - } - - public void setAllowUserManagedAccess(Boolean allowUserManagedAccess) { - this.updated |= ! Objects.equals(this.allowUserManagedAccess, allowUserManagedAccess); - this.allowUserManagedAccess = allowUserManagedAccess; - } - - public Boolean isOfflineSessionMaxLifespanEnabled() { - return offlineSessionMaxLifespanEnabled; - } - - public void setOfflineSessionMaxLifespanEnabled(Boolean offlineSessionMaxLifespanEnabled) { - this.updated |= ! Objects.equals(this.offlineSessionMaxLifespanEnabled, offlineSessionMaxLifespanEnabled); - this.offlineSessionMaxLifespanEnabled = offlineSessionMaxLifespanEnabled; - } - - public Boolean isEventsEnabled() { - return eventsEnabled; - } - - public void setEventsEnabled(Boolean eventsEnabled) { - this.updated |= ! Objects.equals(this.eventsEnabled, eventsEnabled); - this.eventsEnabled = eventsEnabled; - } - - public Integer getRefreshTokenMaxReuse() { - return refreshTokenMaxReuse; - } - - public void setRefreshTokenMaxReuse(Integer refreshTokenMaxReuse) { - this.updated |= ! Objects.equals(this.refreshTokenMaxReuse, refreshTokenMaxReuse); - this.refreshTokenMaxReuse = refreshTokenMaxReuse; - } - - public Integer getSsoSessionIdleTimeout() { - return ssoSessionIdleTimeout; - } - - public void setSsoSessionIdleTimeout(Integer ssoSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.ssoSessionIdleTimeout, ssoSessionIdleTimeout); - this.ssoSessionIdleTimeout = ssoSessionIdleTimeout; - } - - public Integer getSsoSessionMaxLifespan() { - return ssoSessionMaxLifespan; - } - - public void setSsoSessionMaxLifespan(Integer ssoSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.ssoSessionMaxLifespan, ssoSessionMaxLifespan); - this.ssoSessionMaxLifespan = ssoSessionMaxLifespan; - } - - public Integer getSsoSessionIdleTimeoutRememberMe() { - return ssoSessionIdleTimeoutRememberMe; - } - - public void setSsoSessionIdleTimeoutRememberMe(Integer ssoSessionIdleTimeoutRememberMe) { - this.updated |= ! Objects.equals(this.ssoSessionIdleTimeoutRememberMe, ssoSessionIdleTimeoutRememberMe); - this.ssoSessionIdleTimeoutRememberMe = ssoSessionIdleTimeoutRememberMe; - } - - public Integer getSsoSessionMaxLifespanRememberMe() { - return ssoSessionMaxLifespanRememberMe; - } - - public void setSsoSessionMaxLifespanRememberMe(Integer ssoSessionMaxLifespanRememberMe) { - this.updated |= ! Objects.equals(this.ssoSessionMaxLifespanRememberMe, ssoSessionMaxLifespanRememberMe); - this.ssoSessionMaxLifespanRememberMe = ssoSessionMaxLifespanRememberMe; - } - - public Integer getOfflineSessionIdleTimeout() { - return offlineSessionIdleTimeout; - } - - public void setOfflineSessionIdleTimeout(Integer offlineSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.offlineSessionIdleTimeout, offlineSessionIdleTimeout); - this.offlineSessionIdleTimeout = offlineSessionIdleTimeout; - } - - public Integer getAccessTokenLifespan() { - return accessTokenLifespan; - } - - public void setAccessTokenLifespan(Integer accessTokenLifespan) { - this.updated |= ! Objects.equals(this.accessTokenLifespan, accessTokenLifespan); - this.accessTokenLifespan = accessTokenLifespan; - } - - public Integer getAccessTokenLifespanForImplicitFlow() { - return accessTokenLifespanForImplicitFlow; - } - - public void setAccessTokenLifespanForImplicitFlow(Integer accessTokenLifespanForImplicitFlow) { - this.updated |= ! Objects.equals(this.accessTokenLifespanForImplicitFlow, accessTokenLifespanForImplicitFlow); - this.accessTokenLifespanForImplicitFlow = accessTokenLifespanForImplicitFlow; - } - - public Integer getAccessCodeLifespan() { - return accessCodeLifespan; - } - - public void setAccessCodeLifespan(Integer accessCodeLifespan) { - this.updated |= ! Objects.equals(this.accessCodeLifespan, accessCodeLifespan); - this.accessCodeLifespan = accessCodeLifespan; - } - - public Integer getAccessCodeLifespanUserAction() { - return accessCodeLifespanUserAction; - } - - public void setAccessCodeLifespanUserAction(Integer accessCodeLifespanUserAction) { - this.updated |= ! Objects.equals(this.accessCodeLifespanUserAction, accessCodeLifespanUserAction); - this.accessCodeLifespanUserAction = accessCodeLifespanUserAction; - } - - public Integer getAccessCodeLifespanLogin() { - return accessCodeLifespanLogin; - } - - public void setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin) { - this.updated |= ! Objects.equals(this.accessCodeLifespanLogin, accessCodeLifespanLogin); - this.accessCodeLifespanLogin = accessCodeLifespanLogin; - } - - public Integer getNotBefore() { - return notBefore; - } - - public void setNotBefore(Integer notBefore) { - this.updated |= ! Objects.equals(this.notBefore, notBefore); - this.notBefore = notBefore; - } - - public Integer getClientSessionIdleTimeout() { - return clientSessionIdleTimeout; - } - - public void setClientSessionIdleTimeout(Integer clientSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.clientSessionIdleTimeout, clientSessionIdleTimeout); - this.clientSessionIdleTimeout = clientSessionIdleTimeout; - } - - public Integer getClientSessionMaxLifespan() { - return clientSessionMaxLifespan; - } - - public void setClientSessionMaxLifespan(Integer clientSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.clientSessionMaxLifespan, clientSessionMaxLifespan); - this.clientSessionMaxLifespan = clientSessionMaxLifespan; - } - - public Integer getClientOfflineSessionIdleTimeout() { - return clientOfflineSessionIdleTimeout; - } - - public void setClientOfflineSessionIdleTimeout(Integer clientOfflineSessionIdleTimeout) { - this.updated |= ! Objects.equals(this.clientOfflineSessionIdleTimeout, clientOfflineSessionIdleTimeout); - this.clientOfflineSessionIdleTimeout = clientOfflineSessionIdleTimeout; - } - - public Integer getClientOfflineSessionMaxLifespan() { - return clientOfflineSessionMaxLifespan; - } - - public void setClientOfflineSessionMaxLifespan(Integer clientOfflineSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.clientOfflineSessionMaxLifespan, clientOfflineSessionMaxLifespan); - this.clientOfflineSessionMaxLifespan = clientOfflineSessionMaxLifespan; - } - - public Integer getActionTokenGeneratedByAdminLifespan() { - return actionTokenGeneratedByAdminLifespan; - } - - public void setActionTokenGeneratedByAdminLifespan(Integer actionTokenGeneratedByAdminLifespan) { - this.updated |= ! Objects.equals(this.actionTokenGeneratedByAdminLifespan, actionTokenGeneratedByAdminLifespan); - this.actionTokenGeneratedByAdminLifespan = actionTokenGeneratedByAdminLifespan; - } - - public Integer getOfflineSessionMaxLifespan() { - return offlineSessionMaxLifespan; - } - - public void setOfflineSessionMaxLifespan(Integer offlineSessionMaxLifespan) { - this.updated |= ! Objects.equals(this.offlineSessionMaxLifespan, offlineSessionMaxLifespan); - this.offlineSessionMaxLifespan = offlineSessionMaxLifespan; - } - - public Long getEventsExpiration() { - return eventsExpiration; - } - - public void setEventsExpiration(Long eventsExpiration) { - this.updated |= ! Objects.equals(this.eventsExpiration, eventsExpiration); - this.eventsExpiration = eventsExpiration; - } - - public String getPasswordPolicy() { - return passwordPolicy; - } - - public void setPasswordPolicy(String passwordPolicy) { - this.updated |= ! Objects.equals(this.passwordPolicy, passwordPolicy); - this.passwordPolicy = passwordPolicy; - } - - public String getSslRequired() { - return sslRequired; - } - - public void setSslRequired(String sslRequired) { - this.updated |= ! Objects.equals(this.sslRequired, sslRequired); - this.sslRequired = sslRequired; - } - - public String getLoginTheme() { - return loginTheme; - } - - public void setLoginTheme(String loginTheme) { - this.updated |= ! Objects.equals(this.loginTheme, loginTheme); - this.loginTheme = loginTheme; - } - - public String getAccountTheme() { - return accountTheme; - } - - public void setAccountTheme(String accountTheme) { - this.updated |= ! Objects.equals(this.accountTheme, accountTheme); - this.accountTheme = accountTheme; - } - - public String getAdminTheme() { - return adminTheme; - } - - public void setAdminTheme(String adminTheme) { - this.updated |= ! Objects.equals(this.adminTheme, adminTheme); - this.adminTheme = adminTheme; - } - - public String getEmailTheme() { - return emailTheme; - } - - public void setEmailTheme(String emailTheme) { - this.updated |= ! Objects.equals(this.emailTheme, emailTheme); - this.emailTheme = emailTheme; - } - - public String getMasterAdminClient() { - return masterAdminClient; - } - - public void setMasterAdminClient(String masterAdminClient) { - this.updated |= ! Objects.equals(this.masterAdminClient, masterAdminClient); - this.masterAdminClient = masterAdminClient; - } - - public String getDefaultRoleId() { - return defaultRoleId; - } - - public void setDefaultRoleId(String defaultRoleId) { - this.updated |= ! Objects.equals(this.defaultRoleId, defaultRoleId); - this.defaultRoleId = defaultRoleId; - } - - public String getDefaultLocale() { - return defaultLocale; - } - - public void setDefaultLocale(String defaultLocale) { - this.updated |= ! Objects.equals(this.defaultLocale, defaultLocale); - this.defaultLocale = defaultLocale; - } - - public String getBrowserFlow() { - return browserFlow; - } - - public void setBrowserFlow(String browserFlow) { - this.updated |= ! Objects.equals(this.browserFlow, browserFlow); - this.browserFlow = browserFlow; - } - - public String getRegistrationFlow() { - return registrationFlow; - } - - public void setRegistrationFlow(String registrationFlow) { - this.updated |= ! Objects.equals(this.registrationFlow, registrationFlow); - this.registrationFlow = registrationFlow; - } - - public String getDirectGrantFlow() { - return directGrantFlow; - } - - public void setDirectGrantFlow(String directGrantFlow) { - this.updated |= ! Objects.equals(this.directGrantFlow, directGrantFlow); - this.directGrantFlow = directGrantFlow; - } - - public String getResetCredentialsFlow() { - return resetCredentialsFlow; - } - - public void setResetCredentialsFlow(String resetCredentialsFlow) { - this.updated |= ! Objects.equals(this.resetCredentialsFlow, resetCredentialsFlow); - this.resetCredentialsFlow = resetCredentialsFlow; - } - - public String getClientAuthenticationFlow() { - return clientAuthenticationFlow; - } - - public void setClientAuthenticationFlow(String clientAuthenticationFlow) { - this.updated |= ! Objects.equals(this.clientAuthenticationFlow, clientAuthenticationFlow); - this.clientAuthenticationFlow = clientAuthenticationFlow; - } - - public String getDockerAuthenticationFlow() { - return dockerAuthenticationFlow; - } - - public void setDockerAuthenticationFlow(String dockerAuthenticationFlow) { - this.updated |= ! Objects.equals(this.dockerAuthenticationFlow, dockerAuthenticationFlow); - this.dockerAuthenticationFlow = dockerAuthenticationFlow; - } - - public MapOTPPolicyEntity getOTPPolicy() { - return otpPolicy; - } - - public void setOTPPolicy(MapOTPPolicyEntity otpPolicy) { - this.updated |= ! Objects.equals(this.otpPolicy, otpPolicy); - this.otpPolicy = otpPolicy; - } - - public MapWebAuthnPolicyEntity getWebAuthnPolicy() { - return webAuthnPolicy; - } - - public void setWebAuthnPolicy(MapWebAuthnPolicyEntity webAuthnPolicy) { - this.updated |= ! Objects.equals(this.webAuthnPolicy, webAuthnPolicy); - this.webAuthnPolicy = webAuthnPolicy; - } - - public MapWebAuthnPolicyEntity getWebAuthnPolicyPasswordless() { - return webAuthnPolicyPasswordless; - } - - public void setWebAuthnPolicyPasswordless(MapWebAuthnPolicyEntity webAuthnPolicyPasswordless) { - this.updated |= ! Objects.equals(this.webAuthnPolicyPasswordless, webAuthnPolicyPasswordless); - this.webAuthnPolicyPasswordless = webAuthnPolicyPasswordless; - } - - @Override - public void setAttribute(String name, List values) { - this.updated |= ! Objects.equals(this.attributes.put(name, values), values); - } - - @Override - public void removeAttribute(String name) { - this.updated |= attributes.remove(name) != null; - } - - @Override - public List getAttribute(String name) { - return attributes.getOrDefault(name, Collections.EMPTY_LIST); - } - - @Override - public Map> getAttributes() { - return attributes; - } - - @Override - public void setAttributes(Map> attributes) { - this.attributes.clear(); - this.attributes.putAll(attributes); - this.updated = true; - } - - public void addDefaultClientScope(String scopeId) { - this.updated |= this.defaultClientScopes.add(scopeId); - } - - public Stream getDefaultClientScopeIds() { - return defaultClientScopes.stream(); - } - - public void addOptionalClientScope(String scopeId) { - this.updated |= this.optionalClientScopes.add(scopeId); - } - - public Stream getOptionalClientScopeIds() { - return optionalClientScopes.stream(); - } - - public void removeDefaultOrOptionalClientScope(String scopeId) { - if (this.defaultClientScopes.remove(scopeId)) { - this.updated = true; - return ; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@GenerateEntityImplementations( + inherits = "org.keycloak.models.map.realm.MapRealmEntity.AbstractRealmEntity" +) +@DeepCloner.Root +public interface MapRealmEntity extends UpdatableEntity, AbstractEntity, EntityWithAttributes { + + public abstract class AbstractRealmEntity extends UpdatableEntity.Impl implements MapRealmEntity { + + private String id; + + @Override + public String getId() { + return this.id; } - this.updated |= this.optionalClientScopes.remove(scopeId); - } - public Stream getDefaultGroupIds() { - return defaultGroupIds.stream(); - } - - public void addDefaultGroup(String groupId) { - this.updated |= this.defaultGroupIds.add(groupId); - } - - public void removeDefaultGroup(String groupId) { - this.updated |= this.defaultGroupIds.remove(groupId); - } - - public Set getEventsListeners() { - return eventsListeners; - } - - public void setEventsListeners(Set eventsListeners) { - if (eventsListeners == null) return; - this.updated |= ! Objects.equals(eventsListeners, this.eventsListeners); - this.eventsListeners = eventsListeners; - } - - public Set getEnabledEventTypes() { - return enabledEventTypes; - } - - public void setEnabledEventTypes(Set enabledEventTypes) { - if (enabledEventTypes == null) return; - this.updated |= ! Objects.equals(enabledEventTypes, this.enabledEventTypes); - this.enabledEventTypes = enabledEventTypes; - } - - public Set getSupportedLocales() { - return supportedLocales; - } - - public void setSupportedLocales(Set supportedLocales) { - if (supportedLocales == null) return; - this.updated |= ! Objects.equals(supportedLocales, this.supportedLocales); - this.supportedLocales = supportedLocales; - } - - public Map> getLocalizationTexts() { - return localizationTexts; - } - - public Map getLocalizationText(String locale) { - if (localizationTexts.containsKey(locale)) { - return localizationTexts.get(locale); + @Override + public void setId(String id) { + if (this.id != null) throw new IllegalStateException("Id cannot be changed"); + this.id = id; + this.updated |= id != null; } - return Collections.emptyMap(); - } - public void addLocalizationTexts(String locale, Map texts) { - if (! localizationTexts.containsKey(locale)) { - updated = true; - localizationTexts.put(locale, texts); + @Override + public boolean isUpdated() { + return this.updated + || Optional.ofNullable(getAuthenticationExecutions()).orElseGet(Collections::emptySet).stream().anyMatch(MapAuthenticationExecutionEntity::isUpdated) + || Optional.ofNullable(getAuthenticationFlows()).orElseGet(Collections::emptySet).stream().anyMatch(MapAuthenticationFlowEntity::isUpdated) + || Optional.ofNullable(getAuthenticatorConfigs()).orElseGet(Collections::emptySet).stream().anyMatch(MapAuthenticatorConfigEntity::isUpdated) + || Optional.ofNullable(getClientInitialAccesses()).orElseGet(Collections::emptySet).stream().anyMatch(MapClientInitialAccessEntity::isUpdated) + || Optional.ofNullable(getComponents()).orElseGet(Collections::emptySet).stream().anyMatch(MapComponentEntity::isUpdated) + || Optional.ofNullable(getIdentityProviders()).orElseGet(Collections::emptySet).stream().anyMatch(MapIdentityProviderEntity::isUpdated) + || Optional.ofNullable(getIdentityProviderMappers()).orElseGet(Collections::emptySet).stream().anyMatch(MapIdentityProviderMapperEntity::isUpdated) + || Optional.ofNullable(getRequiredActionProviders()).orElseGet(Collections::emptySet).stream().anyMatch(MapRequiredActionProviderEntity::isUpdated) + || Optional.ofNullable(getRequiredCredentials()).orElseGet(Collections::emptySet).stream().anyMatch(MapRequiredCredentialEntity::isUpdated) + || Optional.ofNullable(getOTPPolicy()).map(MapOTPPolicyEntity::isUpdated).orElse(false) + || Optional.ofNullable(getWebAuthnPolicy()).map(MapWebAuthnPolicyEntity::isUpdated).orElse(false) + || Optional.ofNullable(getWebAuthnPolicyPasswordless()).map(MapWebAuthnPolicyEntity::isUpdated).orElse(false); + } + + @Override + public void clearUpdatedFlag() { + this.updated = false; + Optional.ofNullable(getAuthenticationExecutions()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getAuthenticationFlows()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getAuthenticatorConfigs()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getClientInitialAccesses()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getComponents()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getIdentityProviders()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getIdentityProviderMappers()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getRequiredActionProviders()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getRequiredCredentials()).orElseGet(Collections::emptySet).forEach(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getOTPPolicy()).ifPresent(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getWebAuthnPolicy()).ifPresent(UpdatableEntity::clearUpdatedFlag); + Optional.ofNullable(getWebAuthnPolicyPasswordless()).ifPresent(UpdatableEntity::clearUpdatedFlag); + } + + @Override + public Optional getComponent(String componentId) { + Set cs = getComponents(); + if (cs == null || cs.isEmpty()) return Optional.empty(); + + return cs.stream().filter(c -> Objects.equals(c.getId(), componentId)).findFirst(); + } + + @Override + public Boolean removeComponent(String componentId) { + Set cs = getComponents(); + boolean removed = cs != null && cs.removeIf(c -> Objects.equals(c.getId(), componentId)); + this.updated |= removed; + return removed; + } + + @Override + public Optional getAuthenticationFlow(String flowId) { + Set afs = getAuthenticationFlows(); + if (afs == null || afs.isEmpty()) return Optional.empty(); + + return afs.stream().filter(afe -> Objects.equals(afe.getId(), flowId)).findFirst(); + } + + @Override + public Boolean removeAuthenticationFlow(String flowId) { + Set afs = getAuthenticationFlows(); + boolean removed = afs != null && afs.removeIf(af -> Objects.equals(af.getId(), flowId)); + this.updated |= removed; + return removed; + } + + @Override + public Optional getAuthenticationExecution(String executionId) { + Set aes = getAuthenticationExecutions(); + if (aes == null || aes.isEmpty()) return Optional.empty(); + + return aes.stream().filter(ae -> Objects.equals(ae.getId(), executionId)).findFirst(); + } + + @Override + public Boolean removeAuthenticationExecution(String executionId) { + Set aes = getAuthenticationExecutions(); + boolean removed = aes != null && aes.removeIf(ae -> Objects.equals(ae.getId(), executionId)); + this.updated |= removed; + return removed; + } + + @Override + public Optional getAuthenticatorConfig(String authenticatorConfigId) { + Set acs = getAuthenticatorConfigs(); + if (acs == null || acs.isEmpty()) return Optional.empty(); + + return acs.stream().filter(ac -> Objects.equals(ac.getId(), authenticatorConfigId)).findFirst(); + } + + @Override + public Boolean removeAuthenticatorConfig(String authenticatorConfigId) { + Set acs = getAuthenticatorConfigs(); + boolean removed = acs != null && acs.removeIf(ac -> Objects.equals(ac.getId(), authenticatorConfigId)); + this.updated |= removed; + return removed; + } + + @Override + public Optional getRequiredActionProvider(String requiredActionProviderId) { + Set raps = getRequiredActionProviders(); + if (raps == null || raps.isEmpty()) return Optional.empty(); + + return raps.stream().filter(ac -> Objects.equals(ac.getId(), requiredActionProviderId)).findFirst(); + } + + @Override + public Boolean removeRequiredActionProvider(String requiredActionProviderId) { + Set raps = getRequiredActionProviders(); + boolean removed = raps != null && raps.removeIf(rap -> Objects.equals(rap.getId(), requiredActionProviderId)); + this.updated |= removed; + return removed; + } + + @Override + public Boolean removeIdentityProvider(String identityProviderId) { + Set ips = getIdentityProviders(); + boolean removed = ips != null && ips.removeIf(ip -> Objects.equals(ip.getId(), identityProviderId)); + this.updated |= removed; + return removed; + } + + @Override + public Optional getIdentityProviderMapper(String identityProviderMapperId) { + Set ipms = getIdentityProviderMappers(); + if (ipms == null || ipms.isEmpty()) return Optional.empty(); + + return ipms.stream().filter(ipm -> Objects.equals(ipm.getId(), identityProviderMapperId)).findFirst(); + } + + @Override + public Boolean removeIdentityProviderMapper(String identityProviderMapperId) { + Set ipms = getIdentityProviderMappers(); + boolean removed = ipms != null && ipms.removeIf(ipm -> Objects.equals(ipm.getId(), identityProviderMapperId)); + this.updated |= removed; + return removed; + } + + @Override + public Optional getClientInitialAccess(String clientInitialAccessId) { + Set cias = getClientInitialAccesses(); + if (cias == null || cias.isEmpty()) return Optional.empty(); + + return cias.stream().filter(cia -> Objects.equals(cia.getId(), clientInitialAccessId)).findFirst(); + } + + @Override + public Boolean removeClientInitialAccess(String clientInitialAccessId) { + Set cias = getClientInitialAccesses(); + boolean removed = cias != null && cias.removeIf(cia -> Objects.equals(cia.getId(), clientInitialAccessId)); + this.updated |= removed; + return removed; + } + + @Override + public void removeExpiredClientInitialAccesses() { + Set cias = getClientInitialAccesses(); + if (cias != null) + cias.stream() + .filter(this::checkIfExpired) + .map(MapClientInitialAccessEntity::getId) + .collect(Collectors.toSet()) + .forEach(this::removeClientInitialAccess); + } + + @Override + public boolean hasClientInitialAccess() { + Set cias = getClientInitialAccesses(); + return cias != null && !cias.isEmpty(); + } + + private boolean checkIfExpired(MapClientInitialAccessEntity cia) { + return cia.getRemainingCount() < 1 || + (cia.getExpiration() > 0 && (cia.getTimestamp() + cia.getExpiration()) < Time.currentTime()); } } - public void updateLocalizationTexts(String locale, Map texts) { - this.updated |= localizationTexts.replace(locale, texts) != null; - } + String getName(); + void setName(String name); - public boolean removeLocalizationTexts(String locale) { - boolean removed = localizationTexts.remove(locale) != null; - updated |= removed; - return removed; - } + String getDisplayName(); + void setDisplayName(String displayName); - public Map getBrowserSecurityHeaders() { - return browserSecurityHeaders; - } + String getDisplayNameHtml(); + void setDisplayNameHtml(String displayNameHtml); - public void setBrowserSecurityHeaders(Map headers) { - if (headers == null) return; - this.updated |= ! Objects.equals(this.browserSecurityHeaders, headers); - this.browserSecurityHeaders = headers; - } + Boolean isEnabled(); + void setEnabled(Boolean enabled); - public Map getSmtpConfig() { - return smtpConfig; - } + Boolean isRegistrationAllowed(); + void setRegistrationAllowed(Boolean registrationAllowed); - public void setSmtpConfig(Map smtpConfig) { - if (smtpConfig == null) return; - this.updated |= ! Objects.equals(this.smtpConfig, smtpConfig); - this.smtpConfig = smtpConfig; - } + Boolean isRegistrationEmailAsUsername(); + void setRegistrationEmailAsUsername(Boolean registrationEmailAsUsername); - public Stream getRequiredCredentials() { - return requiredCredentials.values().stream(); - } + Boolean isVerifyEmail(); + void setVerifyEmail(Boolean verifyEmail); - public void addRequiredCredential(MapRequiredCredentialEntity requiredCredential) { - if (requiredCredentials.containsKey(requiredCredential.getType())) { - throw new ModelDuplicateException("An RequiredCredential with given type already exists"); - } - this.updated = true; - requiredCredentials.put(requiredCredential.getType(), requiredCredential); - } + Boolean isResetPasswordAllowed(); + void setResetPasswordAllowed(Boolean resetPasswordAllowed); - public void updateRequiredCredential(MapRequiredCredentialEntity requiredCredential) { - this.updated |= requiredCredentials.replace(requiredCredential.getType(), requiredCredential) != null; - } + Boolean isLoginWithEmailAllowed(); + void setLoginWithEmailAllowed(Boolean loginWithEmailAllowed); - public Stream getComponents() { - return components.values().stream(); - } + Boolean isDuplicateEmailsAllowed(); + void setDuplicateEmailsAllowed(Boolean duplicateEmailsAllowed); - public MapComponentEntity getComponent(String id) { - return components.get(id); - } + Boolean isRememberMe(); + void setRememberMe(Boolean rememberMe); - public void addComponent(MapComponentEntity component) { - if (components.containsKey(component.getId())) { - throw new ModelDuplicateException("A Component with given id already exists"); - } - this.updated = true; - components.put(component.getId(), component); - } + Boolean isEditUsernameAllowed(); + void setEditUsernameAllowed(Boolean editUsernameAllowed); - public void updateComponent(MapComponentEntity component) { - this.updated |= components.replace(component.getId(), component) != null; - } + Boolean isRevokeRefreshToken(); + void setRevokeRefreshToken(Boolean revokeRefreshToken); - public boolean removeComponent(String id) { - boolean removed = this.components.remove(id) != null; - this.updated |= removed; - return removed; - } + Boolean isAdminEventsEnabled(); + void setAdminEventsEnabled(Boolean adminEventsEnabled); - public Stream getAuthenticationFlows() { - return authenticationFlows.values().stream(); - } + Boolean isAdminEventsDetailsEnabled(); + void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled); - public MapAuthenticationFlowEntity getAuthenticationFlow(String flowId) { - return authenticationFlows.get(flowId); - } + Boolean isInternationalizationEnabled(); + void setInternationalizationEnabled(Boolean internationalizationEnabled); - public void addAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow) { - if (authenticationFlows.containsKey(authenticationFlow.getId())) { - throw new ModelDuplicateException("An AuthenticationFlow with given id already exists"); - } - this.updated = true; - authenticationFlows.put(authenticationFlow.getId(), authenticationFlow); - } + Boolean isAllowUserManagedAccess(); + void setAllowUserManagedAccess(Boolean allowUserManagedAccess); - public boolean removeAuthenticationFlow(String flowId) { - boolean removed = this.authenticationFlows.remove(flowId) != null; - updated |= removed; - return removed; - } + Boolean isOfflineSessionMaxLifespanEnabled(); + void setOfflineSessionMaxLifespanEnabled(Boolean offlineSessionMaxLifespanEnabled); - public void updateAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow) { - this.updated |= authenticationFlows.replace(authenticationFlow.getId(), authenticationFlow) != null; - } + Boolean isEventsEnabled(); + void setEventsEnabled(Boolean eventsEnabled); - public void addAuthenticatonExecution(MapAuthenticationExecutionEntity authenticationExecution) { - if (authenticationExecutions.containsKey(authenticationExecution.getId())) { - throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); - } + Integer getRefreshTokenMaxReuse(); + void setRefreshTokenMaxReuse(Integer refreshTokenMaxReuse); - this.updated = true; - authenticationExecutions.put(authenticationExecution.getId(), authenticationExecution); - } + Integer getSsoSessionIdleTimeout(); + void setSsoSessionIdleTimeout(Integer ssoSessionIdleTimeout); - public void updateAuthenticatonExecution(MapAuthenticationExecutionEntity authenticationExecution) { - this.updated |= authenticationExecutions.replace(authenticationExecution.getId(), authenticationExecution) != null; - } + Integer getSsoSessionMaxLifespan(); + void setSsoSessionMaxLifespan(Integer ssoSessionMaxLifespan); - public boolean removeAuthenticatonExecution(String id) { - boolean removed = this.authenticationExecutions.remove(id) != null; - updated |= removed; - return removed; - } + Integer getSsoSessionIdleTimeoutRememberMe(); + void setSsoSessionIdleTimeoutRememberMe(Integer ssoSessionIdleTimeoutRememberMe); - public MapAuthenticationExecutionEntity getAuthenticationExecution(String id) { - return authenticationExecutions.get(id); - } + Integer getSsoSessionMaxLifespanRememberMe(); + void setSsoSessionMaxLifespanRememberMe(Integer ssoSessionMaxLifespanRememberMe); - public Stream getAuthenticationExecutions() { - return authenticationExecutions.values().stream(); - } + Integer getOfflineSessionIdleTimeout(); + void setOfflineSessionIdleTimeout(Integer offlineSessionIdleTimeout); - public Stream getAuthenticatorConfigs() { - return authenticatorConfigs.values().stream(); - } + Integer getAccessTokenLifespan(); + void setAccessTokenLifespan(Integer accessTokenLifespan); - public void addAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig) { - this.updated |= ! Objects.equals(authenticatorConfigs.put(authenticatorConfig.getId(), authenticatorConfig), authenticatorConfig); - } + Integer getAccessTokenLifespanForImplicitFlow(); + void setAccessTokenLifespanForImplicitFlow(Integer accessTokenLifespanForImplicitFlow); - public void updateAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig) { - this.updated |= authenticatorConfigs.replace(authenticatorConfig.getId(), authenticatorConfig) != null; - } + Integer getAccessCodeLifespan(); + void setAccessCodeLifespan(Integer accessCodeLifespan); - public boolean removeAuthenticatorConfig(String id) { - boolean removed = this.authenticatorConfigs.remove(id) != null; - updated |= removed; - return removed; - } + Integer getAccessCodeLifespanUserAction(); + void setAccessCodeLifespanUserAction(Integer accessCodeLifespanUserAction); - public MapAuthenticatorConfigEntity getAuthenticatorConfig(String id) { - return authenticatorConfigs.get(id); - } + Integer getAccessCodeLifespanLogin(); + void setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin); - public Stream getRequiredActionProviders() { - return requiredActionProviders.values().stream(); - } + Integer getNotBefore(); + void setNotBefore(Integer notBefore); - public void addRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider) { - if (requiredActionProviders.containsKey(requiredActionProvider.getId())) { - throw new ModelDuplicateException("An RequiredActionProvider with given id already exists"); - } + Integer getClientSessionIdleTimeout(); + void setClientSessionIdleTimeout(Integer clientSessionIdleTimeout); - this.updated = true; - requiredActionProviders.put(requiredActionProvider.getId(), requiredActionProvider); - } + Integer getClientSessionMaxLifespan(); + void setClientSessionMaxLifespan(Integer clientSessionMaxLifespan); - public void updateRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider) { - this.updated |= requiredActionProviders.replace(requiredActionProvider.getId(), requiredActionProvider) != null; - } + Integer getClientOfflineSessionIdleTimeout(); + void setClientOfflineSessionIdleTimeout(Integer clientOfflineSessionIdleTimeout); - public boolean removeRequiredActionProvider(String id) { - boolean removed = this.requiredActionProviders.remove(id) != null; - updated |= removed; - return removed; - } + Integer getClientOfflineSessionMaxLifespan(); + void setClientOfflineSessionMaxLifespan(Integer clientOfflineSessionMaxLifespan); - public MapRequiredActionProviderEntity getRequiredActionProvider(String id) { - return requiredActionProviders.get(id); - } + Integer getActionTokenGeneratedByAdminLifespan(); + void setActionTokenGeneratedByAdminLifespan(Integer actionTokenGeneratedByAdminLifespan); - public Stream getIdentityProviders() { - return identityProviders.values().stream(); - } + Integer getOfflineSessionMaxLifespan(); + void setOfflineSessionMaxLifespan(Integer offlineSessionMaxLifespan); - public void addIdentityProvider(MapIdentityProviderEntity identityProvider) { - if (identityProviders.containsKey(identityProvider.getId())) { - throw new ModelDuplicateException("An IdentityProvider with given id already exists"); - } + Long getEventsExpiration(); + void setEventsExpiration(Long eventsExpiration); - this.updated = true; - identityProviders.put(identityProvider.getId(), identityProvider); - } + String getPasswordPolicy(); + void setPasswordPolicy(String passwordPolicy); - public boolean removeIdentityProvider(String id) { - boolean removed = this.identityProviders.remove(id) != null; - updated |= removed; - return removed; - } + String getSslRequired(); + void setSslRequired(String sslRequired); - public void updateIdentityProvider(MapIdentityProviderEntity identityProvider) { - this.updated |= identityProviders.replace(identityProvider.getId(), identityProvider) != null; - } + String getLoginTheme(); + void setLoginTheme(String loginTheme); - public Stream getIdentityProviderMappers() { - return identityProviderMappers.values().stream(); - } + String getAccountTheme(); + void setAccountTheme(String accountTheme); - public void addIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper) { - if (identityProviderMappers.containsKey(identityProviderMapper.getId())) { - throw new ModelDuplicateException("An IdentityProviderMapper with given id already exists"); - } + String getAdminTheme(); + void setAdminTheme(String adminTheme); - this.updated = true; - identityProviderMappers.put(identityProviderMapper.getId(), identityProviderMapper); - } + String getEmailTheme(); + void setEmailTheme(String emailTheme); - public boolean removeIdentityProviderMapper(String id) { - boolean removed = this.identityProviderMappers.remove(id) != null; - updated |= removed; - return removed; - } + String getMasterAdminClient(); + void setMasterAdminClient(String masterAdminClient); - public void updateIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper) { - this.updated |= identityProviderMappers.replace(identityProviderMapper.getId(), identityProviderMapper) != null; - } + String getDefaultRoleId(); + void setDefaultRoleId(String defaultRoleId); - public MapIdentityProviderMapperEntity getIdentityProviderMapper(String id) { - return identityProviderMappers.get(id); - } + String getDefaultLocale(); + void setDefaultLocale(String defaultLocale); - public boolean hasClientInitialAccess() { - return !clientInitialAccesses.isEmpty(); - } + String getBrowserFlow(); + void setBrowserFlow(String browserFlow); - public void removeExpiredClientInitialAccesses() { - clientInitialAccesses.values().stream() - .filter(this::checkIfExpired) - .map(MapClientInitialAccessEntity::getId) - .collect(Collectors.toSet()) - .forEach(this::removeClientInitialAccess); - } + String getRegistrationFlow(); + void setRegistrationFlow(String registrationFlow); - private boolean checkIfExpired(MapClientInitialAccessEntity cia) { - return cia.getRemainingCount() < 1 || - (cia.getExpiration() > 0 && (cia.getTimestamp() + cia.getExpiration()) < Time.currentTime()); - } + String getDirectGrantFlow(); + void setDirectGrantFlow(String directGrantFlow); - public void addClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess) { - this.updated = true; - clientInitialAccesses.put(clientInitialAccess.getId(), clientInitialAccess); - } + String getResetCredentialsFlow(); + void setResetCredentialsFlow(String resetCredentialsFlow); - public void updateClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess) { - this.updated |= clientInitialAccesses.replace(clientInitialAccess.getId(), clientInitialAccess) != null; - } + String getClientAuthenticationFlow(); + void setClientAuthenticationFlow(String clientAuthenticationFlow); - public MapClientInitialAccessEntity getClientInitialAccess(String id) { - return clientInitialAccesses.get(id); - } + String getDockerAuthenticationFlow(); + void setDockerAuthenticationFlow(String dockerAuthenticationFlow); - public boolean removeClientInitialAccess(String id) { - boolean removed = this.clientInitialAccesses.remove(id) != null; - updated |= removed; - return removed; - } + MapOTPPolicyEntity getOTPPolicy(); + void setOTPPolicy(MapOTPPolicyEntity otpPolicy); - public Collection getClientInitialAccesses() { - return clientInitialAccesses.values(); - } + MapWebAuthnPolicyEntity getWebAuthnPolicy(); + void setWebAuthnPolicy(MapWebAuthnPolicyEntity webAuthnPolicy); + + MapWebAuthnPolicyEntity getWebAuthnPolicyPasswordless(); + void setWebAuthnPolicyPasswordless(MapWebAuthnPolicyEntity webAuthnPolicyPasswordless); + + Set getDefaultClientScopeIds(); + void addDefaultClientScopeId(String scopeId); + Boolean removeDefaultClientScopeId(String scopeId); + + Set getOptionalClientScopeIds(); + void addOptionalClientScopeId(String scopeId); + Boolean removeOptionalClientScopeId(String scopeId); + + Set getDefaultGroupIds(); + void addDefaultGroupId(String groupId); + void removeDefaultGroupId(String groupId); + + Set getEventsListeners(); + void setEventsListeners(Set eventsListeners); + + Set getEnabledEventTypes(); + void setEnabledEventTypes(Set enabledEventTypes); + + Set getSupportedLocales(); + void setSupportedLocales(Set supportedLocales); + + Map> getLocalizationTexts(); + Map getLocalizationText(String locale); + void setLocalizationText(String locale, Map texts); + Boolean removeLocalizationText(String locale); + + Map getBrowserSecurityHeaders(); + void setBrowserSecurityHeaders(Map headers); + + Map getSmtpConfig(); + void setSmtpConfig(Map smtpConfig); + + Set getRequiredCredentials(); + void addRequiredCredential(MapRequiredCredentialEntity requiredCredential); + + Set getComponents(); + Optional getComponent(String id); + void addComponent(MapComponentEntity component); + Boolean removeComponent(String componentId); + + Set getAuthenticationFlows(); + Optional getAuthenticationFlow(String flowId); + void addAuthenticationFlow(MapAuthenticationFlowEntity authenticationFlow); + Boolean removeAuthenticationFlow(String flowId); + + Set getAuthenticationExecutions(); + Optional getAuthenticationExecution(String id); + void addAuthenticationExecution(MapAuthenticationExecutionEntity authenticationExecution); + Boolean removeAuthenticationExecution(String executionId); + + Set getAuthenticatorConfigs(); + void addAuthenticatorConfig(MapAuthenticatorConfigEntity authenticatorConfig); + Optional getAuthenticatorConfig(String authenticatorConfigId); + Boolean removeAuthenticatorConfig(String authenticatorConfigId); + + Set getRequiredActionProviders(); + void addRequiredActionProvider(MapRequiredActionProviderEntity requiredActionProvider); + Optional getRequiredActionProvider(String requiredActionProviderId); + Boolean removeRequiredActionProvider(String requiredActionProviderId); + + Set getIdentityProviders(); + void addIdentityProvider(MapIdentityProviderEntity identityProvider); + Boolean removeIdentityProvider(String identityProviderId); + + Set getIdentityProviderMappers(); + void addIdentityProviderMapper(MapIdentityProviderMapperEntity identityProviderMapper); + Boolean removeIdentityProviderMapper(String identityProviderMapperId); + Optional getIdentityProviderMapper(String identityProviderMapperId); + + Set getClientInitialAccesses(); + void addClientInitialAccess(MapClientInitialAccessEntity clientInitialAccess); + Optional getClientInitialAccess(String clientInitialAccessId); + Boolean removeClientInitialAccess(String clientInitialAccessId); + @IgnoreForEntityImplementationGenerator + void removeExpiredClientInitialAccesses(); + @IgnoreForEntityImplementationGenerator + boolean hasClientInitialAccess(); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java index fe1b0d8e86..4699ab0226 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java @@ -74,7 +74,8 @@ public class MapRealmProvider implements RealmProvider { LOG.tracef("createRealm(%s, %s)%s", id, name, getShortStackTrace()); - MapRealmEntity entity = new MapRealmEntity(id); + MapRealmEntity entity = new MapRealmEntityImpl(); + entity.setId(id); entity.setName(name); entity = tx.create(entity); @@ -170,12 +171,10 @@ public class MapRealmProvider implements RealmProvider { //TODO move the following method to adapter @Override public void saveLocalizationText(RealmModel realm, String locale, String key, String text) { - // implemented according to behaviour in JpaRealmProvider (as java-doc was not added) - if (! updateLocalizationText(realm, locale, key, text)) { - Map texts = new HashMap<>(); - texts.put(key, text); - realm.createOrUpdateRealmLocalizationTexts(locale, texts); - } + if (locale == null || key == null || text == null) return; + Map texts = new HashMap<>(); + texts.put(key, text); + realm.createOrUpdateRealmLocalizationTexts(locale, texts); } //TODO move the following method to adapter @@ -189,9 +188,7 @@ public class MapRealmProvider implements RealmProvider { @Override public boolean updateLocalizationText(RealmModel realm, String locale, String key, String text) { if (locale == null || key == null || text == null || (! realm.getRealmLocalizationTextsByLocale(locale).containsKey(key))) return false; - Map texts = new HashMap<>(realm.getRealmLocalizationTextsByLocale(locale)); - texts.replace(key, text); - realm.createOrUpdateRealmLocalizationTexts(locale, texts); + saveLocalizationText(realm, locale, key, text); return true; } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationExecutionEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationExecutionEntity.java index 8f5e44293c..9603c6c83f 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationExecutionEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationExecutionEntity.java @@ -1,13 +1,13 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,28 +17,19 @@ package org.keycloak.models.map.realm.entity; -import java.util.Objects; import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapAuthenticationExecutionEntity extends UpdatableEntity.Impl { - - private String id; - private String authenticator; - private String authenticatorConfig; - private String flowId; - private String parentFlowId; - private AuthenticationExecutionModel.Requirement requirement; - private Boolean autheticatorFlow = false; - private Integer priority = 0; - - - private MapAuthenticationExecutionEntity() {} - - public static MapAuthenticationExecutionEntity fromModel(AuthenticationExecutionModel model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapAuthenticationExecutionEntity extends UpdatableEntity, AbstractEntity { + static MapAuthenticationExecutionEntity fromModel(AuthenticationExecutionModel model) { if (model == null) return null; - MapAuthenticationExecutionEntity entity = new MapAuthenticationExecutionEntity(); + MapAuthenticationExecutionEntity entity = new MapAuthenticationExecutionEntityImpl(); String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId(); entity.setId(id); entity.setAuthenticator(model.getAuthenticator()); @@ -51,7 +42,7 @@ public class MapAuthenticationExecutionEntity extends UpdatableEntity.Impl { return entity; } - public static AuthenticationExecutionModel toModel(MapAuthenticationExecutionEntity entity) { + static AuthenticationExecutionModel toModel(MapAuthenticationExecutionEntity entity) { if (entity == null) return null; AuthenticationExecutionModel model = new AuthenticationExecutionModel(); model.setId(entity.getId()); @@ -60,94 +51,31 @@ public class MapAuthenticationExecutionEntity extends UpdatableEntity.Impl { model.setFlowId(entity.getFlowId()); model.setParentFlow(entity.getParentFlowId()); model.setRequirement(entity.getRequirement()); - model.setAuthenticatorFlow(entity.isAutheticatorFlow()); - model.setPriority(entity.getPriority()); + Boolean authenticatorFlow = entity.isAutheticatorFlow(); + model.setAuthenticatorFlow(authenticatorFlow == null ? false : authenticatorFlow); + Integer priority = entity.getPriority(); + model.setPriority(priority == null ? 0 : priority); return model; } - public String getId() { - return id; - } + String getAuthenticator(); + void setAuthenticator(String authenticator); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + String getAuthenticatorConfig(); + void setAuthenticatorConfig(String authenticatorConfig); - public String getAuthenticator() { - return authenticator; - } + AuthenticationExecutionModel.Requirement getRequirement(); + void setRequirement(AuthenticationExecutionModel.Requirement requirement); - public void setAuthenticator(String authenticator) { - this.updated = !Objects.equals(this.authenticator, authenticator); - this.authenticator = authenticator; - } + Boolean isAutheticatorFlow(); + void setAutheticatorFlow(Boolean autheticatorFlow); - public String getAuthenticatorConfig() { - return authenticatorConfig; - } + String getFlowId(); + void setFlowId(String flowId); - public void setAuthenticatorConfig(String authenticatorConfig) { - this.updated = !Objects.equals(this.authenticatorConfig, authenticatorConfig); - this.authenticatorConfig = authenticatorConfig; - } - - public AuthenticationExecutionModel.Requirement getRequirement() { - return requirement; - } - - public void setRequirement(AuthenticationExecutionModel.Requirement requirement) { - this.updated = !Objects.equals(this.requirement, requirement); - this.requirement = requirement; - } - - public Boolean isAutheticatorFlow() { - return autheticatorFlow; - } - - public void setAutheticatorFlow(boolean autheticatorFlow) { - this.updated = !Objects.equals(this.requirement, requirement); - this.autheticatorFlow = autheticatorFlow; - } - - public String getFlowId() { - return flowId; - } - - public void setFlowId(String flowId) { - this.updated = !Objects.equals(this.flowId, flowId); - this.flowId = flowId; - } - - public String getParentFlowId() { - return parentFlowId; - } - - public void setParentFlowId(String parentFlowId) { - this.updated = !Objects.equals(this.parentFlowId, parentFlowId); - this.parentFlowId = parentFlowId; - } - - public Integer getPriority() { - return priority; - } - - public void setPriority(Integer priority) { - this.updated = !Objects.equals(this.priority, priority); - this.priority = priority; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapAuthenticationExecutionEntity)) return false; - final MapAuthenticationExecutionEntity other = (MapAuthenticationExecutionEntity) obj; - return Objects.equals(other.getId(), getId()); - } + String getParentFlowId(); + void setParentFlowId(String parentFlowId); + Integer getPriority(); + void setPriority(Integer priority); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationFlowEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationFlowEntity.java index 3aed620431..68b5519960 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationFlowEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticationFlowEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,26 +17,18 @@ package org.keycloak.models.map.realm.entity; -import java.util.Objects; import org.keycloak.models.AuthenticationFlowModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapAuthenticationFlowEntity extends UpdatableEntity.Impl { - - private String id; - private String alias; - private String description; - private String providerId; - private Boolean builtIn = false; - private Boolean topLevel = false; - - - private MapAuthenticationFlowEntity() {} - - public static MapAuthenticationFlowEntity fromModel(AuthenticationFlowModel model) { - if (model == null) return null; - MapAuthenticationFlowEntity entity = new MapAuthenticationFlowEntity(); +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapAuthenticationFlowEntity extends UpdatableEntity, AbstractEntity { + static MapAuthenticationFlowEntity fromModel(AuthenticationFlowModel model) { + MapAuthenticationFlowEntity entity = new MapAuthenticationFlowEntityImpl(); String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId(); entity.setId(id); entity.setAlias(model.getAlias()); @@ -48,82 +40,31 @@ public class MapAuthenticationFlowEntity extends UpdatableEntity.Impl { return entity; } - public static AuthenticationFlowModel toModel(MapAuthenticationFlowEntity entity) { - if (entity == null) return null; + static AuthenticationFlowModel toModel(MapAuthenticationFlowEntity entity) { AuthenticationFlowModel model = new AuthenticationFlowModel(); model.setId(entity.getId()); model.setAlias(entity.getAlias()); - model.setBuiltIn(entity.isBuiltIn()); + Boolean builtIn = entity.isBuiltIn(); + model.setBuiltIn(builtIn == null ? false : builtIn); model.setDescription(entity.getDescription()); model.setProviderId(entity.getProviderId()); - model.setTopLevel(entity.isTopLevel()); + Boolean topLevel = entity.isTopLevel(); + model.setTopLevel(topLevel == null ? false : topLevel); return model; } - public String getId() { - return id; - } + String getAlias(); + void setAlias(String alias); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + String getDescription(); + void setDescription(String description); - public String getAlias() { - return alias; - } + String getProviderId(); + void setProviderId(String providerId); - public void setAlias(String alias) { - this.updated = !Objects.equals(this.alias, alias); - this.alias = alias; - } + Boolean isBuiltIn(); + void setBuiltIn(Boolean builtIn); - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.updated = !Objects.equals(this.description, description); - this.description = description; - } - - public String getProviderId() { - return providerId; - } - - public void setProviderId(String providerId) { - this.updated = !Objects.equals(this.providerId, providerId); - this.providerId = providerId; - } - - public Boolean isBuiltIn() { - return builtIn; - } - - public void setBuiltIn(boolean builtIn) { - this.updated = !Objects.equals(this.builtIn, builtIn); - this.builtIn = builtIn; - } - - public Boolean isTopLevel() { - return topLevel; - } - - public void setTopLevel(boolean topLevel) { - this.updated = !Objects.equals(this.topLevel, topLevel); - this.topLevel = topLevel; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapAuthenticationFlowEntity)) return false; - final MapAuthenticationFlowEntity other = (MapAuthenticationFlowEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Boolean isTopLevel(); + void setTopLevel(Boolean topLevel); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticatorConfigEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticatorConfigEntity.java index 7243c86bad..41d570df12 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticatorConfigEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapAuthenticatorConfigEntity.java @@ -1,13 +1,13 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,78 +17,41 @@ package org.keycloak.models.map.realm.entity; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import org.keycloak.models.AuthenticatorConfigModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapAuthenticatorConfigEntity extends UpdatableEntity.Impl { +import java.util.HashMap; +import java.util.Map; - private String id; - private String alias; - private Map config = new HashMap<>(); - - - private MapAuthenticatorConfigEntity() {} - - public static MapAuthenticatorConfigEntity fromModel(AuthenticatorConfigModel model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapAuthenticatorConfigEntity extends UpdatableEntity, AbstractEntity { + static MapAuthenticatorConfigEntity fromModel(AuthenticatorConfigModel model) { if (model == null) return null; - MapAuthenticatorConfigEntity entity = new MapAuthenticatorConfigEntity(); + MapAuthenticatorConfigEntity entity = new MapAuthenticatorConfigEntityImpl(); String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId(); entity.setId(id); entity.setAlias(model.getAlias()); - entity.setConfig(model.getConfig() == null ? null : new HashMap<>(model.getConfig())); + entity.setConfig(model.getConfig()); return entity; } - public static AuthenticatorConfigModel toModel(MapAuthenticatorConfigEntity entity) { + static AuthenticatorConfigModel toModel(MapAuthenticatorConfigEntity entity) { if (entity == null) return null; AuthenticatorConfigModel model = new AuthenticatorConfigModel(); model.setId(entity.getId()); model.setAlias(entity.getAlias()); - model.setConfig(entity.getConfig() == null ? null : new HashMap<>(entity.getConfig())); + model.setConfig(entity.getConfig()); return model; } - public String getId() { - return id; - } + String getAlias(); + void setAlias(String alias); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } - - public String getAlias() { - return alias; - } - - public void setAlias(String alias) { - this.updated = !Objects.equals(this.alias, alias); - this.alias = alias; - } - - public Map getConfig() { - return config; - } - - public void setConfig(Map config) { - this.updated = !Objects.equals(this.config, config); - this.config = config; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapAuthenticatorConfigEntity)) return false; - final MapAuthenticatorConfigEntity other = (MapAuthenticatorConfigEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Map getConfig(); + void setConfig(Map config); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapClientInitialAccessEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapClientInitialAccessEntity.java index a0299e054e..95e604a8a5 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapClientInitialAccessEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapClientInitialAccessEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,27 +17,21 @@ package org.keycloak.models.map.realm.entity; -import java.util.Objects; import org.keycloak.common.util.Time; import org.keycloak.models.ClientInitialAccessModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapClientInitialAccessEntity extends UpdatableEntity.Impl { - - private String id; - private Integer timestamp = 0; - private Integer expiration = 0; - private Integer count = 0; - private Integer remainingCount = 0; - - - private MapClientInitialAccessEntity() {} - - public static MapClientInitialAccessEntity createEntity(int expiration, int count) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapClientInitialAccessEntity extends UpdatableEntity, AbstractEntity { + static MapClientInitialAccessEntity createEntity(int expiration, int count) { int currentTime = Time.currentTime(); - MapClientInitialAccessEntity entity = new MapClientInitialAccessEntity(); + MapClientInitialAccessEntity entity = new MapClientInitialAccessEntityImpl(); entity.setId(KeycloakModelUtils.generateId()); entity.setTimestamp(currentTime); entity.setExpiration(expiration); @@ -46,72 +40,30 @@ public class MapClientInitialAccessEntity extends UpdatableEntity.Impl { return entity; } - public static ClientInitialAccessModel toModel(MapClientInitialAccessEntity entity) { + static ClientInitialAccessModel toModel(MapClientInitialAccessEntity entity) { if (entity == null) return null; ClientInitialAccessModel model = new ClientInitialAccessModel(); model.setId(entity.getId()); - model.setTimestamp(entity.getTimestamp()); - model.setExpiration(entity.getExpiration()); - model.setCount(entity.getCount()); - model.setRemainingCount(entity.getRemainingCount()); + Integer timestamp = entity.getTimestamp(); + model.setTimestamp(timestamp == null ? 0 : timestamp); + Integer expiration = entity.getExpiration(); + model.setExpiration(expiration == null ? 0 : expiration); + Integer count = entity.getCount(); + model.setCount(count == null ? 0 : count); + Integer remainingCount = entity.getRemainingCount(); + model.setRemainingCount(remainingCount == null ? 0 : remainingCount); return model; } - public String getId() { - return id; - } + Integer getTimestamp(); + void setTimestamp(Integer timestamp); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + Integer getExpiration(); + void setExpiration(Integer expiration); - public Integer getTimestamp() { - return timestamp; - } + Integer getCount(); + void setCount(Integer count); - public void setTimestamp(int timestamp) { - this.updated = !Objects.equals(this.timestamp, timestamp); - this.timestamp = timestamp; - } - - public Integer getExpiration() { - return expiration; - } - - public void setExpiration(int expiration) { - this.updated = !Objects.equals(this.expiration, expiration); - this.expiration = expiration; - } - - public Integer getCount() { - return count; - } - - public void setCount(int count) { - this.updated = !Objects.equals(this.count, count); - this.count = count; - } - - public Integer getRemainingCount() { - return remainingCount; - } - - public void setRemainingCount(int remainingCount) { - this.updated = !Objects.equals(this.remainingCount, remainingCount); - this.remainingCount = remainingCount; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapClientInitialAccessEntity)) return false; - final MapClientInitialAccessEntity other = (MapClientInitialAccessEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Integer getRemainingCount(); + void setRemainingCount(Integer remainingCount); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapComponentEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapComponentEntity.java index f1dbd08d4d..af5721086a 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapComponentEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapComponentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,28 +17,22 @@ package org.keycloak.models.map.realm.entity; -import java.util.Objects; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.component.ComponentModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapComponentEntity extends UpdatableEntity.Impl { +import java.util.List; +import java.util.Map; - private String id; - private String name; - private String providerId; - private String providerType; - private String subType; - private String parentId; - private MultivaluedHashMap config = new MultivaluedHashMap<>(); - - - private MapComponentEntity() {} - - public static MapComponentEntity fromModel(ComponentModel model) { - if (model == null) return null; - MapComponentEntity entity = new MapComponentEntity(); +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapComponentEntity extends UpdatableEntity, AbstractEntity { + static MapComponentEntity fromModel(ComponentModel model) { + MapComponentEntity entity = new MapComponentEntityImpl(); String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId(); entity.setId(id); entity.setName(model.getName()); @@ -46,12 +40,11 @@ public class MapComponentEntity extends UpdatableEntity.Impl { entity.setProviderType(model.getProviderType()); entity.setSubType(model.getSubType()); entity.setParentId(model.getParentId()); - entity.setConfig(model.getConfig() == null ? null : new MultivaluedHashMap<>(model.getConfig())); + entity.setConfig(model.getConfig()); return entity; } - public static ComponentModel toModel(MapComponentEntity entity) { - if (entity == null) return null; + static ComponentModel toModel(MapComponentEntity entity) { ComponentModel model = new ComponentModel(); model.setId(entity.getId()); model.setName(entity.getName()); @@ -59,83 +52,26 @@ public class MapComponentEntity extends UpdatableEntity.Impl { model.setProviderType(entity.getProviderType()); model.setSubType(entity.getSubType()); model.setParentId(entity.getParentId()); - model.setConfig(entity.getConfig() == null ? null : new MultivaluedHashMap<>(entity.getConfig())); + Map> config = entity.getConfig(); + model.setConfig(config == null ? null : new MultivaluedHashMap<>(config)); return model; } - public String getId() { - return id; - } + String getName(); + void setName(String name); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + String getProviderId(); + void setProviderId(String providerId); - public String getName() { - return name; - } + String getProviderType(); + void setProviderType(String providerType); - public void setName(String name) { - this.updated = !Objects.equals(this.name, name); - this.name = name; - } + String getSubType(); + void setSubType(String subType); - public String getProviderId() { - return providerId; - } + String getParentId(); + void setParentId(String parentId); - public void setProviderId(String providerId) { - this.updated = !Objects.equals(this.providerId, providerId); - this.providerId = providerId; - } - - public String getProviderType() { - return providerType; - } - - public void setProviderType(String providerType) { - this.updated = !Objects.equals(this.providerType, providerType); - this.providerType = providerType; - } - - public String getSubType() { - return subType; - } - - public void setSubType(String subType) { - this.updated = !Objects.equals(this.subType, subType); - this.subType = subType; - } - - public String getParentId() { - return parentId; - } - - public void setParentId(String parentId) { - this.updated = !Objects.equals(this.parentId, parentId); - this.parentId = parentId; - } - - public MultivaluedHashMap getConfig() { - return config; - } - - public void setConfig(MultivaluedHashMap config) { - this.updated = !Objects.equals(this.config, config); - this.config = config; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapComponentEntity)) return false; - final MapComponentEntity other = (MapComponentEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Map> getConfig(); + void setConfig(Map> config); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderEntity.java index 9cf4ad504a..588ac3941c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,35 +17,22 @@ package org.keycloak.models.map.realm.entity; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapIdentityProviderEntity extends UpdatableEntity.Impl { +import java.util.HashMap; +import java.util.Map; - private String id; - private String alias; - private String displayName; - private String providerId; - private String firstBrokerLoginFlowId; - private String postBrokerLoginFlowId; - private Boolean enabled = false; - private Boolean trustEmail = false; - private Boolean storeToken = false; - private Boolean linkOnly = false; - private Boolean addReadTokenRoleOnCreate = false; - private Boolean authenticateByDefault = false; - private Map config = new HashMap<>(); - - - private MapIdentityProviderEntity() {} - - public static MapIdentityProviderEntity fromModel(IdentityProviderModel model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapIdentityProviderEntity extends UpdatableEntity, AbstractEntity { + static MapIdentityProviderEntity fromModel(IdentityProviderModel model) { if (model == null) return null; - MapIdentityProviderEntity entity = new MapIdentityProviderEntity(); + MapIdentityProviderEntity entity = new MapIdentityProviderEntityImpl(); String id = model.getInternalId() == null ? KeycloakModelUtils.generateId() : model.getInternalId(); entity.setId(id); entity.setAlias(model.getAlias()); @@ -63,7 +50,7 @@ public class MapIdentityProviderEntity extends UpdatableEntity.Impl { return entity; } - public static IdentityProviderModel toModel(MapIdentityProviderEntity entity) { + static IdentityProviderModel toModel(MapIdentityProviderEntity entity) { if (entity == null) return null; IdentityProviderModel model = new IdentityProviderModel(); model.setInternalId(entity.getId()); @@ -72,143 +59,55 @@ public class MapIdentityProviderEntity extends UpdatableEntity.Impl { model.setProviderId(entity.getProviderId()); model.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId()); model.setPostBrokerLoginFlowId(entity.getPostBrokerLoginFlowId()); - model.setEnabled(entity.isEnabled()); - model.setTrustEmail(entity.isTrustEmail()); - model.setStoreToken(entity.isStoreToken()); - model.setLinkOnly(entity.isLinkOnly()); - model.setAddReadTokenRoleOnCreate(entity.isAddReadTokenRoleOnCreate()); - model.setAuthenticateByDefault(entity.isAuthenticateByDefault()); + Boolean enabled = entity.isEnabled(); + model.setEnabled(enabled == null ? false : enabled); + Boolean trustEmail = entity.isTrustEmail(); + model.setTrustEmail(trustEmail == null ? false : trustEmail); + Boolean storeToken = entity.isStoreToken(); + model.setStoreToken(storeToken == null ? false : storeToken); + Boolean linkOnly = entity.isLinkOnly(); + model.setLinkOnly(linkOnly == null ? false : linkOnly); + Boolean addReadTokenRoleOnCreate = entity.isAddReadTokenRoleOnCreate(); + model.setAddReadTokenRoleOnCreate(addReadTokenRoleOnCreate == null ? false : addReadTokenRoleOnCreate); + Boolean authenticateByDefault = entity.isAuthenticateByDefault(); + model.setAuthenticateByDefault(authenticateByDefault == null ? false : authenticateByDefault); model.setConfig(entity.getConfig() == null ? null : new HashMap<>(entity.getConfig())); return model; } - public String getId() { - return id; - } + String getAlias(); + void setAlias(String alias); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + String getDisplayName(); + void setDisplayName(String displayName); - public String getAlias() { - return alias; - } + String getProviderId(); + void setProviderId(String providerId); - public void setAlias(String alias) { - this.updated = !Objects.equals(this.alias, alias); - this.alias = alias; - } + String getFirstBrokerLoginFlowId(); + void setFirstBrokerLoginFlowId(String firstBrokerLoginFlowId); - public String getDisplayName() { - return displayName; - } + String getPostBrokerLoginFlowId(); + void setPostBrokerLoginFlowId(String postBrokerLoginFlowId); - public void setDisplayName(String displayName) { - this.updated = !Objects.equals(this.displayName, displayName); - this.displayName = displayName; - } + Boolean isEnabled(); + void setEnabled(Boolean enabled); - public String getProviderId() { - return providerId; - } + Boolean isTrustEmail(); + void setTrustEmail(Boolean trustEmail); - public void setProviderId(String providerId) { - this.updated = !Objects.equals(this.providerId, providerId); - this.providerId = providerId; - } + Boolean isStoreToken(); + void setStoreToken(Boolean storeToken); - public String getFirstBrokerLoginFlowId() { - return firstBrokerLoginFlowId; - } + Boolean isLinkOnly(); + void setLinkOnly(Boolean linkOnly); - public void setFirstBrokerLoginFlowId(String firstBrokerLoginFlowId) { - this.updated = !Objects.equals(this.firstBrokerLoginFlowId, firstBrokerLoginFlowId); - this.firstBrokerLoginFlowId = firstBrokerLoginFlowId; - } + Boolean isAddReadTokenRoleOnCreate(); + void setAddReadTokenRoleOnCreate(Boolean addReadTokenRoleOnCreate); - public String getPostBrokerLoginFlowId() { - return postBrokerLoginFlowId; - } + Boolean isAuthenticateByDefault(); + void setAuthenticateByDefault(Boolean authenticateByDefault); - public void setPostBrokerLoginFlowId(String postBrokerLoginFlowId) { - this.updated = !Objects.equals(this.postBrokerLoginFlowId, postBrokerLoginFlowId); - this.postBrokerLoginFlowId = postBrokerLoginFlowId; - } - - public Boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.updated = !Objects.equals(this.enabled, enabled); - this.enabled = enabled; - } - - public Boolean isTrustEmail() { - return trustEmail; - } - - public void setTrustEmail(boolean trustEmail) { - this.updated = !Objects.equals(this.trustEmail, trustEmail); - this.trustEmail = trustEmail; - } - - public Boolean isStoreToken() { - return storeToken; - } - - public void setStoreToken(boolean storeToken) { - this.updated = !Objects.equals(this.storeToken, storeToken); - this.storeToken = storeToken; - } - - public Boolean isLinkOnly() { - return linkOnly; - } - - public void setLinkOnly(boolean linkOnly) { - this.updated = !Objects.equals(this.linkOnly, linkOnly); - this.linkOnly = linkOnly; - } - - public Boolean isAddReadTokenRoleOnCreate() { - return addReadTokenRoleOnCreate; - } - - public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) { - this.updated = !Objects.equals(this.addReadTokenRoleOnCreate, addReadTokenRoleOnCreate); - this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate; - } - - public Boolean isAuthenticateByDefault() { - return authenticateByDefault; - } - - public void setAuthenticateByDefault(boolean authenticateByDefault) { - this.updated = !Objects.equals(this.authenticateByDefault, authenticateByDefault); - this.authenticateByDefault = authenticateByDefault; - } - - public Map getConfig() { - return config; - } - - public void setConfig(Map config) { - this.updated = !Objects.equals(this.config, config); - this.config = config; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapIdentityProviderEntity)) return false; - final MapIdentityProviderEntity other = (MapIdentityProviderEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Map getConfig(); + void setConfig(Map config); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderMapperEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderMapperEntity.java index fc15143d6c..01d5454200 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderMapperEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapIdentityProviderMapperEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,27 +17,22 @@ package org.keycloak.models.map.realm.entity; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import org.keycloak.models.IdentityProviderMapperModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapIdentityProviderMapperEntity extends UpdatableEntity.Impl { +import java.util.HashMap; +import java.util.Map; - private String id; - private String name; - private String identityProviderAlias; - private String identityProviderMapper; - private Map config = new HashMap<>(); - - - private MapIdentityProviderMapperEntity() {} - - public static MapIdentityProviderMapperEntity fromModel(IdentityProviderMapperModel model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapIdentityProviderMapperEntity extends UpdatableEntity, AbstractEntity { + static MapIdentityProviderMapperEntity fromModel(IdentityProviderMapperModel model) { if (model == null) return null; - MapIdentityProviderMapperEntity entity = new MapIdentityProviderMapperEntity(); + MapIdentityProviderMapperEntity entity = new MapIdentityProviderMapperEntityImpl(); String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId(); entity.setId(id); entity.setName(model.getName()); @@ -47,7 +42,7 @@ public class MapIdentityProviderMapperEntity extends UpdatableEntity.Impl { return entity; } - public static IdentityProviderMapperModel toModel(MapIdentityProviderMapperEntity entity) { + static IdentityProviderMapperModel toModel(MapIdentityProviderMapperEntity entity) { if (entity == null) return null; IdentityProviderMapperModel model = new IdentityProviderMapperModel(); model.setId(entity.getId()); @@ -58,61 +53,15 @@ public class MapIdentityProviderMapperEntity extends UpdatableEntity.Impl { return model; } - public String getId() { - return id; - } + String getName(); + void setName(String name); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + String getIdentityProviderAlias(); + void setIdentityProviderAlias(String identityProviderAlias); - public String getName() { - return name; - } + String getIdentityProviderMapper(); + void setIdentityProviderMapper(String identityProviderMapper); - public void setName(String name) { - this.updated = !Objects.equals(this.name, name); - this.name = name; - } - - public String getIdentityProviderAlias() { - return identityProviderAlias; - } - - public void setIdentityProviderAlias(String identityProviderAlias) { - this.updated = !Objects.equals(this.identityProviderAlias, identityProviderAlias); - this.identityProviderAlias = identityProviderAlias; - } - - public String getIdentityProviderMapper() { - return identityProviderMapper; - } - - public void setIdentityProviderMapper(String identityProviderMapper) { - this.updated = !Objects.equals(this.identityProviderMapper, identityProviderMapper); - this.identityProviderMapper = identityProviderMapper; - } - - public Map getConfig() { - return config; - } - - public void setConfig(Map config) { - this.updated = !Objects.equals(this.config, config); - this.config = config; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapIdentityProviderMapperEntity)) return false; - final MapIdentityProviderMapperEntity other = (MapIdentityProviderMapperEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Map getConfig(); + void setConfig(Map config); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapOTPPolicyEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapOTPPolicyEntity.java index 30c5e6c059..e93266a7b6 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapOTPPolicyEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapOTPPolicyEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,25 +17,17 @@ package org.keycloak.models.map.realm.entity; -import java.util.Objects; import org.keycloak.models.OTPPolicy; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; -public class MapOTPPolicyEntity extends UpdatableEntity.Impl { - - private Integer otpPolicyInitialCounter = 0; - private Integer otpPolicyDigits = 0; - private Integer otpPolicyLookAheadWindow = 0; - private Integer otpPolicyPeriod = 0; - private String otpPolicyType; - private String otpPolicyAlgorithm; - - - private MapOTPPolicyEntity() {} - - public static MapOTPPolicyEntity fromModel(OTPPolicy model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapOTPPolicyEntity extends UpdatableEntity { + static MapOTPPolicyEntity fromModel(OTPPolicy model) { if (model == null) return null; - MapOTPPolicyEntity entity = new MapOTPPolicyEntity(); + MapOTPPolicyEntity entity = new MapOTPPolicyEntityImpl(); entity.setOtpPolicyAlgorithm(model.getAlgorithm()); entity.setOtpPolicyDigits(model.getDigits()); entity.setOtpPolicyInitialCounter(model.getInitialCounter()); @@ -45,95 +37,37 @@ public class MapOTPPolicyEntity extends UpdatableEntity.Impl { return entity; } - public static OTPPolicy toModel(MapOTPPolicyEntity entity) { + static OTPPolicy toModel(MapOTPPolicyEntity entity) { if (entity == null) return null; OTPPolicy model = new OTPPolicy(); - model.setDigits(entity.getOtpPolicyDigits()); + Integer otpPolicyDigits = entity.getOtpPolicyDigits(); + model.setDigits(otpPolicyDigits == null ? 0 : otpPolicyDigits); model.setAlgorithm(entity.getOtpPolicyAlgorithm()); - model.setInitialCounter(entity.getOtpPolicyInitialCounter()); - model.setLookAheadWindow(entity.getOtpPolicyLookAheadWindow()); + Integer otpPolicyInitialCounter = entity.getOtpPolicyInitialCounter(); + model.setInitialCounter(otpPolicyInitialCounter == null ? 0 : otpPolicyInitialCounter); + Integer otpPolicyLookAheadWindow = entity.getOtpPolicyLookAheadWindow(); + model.setLookAheadWindow(otpPolicyLookAheadWindow == null ? 0 : otpPolicyLookAheadWindow); model.setType(entity.getOtpPolicyType()); - model.setPeriod(entity.getOtpPolicyPeriod()); + Integer otpPolicyPeriod = entity.getOtpPolicyPeriod(); + model.setPeriod(otpPolicyPeriod == null ? 0 : otpPolicyPeriod); return model; } - public Integer getOtpPolicyInitialCounter() { - return otpPolicyInitialCounter; - } + Integer getOtpPolicyInitialCounter(); + void setOtpPolicyInitialCounter(Integer otpPolicyInitialCounter); - public void setOtpPolicyInitialCounter(int otpPolicyInitialCounter) { - this.updated = !Objects.equals(this.otpPolicyInitialCounter, otpPolicyInitialCounter); - this.otpPolicyInitialCounter = otpPolicyInitialCounter; - } + Integer getOtpPolicyDigits(); + void setOtpPolicyDigits(Integer otpPolicyDigits); - public Integer getOtpPolicyDigits() { - return otpPolicyDigits; - } + Integer getOtpPolicyLookAheadWindow(); + void setOtpPolicyLookAheadWindow(Integer otpPolicyLookAheadWindow); - public void setOtpPolicyDigits(int otpPolicyDigits) { - this.updated = !Objects.equals(this.otpPolicyDigits, otpPolicyDigits); - this.otpPolicyDigits = otpPolicyDigits; - } + Integer getOtpPolicyPeriod(); + void setOtpPolicyPeriod(Integer otpPolicyPeriod); - public Integer getOtpPolicyLookAheadWindow() { - return otpPolicyLookAheadWindow; - } + String getOtpPolicyType(); + void setOtpPolicyType(String otpPolicyType); - public void setOtpPolicyLookAheadWindow(int otpPolicyLookAheadWindow) { - this.updated = !Objects.equals(this.otpPolicyLookAheadWindow, otpPolicyLookAheadWindow); - this.otpPolicyLookAheadWindow = otpPolicyLookAheadWindow; - } - - public Integer getOtpPolicyPeriod() { - return otpPolicyPeriod; - } - - public void setOtpPolicyPeriod(int otpPolicyPeriod) { - this.updated = !Objects.equals(this.otpPolicyPeriod, otpPolicyPeriod); - this.otpPolicyPeriod = otpPolicyPeriod; - } - - public String getOtpPolicyType() { - return otpPolicyType; - } - - public void setOtpPolicyType(String otpPolicyType) { - this.updated = !Objects.equals(this.otpPolicyType, otpPolicyType); - this.otpPolicyType = otpPolicyType; - } - - public String getOtpPolicyAlgorithm() { - return otpPolicyAlgorithm; - } - - public void setOtpPolicyAlgorithm(String otpPolicyAlgorithm) { - this.updated = !Objects.equals(this.otpPolicyAlgorithm, otpPolicyAlgorithm); - this.otpPolicyAlgorithm = otpPolicyAlgorithm; - } - - @Override - public int hashCode() { - int hash = 5; - hash = 59 * hash + this.otpPolicyInitialCounter; - hash = 59 * hash + this.otpPolicyDigits; - hash = 59 * hash + this.otpPolicyLookAheadWindow; - hash = 59 * hash + this.otpPolicyPeriod; - hash = 59 * hash + Objects.hashCode(this.otpPolicyType); - hash = 59 * hash + Objects.hashCode(this.otpPolicyAlgorithm); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapOTPPolicyEntity)) return false; - final MapOTPPolicyEntity other = (MapOTPPolicyEntity) obj; - return Objects.equals(other.getOtpPolicyAlgorithm(), getOtpPolicyAlgorithm()) && - Objects.equals(other.getOtpPolicyDigits(), getOtpPolicyDigits()) && - Objects.equals(other.getOtpPolicyInitialCounter(), getOtpPolicyInitialCounter()) && - Objects.equals(other.getOtpPolicyLookAheadWindow(), getOtpPolicyLookAheadWindow()) && - Objects.equals(other.getOtpPolicyPeriod(), getOtpPolicyPeriod()) && - Objects.equals(other.getOtpPolicyType(), getOtpPolicyType()); - } - + String getOtpPolicyAlgorithm(); + void setOtpPolicyAlgorithm(String otpPolicyAlgorithm); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredActionProviderEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredActionProviderEntity.java index 31b7d2d11a..c1faed9c96 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredActionProviderEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredActionProviderEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,30 +17,22 @@ package org.keycloak.models.map.realm.entity; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import org.keycloak.models.RequiredActionProviderModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; -public class MapRequiredActionProviderEntity extends UpdatableEntity.Impl { +import java.util.HashMap; +import java.util.Map; - private String id; - private String alias; - private String name; - private String providerId; - private Integer priority = 0; - private Boolean enabled = false; - private Boolean defaultAction = false; - private Map config = new HashMap<>(); - - - private MapRequiredActionProviderEntity() {} - - public static MapRequiredActionProviderEntity fromModel(RequiredActionProviderModel model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapRequiredActionProviderEntity extends UpdatableEntity, AbstractEntity { + static MapRequiredActionProviderEntity fromModel(RequiredActionProviderModel model) { if (model == null) return null; - MapRequiredActionProviderEntity entity = new MapRequiredActionProviderEntity(); + MapRequiredActionProviderEntity entity = new MapRequiredActionProviderEntityImpl(); String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId(); entity.setId(id); entity.setAlias(model.getAlias()); @@ -49,106 +41,45 @@ public class MapRequiredActionProviderEntity extends UpdatableEntity.Impl { entity.setPriority(model.getPriority()); entity.setEnabled(model.isEnabled()); entity.setDefaultAction(model.isDefaultAction()); - entity.setConfig(model.getConfig() == null ? null : new HashMap<>(model.getConfig())); + entity.setConfig(model.getConfig()); return entity; } - public static RequiredActionProviderModel toModel(MapRequiredActionProviderEntity entity) { + static RequiredActionProviderModel toModel(MapRequiredActionProviderEntity entity) { if (entity == null) return null; RequiredActionProviderModel model = new RequiredActionProviderModel(); model.setId(entity.getId()); model.setAlias(entity.getAlias()); model.setName(entity.getName()); model.setProviderId(entity.getProviderId()); - model.setPriority(entity.getPriority()); - model.setEnabled(entity.isEnabled()); - model.setDefaultAction(entity.isDefaultAction()); - model.setConfig(entity.getConfig() == null ? null : new HashMap<>(entity.getConfig())); + Integer priority = entity.getPriority(); + model.setPriority(priority == null ? 0 : priority); + Boolean enabled = entity.isEnabled(); + model.setEnabled(enabled == null ? false : enabled); + Boolean defaultAction = entity.isDefaultAction(); + model.setDefaultAction(defaultAction == null ? false : defaultAction); + model.setConfig(entity.getConfig()); return model; } - public String getId() { - return id; - } + String getAlias(); + void setAlias(String alias); - public void setId(String id) { - this.updated = !Objects.equals(this.id, id); - this.id = id; - } + String getName(); + void setName(String name); - public String getAlias() { - return alias; - } + String getProviderId(); + void setProviderId(String providerId); - public void setAlias(String alias) { - this.updated = !Objects.equals(this.alias, alias); - this.alias = alias; - } + Integer getPriority(); + void setPriority(Integer priority); - public String getName() { - return name; - } + Boolean isEnabled(); + void setEnabled(Boolean enabled); - public void setName(String name) { - this.updated = !Objects.equals(this.name, name); - this.name = name; - } + Boolean isDefaultAction(); + void setDefaultAction(Boolean defaultAction); - public String getProviderId() { - return providerId; - } - - public void setProviderId(String providerId) { - this.updated = !Objects.equals(this.providerId, providerId); - this.providerId = providerId; - } - - public Integer getPriority() { - return priority; - } - - public void setPriority(int priority) { - this.updated = !Objects.equals(this.priority, priority); - this.priority = priority; - } - - public Boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.updated = !Objects.equals(this.enabled, enabled); - this.enabled = enabled; - } - - public Boolean isDefaultAction() { - return defaultAction; - } - - public void setDefaultAction(boolean defaultAction) { - this.updated = !Objects.equals(this.defaultAction, defaultAction); - this.defaultAction = defaultAction; - } - - public Map getConfig() { - return config; - } - - public void setConfig(Map config) { - this.updated = !Objects.equals(this.config, config); - this.config = config; - } - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapRequiredActionProviderEntity)) return false; - final MapRequiredActionProviderEntity other = (MapRequiredActionProviderEntity) obj; - return Objects.equals(other.getId(), getId()); - } + Map getConfig(); + void setConfig(Map config); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredCredentialEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredCredentialEntity.java index 422bf5f25a..72ac21f2ef 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredCredentialEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapRequiredCredentialEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,23 +17,17 @@ package org.keycloak.models.map.realm.entity; -import java.util.Objects; import org.keycloak.models.RequiredCredentialModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; -public class MapRequiredCredentialEntity extends UpdatableEntity.Impl { - - private String type; - private String formLabel; - private Boolean input = false; - private Boolean secret = false; - - - private MapRequiredCredentialEntity() {} - - public static MapRequiredCredentialEntity fromModel(RequiredCredentialModel model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapRequiredCredentialEntity extends UpdatableEntity { + static MapRequiredCredentialEntity fromModel(RequiredCredentialModel model) { if (model == null) return null; - MapRequiredCredentialEntity entity = new MapRequiredCredentialEntity(); + MapRequiredCredentialEntity entity = new MapRequiredCredentialEntityImpl(); entity.setFormLabel(model.getFormLabel()); entity.setType(model.getType()); entity.setInput(model.isInput()); @@ -41,62 +35,27 @@ public class MapRequiredCredentialEntity extends UpdatableEntity.Impl { return entity; } - public static RequiredCredentialModel toModel(MapRequiredCredentialEntity entity) { + static RequiredCredentialModel toModel(MapRequiredCredentialEntity entity) { if (entity == null) return null; RequiredCredentialModel model = new RequiredCredentialModel(); model.setFormLabel(entity.getFormLabel()); model.setType(entity.getType()); - model.setSecret(entity.isSecret()); - model.setInput(entity.isInput()); + Boolean secret = entity.isSecret(); + model.setSecret(secret == null ? false : secret); + Boolean input = entity.isInput(); + model.setInput(input == null ? false : input); return model; } - public String getType() { - return type; - } + String getType(); + void setType(String type); - public void setType(String type) { - this.updated = !Objects.equals(this.type, type); - this.type = type; - } + String getFormLabel(); + void setFormLabel(String formLabel); - public String getFormLabel() { - return formLabel; - } + Boolean isSecret(); + void setSecret(Boolean secret); - public void setFormLabel(String formLabel) { - this.updated = !Objects.equals(this.formLabel, formLabel); - this.formLabel = formLabel; - } - - public Boolean isSecret() { - return secret; - } - - public void setSecret(boolean secret) { - this.updated = !Objects.equals(this.formLabel, formLabel); - this.secret = secret; - } - - public Boolean isInput() { - return input; - } - - public void setInput(boolean input) { - this.updated = !Objects.equals(this.input, input); - this.input = input; - } - - @Override - public int hashCode() { - return getType().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapRequiredCredentialEntity)) return false; - final MapRequiredCredentialEntity other = (MapRequiredCredentialEntity) obj; - return Objects.equals(other.getType(), getType()); - } + Boolean isInput(); + void setInput(Boolean input); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapWebAuthnPolicyEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapWebAuthnPolicyEntity.java index 73dd14980a..8445af4905 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapWebAuthnPolicyEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/entity/MapWebAuthnPolicyEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Red Hat, Inc. and/or its affiliates + * Copyright 2022 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,36 +17,22 @@ package org.keycloak.models.map.realm.entity; +import org.keycloak.models.Constants; +import org.keycloak.models.WebAuthnPolicy; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; +import org.keycloak.models.map.common.DeepCloner; +import org.keycloak.models.map.common.UpdatableEntity; + import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Objects; -import org.keycloak.models.Constants; -import org.keycloak.models.WebAuthnPolicy; -import org.keycloak.models.map.common.UpdatableEntity; -public class MapWebAuthnPolicyEntity extends UpdatableEntity.Impl { - - // mandatory - private String rpEntityName; - private List signatureAlgorithms = new LinkedList<>(); - - // optional - private String rpId; - private String attestationConveyancePreference; - private String authenticatorAttachment; - private String requireResidentKey; - private String userVerificationRequirement; - private Integer createTimeout = 0; - private Boolean avoidSameAuthenticatorRegister = false; - private List acceptableAaguids = new LinkedList<>(); - - - private MapWebAuthnPolicyEntity() {} - - public static MapWebAuthnPolicyEntity fromModel(WebAuthnPolicy model) { +@GenerateEntityImplementations +@DeepCloner.Root +public interface MapWebAuthnPolicyEntity extends UpdatableEntity { + static MapWebAuthnPolicyEntity fromModel(WebAuthnPolicy model) { if (model == null) return null; - MapWebAuthnPolicyEntity entity = new MapWebAuthnPolicyEntity(); + MapWebAuthnPolicyEntity entity = new MapWebAuthnPolicyEntityImpl(); entity.setRpEntityName(model.getRpEntityName()); entity.setSignatureAlgorithms(model.getSignatureAlgorithm()); entity.setRpId(model.getRpId()); @@ -60,7 +46,7 @@ public class MapWebAuthnPolicyEntity extends UpdatableEntity.Impl { return entity; } - public static WebAuthnPolicy toModel(MapWebAuthnPolicyEntity entity) { + static WebAuthnPolicy toModel(MapWebAuthnPolicyEntity entity) { if (entity == null) return null; WebAuthnPolicy model = new WebAuthnPolicy(); model.setRpEntityName(entity.getRpEntityName()); @@ -76,8 +62,8 @@ public class MapWebAuthnPolicyEntity extends UpdatableEntity.Impl { return model; } - public static MapWebAuthnPolicyEntity defaultWebAuthnPolicy() { - MapWebAuthnPolicyEntity entity = new MapWebAuthnPolicyEntity(); + static MapWebAuthnPolicyEntity defaultWebAuthnPolicy() { + MapWebAuthnPolicyEntity entity = new MapWebAuthnPolicyEntityImpl(); entity.setRpEntityName(Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME); entity.setSignatureAlgorithms(Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(","))); entity.setRpId(""); @@ -91,106 +77,33 @@ public class MapWebAuthnPolicyEntity extends UpdatableEntity.Impl { return entity; } - public String getRpEntityName() { - return rpEntityName; - } + String getRpEntityName(); + void setRpEntityName(String rpEntityName); - public void setRpEntityName(String rpEntityName) { - this.updated = !Objects.equals(this.rpEntityName, rpEntityName); - this.rpEntityName = rpEntityName; - } + List getSignatureAlgorithms(); + void setSignatureAlgorithms(List signatureAlgorithms); - public List getSignatureAlgorithms() { - return signatureAlgorithms; - } + String getRpId(); + void setRpId(String rpId); - public void setSignatureAlgorithms(List signatureAlgorithms) { - this.updated = !Objects.equals(this.signatureAlgorithms, signatureAlgorithms); - this.signatureAlgorithms = signatureAlgorithms; - } + String getAttestationConveyancePreference(); + void setAttestationConveyancePreference(String attestationConveyancePreference); - public String getRpId() { - return rpId; - } + String getAuthenticatorAttachment(); + void setAuthenticatorAttachment(String authenticatorAttachment); - public void setRpId(String rpId) { - this.updated = !Objects.equals(this.rpId, rpId); - this.rpId = rpId; - } + String getRequireResidentKey(); + void setRequireResidentKey(String requireResidentKey); - public String getAttestationConveyancePreference() { - return attestationConveyancePreference; - } + String getUserVerificationRequirement(); + void setUserVerificationRequirement(String userVerificationRequirement); - public void setAttestationConveyancePreference(String attestationConveyancePreference) { - this.updated = !Objects.equals(this.attestationConveyancePreference, attestationConveyancePreference); - this.attestationConveyancePreference = attestationConveyancePreference; - } + Integer getCreateTimeout(); + void setCreateTimeout(Integer createTimeout); - public String getAuthenticatorAttachment() { - return authenticatorAttachment; - } + Boolean isAvoidSameAuthenticatorRegister(); + void setAvoidSameAuthenticatorRegister(Boolean avoidSameAuthenticatorRegister); - public void setAuthenticatorAttachment(String authenticatorAttachment) { - this.updated = !Objects.equals(this.authenticatorAttachment, authenticatorAttachment); - this.authenticatorAttachment = authenticatorAttachment; - } - - public String getRequireResidentKey() { - return requireResidentKey; - } - - public void setRequireResidentKey(String requireResidentKey) { - this.updated = !Objects.equals(this.requireResidentKey, requireResidentKey); - this.requireResidentKey = requireResidentKey; - } - - public String getUserVerificationRequirement() { - return userVerificationRequirement; - } - - public void setUserVerificationRequirement(String userVerificationRequirement) { - this.updated = !Objects.equals(this.userVerificationRequirement, userVerificationRequirement); - this.userVerificationRequirement = userVerificationRequirement; - } - - public Integer getCreateTimeout() { - return createTimeout; - } - - public void setCreateTimeout(int createTimeout) { - this.updated = !Objects.equals(this.createTimeout, createTimeout); - this.createTimeout = createTimeout; - } - - public Boolean isAvoidSameAuthenticatorRegister() { - return avoidSameAuthenticatorRegister; - } - - public void setAvoidSameAuthenticatorRegister(boolean avoidSameAuthenticatorRegister) { - this.updated = !Objects.equals(this.avoidSameAuthenticatorRegister, avoidSameAuthenticatorRegister); - this.avoidSameAuthenticatorRegister = avoidSameAuthenticatorRegister; - } - - public List getAcceptableAaguids() { - return acceptableAaguids; - } - - public void setAcceptableAaguids(List acceptableAaguids) { - this.updated = !Objects.equals(this.acceptableAaguids, acceptableAaguids); - this.acceptableAaguids = acceptableAaguids; - } - - @Override - public int hashCode() { - return getRpEntityName().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof MapWebAuthnPolicyEntity)) return false; - final MapWebAuthnPolicyEntity other = (MapWebAuthnPolicyEntity) obj; - return Objects.equals(other.getRpEntityName(), getRpEntityName()); - } + List getAcceptableAaguids(); + void setAcceptableAaguids(List acceptableAaguids); } 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 f8275e913f..610cf9faf1 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 @@ -42,6 +42,9 @@ import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.Serialization; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.map.group.MapGroupEntityImpl; +import org.keycloak.models.map.realm.MapRealmEntity; +import org.keycloak.models.map.realm.MapRealmEntityImpl; +import org.keycloak.models.map.realm.entity.*; import org.keycloak.models.map.role.MapRoleEntityImpl; import com.fasterxml.jackson.databind.JavaType; import java.io.File; @@ -107,6 +110,18 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide .constructor(MapScopeEntity.class, MapScopeEntityImpl::new) .constructor(MapPolicyEntity.class, MapPolicyEntityImpl::new) .constructor(MapPermissionTicketEntity.class, MapPermissionTicketEntityImpl::new) + .constructor(MapRealmEntity.class, MapRealmEntityImpl::new) + .constructor(MapAuthenticationExecutionEntity.class, MapAuthenticationExecutionEntityImpl::new) + .constructor(MapAuthenticationFlowEntity.class, MapAuthenticationFlowEntityImpl::new) + .constructor(MapAuthenticatorConfigEntity.class, MapAuthenticatorConfigEntityImpl::new) + .constructor(MapClientInitialAccessEntity.class, MapClientInitialAccessEntityImpl::new) + .constructor(MapComponentEntity.class, MapComponentEntityImpl::new) + .constructor(MapIdentityProviderEntity.class, MapIdentityProviderEntityImpl::new) + .constructor(MapIdentityProviderMapperEntity.class, MapIdentityProviderMapperEntityImpl::new) + .constructor(MapOTPPolicyEntity.class, MapOTPPolicyEntityImpl::new) + .constructor(MapRequiredActionProviderEntity.class, MapRequiredActionProviderEntityImpl::new) + .constructor(MapRequiredCredentialEntity.class, MapRequiredCredentialEntityImpl::new) + .constructor(MapWebAuthnPolicyEntity.class, MapWebAuthnPolicyEntityImpl::new) .build(); private static final Map KEY_CONVERTORS = new HashMap<>(); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java index 3cc7165643..1f54a3b561 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java @@ -485,7 +485,7 @@ public class MapFieldPredicates { private static MapModelCriteriaBuilder checkRealmsWithComponentType(MapModelCriteriaBuilder mcb, Operator op, Object[] values) { String providerType = ensureEqSingleValue(RealmModel.SearchableFields.COMPONENT_PROVIDER_TYPE, "component_provider_type", op, values); - Function getter = realmEntity -> realmEntity.getComponents().anyMatch(component -> component.getProviderType().equals(providerType)); + Function getter = realmEntity -> Optional.ofNullable(realmEntity.getComponents()).orElseGet(Collections::emptySet).stream().anyMatch(component -> component.getProviderType().equals(providerType)); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java index 23267f2e29..e033c65c4f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java @@ -188,7 +188,7 @@ public class FlowOverrideTest extends AbstractTestRealmKeycloakTest { challengeOTP.setTopLevel(true); challengeOTP.setBuiltIn(true); - realm.addAuthenticationFlow(challengeOTP); + challengeOTP = realm.addAuthenticationFlow(challengeOTP); execution = new AuthenticationExecutionModel(); execution.setParentFlow(challengeOTP.getId()); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/RealmModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/RealmModelTest.java new file mode 100644 index 0000000000..697bb47dca --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/RealmModelTest.java @@ -0,0 +1,98 @@ +/* + * 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.testsuite.model; + +import org.junit.Test; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RealmProvider; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; + +@RequireProvider(RealmProvider.class) +public class RealmModelTest extends KeycloakModelTest { + + private String realmId; + + @Override + public void createEnvironment(KeycloakSession s) { + RealmModel realm = s.realms().createRealm("realm"); + realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName())); + this.realmId = realm.getId(); + } + + @Override + public void cleanEnvironment(KeycloakSession s) { + s.realms().removeRealm(realmId); + } + + @Test + public void testRealmLocalizationTexts() { + withRealm(realmId, (session, realm) -> { + // Assert emptyMap + assertThat(realm.getRealmLocalizationTexts(), anEmptyMap()); + // Add a localization test + session.realms().saveLocalizationText(realm, "en", "key-a", "text-a_en"); + return null; + }); + + withRealm(realmId, (session, realm) -> { + // Assert the map contains the added value + assertThat(realm.getRealmLocalizationTexts(), aMapWithSize(1)); + assertThat(realm.getRealmLocalizationTexts(), + hasEntry(equalTo("en"), allOf(aMapWithSize(1), + hasEntry(equalTo("key-a"), equalTo("text-a_en"))))); + + // Add another localization text to previous locale + session.realms().saveLocalizationText(realm, "en", "key-b", "text-b_en"); + return null; + }); + + withRealm(realmId, (session, realm) -> { + assertThat(realm.getRealmLocalizationTexts(), aMapWithSize(1)); + assertThat(realm.getRealmLocalizationTexts(), + hasEntry(equalTo("en"), allOf(aMapWithSize(2), + hasEntry(equalTo("key-a"), equalTo("text-a_en")), + hasEntry(equalTo("key-b"), equalTo("text-b_en"))))); + + // Add new locale + session.realms().saveLocalizationText(realm, "de", "key-a", "text-a_de"); + return null; + }); + + withRealm(realmId, (session, realm) -> { + // Check everything created successfully + assertThat(realm.getRealmLocalizationTexts(), aMapWithSize(2)); + assertThat(realm.getRealmLocalizationTexts(), + hasEntry(equalTo("en"), allOf(aMapWithSize(2), + hasEntry(equalTo("key-a"), equalTo("text-a_en")), + hasEntry(equalTo("key-b"), equalTo("text-b_en"))))); + assertThat(realm.getRealmLocalizationTexts(), + hasEntry(equalTo("de"), allOf(aMapWithSize(1), + hasEntry(equalTo("key-a"), equalTo("text-a_de"))))); + + return null; + }); + + } +}