parent
22a16ee899
commit
8ff768b33b
24 changed files with 1076 additions and 45 deletions
|
@ -17,6 +17,7 @@
|
|||
package org.keycloak.models.map.storage.jpa;
|
||||
|
||||
public interface Constants {
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_AUTH_SESSION = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_CLIENT = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_CLIENT_SCOPE = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_GROUP = 1;
|
||||
|
|
|
@ -44,8 +44,10 @@ public abstract class JpaDelegateProvider<T extends JpaRootEntity & AbstractEnti
|
|||
if (newDelegate == null) {
|
||||
throw new ModelIllegalStateException("Unable to retrieve entity: " + delegate.getClass().getName() + "#" + delegate.getId());
|
||||
}
|
||||
if (newDelegate.getVersion() != delegate.getVersion()) {
|
||||
throw new ModelIllegalStateException("Version of entity changed between two loads: " + delegate.getClass().getName() + "#" + delegate.getId());
|
||||
if (newDelegate instanceof JpaRootVersionedEntity && delegate instanceof JpaRootVersionedEntity) {
|
||||
if (((JpaRootVersionedEntity) newDelegate).getVersion() != ((JpaRootVersionedEntity) delegate).getVersion()) {
|
||||
throw new ModelIllegalStateException("Version of entity changed between two loads: " + delegate.getClass().getName() + "#" + delegate.getId());
|
||||
}
|
||||
}
|
||||
this.delegate = newDelegate;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.UUID;
|
|||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Stream;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaDelete;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
|
@ -29,7 +30,8 @@ import javax.persistence.criteria.Order;
|
|||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.connections.jpa.JpaKeycloakTransaction;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.jpa.PersistenceExceptionConverter;
|
||||
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.common.StringKeyConverter;
|
||||
|
@ -39,13 +41,15 @@ import org.keycloak.models.map.storage.QueryParameters;
|
|||
import static org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory.CLONER;
|
||||
import static org.keycloak.utils.StreamsUtil.closing;
|
||||
|
||||
public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E extends AbstractEntity, M> extends JpaKeycloakTransaction implements MapKeycloakTransaction<E, M> {
|
||||
public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E extends AbstractEntity, M> implements MapKeycloakTransaction<E, M> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(JpaMapKeycloakTransaction.class);
|
||||
private final Class<RE> entityType;
|
||||
protected EntityManager em;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaMapKeycloakTransaction(Class<RE> entityType, EntityManager em) {
|
||||
super(em);
|
||||
this.em = em;
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
|
@ -58,10 +62,10 @@ public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E exte
|
|||
@SuppressWarnings("unchecked")
|
||||
public E create(E mapEntity) {
|
||||
JpaRootEntity jpaEntity = entityType.cast(CLONER.from(mapEntity));
|
||||
CLONER.from(mapEntity);
|
||||
if (mapEntity.getId() == null) {
|
||||
jpaEntity.setId(StringKeyConverter.UUIDKey.INSTANCE.yieldNewUniqueKey().toString());
|
||||
}
|
||||
logger.tracef("tx %d: create entity %s", hashCode(), jpaEntity.getId());
|
||||
setEntityVersion(jpaEntity);
|
||||
em.persist(jpaEntity);
|
||||
return (E) jpaEntity;
|
||||
|
@ -137,6 +141,7 @@ public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E exte
|
|||
UUID uuid = UUIDKey.INSTANCE.fromStringSafe(key);
|
||||
if (uuid == null) return false;
|
||||
em.remove(em.getReference(entityType, uuid));
|
||||
logger.tracef("tx %d: delete entity %s", hashCode(), key);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -155,14 +160,43 @@ public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E exte
|
|||
BiFunction<CriteriaBuilder, Root<RE>, Predicate> predicateFunc = mcb.getPredicateFunc();
|
||||
if (predicateFunc != null) deleteQuery.where(predicateFunc.apply(cb, root));
|
||||
|
||||
// TODO find out if the flush and clear are needed here or not, since delete(QueryParameters)
|
||||
// is not used yet from the code it's difficult to investigate its potential purpose here
|
||||
// according to https://thorben-janssen.com/5-common-hibernate-mistakes-that-cause-dozens-of-unexpected-queries/#Remove_Child_Entities_With_a_Bulk_Operation
|
||||
// it seems it is necessary unless it is sure that any of removed entities wasn't fetched
|
||||
// Once KEYCLOAK-19697 is done we could test our scenarios and see if we need the flush and clear
|
||||
// em.flush();
|
||||
// em.clear();
|
||||
|
||||
return em.createQuery(deleteQuery).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
logger.tracef("tx %d: begin", hashCode());
|
||||
em.getTransaction().begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
try {
|
||||
logger.tracef("tx %d: commit", hashCode());
|
||||
em.getTransaction().commit();
|
||||
} catch (PersistenceException e) {
|
||||
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
logger.tracef("tx %d: rollback", hashCode());
|
||||
em.getTransaction().rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
em.getTransaction().setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return em.getTransaction().getRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return em.getTransaction().isActive();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,9 @@ import org.keycloak.models.map.common.DeepCloner;
|
|||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.JpaRootAuthenticationSessionMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionEntity;
|
||||
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;
|
||||
|
@ -75,6 +78,7 @@ import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProvider;
|
|||
import static org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProvider.Status.VALID;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
public class JpaMapStorageProviderFactory implements
|
||||
AmphibianProviderFactory<MapStorageProvider>,
|
||||
|
@ -89,23 +93,27 @@ public class JpaMapStorageProviderFactory implements
|
|||
private Config.Scope config;
|
||||
|
||||
public final static DeepCloner CLONER = new DeepCloner.Builder()
|
||||
//client
|
||||
.constructor(JpaClientEntity.class, JpaClientEntity::new)
|
||||
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
||||
//client-scope
|
||||
.constructor(JpaClientScopeEntity.class, JpaClientScopeEntity::new)
|
||||
//group
|
||||
.constructor(JpaGroupEntity.class, JpaGroupEntity::new)
|
||||
//role
|
||||
.constructor(JpaRoleEntity.class, JpaRoleEntity::new)
|
||||
.build();
|
||||
//auth-session
|
||||
.constructor(JpaRootAuthenticationSessionEntity.class, JpaRootAuthenticationSessionEntity::new)
|
||||
.constructor(JpaAuthenticationSessionEntity.class, JpaAuthenticationSessionEntity::new)
|
||||
//client
|
||||
.constructor(JpaClientEntity.class, JpaClientEntity::new)
|
||||
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
||||
//client-scope
|
||||
.constructor(JpaClientScopeEntity.class, JpaClientScopeEntity::new)
|
||||
//group
|
||||
.constructor(JpaGroupEntity.class, JpaGroupEntity::new)
|
||||
//role
|
||||
.constructor(JpaRoleEntity.class, JpaRoleEntity::new)
|
||||
.build();
|
||||
|
||||
private static final Map<Class<?>, Function<EntityManager, MapKeycloakTransaction>> MODEL_TO_TX = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_TX.put(ClientScopeModel.class, JpaClientScopeMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(ClientModel.class, JpaClientMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(GroupModel.class, JpaGroupMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(RootAuthenticationSessionModel.class, JpaRootAuthenticationSessionMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(ClientScopeModel.class, JpaClientScopeMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(ClientModel.class, JpaClientMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(GroupModel.class, JpaGroupMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new);
|
||||
}
|
||||
|
||||
public MapKeycloakTransaction createTransaction(Class<?> modelType, EntityManager em) {
|
||||
|
|
|
@ -26,11 +26,6 @@ import org.keycloak.models.map.common.AbstractEntity;
|
|||
*/
|
||||
public interface JpaRootEntity extends AbstractEntity, Serializable {
|
||||
|
||||
/**
|
||||
* Version of the JPA entity used for optimistic locking
|
||||
*/
|
||||
int getVersion();
|
||||
|
||||
/**
|
||||
* @return current supported version of the JPA entity used for schema versioning.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface for all root entities which implements optimistic locking.
|
||||
*/
|
||||
public interface JpaRootVersionedEntity extends JpaRootEntity {
|
||||
|
||||
/**
|
||||
* Version of the JPA entity used for optimistic locking
|
||||
*/
|
||||
int getVersion();
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.authSession;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntityDelegate;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTH_SESSION;
|
||||
import org.keycloak.models.map.storage.jpa.JpaMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.delegate.JpaRootAuthenticationSessionDelegateProvider;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionEntity;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
public class JpaRootAuthenticationSessionMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaRootAuthenticationSessionEntity, MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaRootAuthenticationSessionMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaRootAuthenticationSessionEntity.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Selection<JpaRootAuthenticationSessionEntity> selectCbConstruct(CriteriaBuilder cb, Root<JpaRootAuthenticationSessionEntity> root) {
|
||||
return cb.construct(JpaRootAuthenticationSessionEntity.class,
|
||||
root.get("id"),
|
||||
root.get("version"),
|
||||
root.get("entityVersion"),
|
||||
root.get("realmId"),
|
||||
root.get("timestamp"),
|
||||
root.get("expiration")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityVersion(JpaRootEntity entity) {
|
||||
entity.setEntityVersion(CURRENT_SCHEMA_VERSION_AUTH_SESSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaModelCriteriaBuilder createJpaModelCriteriaBuilder() {
|
||||
return new JpaRootAuthenticationSessionModelCriteriaBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapRootAuthenticationSessionEntity mapToEntityDelegate(JpaRootAuthenticationSessionEntity original) {
|
||||
return new MapRootAuthenticationSessionEntityDelegate(new JpaRootAuthenticationSessionDelegateProvider(original, em));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.authSession;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionEntity;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel.SearchableFields;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaRootAuthenticationSessionModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaRootAuthenticationSessionEntity, RootAuthenticationSessionModel, JpaRootAuthenticationSessionModelCriteriaBuilder> {
|
||||
|
||||
public JpaRootAuthenticationSessionModelCriteriaBuilder() {
|
||||
super(JpaRootAuthenticationSessionModelCriteriaBuilder::new);
|
||||
}
|
||||
|
||||
private JpaRootAuthenticationSessionModelCriteriaBuilder(BiFunction<CriteriaBuilder, Root<JpaRootAuthenticationSessionEntity>, Predicate> predicateFunc) {
|
||||
super(JpaRootAuthenticationSessionModelCriteriaBuilder::new, predicateFunc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaRootAuthenticationSessionModelCriteriaBuilder compare(SearchableModelField<? super RootAuthenticationSessionModel> modelField, Operator op, Object... value) {
|
||||
switch (op) {
|
||||
case EQ:
|
||||
if (modelField.equals(SearchableFields.REALM_ID)) {
|
||||
|
||||
validateValue(value, modelField, op, String.class);
|
||||
|
||||
return new JpaRootAuthenticationSessionModelCriteriaBuilder((cb, root) ->
|
||||
cb.equal(root.get(modelField.getName()), value[0])
|
||||
);
|
||||
} else {
|
||||
throw new CriterionNotSupportedException(modelField, op);
|
||||
}
|
||||
|
||||
case LT:
|
||||
if (modelField.equals(SearchableFields.EXPIRATION)) {
|
||||
validateValue(value, modelField, op, Number.class);
|
||||
|
||||
Number expiration = (Number) value[0];
|
||||
return new JpaRootAuthenticationSessionModelCriteriaBuilder((cb, root) ->
|
||||
cb.lt(root.get(modelField.getName()), expiration)
|
||||
);
|
||||
} else {
|
||||
throw new CriterionNotSupportedException(modelField, op);
|
||||
}
|
||||
default:
|
||||
throw new CriterionNotSupportedException(modelField, op);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.authSession.delegate;
|
||||
|
||||
import java.util.UUID;
|
||||
import javax.persistence.EntityManager;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntityFields;
|
||||
import org.keycloak.models.map.common.EntityField;
|
||||
import org.keycloak.models.map.common.delegate.DelegateProvider;
|
||||
import org.keycloak.models.map.storage.jpa.JpaDelegateProvider;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionEntity;
|
||||
|
||||
public class JpaRootAuthenticationSessionDelegateProvider extends JpaDelegateProvider<JpaRootAuthenticationSessionEntity> implements DelegateProvider<MapRootAuthenticationSessionEntity> {
|
||||
|
||||
private final EntityManager em;
|
||||
|
||||
public JpaRootAuthenticationSessionDelegateProvider(JpaRootAuthenticationSessionEntity delegate, EntityManager em) {
|
||||
super(delegate);
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapRootAuthenticationSessionEntity getDelegate(boolean isRead, Enum<? extends EntityField<MapRootAuthenticationSessionEntity>> field, Object... parameters) {
|
||||
if (getDelegate().isMetadataInitialized()) return getDelegate();
|
||||
if (isRead) {
|
||||
if (field instanceof MapRootAuthenticationSessionEntityFields) {
|
||||
switch ((MapRootAuthenticationSessionEntityFields) field) {
|
||||
case ID:
|
||||
case REALM_ID:
|
||||
case TIMESTAMP:
|
||||
case EXPIRATION:
|
||||
return getDelegate();
|
||||
|
||||
default:
|
||||
setDelegate(em.find(JpaRootAuthenticationSessionEntity.class, UUID.fromString(getDelegate().getId())));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Not a valid client field: " + field);
|
||||
}
|
||||
} else {
|
||||
setDelegate(em.find(JpaRootAuthenticationSessionEntity.class, UUID.fromString(getDelegate().getId())));
|
||||
}
|
||||
return getDelegate();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* 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.authSession.entity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
import org.hibernate.annotations.TypeDefs;
|
||||
import org.keycloak.models.map.authSession.MapAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.common.UpdatableEntity;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTH_SESSION;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
|
||||
/**
|
||||
* Entity represents individual authentication session.
|
||||
*
|
||||
* It implements {@link JpaRootEntity} as it contains json field.
|
||||
*
|
||||
* Authentication session is modified from multiple transactions within one request,
|
||||
* via {@code KeycloakModelUtils.runJobInTransaction}. Therefore it doesn't
|
||||
* implement {@code JpaRootVersionedEntity} nor {@code JpaChildEntity}.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kc_auth_session")
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaAuthenticationSessionEntity extends UpdatableEntity.Impl implements MapAuthenticationSessionEntity, JpaRootEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Type(type = "jsonb")
|
||||
@Column(columnDefinition = "jsonb")
|
||||
private final JpaAuthenticationSessionMetadata metadata;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name="fk_root")
|
||||
private JpaRootAuthenticationSessionEntity root;
|
||||
|
||||
/**
|
||||
* No-argument constructor, used by hibernate to instantiate entities.
|
||||
*/
|
||||
public JpaAuthenticationSessionEntity() {
|
||||
this.metadata = new JpaAuthenticationSessionMetadata();
|
||||
}
|
||||
|
||||
public JpaAuthenticationSessionEntity(DeepCloner cloner) {
|
||||
this.metadata = new JpaAuthenticationSessionMetadata(cloner);
|
||||
}
|
||||
|
||||
public void setParent(JpaRootAuthenticationSessionEntity root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
this.id = id == null ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getEntityVersion() {
|
||||
return metadata.getEntityVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityVersion(Integer version) {
|
||||
metadata.setEntityVersion(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCurrentSchemaVersion() {
|
||||
return CURRENT_SCHEMA_VERSION_AUTH_SESSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabId() {
|
||||
return metadata.getTabId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabId(String tabId) {
|
||||
metadata.setTabId(tabId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getUserSessionNotes() {
|
||||
return metadata.getUserSessionNotes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserSessionNotes(Map<String, String> userSessionNotes) {
|
||||
metadata.setUserSessionNotes(userSessionNotes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserSessionNote(String name, String value) {
|
||||
metadata.setUserSessionNote(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientUUID() {
|
||||
return metadata.getClientUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientUUID(String clientUUID) {
|
||||
metadata.setClientUUID(clientUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthUserId() {
|
||||
return metadata.getAuthUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthUserId(String authUserId) {
|
||||
metadata.setAuthUserId(authUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTimestamp() {
|
||||
return metadata.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(Integer timestamp) {
|
||||
metadata.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return metadata.getRedirectUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
metadata.setRedirectUri(redirectUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAction() {
|
||||
return metadata.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(String action) {
|
||||
metadata.setAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getClientScopes() {
|
||||
return metadata.getClientScopes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientScopes(Set<String> clientScopes) {
|
||||
metadata.setClientScopes(clientScopes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequiredActions() {
|
||||
return metadata.getRequiredActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequiredActions(Set<String> requiredActions) {
|
||||
metadata.setRequiredActions(requiredActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredAction(String requiredAction) {
|
||||
metadata.addRequiredAction(requiredAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRequiredAction(String action) {
|
||||
metadata.removeRequiredAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return metadata.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProtocol(String protocol) {
|
||||
metadata.setProtocol(protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getClientNotes() {
|
||||
return metadata.getClientNotes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientNotes(Map<String, String> clientNotes) {
|
||||
metadata.setClientNotes(clientNotes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientNote(String name, String value) {
|
||||
metadata.setClientNote(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeClientNote(String name) {
|
||||
metadata.removeClientNote(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAuthNotes() {
|
||||
return metadata.getAuthNotes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthNotes(Map<String, String> authNotes) {
|
||||
metadata.setAuthNotes(authNotes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthNote(String name, String value) {
|
||||
metadata.setAuthNote(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthNote(String name) {
|
||||
metadata.removeAuthNote(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, CommonClientSessionModel.ExecutionStatus> getExecutionStatuses() {
|
||||
return metadata.getExecutionStatuses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecutionStatuses(Map<String, CommonClientSessionModel.ExecutionStatus> executionStatus) {
|
||||
metadata.setExecutionStatuses(executionStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecutionStatus(String authenticator, CommonClientSessionModel.ExecutionStatus status) {
|
||||
metadata.setExecutionStatus(authenticator, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof JpaAuthenticationSessionEntity)) return false;
|
||||
return Objects.equals(getId(), ((JpaAuthenticationSessionEntity) obj).getId()) &&
|
||||
Objects.equals(getTabId(), ((JpaAuthenticationSessionEntity) obj).getTabId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.authSession.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.keycloak.models.map.authSession.MapAuthenticationSessionEntityImpl;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
|
||||
public class JpaAuthenticationSessionMetadata extends MapAuthenticationSessionEntityImpl implements Serializable {
|
||||
|
||||
public JpaAuthenticationSessionMetadata(DeepCloner cloner) {
|
||||
super(cloner);
|
||||
}
|
||||
|
||||
public JpaAuthenticationSessionMetadata() {
|
||||
super();
|
||||
}
|
||||
|
||||
private Integer entityVersion;
|
||||
|
||||
public Integer getEntityVersion() {
|
||||
return entityVersion;
|
||||
}
|
||||
|
||||
public void setEntityVersion(Integer entityVersion) {
|
||||
this.entityVersion = entityVersion;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* 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.authSession.entity;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
import org.hibernate.annotations.TypeDefs;
|
||||
import org.keycloak.models.map.authSession.MapAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity.AbstractRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTH_SESSION;
|
||||
import static org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory.CLONER;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
|
||||
|
||||
/**
|
||||
* Entity represents root authentication session.
|
||||
*
|
||||
* There are some fields marked by {@code @Column(insertable = false, updatable = false)}.
|
||||
* Those fields are automatically generated by database from json field,
|
||||
* therefore marked as non-insertable and non-updatable to instruct hibernate.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kc_auth_root_session")
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaRootAuthenticationSessionEntity extends AbstractRootAuthenticationSessionEntity implements JpaRootVersionedEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
private UUID id;
|
||||
|
||||
//used for implicit optimistic locking
|
||||
@Version
|
||||
@Column
|
||||
private int version;
|
||||
|
||||
@Type(type = "jsonb")
|
||||
@Column(columnDefinition = "jsonb")
|
||||
private final JpaRootAuthenticationSessionMetadata metadata;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Integer entityVersion;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String realmId;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Integer timestamp;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Long expiration;
|
||||
|
||||
@OneToMany(mappedBy = "root", cascade = CascadeType.PERSIST, orphanRemoval = true)
|
||||
private final Set<JpaAuthenticationSessionEntity> authSessions = new HashSet<>();
|
||||
|
||||
/**
|
||||
* No-argument constructor, used by hibernate to instantiate entities.
|
||||
*/
|
||||
public JpaRootAuthenticationSessionEntity() {
|
||||
this.metadata = new JpaRootAuthenticationSessionMetadata();
|
||||
}
|
||||
|
||||
public JpaRootAuthenticationSessionEntity(DeepCloner cloner) {
|
||||
this.metadata = new JpaRootAuthenticationSessionMetadata(cloner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by hibernate when calling cb.construct from read(QueryParameters) method.
|
||||
* It is used to select root auth session without metadata(json) field.
|
||||
*/
|
||||
public JpaRootAuthenticationSessionEntity(UUID id, Integer entityVersion, String realmId, Integer timestamp, Long expiration) {
|
||||
this.id = id;
|
||||
this.entityVersion = entityVersion;
|
||||
this.realmId = realmId;
|
||||
this.timestamp = timestamp;
|
||||
this.expiration = expiration;
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
public boolean isMetadataInitialized() {
|
||||
return metadata != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getEntityVersion() {
|
||||
if (isMetadataInitialized()) return metadata.getEntityVersion();
|
||||
return entityVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityVersion(Integer entityVersion) {
|
||||
metadata.setEntityVersion(entityVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCurrentSchemaVersion() {
|
||||
return CURRENT_SCHEMA_VERSION_AUTH_SESSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
this.id = id == null ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealmId() {
|
||||
if (isMetadataInitialized()) return metadata.getRealmId();
|
||||
return realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRealmId(String realmId) {
|
||||
metadata.setRealmId(realmId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTimestamp() {
|
||||
if (isMetadataInitialized()) return metadata.getTimestamp();
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(Integer timestamp) {
|
||||
metadata.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpiration() {
|
||||
if (isMetadataInitialized()) return metadata.getExpiration();
|
||||
return expiration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiration(Long expiration) {
|
||||
metadata.setExpiration(expiration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MapAuthenticationSessionEntity> getAuthenticationSessions() {
|
||||
return authSessions.stream().map(MapAuthenticationSessionEntity.class::cast).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticationSessions(Set<MapAuthenticationSessionEntity> authenticationSessions) {
|
||||
authSessions.clear();
|
||||
if (authenticationSessions != null) {
|
||||
for (MapAuthenticationSessionEntity authenticationSession : authenticationSessions) {
|
||||
addAuthenticationSession(authenticationSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAuthenticationSession(MapAuthenticationSessionEntity authenticationSession) {
|
||||
JpaAuthenticationSessionEntity jpaAuthSession = JpaAuthenticationSessionEntity.class.cast(CLONER.from(authenticationSession));
|
||||
jpaAuthSession.setParent(this);
|
||||
jpaAuthSession.setEntityVersion(this.getEntityVersion());
|
||||
authSessions.add(jpaAuthSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MapAuthenticationSessionEntity> getAuthenticationSession(String tabId) {
|
||||
return authSessions.stream().filter(as -> Objects.equals(as.getTabId(), tabId)).findFirst().map(MapAuthenticationSessionEntity.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean removeAuthenticationSession(String tabId) {
|
||||
return authSessions.removeIf(as -> Objects.equals(as.getTabId(), tabId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof JpaRootAuthenticationSessionEntity)) return false;
|
||||
return Objects.equals(getId(), ((JpaRootAuthenticationSessionEntity) obj).getId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.authSession.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntityImpl;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
|
||||
public class JpaRootAuthenticationSessionMetadata extends MapRootAuthenticationSessionEntityImpl implements Serializable {
|
||||
|
||||
public JpaRootAuthenticationSessionMetadata(DeepCloner cloner) {
|
||||
super(cloner);
|
||||
}
|
||||
|
||||
public JpaRootAuthenticationSessionMetadata() {
|
||||
super();
|
||||
}
|
||||
|
||||
private Integer entityVersion;
|
||||
|
||||
public Integer getEntityVersion() {
|
||||
return entityVersion;
|
||||
}
|
||||
|
||||
public void setEntityVersion(Integer entityVersion) {
|
||||
this.entityVersion = entityVersion;
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ import org.keycloak.models.map.client.MapClientEntity.AbstractClientEntity;
|
|||
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_CLIENT;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
|
||||
/**
|
||||
|
@ -59,7 +59,7 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
|||
)
|
||||
})
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaClientEntity extends AbstractClientEntity implements JpaRootEntity {
|
||||
public class JpaClientEntity extends AbstractClientEntity implements JpaRootVersionedEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
|||
import org.keycloak.models.map.clientscope.MapClientScopeEntity.AbstractClientScopeEntity;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_CLIENT_SCOPE;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
|
||||
/**
|
||||
|
@ -54,7 +54,7 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
|||
@Entity
|
||||
@Table(name = "kc_client_scope", uniqueConstraints = {@UniqueConstraint(columnNames = {"realmId", "name"})})
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaClientScopeEntity extends AbstractClientScopeEntity implements JpaRootEntity {
|
||||
public class JpaClientScopeEntity extends AbstractClientScopeEntity implements JpaRootVersionedEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
|
|
|
@ -41,7 +41,7 @@ import org.hibernate.annotations.TypeDefs;
|
|||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.group.MapGroupEntity.AbstractGroupEntity;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_GROUP;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +52,7 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
|||
@Entity
|
||||
@Table(name = "kc_group", uniqueConstraints = {@UniqueConstraint(columnNames = {"realmId", "name", "parentId"})})
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaGroupEntity extends AbstractGroupEntity implements JpaRootEntity {
|
||||
public class JpaGroupEntity extends AbstractGroupEntity implements JpaRootVersionedEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
|
|
|
@ -22,17 +22,22 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTH_SESSION;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_CLIENT;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_CLIENT_SCOPE;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_GROUP;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ROLE;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaAuthenticationSessionMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.group.entity.JpaGroupMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaAuthenticationSessionMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaClientMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaClientScopeMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaGroupMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRoleMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRootAuthenticationSessionMigration;
|
||||
import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleMetadata;
|
||||
|
||||
|
||||
|
@ -41,10 +46,12 @@ public class JpaEntityMigration {
|
|||
|
||||
static final Map<Class<?>, BiFunction<ObjectNode, Integer, ObjectNode>> MIGRATIONS = new HashMap<>();
|
||||
static {
|
||||
MIGRATIONS.put(JpaClientMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT, tree, JpaClientMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientScopeMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT_SCOPE, tree, JpaClientScopeMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaGroupMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_GROUP, tree, JpaGroupMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaRoleMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_ROLE, tree, JpaRoleMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaAuthenticationSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_AUTH_SESSION, tree, JpaAuthenticationSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaRootAuthenticationSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_AUTH_SESSION, tree, JpaRootAuthenticationSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT, tree, JpaClientMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientScopeMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT_SCOPE, tree, JpaClientScopeMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaGroupMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_GROUP, tree, JpaGroupMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaRoleMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_ROLE, tree, JpaRoleMigration.MIGRATORS));
|
||||
}
|
||||
|
||||
private static ObjectNode migrateTreeTo(int entityVersion, Integer supportedVersion, ObjectNode node, List<Function<ObjectNode, ObjectNode>> migrators) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.jsonb.migration;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class JpaAuthenticationSessionMigration {
|
||||
|
||||
public static final List<Function<ObjectNode, ObjectNode>> MIGRATORS = Arrays.asList(
|
||||
o -> o // no migration yet
|
||||
);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.jsonb.migration;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class JpaRootAuthenticationSessionMigration {
|
||||
|
||||
public static final List<Function<ObjectNode, ObjectNode>> MIGRATORS = Arrays.asList(
|
||||
o -> o // no migration yet
|
||||
);
|
||||
}
|
|
@ -41,7 +41,7 @@ import org.hibernate.annotations.TypeDefs;
|
|||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.role.MapRoleEntity.AbstractRoleEntity;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ROLE;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +52,7 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
|||
@Entity
|
||||
@Table(name = "kc_role", uniqueConstraints = {@UniqueConstraint(columnNames = {"realmId", "clientId", "name"})})
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootEntity {
|
||||
public class JpaRoleEntity extends AbstractRoleEntity implements JpaRootVersionedEntity {
|
||||
|
||||
@Id
|
||||
@Column
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
|
||||
|
||||
<!-- format of id of changeSet: auth-sessions-${org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTH_SESSION} -->
|
||||
<changeSet author="keycloak" id="auth-sessions-1">
|
||||
|
||||
<createTable tableName="kc_auth_root_session">
|
||||
<column name="id" type="UUID">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="version" type="INTEGER" defaultValueNumeric="0">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="metadata" type="json"/>
|
||||
</createTable>
|
||||
<ext:addGeneratedColumn tableName="kc_auth_root_session">
|
||||
<ext:column name="entityversion" type="INTEGER" jsonColumn="metadata" jsonProperty="entityVersion"/>
|
||||
<ext:column name="realmid" type="VARCHAR(36)" jsonColumn="metadata" jsonProperty="fRealmId"/>
|
||||
<ext:column name="timestamp" type="INTEGER" jsonColumn="metadata" jsonProperty="fTimestamp"/>
|
||||
<ext:column name="expiration" type="BIGINT" jsonColumn="metadata" jsonProperty="fExpiration"/>
|
||||
</ext:addGeneratedColumn>
|
||||
<createIndex tableName="kc_auth_root_session" indexName="auth_root_session_entityVersion">
|
||||
<column name="entityversion"/>
|
||||
</createIndex>
|
||||
<createIndex tableName="kc_auth_root_session" indexName="auth_root_session_realmId_expiration">
|
||||
<column name="realmid"/>
|
||||
<column name="expiration"/>
|
||||
</createIndex>
|
||||
|
||||
<createTable tableName="kc_auth_session">
|
||||
<column name="id" type="UUID">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="fk_root" type="UUID">
|
||||
<constraints foreignKeyName="auth_session_fk_root_fkey" references="kc_auth_root_session(id)" deleteCascade="true"/>
|
||||
</column>
|
||||
<column name="metadata" type="json"/>
|
||||
</createTable>
|
||||
<createIndex tableName="kc_auth_session" indexName="auth_session_fk_root">
|
||||
<column name="fk_root"/>
|
||||
</createIndex>
|
||||
<ext:createJsonIndex tableName="kc_auth_session" indexName="auth_session_tabId">
|
||||
<ext:column jsonColumn="metadata" jsonProperty="fTabId"/>
|
||||
</ext:createJsonIndex>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<!-- format of id of changelog file names: jpa-auth-sessions-changelog-${org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTH_SESSION}.xml -->
|
||||
<include file="META-INF/auth-sessions/jpa-auth-sessions-changelog-1.xml"/>
|
||||
</databaseChangeLog>
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
|
||||
<persistence-unit name="keycloak-jpa-default">
|
||||
<!--auth-sessions-->
|
||||
<class>org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionEntity</class>
|
||||
<class>org.keycloak.models.map.storage.jpa.authSession.entity.JpaAuthenticationSessionEntity</class>
|
||||
<!--client-scopes-->
|
||||
<class>org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeEntity</class>
|
||||
<class>org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeAttributeEntity</class>
|
||||
|
|
|
@ -118,3 +118,7 @@ log4j.logger.org.apache.directory.server.ldap.LdapProtocolHandler=error
|
|||
#log4j.logger.org.keycloak.services.clientpolicy=trace
|
||||
|
||||
#log4j.logger.org.keycloak.STACK_TRACE=trace
|
||||
|
||||
# Enable logs the SQL statements
|
||||
#log4j.logger.org.hibernate.SQL=debug
|
||||
#log4j.logger.org.hibernate.type=trace
|
||||
|
|
Loading…
Reference in a new issue