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
This commit is contained in:
parent
571f2d5107
commit
de2c1fbb45
7 changed files with 126 additions and 109 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<String> 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<String> 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<String> 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<JpaClientAttributeEntity> 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<String> 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<String, List<String>> attributes) {
|
||||
checkEntityVersionForUpdate();
|
||||
for (Iterator<JpaClientAttributeEntity> iterator = this.attributes.iterator(); iterator.hasNext();) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
|
|
@ -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<JpaClientScopeAttributeEntity> 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<String> 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<String, List<String>> attributes) {
|
||||
checkEntityVersionForUpdate();
|
||||
for (Iterator<JpaClientScopeAttributeEntity> iterator = this.attributes.iterator(); iterator.hasNext();) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<String> 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<String, List<String>> attributes) {
|
||||
checkEntityVersionForUpdate();
|
||||
for (Iterator<JpaRoleAttributeEntity> 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<String> 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<JpaRoleAttributeEntity> iterator = attributes.iterator(); iterator.hasNext();) {
|
||||
JpaRoleAttributeEntity attr = iterator.next();
|
||||
if (Objects.equals(attr.getName(), name)) {
|
||||
|
|
Loading…
Reference in a new issue