From de2c1fbb456cd18954f5cbb4836a36d0d1caa8e8 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 25 Jan 2022 15:36:30 +0100 Subject: [PATCH] Update the entityVersion also for downgrades, as it needs to match the JSON and auxiliary tables. Will trigger also when changes to a child occur, like for example when attributes change. Closes #9716 --- .../jpa/JpaMapStorageProviderFactory.java | 12 +++- .../models/map/storage/jpa/JpaRootEntity.java | 28 +++++++- .../jpa/client/entity/JpaClientEntity.java | 59 ++-------------- .../entity/JpaClientScopeEntity.java | 26 ++----- .../listeners/JpaEntityVersionListener.java | 69 +++++++++++++++++++ .../JpaOptimisticLockingListener.java} | 19 ++--- .../jpa/role/entity/JpaRoleEntity.java | 22 +----- 7 files changed, 126 insertions(+), 109 deletions(-) create mode 100644 model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaEntityVersionListener.java rename model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/{JpaChildEntityListener.java => hibernate/listeners/JpaOptimisticLockingListener.java} (72%) diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java index 97126c1af1d..900752b5460 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java @@ -62,6 +62,8 @@ import org.keycloak.models.map.storage.MapStorageProviderFactory; import org.keycloak.models.map.storage.jpa.client.JpaClientMapKeycloakTransaction; import org.keycloak.models.map.storage.jpa.clientscope.JpaClientScopeMapKeycloakTransaction; import org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeEntity; +import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaEntityVersionListener; +import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaOptimisticLockingListener; import org.keycloak.models.map.storage.jpa.role.JpaRoleMapKeycloakTransaction; import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity; import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProvider; @@ -185,9 +187,13 @@ public class JpaMapStorageProviderFactory implements final EventListenerRegistry eventListenerRegistry = sessionFactoryServiceRegistry.getService( EventListenerRegistry.class ); - eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaChildEntityListener.INSTANCE); - eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaChildEntityListener.INSTANCE); - eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaChildEntityListener.INSTANCE); + eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaOptimisticLockingListener.INSTANCE); + eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaOptimisticLockingListener.INSTANCE); + eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaOptimisticLockingListener.INSTANCE); + + eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaEntityVersionListener.INSTANCE); + eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaEntityVersionListener.INSTANCE); + eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaEntityVersionListener.INSTANCE); } @Override diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaRootEntity.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaRootEntity.java index 3d1970a069f..309582b8aa6 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaRootEntity.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaRootEntity.java @@ -1,13 +1,13 @@ /* * 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,6 +17,7 @@ package org.keycloak.models.map.storage.jpa; import java.io.Serializable; +import java.util.Objects; import org.keycloak.models.map.common.AbstractEntity; @@ -39,4 +40,25 @@ public interface JpaRootEntity extends AbstractEntity, Serializable { * @param entityVersion sets current supported version to JPA entity. */ void setEntityVersion(Integer entityVersion); + + /** + * In case of any update on entity, we want to update the entityVersion + * to current one. + * This includes downgrading from a future version of Keycloak, as the entityVersion must match the JSON + * and the additional tables this version writes. + * + * The listener {@link org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaEntityVersionListener} + * calls this method whenever the root entity or one of its children changes. + * + * Future versions of this method might restrict downgrading to downgrade only from the next version. + */ + default void updateEntityVersion() { + Integer ev = getEntityVersion(); + Integer currentEv = getCurrentSchemaVersion(); + if (ev != null && !Objects.equals(ev, currentEv)) { + setEntityVersion(currentEv); + } + } + + Integer getCurrentSchemaVersion(); } diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/client/entity/JpaClientEntity.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/client/entity/JpaClientEntity.java index 9fc57653e62..71759908a3b 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/client/entity/JpaClientEntity.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/client/entity/JpaClientEntity.java @@ -129,17 +129,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti return metadata != null; } - /** - * In case of any update on entity, we want to update the entityVerion - * to current one. - */ - private void checkEntityVersionForUpdate() { - Integer ev = getEntityVersion(); - if (ev != null && ev < CURRENT_SCHEMA_VERSION_CLIENT) { - setEntityVersion(CURRENT_SCHEMA_VERSION_CLIENT); - } - } - @Override public Integer getEntityVersion() { if (isMetadataInitialized()) return metadata.getEntityVersion(); @@ -151,6 +140,11 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti metadata.setEntityVersion(entityVersion); } + @Override + public Integer getCurrentSchemaVersion() { + return CURRENT_SCHEMA_VERSION_CLIENT; + } + @Override public int getVersion() { return version; @@ -174,7 +168,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setRealmId(String realmId) { - checkEntityVersionForUpdate(); metadata.setRealmId(realmId); } @@ -186,13 +179,11 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setClientId(String clientId) { - checkEntityVersionForUpdate(); metadata.setClientId(clientId); } @Override public void setEnabled(Boolean enabled) { - checkEntityVersionForUpdate(); metadata.setEnabled(enabled); } @@ -209,13 +200,11 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setClientScope(String id, Boolean defaultScope) { - checkEntityVersionForUpdate(); metadata.setClientScope(id, defaultScope); } @Override public void removeClientScope(String id) { - checkEntityVersionForUpdate(); metadata.removeClientScope(id); } @@ -231,19 +220,16 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void removeProtocolMapper(String id) { - checkEntityVersionForUpdate(); metadata.removeProtocolMapper(id); } @Override public void setProtocolMapper(String id, MapProtocolMapperEntity mapping) { - checkEntityVersionForUpdate(); metadata.setProtocolMapper(id, mapping); } @Override public void addRedirectUri(String redirectUri) { - checkEntityVersionForUpdate(); metadata.addRedirectUri(redirectUri); } @@ -254,25 +240,21 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void removeRedirectUri(String redirectUri) { - checkEntityVersionForUpdate(); metadata.removeRedirectUri(redirectUri); } @Override public void setRedirectUris(Set redirectUris) { - checkEntityVersionForUpdate(); metadata.setRedirectUris(redirectUris); } @Override public void addScopeMapping(String id) { - checkEntityVersionForUpdate(); metadata.addScopeMapping(id); } @Override public void removeScopeMapping(String id) { - checkEntityVersionForUpdate(); metadata.removeScopeMapping(id); } @@ -283,7 +265,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void addWebOrigin(String webOrigin) { - checkEntityVersionForUpdate(); metadata.addWebOrigin(webOrigin); } @@ -294,13 +275,11 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void removeWebOrigin(String webOrigin) { - checkEntityVersionForUpdate(); metadata.removeWebOrigin(webOrigin); } @Override public void setWebOrigins(Set webOrigins) { - checkEntityVersionForUpdate(); metadata.setWebOrigins(webOrigins); } @@ -316,13 +295,11 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void removeAuthenticationFlowBindingOverride(String binding) { - checkEntityVersionForUpdate(); metadata.removeAuthenticationFlowBindingOverride(binding); } @Override public void setAuthenticationFlowBindingOverride(String binding, String flowId) { - checkEntityVersionForUpdate(); metadata.setAuthenticationFlowBindingOverride(binding, flowId); } @@ -333,7 +310,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setBaseUrl(String baseUrl) { - checkEntityVersionForUpdate(); metadata.setBaseUrl(baseUrl); } @@ -344,7 +320,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setClientAuthenticatorType(String clientAuthenticatorType) { - checkEntityVersionForUpdate(); metadata.setClientAuthenticatorType(clientAuthenticatorType); } @@ -355,7 +330,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setDescription(String description) { - checkEntityVersionForUpdate(); metadata.setDescription(description); } @@ -366,7 +340,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setManagementUrl(String managementUrl) { - checkEntityVersionForUpdate(); metadata.setManagementUrl(managementUrl); } @@ -377,7 +350,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setName(String name) { - checkEntityVersionForUpdate(); metadata.setName(name); } @@ -388,7 +360,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) { - checkEntityVersionForUpdate(); metadata.setNodeReRegistrationTimeout(nodeReRegistrationTimeout); } @@ -399,7 +370,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setNotBefore(Integer notBefore) { - checkEntityVersionForUpdate(); metadata.setNotBefore(notBefore); } @@ -411,7 +381,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setProtocol(String protocol) { - checkEntityVersionForUpdate(); metadata.setProtocol(protocol); } @@ -422,7 +391,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setRegistrationToken(String registrationToken) { - checkEntityVersionForUpdate(); metadata.setRegistrationToken(registrationToken); } @@ -433,7 +401,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setRootUrl(String rootUrl) { - checkEntityVersionForUpdate(); metadata.setRootUrl(rootUrl); } @@ -444,7 +411,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setScope(Set scope) { - checkEntityVersionForUpdate(); metadata.setScope(scope); } @@ -455,7 +421,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setSecret(String secret) { - checkEntityVersionForUpdate(); metadata.setSecret(secret); } @@ -466,7 +431,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) { - checkEntityVersionForUpdate(); metadata.setAlwaysDisplayInConsole(alwaysDisplayInConsole); } @@ -477,7 +441,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setBearerOnly(Boolean bearerOnly) { - checkEntityVersionForUpdate(); metadata.setBearerOnly(bearerOnly); } @@ -488,7 +451,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setConsentRequired(Boolean consentRequired) { - checkEntityVersionForUpdate(); metadata.setConsentRequired(consentRequired); } @@ -499,7 +461,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) { - checkEntityVersionForUpdate(); metadata.setDirectAccessGrantsEnabled(directAccessGrantsEnabled); } @@ -510,7 +471,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setFrontchannelLogout(Boolean frontchannelLogout) { - checkEntityVersionForUpdate(); metadata.setFrontchannelLogout(frontchannelLogout); } @@ -521,7 +481,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setFullScopeAllowed(Boolean fullScopeAllowed) { - checkEntityVersionForUpdate(); metadata.setFullScopeAllowed(fullScopeAllowed); } @@ -532,7 +491,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) { - checkEntityVersionForUpdate(); metadata.setImplicitFlowEnabled(implicitFlowEnabled); } @@ -543,7 +501,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setPublicClient(Boolean publicClient) { - checkEntityVersionForUpdate(); metadata.setPublicClient(publicClient); } @@ -554,7 +511,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) { - checkEntityVersionForUpdate(); metadata.setServiceAccountsEnabled(serviceAccountsEnabled); } @@ -565,7 +521,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setStandardFlowEnabled(Boolean standardFlowEnabled) { - checkEntityVersionForUpdate(); metadata.setStandardFlowEnabled(standardFlowEnabled); } @@ -576,13 +531,11 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) { - checkEntityVersionForUpdate(); metadata.setSurrogateAuthRequired(surrogateAuthRequired); } @Override public void removeAttribute(String name) { - checkEntityVersionForUpdate(); for (Iterator iterator = attributes.iterator(); iterator.hasNext();) { JpaClientAttributeEntity attr = iterator.next(); if (Objects.equals(attr.getName(), name)) { @@ -593,7 +546,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setAttribute(String name, List values) { - checkEntityVersionForUpdate(); removeAttribute(name); for (String value : values) { JpaClientAttributeEntity attribute = new JpaClientAttributeEntity(this, name, value); @@ -622,7 +574,6 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootEnti @Override public void setAttributes(Map> attributes) { - checkEntityVersionForUpdate(); for (Iterator iterator = this.attributes.iterator(); iterator.hasNext();) { iterator.remove(); } diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/clientscope/entity/JpaClientScopeEntity.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/clientscope/entity/JpaClientScopeEntity.java index 6a8dbafb4bf..2944a652c55 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/clientscope/entity/JpaClientScopeEntity.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/clientscope/entity/JpaClientScopeEntity.java @@ -113,17 +113,6 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J return metadata != null; } - /** - * In case of any update on entity, we want to update the entityVerion - * to current one. - */ - private void checkEntityVersionForUpdate() { - Integer ev = getEntityVersion(); - if (ev != null && ev < CURRENT_SCHEMA_VERSION_CLIENT_SCOPE) { - setEntityVersion(CURRENT_SCHEMA_VERSION_CLIENT_SCOPE); - } - } - @Override public Integer getEntityVersion() { if (isMetadataInitialized()) return metadata.getEntityVersion(); @@ -135,6 +124,11 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J metadata.setEntityVersion(entityVersion); } + @Override + public Integer getCurrentSchemaVersion() { + return CURRENT_SCHEMA_VERSION_CLIENT_SCOPE; + } + @Override public int getVersion() { return version; @@ -158,7 +152,6 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void setRealmId(String realmId) { - checkEntityVersionForUpdate(); metadata.setRealmId(realmId); } @@ -169,19 +162,16 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void addProtocolMapper(MapProtocolMapperEntity mapping) { - checkEntityVersionForUpdate(); metadata.addProtocolMapper(mapping); } @Override public void addScopeMapping(String id) { - checkEntityVersionForUpdate(); metadata.addScopeMapping(id); } @Override public void removeScopeMapping(String id) { - checkEntityVersionForUpdate(); metadata.removeScopeMapping(id); } @@ -197,7 +187,6 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void setDescription(String description) { - checkEntityVersionForUpdate(); metadata.setDescription(description); } @@ -209,7 +198,6 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void setName(String name) { - checkEntityVersionForUpdate(); metadata.setName(name); } @@ -220,13 +208,11 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void setProtocol(String protocol) { - checkEntityVersionForUpdate(); metadata.setProtocol(protocol); } @Override public void removeAttribute(String name) { - checkEntityVersionForUpdate(); for (Iterator iterator = attributes.iterator(); iterator.hasNext();) { JpaClientScopeAttributeEntity attr = iterator.next(); if (Objects.equals(attr.getName(), name)) { @@ -237,7 +223,6 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void setAttribute(String name, List values) { - checkEntityVersionForUpdate(); removeAttribute(name); for (String value : values) { JpaClientScopeAttributeEntity attribute = new JpaClientScopeAttributeEntity(this, name, value); @@ -266,7 +251,6 @@ public class JpaClientScopeEntity extends AbstractClientScopeEntity implements J @Override public void setAttributes(Map> attributes) { - checkEntityVersionForUpdate(); for (Iterator iterator = this.attributes.iterator(); iterator.hasNext();) { iterator.remove(); } diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaEntityVersionListener.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaEntityVersionListener.java new file mode 100644 index 00000000000..2749ea01d7f --- /dev/null +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaEntityVersionListener.java @@ -0,0 +1,69 @@ +/* + * 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.storage.jpa.hibernate.listeners; + +import org.hibernate.HibernateException; +import org.hibernate.event.spi.PreDeleteEvent; +import org.hibernate.event.spi.PreDeleteEventListener; +import org.hibernate.event.spi.PreInsertEvent; +import org.hibernate.event.spi.PreInsertEventListener; +import org.hibernate.event.spi.PreUpdateEvent; +import org.hibernate.event.spi.PreUpdateEventListener; +import org.keycloak.models.map.storage.jpa.JpaChildEntity; +import org.keycloak.models.map.storage.jpa.JpaRootEntity; + +/** + * Listen on changes on child- and root entities and updates the current entity version of the root. + * + * This support a multiple level parent-child relationship, where the upmost parent needs the entity version to be updated. + */ +public class JpaEntityVersionListener implements PreInsertEventListener, PreDeleteEventListener, PreUpdateEventListener { + + public static final JpaEntityVersionListener INSTANCE = new JpaEntityVersionListener(); + + /** + * Traverse from current entity up to the upmost parent, then update the entity version if it is a root entity. + */ + public void updateEntityVersion(Object entity) throws HibernateException { + Object root = entity; + while(root instanceof JpaChildEntity) { + root = ((JpaChildEntity) entity).getParent(); + } + if (root instanceof JpaRootEntity) { + ((JpaRootEntity) root).updateEntityVersion(); + } + } + + @Override + public boolean onPreInsert(PreInsertEvent event) { + updateEntityVersion(event.getEntity()); + return false; + } + + @Override + public boolean onPreDelete(PreDeleteEvent event) { + updateEntityVersion(event.getEntity()); + return false; + } + + @Override + public boolean onPreUpdate(PreUpdateEvent event) { + updateEntityVersion(event.getEntity()); + return false; + } +} diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaChildEntityListener.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaOptimisticLockingListener.java similarity index 72% rename from model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaChildEntityListener.java rename to model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaOptimisticLockingListener.java index 774b8158be9..1ef3adf6772 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaChildEntityListener.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/hibernate/listeners/JpaOptimisticLockingListener.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.models.map.storage.jpa; +package org.keycloak.models.map.storage.jpa.hibernate.listeners; import org.hibernate.HibernateException; import org.hibernate.Session; @@ -25,22 +25,23 @@ import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.event.spi.PreUpdateEvent; import org.hibernate.event.spi.PreUpdateEventListener; +import org.keycloak.models.map.storage.jpa.JpaChildEntity; import javax.persistence.LockModeType; /** - * Listen on changes on child entities and forces an optimistic locking increment on the topmost parent. + * Listen on changes on child entities and forces an optimistic locking increment on the topmost parent aka root. * * This support a multiple level parent-child relationship, where only the upmost parent is locked. */ -public class JpaChildEntityListener implements PreInsertEventListener, PreDeleteEventListener, PreUpdateEventListener { +public class JpaOptimisticLockingListener implements PreInsertEventListener, PreDeleteEventListener, PreUpdateEventListener { - public static final JpaChildEntityListener INSTANCE = new JpaChildEntityListener(); + public static final JpaOptimisticLockingListener INSTANCE = new JpaOptimisticLockingListener(); /** - * Check if the entity is a child with a parent and force optimistic locking increment on the upmost parent. + * Check if the entity is a child with a parent and force optimistic locking increment on the upmost parent aka root. */ - public void checkRoot(Session session, Object entity) throws HibernateException { + public void lockRootEntity(Session session, Object entity) throws HibernateException { if(entity instanceof JpaChildEntity) { Object root = entity; while (root instanceof JpaChildEntity) { @@ -52,19 +53,19 @@ public class JpaChildEntityListener implements PreInsertEventListener, PreDelete @Override public boolean onPreInsert(PreInsertEvent event) { - checkRoot(event.getSession(), event.getEntity()); + lockRootEntity(event.getSession(), event.getEntity()); return false; } @Override public boolean onPreDelete(PreDeleteEvent event) { - checkRoot(event.getSession(), event.getEntity()); + lockRootEntity(event.getSession(), event.getEntity()); return false; } @Override public boolean onPreUpdate(PreUpdateEvent event) { - checkRoot(event.getSession(), event.getEntity()); + lockRootEntity(event.getSession(), event.getEntity()); return false; } } diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/role/entity/JpaRoleEntity.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/role/entity/JpaRoleEntity.java index b4c566e36bf..2ef5377afe1 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/role/entity/JpaRoleEntity.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/role/entity/JpaRoleEntity.java @@ -121,15 +121,9 @@ public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity { return metadata != null; } - /** - * In case of any update on entity, we want to update the entityVerion - * to current one. - */ - private void checkEntityVersionForUpdate() { - Integer ev = getEntityVersion(); - if (ev != null && ev < CURRENT_SCHEMA_VERSION_ROLE) { - setEntityVersion(CURRENT_SCHEMA_VERSION_ROLE); - } + @Override + public Integer getCurrentSchemaVersion() { + return CURRENT_SCHEMA_VERSION_ROLE; } @Override @@ -189,25 +183,21 @@ public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity { @Override public void setRealmId(String realmId) { - checkEntityVersionForUpdate(); metadata.setRealmId(realmId); } @Override public void setClientId(String clientId) { - checkEntityVersionForUpdate(); metadata.setClientId(clientId); } @Override public void setName(String name) { - checkEntityVersionForUpdate(); metadata.setName(name); } @Override public void setDescription(String description) { - checkEntityVersionForUpdate(); metadata.setDescription(description); } @@ -218,19 +208,16 @@ public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity { @Override public void setCompositeRoles(Set compositeRoles) { - checkEntityVersionForUpdate(); metadata.setCompositeRoles(compositeRoles); } @Override public void addCompositeRole(String roleId) { - checkEntityVersionForUpdate(); metadata.addCompositeRole(roleId); } @Override public void removeCompositeRole(String roleId) { - checkEntityVersionForUpdate(); metadata.removeCompositeRole(roleId); } @@ -255,7 +242,6 @@ public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity { @Override public void setAttributes(Map> attributes) { - checkEntityVersionForUpdate(); for (Iterator iterator = this.attributes.iterator(); iterator.hasNext();) { iterator.remove(); } @@ -268,7 +254,6 @@ public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity { @Override public void setAttribute(String name, List values) { - checkEntityVersionForUpdate(); removeAttribute(name); for (String value : values) { JpaRoleAttributeEntity attribute = new JpaRoleAttributeEntity(this, name, value); @@ -278,7 +263,6 @@ public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity { @Override public void removeAttribute(String name) { - checkEntityVersionForUpdate(); for (Iterator iterator = attributes.iterator(); iterator.hasNext();) { JpaRoleAttributeEntity attr = iterator.next(); if (Objects.equals(attr.getName(), name)) {