JPA map storage: User / client session no-downtime store (#12241)
Co-authored-by: Alexander Schwartz <alexander.schwartz@gmx.net> Closes #9666
This commit is contained in:
parent
84ac2a2ba4
commit
ec853a6b83
84 changed files with 1519 additions and 144 deletions
|
@ -16,8 +16,6 @@
|
|||
*/
|
||||
package org.keycloak.models.map.storage.jpa;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
|
||||
public interface Constants {
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_ADMIN_EVENT = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_AUTH_EVENT = 1;
|
||||
|
@ -28,6 +26,7 @@ public interface Constants {
|
|||
public static final Integer CURRENT_SCHEMA_VERSION_AUTHZ_RESOURCE_SERVER = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_AUTHZ_SCOPE = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_CLIENT = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_CLIENT_SESSION = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_CLIENT_SCOPE = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_COMPONENT = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_GROUP = 1;
|
||||
|
@ -39,5 +38,5 @@ public interface Constants {
|
|||
public static final Integer CURRENT_SCHEMA_VERSION_USER = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_USER_CONSENT = 1;
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_USER_FEDERATED_IDENTITY = 1;
|
||||
|
||||
public static final Integer CURRENT_SCHEMA_VERSION_USER_SESSION = 1;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaDelete;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
|
@ -32,6 +34,7 @@ import javax.persistence.criteria.Selection;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.common.ExpirableEntity;
|
||||
import org.keycloak.models.map.common.StringKeyConverter;
|
||||
|
@ -40,7 +43,7 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
|||
import org.keycloak.models.map.storage.QueryParameters;
|
||||
import org.keycloak.models.map.storage.chm.MapFieldPredicates;
|
||||
import org.keycloak.models.map.storage.chm.MapModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.utils.LockObjectsForModification;
|
||||
|
||||
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||
import static org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory.CLONER;
|
||||
|
@ -50,12 +53,14 @@ import static org.keycloak.utils.StreamsUtil.closing;
|
|||
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 KeycloakSession session;
|
||||
private final Class<RE> entityType;
|
||||
private final Class<M> modelType;
|
||||
private final boolean isExpirableEntity;
|
||||
protected EntityManager em;
|
||||
|
||||
public JpaMapKeycloakTransaction(Class<RE> entityType, Class<M> modelType, EntityManager em) {
|
||||
public JpaMapKeycloakTransaction(KeycloakSession session, Class<RE> entityType, Class<M> modelType, EntityManager em) {
|
||||
this.session = session;
|
||||
this.em = em;
|
||||
this.entityType = entityType;
|
||||
this.modelType = modelType;
|
||||
|
@ -67,6 +72,15 @@ public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E exte
|
|||
protected abstract JpaModelCriteriaBuilder createJpaModelCriteriaBuilder();
|
||||
protected abstract E mapToEntityDelegate(RE original);
|
||||
|
||||
/**
|
||||
* Indicates of pessimistic locking should be allowed for this entity. This should be enabled only for those entities
|
||||
* where there is no expected contention from different callers. For UserSessions and ClientSessions this should be possible.
|
||||
* The locking on Clients would be problematic as such a lock affects multiple callers.
|
||||
*/
|
||||
protected boolean lockingSupportedForEntity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private final HashMap<String, E> cacheWithinSession = new HashMap<>();
|
||||
|
||||
/**
|
||||
|
@ -101,7 +115,11 @@ public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E exte
|
|||
if (key == null) return null;
|
||||
UUID uuid = StringKeyConverter.UUIDKey.INSTANCE.fromStringSafe(key);
|
||||
if (uuid == null) return null;
|
||||
E e = mapToEntityDelegateUnique(em.find(entityType, uuid));
|
||||
E e = mapToEntityDelegateUnique(
|
||||
lockingSupportedForEntity() && LockObjectsForModification.isEnabled(session) ?
|
||||
em.find(entityType, uuid, LockModeType.PESSIMISTIC_WRITE) :
|
||||
em.find(entityType, uuid)
|
||||
);
|
||||
return e != null && isExpirableEntity && isExpired((ExpirableEntity) e, true) ? null : e;
|
||||
}
|
||||
|
||||
|
@ -142,7 +160,11 @@ public abstract class JpaMapKeycloakTransaction<RE extends JpaRootEntity, E exte
|
|||
}
|
||||
if (predicateFunc != null) query.where(predicateFunc.apply(cb, query::subquery, root));
|
||||
|
||||
return closing(paginateQuery(em.createQuery(query), queryParameters.getOffset(), queryParameters.getLimit()).getResultStream())
|
||||
TypedQuery<RE> emQuery = em.createQuery(query);
|
||||
if (lockingSupportedForEntity() && LockObjectsForModification.isEnabled(session)) {
|
||||
emQuery = emQuery.setLockMode(LockModeType.PESSIMISTIC_WRITE);
|
||||
}
|
||||
return closing(paginateQuery(emQuery, queryParameters.getOffset(), queryParameters.getLimit()).getResultStream())
|
||||
.map(this::mapToEntityDelegateUnique);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public class JpaMapStorageProvider implements MapStorageProvider {
|
|||
return new MapStorage<V, M>() {
|
||||
@Override
|
||||
public MapKeycloakTransaction<V, M> createTransaction(KeycloakSession session) {
|
||||
return factory.createTransaction(modelType, em);
|
||||
return factory.createTransaction(session, modelType, em);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
|
@ -59,6 +59,7 @@ import org.keycloak.component.AmphibianProviderFactory;
|
|||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.ActionTokenValueModel;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
|
@ -68,6 +69,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.dblock.DBLockProvider;
|
||||
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
||||
import org.keycloak.models.map.client.MapProtocolMapperEntityImpl;
|
||||
|
@ -131,6 +133,9 @@ import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity;
|
|||
import org.keycloak.models.map.storage.jpa.singleUseObject.JpaSingleUseObjectMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.singleUseObject.entity.JpaSingleUseObjectEntity;
|
||||
import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProvider;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.JpaUserSessionMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.entity.JpaClientSessionEntity;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.entity.JpaUserSessionEntity;
|
||||
import org.keycloak.models.map.storage.jpa.user.JpaUserMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserConsentEntity;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserEntity;
|
||||
|
@ -203,9 +208,12 @@ public class JpaMapStorageProviderFactory implements
|
|||
.constructor(JpaUserConsentEntity.class, JpaUserConsentEntity::new)
|
||||
.constructor(JpaUserFederatedIdentityEntity.class, JpaUserFederatedIdentityEntity::new)
|
||||
.constructor(MapUserCredentialEntity.class, MapUserCredentialEntityImpl::new)
|
||||
//user/client session
|
||||
.constructor(JpaClientSessionEntity.class, JpaClientSessionEntity::new)
|
||||
.constructor(JpaUserSessionEntity.class, JpaUserSessionEntity::new)
|
||||
.build();
|
||||
|
||||
private static final Map<Class<?>, Function<EntityManager, MapKeycloakTransaction>> MODEL_TO_TX = new HashMap<>();
|
||||
private static final Map<Class<?>, BiFunction<KeycloakSession, EntityManager, MapKeycloakTransaction>> MODEL_TO_TX = new HashMap<>();
|
||||
static {
|
||||
//auth-sessions
|
||||
MODEL_TO_TX.put(RootAuthenticationSessionModel.class, JpaRootAuthenticationSessionMapKeycloakTransaction::new);
|
||||
|
@ -234,6 +242,8 @@ public class JpaMapStorageProviderFactory implements
|
|||
MODEL_TO_TX.put(UserLoginFailureModel.class, JpaUserLoginFailureMapKeycloakTransaction::new);
|
||||
//users
|
||||
MODEL_TO_TX.put(UserModel.class, JpaUserMapKeycloakTransaction::new);
|
||||
//sessions
|
||||
MODEL_TO_TX.put(UserSessionModel.class, JpaUserSessionMapKeycloakTransaction::new);
|
||||
}
|
||||
|
||||
public JpaMapStorageProviderFactory() {
|
||||
|
@ -242,8 +252,8 @@ public class JpaMapStorageProviderFactory implements
|
|||
this.sessionTxKey = SESSION_TX_PREFIX + index;
|
||||
}
|
||||
|
||||
public MapKeycloakTransaction createTransaction(Class<?> modelType, EntityManager em) {
|
||||
return MODEL_TO_TX.get(modelType).apply(em);
|
||||
public MapKeycloakTransaction createTransaction(KeycloakSession session, Class<?> modelType, EntityManager em) {
|
||||
return MODEL_TO_TX.get(modelType).apply(session, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -358,17 +368,6 @@ public class JpaMapStorageProviderFactory implements
|
|||
)
|
||||
);
|
||||
|
||||
Integer isolation = config.getInt("isolation");
|
||||
if (isolation != null) {
|
||||
if (isolation < Connection.TRANSACTION_REPEATABLE_READ) {
|
||||
logger.warn("Concurrent requests may not be reliable with transaction level lower than TRANSACTION_REPEATABLE_READ.");
|
||||
}
|
||||
properties.put(AvailableSettings.ISOLATION, String.valueOf(isolation));
|
||||
} else {
|
||||
// default value is TRANSACTION_READ_COMMITTED
|
||||
logger.warn("Concurrent requests may not be reliable with transaction level lower than TRANSACTION_REPEATABLE_READ.");
|
||||
}
|
||||
|
||||
logger.trace("Creating EntityManagerFactory");
|
||||
this.emf = Persistence.createEntityManagerFactory(unitName, properties);
|
||||
logger.trace("EntityManagerFactory created");
|
||||
|
|
|
@ -35,7 +35,6 @@ import org.keycloak.models.map.common.StringKeyConverter;
|
|||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.storage.jpa.role;
|
||||
package org.keycloak.models.map.storage.jpa;
|
||||
|
||||
import org.keycloak.models.map.storage.jpa.JpaSubqueryProvider;
|
||||
|
|
@ -20,7 +20,6 @@ import javax.persistence.EntityTransaction;
|
|||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.jpa.PersistenceExceptionConverter;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.connections.jpa;
|
||||
package org.keycloak.models.map.storage.jpa;
|
||||
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.keycloak.models.Constants;
|
|
@ -20,6 +20,8 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
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;
|
||||
|
@ -33,8 +35,8 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|||
public class JpaRootAuthenticationSessionMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaRootAuthenticationSessionEntity, MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaRootAuthenticationSessionMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaRootAuthenticationSessionEntity.class, RootAuthenticationSessionModel.class, em);
|
||||
public JpaRootAuthenticationSessionMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaRootAuthenticationSessionEntity.class, RootAuthenticationSessionModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,8 +18,8 @@ package org.keycloak.models.map.storage.jpa.authSession;
|
|||
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel.SearchableFields;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
|
|
@ -36,19 +36,12 @@ 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.JpaRootVersionedEntity;
|
||||
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")
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntityDelegate;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.authorization.permission.entity.JpaPe
|
|||
public class JpaPermissionMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaPermissionEntity, MapPermissionTicketEntity, PermissionTicket> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaPermissionMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaPermissionEntity.class, PermissionTicket.class, em);
|
||||
public JpaPermissionMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaPermissionEntity.class, PermissionTicket.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,9 +27,9 @@ import org.keycloak.models.map.common.StringKeyConverter.UUIDKey;
|
|||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.authorization.permission.entity.JpaPermissionEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaPermissionModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaPermissionEntity, PermissionTicket, JpaPermissionModelCriteriaBuilder> {
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntityDelegate;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.authorization.policy.entity.JpaPolicy
|
|||
public class JpaPolicyMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaPolicyEntity, MapPolicyEntity, Policy> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaPolicyMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaPolicyEntity.class, Policy.class, em);
|
||||
public JpaPolicyMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaPolicyEntity.class, Policy.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,10 +30,10 @@ import org.keycloak.models.map.common.StringKeyConverter.UUIDKey;
|
|||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.authorization.policy.entity.JpaPolicyConfigEntity;
|
||||
import org.keycloak.models.map.storage.jpa.authorization.policy.entity.JpaPolicyEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaPolicyModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaPolicyEntity, Policy, JpaPolicyModelCriteriaBuilder> {
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntityDelegate;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.authorization.resource.entity.JpaReso
|
|||
public class JpaResourceMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaResourceEntity, MapResourceEntity, Resource> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaResourceMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaResourceEntity.class, Resource.class, em);
|
||||
public JpaResourceMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaResourceEntity.class, Resource.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.keycloak.models.map.common.StringKeyConverter.UUIDKey;
|
|||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.authorization.resource.entity.JpaResourceEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaResourceModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaResourceEntity, Resource, JpaResourceModelCriteriaBuilder> {
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntityDelegate;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.authorization.resourceServer.entity.J
|
|||
public class JpaResourceServerMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaResourceServerEntity, MapResourceServerEntity, ResourceServer> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaResourceServerMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaResourceServerEntity.class, ResourceServer.class, em);
|
||||
public JpaResourceServerMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaResourceServerEntity.class, ResourceServer.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
import org.keycloak.authorization.model.ResourceServer.SearchableFields;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.authorization.resourceServer.entity.JpaResourceServerEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaResourceServerModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaResourceServerEntity, ResourceServer, JpaResourceServerModelCriteriaBuilder> {
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntityDelegate;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.authorization.scope.entity.JpaScopeEn
|
|||
public class JpaScopeMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaScopeEntity, MapScopeEntity, Scope> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaScopeMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaScopeEntity.class, Scope.class, em);
|
||||
public JpaScopeMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaScopeEntity.class, Scope.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,8 +27,8 @@ import org.keycloak.models.map.common.StringKeyConverter.UUIDKey;
|
|||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.authorization.scope.entity.JpaScopeEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaScopeModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaScopeEntity, Scope, JpaScopeModelCriteriaBuilder> {
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.client.MapClientEntity;
|
||||
import org.keycloak.models.map.client.MapClientEntityDelegate;
|
||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientEntity;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.client.delegate.JpaClientDelegateProv
|
|||
public class JpaClientMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaClientEntity, MapClientEntity, ClientModel> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaClientMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaClientEntity.class, ClientModel.class, em);
|
||||
public JpaClientMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaClientEntity.class, ClientModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,10 +23,10 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.ClientModel.SearchableFields;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientEntity;
|
||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientAttributeEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientAttributeEntity;
|
||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientEntity;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaClientModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaClientEntity, ClientModel, JpaClientModelCriteriaBuilder> {
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeEntity;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeEntityDelegate;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_CLIENT_SCOPE;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeEnti
|
|||
public class JpaClientScopeMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaClientScopeEntity, MapClientScopeEntity, ClientScopeModel> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaClientScopeMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaClientScopeEntity.class, ClientScopeModel.class, em);
|
||||
public JpaClientScopeMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaClientScopeEntity.class, ClientScopeModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.keycloak.models.ClientScopeModel;
|
|||
import org.keycloak.models.ClientScopeModel.SearchableFields;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaClientScopeModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaClientScopeEntity, ClientScopeModel, JpaClientScopeModelCriteriaBuilder> {
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.persistence.criteria.Root;
|
|||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.events.MapAdminEventEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
|
@ -37,8 +38,8 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
*/
|
||||
public class JpaAdminEventMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaAdminEventEntity, MapAdminEventEntity, AdminEvent> {
|
||||
|
||||
public JpaAdminEventMapKeycloakTransaction(final EntityManager em) {
|
||||
super(JpaAdminEventEntity.class, AdminEvent.class, em);
|
||||
public JpaAdminEventMapKeycloakTransaction(KeycloakSession session, final EntityManager em) {
|
||||
super(session, JpaAdminEventEntity.class, AdminEvent.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,8 +26,8 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.event.admin.entity.JpaAdminEventEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
import org.keycloak.util.EnumWithStableIndex;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.persistence.criteria.Root;
|
|||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.events.MapAuthEventEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
|
@ -37,8 +38,8 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
*/
|
||||
public class JpaAuthEventMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaAuthEventEntity, MapAuthEventEntity, Event> {
|
||||
|
||||
public JpaAuthEventMapKeycloakTransaction(final EntityManager em) {
|
||||
super(JpaAuthEventEntity.class, Event.class, em);
|
||||
public JpaAuthEventMapKeycloakTransaction(KeycloakSession session, final EntityManager em) {
|
||||
super(session, JpaAuthEventEntity.class, Event.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,8 +26,8 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import org.keycloak.events.Event;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.event.auth.entity.JpaAuthEventEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
import org.keycloak.util.EnumWithStableIndex;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.group.MapGroupEntity;
|
||||
import org.keycloak.models.map.group.MapGroupEntityDelegate;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_GROUP;
|
||||
|
@ -33,8 +34,8 @@ import org.keycloak.models.map.storage.jpa.JpaRootEntity;
|
|||
public class JpaGroupMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaGroupEntity, MapGroupEntity, GroupModel> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaGroupMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaGroupEntity.class, GroupModel.class, em);
|
||||
public JpaGroupMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaGroupEntity.class, GroupModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,8 +25,8 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.group.entity.JpaGroupEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaGroupModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaGroupEntity, GroupModel, JpaGroupModelCriteriaBuilder> {
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaAdminEve
|
|||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaAuthEventMigration;
|
||||
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.JpaClientSessionMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaClientScopeMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaComponentMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaGroupMigration;
|
||||
|
@ -55,6 +56,7 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaUserCons
|
|||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaUserFederatedIdentityMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaUserLoginFailureMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaUserMigration;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaUserSessionMigration;
|
||||
import org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaComponentMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmMetadata;
|
||||
|
@ -63,6 +65,8 @@ import org.keycloak.models.map.storage.jpa.singleUseObject.entity.JpaSingleUseOb
|
|||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserConsentMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserFederatedIdentityMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.entity.JpaClientSessionMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.entity.JpaUserSessionMetadata;
|
||||
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ADMIN_EVENT;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_AUTHZ_PERMISSION;
|
||||
|
@ -74,6 +78,7 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
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_CLIENT_SESSION;
|
||||
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_REALM;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ROLE;
|
||||
|
@ -82,6 +87,7 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_CONSENT;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_FEDERATED_IDENTITY;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_SESSION;
|
||||
|
||||
public class JpaEntityMigration {
|
||||
|
||||
|
@ -110,6 +116,9 @@ public class JpaEntityMigration {
|
|||
MIGRATIONS.put(JpaRealmMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_REALM, tree, JpaRealmMigration.MIGRATORS));
|
||||
//roles
|
||||
MIGRATIONS.put(JpaRoleMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_ROLE, tree, JpaRoleMigration.MIGRATORS));
|
||||
//sessions
|
||||
MIGRATIONS.put(JpaClientSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT_SESSION, tree, JpaClientSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaUserSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_USER_SESSION, tree, JpaUserSessionMigration.MIGRATORS));
|
||||
//single-use-objects
|
||||
MIGRATIONS.put(JpaSingleUseObjectMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_SINGLE_USE_OBJECT, tree, JpaSingleUseObjectMigration.MIGRATORS));
|
||||
//user-login-failures
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
public class JpaClientSessionMigration {
|
||||
|
||||
public static final List<Function<ObjectNode, ObjectNode>> MIGRATORS = Arrays.asList(
|
||||
o -> o // no migration yet
|
||||
);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
public class JpaUserSessionMigration {
|
||||
|
||||
public static final List<Function<ObjectNode, ObjectNode>> MIGRATORS = Arrays.asList(
|
||||
o -> o // no migration yet
|
||||
);
|
||||
}
|
|
@ -26,6 +26,7 @@ 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.JpaRootVersionedEntity;
|
||||
|
||||
import javax.persistence.LockModeType;
|
||||
import java.util.Objects;
|
||||
|
@ -50,6 +51,9 @@ public class JpaOptimisticLockingListener implements PreInsertEventListener, Pre
|
|||
Objects.requireNonNull(root, "children must always return their parent, never null");
|
||||
}
|
||||
|
||||
// do not lock if root doesn't implement implicit optimistic locking mechanism
|
||||
if (! (root instanceof JpaRootVersionedEntity)) return;
|
||||
|
||||
// a session would not contain the entity if it has been deleted
|
||||
// if the entity has been deleted JPA would throw an IllegalArgumentException with the message
|
||||
// "entity not in the persistence context".
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntityDelegate;
|
||||
|
@ -40,8 +41,8 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
public class JpaUserLoginFailureMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaUserLoginFailureEntity, MapUserLoginFailureEntity, UserLoginFailureModel> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaUserLoginFailureMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaUserLoginFailureEntity.class, UserLoginFailureModel.class, em);
|
||||
public JpaUserLoginFailureMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaUserLoginFailureEntity.class, UserLoginFailureModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,8 +19,8 @@ package org.keycloak.models.map.storage.jpa.loginFailure;
|
|||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||
import org.keycloak.models.map.realm.MapRealmEntityDelegate;
|
||||
|
@ -39,8 +40,8 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
*/
|
||||
public class JpaRealmMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaRealmEntity, MapRealmEntity, RealmModel> {
|
||||
|
||||
public JpaRealmMapKeycloakTransaction(final EntityManager em) {
|
||||
super(JpaRealmEntity.class, RealmModel.class, em);
|
||||
public JpaRealmMapKeycloakTransaction(KeycloakSession session, final EntityManager em) {
|
||||
super(session, JpaRealmEntity.class, RealmModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,9 +21,9 @@ import javax.persistence.criteria.JoinType;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.map.role.MapRoleEntity;
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ROLE;
|
||||
|
@ -31,8 +33,9 @@ import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity;
|
|||
|
||||
public class JpaRoleMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaRoleEntity, MapRoleEntity, RoleModel> {
|
||||
|
||||
public JpaRoleMapKeycloakTransaction(EntityManager em) {
|
||||
super(JpaRoleEntity.class, RoleModel.class, em);
|
||||
@SuppressWarnings("unchecked")
|
||||
public JpaRoleMapKeycloakTransaction(KeycloakSession session, EntityManager em) {
|
||||
super(session, JpaRoleEntity.class, RoleModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.RoleModel.SearchableFields;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleCompositeEntity;
|
||||
import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.persistence.criteria.Root;
|
|||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.ActionTokenValueModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectEntity;
|
||||
import org.keycloak.models.map.storage.jpa.JpaMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
|
@ -37,8 +38,8 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
*/
|
||||
public class JpaSingleUseObjectMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaSingleUseObjectEntity, MapSingleUseObjectEntity, ActionTokenValueModel> {
|
||||
|
||||
public JpaSingleUseObjectMapKeycloakTransaction(final EntityManager em) {
|
||||
super(JpaSingleUseObjectEntity.class, ActionTokenValueModel.class, em);
|
||||
public JpaSingleUseObjectMapKeycloakTransaction(KeycloakSession session, final EntityManager em) {
|
||||
super(session, JpaSingleUseObjectEntity.class, ActionTokenValueModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,16 +18,11 @@ package org.keycloak.models.map.storage.jpa.singleUseObject;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.keycloak.models.ActionTokenValueModel;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.singleUseObject.entity.JpaSingleUseObjectEntity;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.map.storage.jpa.JpaMapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
|
@ -39,8 +40,8 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI
|
|||
*/
|
||||
public class JpaUserMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaUserEntity, MapUserEntity, UserModel> {
|
||||
|
||||
public JpaUserMapKeycloakTransaction(final EntityManager em) {
|
||||
super(JpaUserEntity.class, UserModel.class, em);
|
||||
public JpaUserMapKeycloakTransaction(KeycloakSession session,final EntityManager em) {
|
||||
super(session, JpaUserEntity.class, UserModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,8 +26,8 @@ import javax.persistence.criteria.JoinType;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.storage.jpa.role.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserAttributeEntity;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserConsentEntity;
|
||||
import org.keycloak.models.map.storage.jpa.user.entity.JpaUserEntity;
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.userSession;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
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.userSession.entity.JpaUserSessionEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||
|
||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_SESSION;
|
||||
|
||||
public class JpaUserSessionMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaUserSessionEntity, MapUserSessionEntity, UserSessionModel> {
|
||||
|
||||
public JpaUserSessionMapKeycloakTransaction(KeycloakSession session, final EntityManager em) {
|
||||
super(session, JpaUserSessionEntity.class, UserSessionModel.class, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Selection<? extends JpaUserSessionEntity> selectCbConstruct(CriteriaBuilder cb, Root<JpaUserSessionEntity> root) {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setEntityVersion(JpaRootEntity entity) {
|
||||
entity.setEntityVersion(CURRENT_SCHEMA_VERSION_USER_SESSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JpaModelCriteriaBuilder createJpaModelCriteriaBuilder() {
|
||||
return new JpaUserSessionModelCriteriaBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapUserSessionEntity mapToEntityDelegate(JpaUserSessionEntity original) {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean lockingSupportedForEntity() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.userSession;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.UserSessionModel.SearchableFields;
|
||||
import org.keycloak.models.map.common.StringKeyConverter.UUIDKey;
|
||||
import org.keycloak.models.map.storage.CriterionNotSupportedException;
|
||||
import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.jpa.JpaPredicateFunction;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.entity.JpaUserSessionEntity;
|
||||
import org.keycloak.models.map.storage.jpa.userSession.entity.JpaUserSessionNoteEntity;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
public class JpaUserSessionModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaUserSessionEntity, UserSessionModel, JpaUserSessionModelCriteriaBuilder> {
|
||||
|
||||
public JpaUserSessionModelCriteriaBuilder() {
|
||||
super(JpaUserSessionModelCriteriaBuilder::new);
|
||||
}
|
||||
|
||||
private JpaUserSessionModelCriteriaBuilder(JpaPredicateFunction<JpaUserSessionEntity> predicateFunc) {
|
||||
super(JpaUserSessionModelCriteriaBuilder::new, predicateFunc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaUserSessionModelCriteriaBuilder compare(SearchableModelField<? super UserSessionModel> modelField, Operator op, Object... value) {
|
||||
switch(op) {
|
||||
case EQ:
|
||||
if (modelField == SearchableFields.ID) {
|
||||
|
||||
validateValue(value, modelField, op, String.class);
|
||||
|
||||
return new JpaUserSessionModelCriteriaBuilder((cb, query, root) -> {
|
||||
UUID uuid = UUIDKey.INSTANCE.fromStringSafe(Objects.toString(value[0], null));
|
||||
if (uuid == null) return cb.or();
|
||||
return cb.equal(root.get(modelField.getName()), uuid);
|
||||
});
|
||||
} else if (modelField == SearchableFields.REALM_ID ||
|
||||
modelField == SearchableFields.USER_ID ||
|
||||
modelField == SearchableFields.BROKER_USER_ID ||
|
||||
modelField == SearchableFields.BROKER_SESSION_ID) {
|
||||
|
||||
validateValue(value, modelField, op, String.class);
|
||||
|
||||
return new JpaUserSessionModelCriteriaBuilder((cb, query, root) ->
|
||||
cb.equal(root.get(modelField.getName()), value[0])
|
||||
);
|
||||
} else if (modelField == SearchableFields.IS_OFFLINE) {
|
||||
|
||||
validateValue(value, modelField, op, Boolean.class);
|
||||
|
||||
return new JpaUserSessionModelCriteriaBuilder((cb, query, root) ->
|
||||
cb.equal(root.get("offline"), value[0])
|
||||
);
|
||||
} else if (modelField == SearchableFields.CORRESPONDING_SESSION_ID) {
|
||||
|
||||
validateValue(value, modelField, op, String.class);
|
||||
|
||||
return new JpaUserSessionModelCriteriaBuilder((cb, query, root) -> {
|
||||
Join<JpaUserSessionEntity, JpaUserSessionNoteEntity> join = root.join("notes", JoinType.LEFT);
|
||||
return cb.and(
|
||||
cb.equal(join.get("name"), UserSessionModel.CORRESPONDING_SESSION_ID),
|
||||
cb.equal(join.get("value"), value[0])
|
||||
);
|
||||
});
|
||||
} else if (modelField == SearchableFields.CLIENT_ID) {
|
||||
|
||||
validateValue(value, modelField, op, String.class);
|
||||
|
||||
return new JpaUserSessionModelCriteriaBuilder((cb, query, root) -> {
|
||||
return cb.equal(root.join("clientSessions", JoinType.LEFT).get("clientId"), value[0]);
|
||||
});
|
||||
} else {
|
||||
throw new CriterionNotSupportedException(modelField, op);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new CriterionNotSupportedException(modelField, op);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* 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.userSession.entity;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
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.common.DeepCloner;
|
||||
import org.keycloak.models.map.common.UuidValidator;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity.AbstractAuthenticatedClientSessionEntity;
|
||||
|
||||
/**
|
||||
* Entity represents authenticated client session.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kc_client_session")
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaClientSessionEntity extends AbstractAuthenticatedClientSessionEntity 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 JpaClientSessionMetadata metadata;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Integer entityVersion;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String clientId;
|
||||
|
||||
@OneToMany(mappedBy = "root", cascade = CascadeType.PERSIST, orphanRemoval = true)
|
||||
private final Set<JpaClientSessionNoteEntity> notes = new HashSet<>();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "fk_root")
|
||||
private JpaUserSessionEntity root;
|
||||
|
||||
/**
|
||||
* No-argument constructor, used by hibernate to instantiate entities.
|
||||
*/
|
||||
public JpaClientSessionEntity() {
|
||||
this.metadata = new JpaClientSessionMetadata();
|
||||
}
|
||||
|
||||
public JpaClientSessionEntity(DeepCloner cloner) {
|
||||
this.metadata = new JpaClientSessionMetadata(cloner);
|
||||
}
|
||||
|
||||
public void setParent(JpaUserSessionEntity root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getEntityVersion() {
|
||||
return metadata.getEntityVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityVersion(Integer entityVersion) {
|
||||
metadata.setEntityVersion(entityVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCurrentSchemaVersion() {
|
||||
return Constants.CURRENT_SCHEMA_VERSION_CLIENT_SESSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
String validatedId = UuidValidator.validateAndConvert(id);
|
||||
this.id = UUID.fromString(validatedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealmId() {
|
||||
return metadata.getRealmId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRealmId(String realmId) {
|
||||
metadata.setRealmId(realmId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return metadata.getClientId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientId(String clientId) {
|
||||
metadata.setClientId(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthMethod() {
|
||||
return metadata.getAuthMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthMethod(String authMethod) {
|
||||
metadata.setAuthMethod(authMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return metadata.getRedirectUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
metadata.setRedirectUri(redirectUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getTimestamp() {
|
||||
return metadata.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(Long timestamp) {
|
||||
metadata.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpiration() {
|
||||
return metadata.getExpiration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiration(Long expiration) {
|
||||
metadata.setExpiration(expiration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAction() {
|
||||
return metadata.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(String action) {
|
||||
metadata.setAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentRefreshToken() {
|
||||
return metadata.getCurrentRefreshToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentRefreshToken(String currentRefreshToken) {
|
||||
metadata.setCurrentRefreshToken(currentRefreshToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCurrentRefreshTokenUseCount() {
|
||||
return metadata.getCurrentRefreshTokenUseCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentRefreshTokenUseCount(Integer currentRefreshTokenUseCount) {
|
||||
metadata.setCurrentRefreshTokenUseCount(currentRefreshTokenUseCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isOffline() {
|
||||
return metadata.isOffline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffline(Boolean offline) {
|
||||
metadata.setOffline(offline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getNotes() {
|
||||
return notes.stream().collect(Collectors.toMap(JpaClientSessionNoteEntity::getName, JpaClientSessionNoteEntity::getValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.notes.clear();
|
||||
if (notes == null) return;
|
||||
for (Map.Entry<String, String> entry : notes.entrySet()) {
|
||||
setNote(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNote(String name) {
|
||||
return notes.stream()
|
||||
.filter(obj -> Objects.equals(obj.getName(), name))
|
||||
.findFirst()
|
||||
.map(JpaClientSessionNoteEntity::getValue)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean removeNote(String name) {
|
||||
return notes.removeIf(obj -> Objects.equals(obj.getName(), name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNote(String name, String value) {
|
||||
removeNote(name);
|
||||
if (name == null || value == null || value.trim().isEmpty()) return;
|
||||
notes.add(new JpaClientSessionNoteEntity(this, name, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof JpaClientSessionEntity)) return false;
|
||||
return Objects.equals(getId(), ((JpaClientSessionEntity) obj).getId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.userSession.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntityImpl;
|
||||
|
||||
public class JpaClientSessionMetadata extends MapAuthenticatedClientSessionEntityImpl implements Serializable {
|
||||
|
||||
public JpaClientSessionMetadata(DeepCloner cloner) {
|
||||
super(cloner);
|
||||
}
|
||||
|
||||
public JpaClientSessionMetadata() {
|
||||
super();
|
||||
}
|
||||
|
||||
private Integer entityVersion;
|
||||
|
||||
public Integer getEntityVersion() {
|
||||
return entityVersion;
|
||||
}
|
||||
|
||||
public void setEntityVersion(Integer entityVersion) {
|
||||
this.entityVersion = entityVersion;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.userSession.entity;
|
||||
|
||||
import java.util.Objects;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
|
||||
|
||||
@Entity
|
||||
@Table(name = "kc_client_session_note")
|
||||
public class JpaClientSessionNoteEntity extends JpaAttributeEntity<JpaClientSessionEntity> {
|
||||
|
||||
public JpaClientSessionNoteEntity() {
|
||||
}
|
||||
|
||||
public JpaClientSessionNoteEntity(JpaClientSessionEntity root, String name, String value) {
|
||||
super(root, name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof JpaClientSessionNoteEntity)) return false;
|
||||
JpaClientSessionNoteEntity that = (JpaClientSessionNoteEntity) obj;
|
||||
return Objects.equals(getParent(), that.getParent()) &&
|
||||
Objects.equals(getName(), that.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* 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.userSession.entity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
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.UserSessionModel;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.common.UuidValidator;
|
||||
import org.keycloak.models.map.storage.jpa.Constants;
|
||||
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity.AbstractUserSessionEntity;
|
||||
|
||||
import static org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory.CLONER;
|
||||
|
||||
/**
|
||||
* 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_user_session")
|
||||
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||
public class JpaUserSessionEntity extends AbstractUserSessionEntity 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 JpaUserSessionMetadata 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 String userId;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String brokerSessionId;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String brokerUserId;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Boolean offline;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Long lastSessionRefresh;
|
||||
|
||||
@Column(insertable = false, updatable = false)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private Long expiration;
|
||||
|
||||
@OneToMany(mappedBy = "root", cascade = CascadeType.PERSIST, orphanRemoval = true)
|
||||
private final Set<JpaUserSessionNoteEntity> notes = new HashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "root", cascade = CascadeType.PERSIST, orphanRemoval = true)
|
||||
private final Set<JpaClientSessionEntity> clientSessions = new HashSet<>();
|
||||
|
||||
/**
|
||||
* No-argument constructor, used by hibernate to instantiate entities.
|
||||
*/
|
||||
public JpaUserSessionEntity() {
|
||||
this.metadata = new JpaUserSessionMetadata();
|
||||
}
|
||||
|
||||
public JpaUserSessionEntity(DeepCloner cloner) {
|
||||
this.metadata = new JpaUserSessionMetadata(cloner);
|
||||
}
|
||||
|
||||
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 Constants.CURRENT_SCHEMA_VERSION_USER_SESSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
String validatedId = UuidValidator.validateAndConvert(id);
|
||||
this.id = UUID.fromString(validatedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealmId() {
|
||||
if (isMetadataInitialized()) return metadata.getRealmId();
|
||||
return realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRealmId(String realmId) {
|
||||
metadata.setRealmId(realmId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserId() {
|
||||
if (isMetadataInitialized()) return metadata.getUserId();
|
||||
return userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserId(String userId) {
|
||||
metadata.setUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginUsername() {
|
||||
return metadata.getLoginUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginUsername(String loginUsername) {
|
||||
metadata.setLoginUsername(loginUsername);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpAddress() {
|
||||
return metadata.getIpAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIpAddress(String ipAddress) {
|
||||
metadata.setIpAddress(ipAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthMethod() {
|
||||
return metadata.getAuthMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthMethod(String authMethod) {
|
||||
metadata.setAuthMethod(authMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isOffline() {
|
||||
if (isMetadataInitialized()) return metadata.isOffline();
|
||||
return offline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffline(Boolean offline) {
|
||||
metadata.setOffline(offline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isRememberMe() {
|
||||
return metadata.isRememberMe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRememberMe(Boolean rememberMe) {
|
||||
metadata.setRememberMe(rememberMe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getTimestamp() {
|
||||
return metadata.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(Long timestamp) {
|
||||
metadata.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastSessionRefresh() {
|
||||
if (isMetadataInitialized()) return metadata.getLastSessionRefresh();
|
||||
return lastSessionRefresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastSessionRefresh(Long lastSessionRefresh) {
|
||||
metadata.setLastSessionRefresh(lastSessionRefresh);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpiration() {
|
||||
if (isMetadataInitialized()) return metadata.getExpiration();
|
||||
return expiration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiration(Long expiration) {
|
||||
metadata.setExpiration(expiration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionModel.State getState() {
|
||||
return metadata.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(UserSessionModel.State state) {
|
||||
metadata.setState(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionModel.SessionPersistenceState getPersistenceState() {
|
||||
return UserSessionModel.SessionPersistenceState.PERSISTENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState) {
|
||||
// no-op: each non-transient user session (stored in the db) has PERSISTENT state
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getNotes() {
|
||||
return Collections.unmodifiableMap(notes.stream().collect(Collectors.toMap(JpaUserSessionNoteEntity::getName, JpaUserSessionNoteEntity::getValue)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNote(String name) {
|
||||
return notes.stream()
|
||||
.filter(obj -> Objects.equals(obj.getName(), name))
|
||||
.findFirst()
|
||||
.map(JpaUserSessionNoteEntity::getValue)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.notes.clear();
|
||||
if (notes == null) return;
|
||||
for (Map.Entry<String, String> entry : notes.entrySet()) {
|
||||
setNote(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean removeNote(String name) {
|
||||
return notes.removeIf(obj -> Objects.equals(obj.getName(), name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNote(String name, String value) {
|
||||
removeNote(name);
|
||||
if (name == null || value == null || value.trim().isEmpty()) return;
|
||||
notes.add(new JpaUserSessionNoteEntity(this, name, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBrokerSessionId() {
|
||||
if (isMetadataInitialized()) return metadata.getBrokerSessionId();
|
||||
return brokerSessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrokerSessionId(String brokerSessionId) {
|
||||
metadata.setBrokerSessionId(brokerSessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBrokerUserId() {
|
||||
if (isMetadataInitialized()) return metadata.getBrokerUserId();
|
||||
return brokerUserId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrokerUserId(String brokerUserId) {
|
||||
metadata.setBrokerUserId(brokerUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MapAuthenticatedClientSessionEntity> getAuthenticatedClientSessions() {
|
||||
return clientSessions.stream().map(MapAuthenticatedClientSessionEntity.class::cast).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAuthenticatedClientSession(MapAuthenticatedClientSessionEntity clientSession) {
|
||||
JpaClientSessionEntity jpaClientSession = JpaClientSessionEntity.class.cast(CLONER.from(clientSession));
|
||||
jpaClientSession.setParent(this);
|
||||
jpaClientSession.setEntityVersion(this.getEntityVersion());
|
||||
clientSessions.add(jpaClientSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MapAuthenticatedClientSessionEntity> getAuthenticatedClientSession(String clientUUID) {
|
||||
return clientSessions.stream().filter(cs -> Objects.equals(cs.getClientId(), clientUUID)).findFirst().map(MapAuthenticatedClientSessionEntity.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean removeAuthenticatedClientSession(String clientUUID) {
|
||||
return clientSessions.removeIf(cs -> Objects.equals(cs.getClientId(), clientUUID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAuthenticatedClientSessions() {
|
||||
clientSessions.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof JpaUserSessionEntity)) return false;
|
||||
return Objects.equals(getId(), ((JpaUserSessionEntity) obj).getId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.userSession.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.keycloak.models.map.common.DeepCloner;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntityImpl;
|
||||
|
||||
public class JpaUserSessionMetadata extends MapUserSessionEntityImpl implements Serializable {
|
||||
|
||||
public JpaUserSessionMetadata(DeepCloner cloner) {
|
||||
super(cloner);
|
||||
}
|
||||
|
||||
public JpaUserSessionMetadata() {
|
||||
super();
|
||||
}
|
||||
|
||||
private Integer entityVersion;
|
||||
|
||||
public Integer getEntityVersion() {
|
||||
return entityVersion;
|
||||
}
|
||||
|
||||
public void setEntityVersion(Integer entityVersion) {
|
||||
this.entityVersion = entityVersion;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.userSession.entity;
|
||||
|
||||
import java.util.Objects;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
|
||||
|
||||
@Entity
|
||||
@Table(name = "kc_user_session_note")
|
||||
public class JpaUserSessionNoteEntity extends JpaAttributeEntity<JpaUserSessionEntity> {
|
||||
|
||||
public JpaUserSessionNoteEntity() {
|
||||
}
|
||||
|
||||
public JpaUserSessionNoteEntity(JpaUserSessionEntity root, String name, String value) {
|
||||
super(root, name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof JpaUserSessionNoteEntity)) return false;
|
||||
JpaUserSessionNoteEntity that = (JpaUserSessionNoteEntity) obj;
|
||||
return Objects.equals(getParent(), that.getParent()) &&
|
||||
Objects.equals(getName(), that.getName());
|
||||
}
|
||||
}
|
|
@ -29,5 +29,6 @@ limitations under the License.
|
|||
<include file="META-INF/jpa-roles-changelog.xml"/>
|
||||
<include file="META-INF/jpa-single-use-objects-changelog.xml"/>
|
||||
<include file="META-INF/jpa-user-login-failures-changelog.xml"/>
|
||||
<include file="META-INF/jpa-user-sessions-changelog.xml"/>
|
||||
<include file="META-INF/jpa-users-changelog.xml"/>
|
||||
</databaseChangeLog>
|
|
@ -0,0 +1,22 @@
|
|||
<?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">
|
||||
<include file="META-INF/user-sessions/jpa-user-sessions-changelog-1.xml"/>
|
||||
</databaseChangeLog>
|
|
@ -38,6 +38,11 @@
|
|||
<class>org.keycloak.models.map.storage.jpa.singleUseObject.entity.JpaSingleUseObjectNoteEntity</class>
|
||||
<!--user-login-failures-->
|
||||
<class>org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureEntity</class>
|
||||
<!--user-sessions-->
|
||||
<class>org.keycloak.models.map.storage.jpa.userSession.entity.JpaClientSessionEntity</class>
|
||||
<class>org.keycloak.models.map.storage.jpa.userSession.entity.JpaClientSessionNoteEntity</class>
|
||||
<class>org.keycloak.models.map.storage.jpa.userSession.entity.JpaUserSessionEntity</class>
|
||||
<class>org.keycloak.models.map.storage.jpa.userSession.entity.JpaUserSessionNoteEntity</class>
|
||||
<!--users-->
|
||||
<class>org.keycloak.models.map.storage.jpa.user.entity.JpaUserEntity</class>
|
||||
<class>org.keycloak.models.map.storage.jpa.user.entity.JpaUserAttributeEntity</class>
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
<?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">
|
||||
|
||||
<changeSet author="keycloak" id="user-sessions-1">
|
||||
|
||||
<createTable tableName="kc_user_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_user_session">
|
||||
<ext:column name="entityversion" type="INTEGER" jsonColumn="metadata" jsonProperty="entityVersion"/>
|
||||
<ext:column name="offline" type="BOOLEAN" jsonColumn="metadata" jsonProperty="fOffline"/>
|
||||
<ext:column name="realmid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fRealmId"/>
|
||||
<ext:column name="userid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fUserId"/>
|
||||
<ext:column name="brokersessionid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fBrokerSessionId"/>
|
||||
<ext:column name="brokeruserid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fBrokerUserId"/>
|
||||
<ext:column name="lastsessionrefresh" type="BIGINT" jsonColumn="metadata" jsonProperty="fLastSessionRefresh"/>
|
||||
<ext:column name="expiration" type="BIGINT" jsonColumn="metadata" jsonProperty="fExpiration"/>
|
||||
</ext:addGeneratedColumn>
|
||||
<createIndex tableName="kc_user_session" indexName="user_session_entityVersion">
|
||||
<column name="entityversion"/>
|
||||
</createIndex>
|
||||
<createIndex tableName="kc_user_session" indexName="user_session_realm_offline_user">
|
||||
<column name="realmid"/>
|
||||
<column name="offline"/>
|
||||
<column name="userid"/>
|
||||
</createIndex>
|
||||
|
||||
<createTable tableName="kc_user_session_note">
|
||||
<column name="id" type="UUID">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="fk_root" type="UUID">
|
||||
<constraints foreignKeyName="user_session_note_fk_root_fkey" references="kc_user_session(id)" deleteCascade="true"/>
|
||||
</column>
|
||||
<column name="name" type="VARCHAR(255)"/>
|
||||
<column name="value" type="TEXT"/>
|
||||
</createTable>
|
||||
<createIndex tableName="kc_user_session_note" indexName="kc_user_session_note_fk_root_name_key">
|
||||
<column name="fk_root"/>
|
||||
<column name="name"/>
|
||||
</createIndex>
|
||||
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="keycloak" id="user-sessions-2" dbms="postgresql">
|
||||
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
|
||||
<!-- this will not work on cockroachdb as deferred indexes are not supported in version 22.1 yet, therefore, only run it on postgresql -->
|
||||
<!-- see https://go.crdb.dev/issue-v/31632/v21.2 for the current status of the implementation -->
|
||||
<addUniqueConstraint tableName="kc_user_session_note" columnNames="fk_root, name" deferrable="true" initiallyDeferred="true" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="keycloak" id="client-sessions-1">
|
||||
|
||||
<createTable tableName="kc_client_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="fk_root" type="UUID">
|
||||
<constraints foreignKeyName="client_session_fk_root_fkey" references="kc_user_session(id)" deleteCascade="true"/>
|
||||
</column>
|
||||
<column name="metadata" type="json"/>
|
||||
</createTable>
|
||||
<ext:addGeneratedColumn tableName="kc_client_session">
|
||||
<ext:column name="entityversion" type="INTEGER" jsonColumn="metadata" jsonProperty="entityVersion"/>
|
||||
<ext:column name="clientid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fClientId"/>
|
||||
</ext:addGeneratedColumn>
|
||||
<createIndex tableName="kc_client_session" indexName="client_session_entityVersion">
|
||||
<column name="entityversion"/>
|
||||
</createIndex>
|
||||
<createIndex tableName="kc_client_session" indexName="client_session_fk_root">
|
||||
<column name="fk_root"/>
|
||||
</createIndex>
|
||||
|
||||
<createTable tableName="kc_client_session_note">
|
||||
<column name="id" type="UUID">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="fk_root" type="UUID">
|
||||
<constraints foreignKeyName="client_session_fk_root_fkey" references="kc_client_session(id)" deleteCascade="true"/>
|
||||
</column>
|
||||
<column name="name" type="VARCHAR(255)"/>
|
||||
<column name="value" type="TEXT"/>
|
||||
</createTable>
|
||||
<createIndex tableName="kc_client_session_note" indexName="kc_client_session_note_fk_root_name_key">
|
||||
<column name="fk_root"/>
|
||||
<column name="name"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="keycloak" id="client-sessions-2" dbms="postgresql">
|
||||
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
|
||||
<!-- this will not work on cockroachdb as deferred indexes are not supported in version 22.1 yet, therefore, only run it on postgresql -->
|
||||
<!-- see https://go.crdb.dev/issue-v/31632/v21.2 for the current status of the implementation -->
|
||||
<addUniqueConstraint tableName="kc_client_session_note" columnNames="fk_root, name" deferrable="true" initiallyDeferred="true" />
|
||||
</changeSet>
|
||||
|
||||
|
||||
</databaseChangeLog>
|
|
@ -23,7 +23,6 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.ActionTokenValueModel;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
|
@ -49,7 +48,6 @@ import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
|||
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||
import org.keycloak.models.map.role.MapRoleEntity;
|
||||
import org.keycloak.models.map.user.MapUserEntity;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import java.util.HashMap;
|
||||
|
@ -67,7 +65,6 @@ public class ModelEntityUtil {
|
|||
private static final Map<Class<?>, String> MODEL_TO_NAME = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_NAME.put(ActionTokenValueModel.class, "single-use-objects");
|
||||
MODEL_TO_NAME.put(AuthenticatedClientSessionModel.class, "client-sessions");
|
||||
MODEL_TO_NAME.put(ClientScopeModel.class, "client-scopes");
|
||||
MODEL_TO_NAME.put(ClientModel.class, "clients");
|
||||
MODEL_TO_NAME.put(GroupModel.class, "groups");
|
||||
|
@ -94,7 +91,6 @@ public class ModelEntityUtil {
|
|||
private static final Map<Class<?>, Class<? extends AbstractEntity>> MODEL_TO_ENTITY_TYPE = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_ENTITY_TYPE.put(ActionTokenValueModel.class, MapSingleUseObjectEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(AuthenticatedClientSessionModel.class, MapAuthenticatedClientSessionEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(ClientScopeModel.class, MapClientScopeEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(ClientModel.class, MapClientEntity.class);
|
||||
MODEL_TO_ENTITY_TYPE.put(GroupModel.class, MapGroupEntity.class);
|
||||
|
|
|
@ -181,6 +181,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
LOG.tracef("getUserSession(%s, %s)%s", realm, id, getShortStackTrace());
|
||||
|
||||
if (id == null) return null;
|
||||
|
||||
MapUserSessionEntity userSessionEntity = transientUserSessions.get(id);
|
||||
if (userSessionEntity != null) {
|
||||
return userEntityToAdapterFunc(realm).apply(userSessionEntity);
|
||||
|
@ -565,6 +567,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
private MapUserSessionEntity getUserSessionById(String id) {
|
||||
if (id == null) return null;
|
||||
|
||||
MapUserSessionEntity userSessionEntity = transientUserSessions.get(id);
|
||||
|
||||
if (userSessionEntity == null) {
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.utils;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* This flags the session that all information loaded from the stores should be locked as the service layer
|
||||
* plans to modify it.
|
||||
*
|
||||
* This is just a hint to the underlying storage, and a store might choose to ignore it.
|
||||
* The lock for any object retrieved from the session will be kept until the end of the transaction.
|
||||
*
|
||||
* If the store supports it, this could prevent exceptions due to optimistic locking
|
||||
* problems later in the processing. If the caller retrieved objects without this wrapper, they would still be
|
||||
* able to modify those objects, and those changes would be written to the store at the end of the transaction at the lastet,
|
||||
* but they won't be locked.
|
||||
*
|
||||
*
|
||||
* @author Alexander Schwartz
|
||||
*/
|
||||
public class LockObjectsForModification {
|
||||
|
||||
private static final String ATTRIBUTE = LockObjectsForModification.class.getCanonicalName();
|
||||
|
||||
public static LockObjectsForModification.Enabled enable(KeycloakSession session) {
|
||||
return new Enabled(session);
|
||||
}
|
||||
|
||||
public static boolean isEnabled(KeycloakSession session) {
|
||||
return session.getAttribute(ATTRIBUTE) != null;
|
||||
}
|
||||
|
||||
public static <V> V lockObjectsForModification(KeycloakSession session, CallableWithoutThrowingAnException<V> callable) {
|
||||
if (LockObjectsForModification.isEnabled(session)) {
|
||||
// If someone nests the call, and it would already be locked, don't try to lock it a second time.
|
||||
// Otherwise, the inner unlocking might also unlock the outer lock.
|
||||
return callable.call();
|
||||
}
|
||||
try (LockObjectsForModification.Enabled ignored = LockObjectsForModification.enable(session)) {
|
||||
return callable.call();
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CallableWithoutThrowingAnException<V> {
|
||||
/**
|
||||
* Computes a result.
|
||||
*
|
||||
* @return computed result
|
||||
*/
|
||||
V call();
|
||||
}
|
||||
|
||||
public static class Enabled implements AutoCloseable {
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
||||
public Enabled(KeycloakSession session) {
|
||||
this.session = session;
|
||||
session.setAttribute(ATTRIBUTE, Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
session.removeAttribute(ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ package org.keycloak.models;
|
|||
import java.util.Map;
|
||||
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
|
|
@ -67,6 +67,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -1066,7 +1068,7 @@ public class AuthenticationProcessor {
|
|||
|
||||
if (userSession == null) { // if no authenticator attached a usersession
|
||||
|
||||
userSession = session.sessions().getUserSession(realm, authSession.getParentSession().getId());
|
||||
userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, authSession.getParentSession().getId()));
|
||||
if (userSession == null) {
|
||||
UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.fromString(authSession.getClientNote(AuthenticationManager.USER_SESSION_PERSISTENT_STATE));
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.utils.StringUtil;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
public class UserSessionLimitsAuthenticator implements Authenticator {
|
||||
|
||||
private static Logger logger = Logger.getLogger(UserSessionLimitsAuthenticator.class);
|
||||
|
@ -51,7 +53,7 @@ public class UserSessionLimitsAuthenticator implements Authenticator {
|
|||
if (context.getRealm() != null && context.getUser() != null) {
|
||||
|
||||
// Get the session count in this realm for this specific user
|
||||
List<UserSessionModel> userSessionsForRealm = session.sessions().getUserSessionsStream(context.getRealm(), context.getUser()).collect(Collectors.toList());
|
||||
List<UserSessionModel> userSessionsForRealm = lockObjectsForModification(session, () -> session.sessions().getUserSessionsStream(context.getRealm(), context.getUser()).collect(Collectors.toList()));
|
||||
int userSessionCountForRealm = userSessionsForRealm.size();
|
||||
|
||||
// Get the session count related to the current client for this user
|
||||
|
|
|
@ -95,6 +95,8 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -311,7 +313,7 @@ public class AuthorizationTokenService {
|
|||
userSessionModel = sessions.createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), request.getClientConnection().getRemoteAddr(),
|
||||
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
|
||||
} else {
|
||||
userSessionModel = sessions.getUserSession(realm, accessToken.getSessionState());
|
||||
userSessionModel = lockObjectsForModification(keycloakSession, () -> sessions.getUserSession(realm, accessToken.getSessionState()));
|
||||
|
||||
if (userSessionModel == null) {
|
||||
userSessionModel = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
||||
|
|
|
@ -46,6 +46,8 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -118,7 +120,7 @@ public class KeycloakIdentity implements Identity {
|
|||
this.accessToken = AccessToken.class.cast(token);
|
||||
} else {
|
||||
UserSessionProvider sessions = keycloakSession.sessions();
|
||||
UserSessionModel userSession = sessions.getUserSession(realm, token.getSessionState());
|
||||
UserSessionModel userSession = lockObjectsForModification(keycloakSession, () -> sessions.getUserSession(realm, token.getSessionState()));
|
||||
|
||||
if (userSession == null) {
|
||||
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
||||
|
@ -284,7 +286,7 @@ public class KeycloakIdentity implements Identity {
|
|||
}
|
||||
|
||||
UserSessionProvider sessions = keycloakSession.sessions();
|
||||
UserSessionModel userSession = sessions.getUserSession(realm, accessToken.getSessionState());
|
||||
UserSessionModel userSession = lockObjectsForModification(keycloakSession, () -> sessions.getUserSession(realm, accessToken.getSessionState()));
|
||||
|
||||
if (userSession == null) {
|
||||
userSession = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
||||
|
|
|
@ -45,6 +45,8 @@ import javax.ws.rs.core.MultivaluedMap;
|
|||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -100,7 +102,7 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
|||
if (action.getKeycloakSessionIds() != null) {
|
||||
for (String sessionId : action.getKeycloakSessionIds()) {
|
||||
String brokerSessionId = getConfig().getAlias() + "." + sessionId;
|
||||
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId));
|
||||
if (userSession != null
|
||||
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
|
||||
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
|
||||
|
|
|
@ -72,6 +72,8 @@ import javax.ws.rs.core.UriInfo;
|
|||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
|
@ -336,7 +338,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
|||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
|
||||
}
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, state);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, state));
|
||||
if (userSession == null) {
|
||||
logger.error("no valid user session");
|
||||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||
|
|
|
@ -120,6 +120,8 @@ import java.util.Collections;
|
|||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.xml.crypto.dsig.XMLSignature;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -329,7 +331,7 @@ public class SAMLEndpoint {
|
|||
} else {
|
||||
for (String sessionIndex : request.getSessionIndex()) {
|
||||
String brokerSessionId = config.getAlias() + "." + sessionIndex;
|
||||
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId));
|
||||
if (userSession != null) {
|
||||
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
|
||||
continue;
|
||||
|
@ -669,7 +671,7 @@ public class SAMLEndpoint {
|
|||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, relayState));
|
||||
if (userSession == null) {
|
||||
logger.error("no valid user session");
|
||||
event.event(EventType.LOGOUT);
|
||||
|
|
|
@ -99,6 +99,7 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import static org.keycloak.representations.IDToken.NONCE;
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* Stateless object that creates tokens and manages oauth access codes
|
||||
|
@ -146,7 +147,7 @@ public class TokenManager {
|
|||
}
|
||||
} else {
|
||||
// Find userSession regularly for online tokens
|
||||
userSession = session.sessions().getUserSession(realm, oldToken.getSessionState());
|
||||
userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, oldToken.getSessionState()));
|
||||
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
|
||||
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
|
||||
|
@ -300,10 +301,10 @@ public class TokenManager {
|
|||
private boolean validateTokenReuseForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token) {
|
||||
UserSessionModel userSession = null;
|
||||
if (token.getType().equals(TokenUtil.TOKEN_TYPE_REFRESH)) {
|
||||
userSession = session.sessions().getUserSession(realm, token.getSessionState());
|
||||
userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, token.getSessionState()));
|
||||
} else {
|
||||
UserSessionManager sessionManager = new UserSessionManager(session);
|
||||
userSession = sessionManager.findOfflineUserSession(realm, token.getSessionState());
|
||||
userSession = lockObjectsForModification(session, () -> sessionManager.findOfflineUserSession(realm, token.getSessionState()));
|
||||
}
|
||||
|
||||
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.protocol.oidc.endpoints;
|
|||
import static org.keycloak.models.UserSessionModel.State.LOGGED_OUT;
|
||||
import static org.keycloak.models.UserSessionModel.State.LOGGING_OUT;
|
||||
import static org.keycloak.services.resources.LoginActionsService.SESSION_CODE;
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
|
@ -425,7 +426,7 @@ public class LogoutEndpoint {
|
|||
String idTokenIssuedAtStr = logoutSession.getAuthNote(OIDCLoginProtocol.LOGOUT_VALIDATED_ID_TOKEN_ISSUED_AT);
|
||||
if (userSessionIdFromIdToken != null && idTokenIssuedAtStr != null) {
|
||||
try {
|
||||
userSession = session.sessions().getUserSession(realm, userSessionIdFromIdToken);
|
||||
userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, userSessionIdFromIdToken));
|
||||
|
||||
if (userSession != null) {
|
||||
Integer idTokenIssuedAt = Integer.parseInt(idTokenIssuedAtStr);
|
||||
|
@ -439,7 +440,8 @@ public class LogoutEndpoint {
|
|||
}
|
||||
|
||||
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
|
||||
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
|
||||
AuthenticationManager.AuthResult authResult = lockObjectsForModification(session,
|
||||
() -> AuthenticationManager.authenticateIdentityCookie(session, realm, false));
|
||||
if (authResult != null) {
|
||||
userSession = userSession != null ? userSession : authResult.getSession();
|
||||
return initiateBrowserLogout(userSession);
|
||||
|
@ -517,7 +519,8 @@ public class LogoutEndpoint {
|
|||
UserSessionManager sessionManager = new UserSessionManager(session);
|
||||
userSessionModel = sessionManager.findOfflineUserSession(realm, token.getSessionState());
|
||||
} else {
|
||||
userSessionModel = session.sessions().getUserSession(realm, token.getSessionState());
|
||||
String sessionState = token.getSessionState();
|
||||
userSessionModel = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, sessionState));
|
||||
}
|
||||
|
||||
if (userSessionModel != null) {
|
||||
|
@ -617,8 +620,8 @@ public class LogoutEndpoint {
|
|||
AtomicReference<BackchannelLogoutResponse> backchannelLogoutResponse = new AtomicReference<>(new BackchannelLogoutResponse());
|
||||
backchannelLogoutResponse.get().setLocalLogoutSucceeded(true);
|
||||
identityProviderAliases.forEach(identityProviderAlias -> {
|
||||
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm,
|
||||
identityProviderAlias + "." + sessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSessionByBrokerSessionId(realm,
|
||||
identityProviderAlias + "." + sessionId));
|
||||
|
||||
if (logoutOfflineSessions) {
|
||||
if (offlineSessionsLazyLoadingEnabled) {
|
||||
|
|
|
@ -116,6 +116,8 @@ import java.util.Objects;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -506,7 +508,7 @@ public class TokenEndpoint {
|
|||
res = responseBuilder.build();
|
||||
|
||||
if (!responseBuilder.isOfflineToken()) {
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, res.getSessionState()));
|
||||
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||
updateClientSession(clientSession);
|
||||
updateUserSessionFromClientAuth(userSession);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.protocol.oidc.grants.device;
|
||||
|
||||
import static org.keycloak.protocol.oidc.OIDCLoginProtocolService.tokenServiceBaseUrl;
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
|
@ -278,7 +279,7 @@ public class DeviceGrantType {
|
|||
client.getId());
|
||||
|
||||
if (userSession == null) {
|
||||
userSession = session.sessions().getUserSession(realm, userSessionId);
|
||||
userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, userSessionId));
|
||||
if (userSession == null) {
|
||||
throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING,
|
||||
"The authorization request is verified but can not lookup the user session yet",
|
||||
|
|
|
@ -31,6 +31,8 @@ import org.keycloak.models.SingleUseObjectProvider;
|
|||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.services.managers.UserSessionCrossDCManager;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
|
@ -99,10 +101,10 @@ public class OAuth2CodeParser {
|
|||
}
|
||||
|
||||
// Retrieve UserSession
|
||||
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, clientUUID);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, clientUUID));
|
||||
if (userSession == null) {
|
||||
// Needed to track if code is invalid or was already used.
|
||||
userSession = session.sessions().getUserSession(realm, userSessionId);
|
||||
userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, userSessionId));
|
||||
if (userSession == null) {
|
||||
return result.illegalCode();
|
||||
}
|
||||
|
|
|
@ -145,6 +145,7 @@ import javax.ws.rs.core.MultivaluedMap;
|
|||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1143,7 +1144,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
return emptyArtifactResponseMessage(artifactResolveMessage, null);
|
||||
}
|
||||
|
||||
UserSessionModel userSessionModel = session.sessions().getUserSession(realm, sessionMapping.get(SamlProtocol.USER_SESSION_ID));
|
||||
UserSessionModel userSessionModel = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, sessionMapping.get(SamlProtocol.USER_SESSION_ID)));
|
||||
if (userSessionModel == null) {
|
||||
logger.errorf("UserSession with id: %s, that corresponds to artifact: %s does not exist.", sessionMapping.get(SamlProtocol.USER_SESSION_ID), artifact);
|
||||
return emptyArtifactResponseMessage(artifactResolveMessage, null);
|
||||
|
|
|
@ -114,6 +114,7 @@ import static org.keycloak.common.util.ServerCookie.SameSiteAttributeValue;
|
|||
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
|
||||
import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.isOAuth2DeviceVerificationFlow;
|
||||
import static org.keycloak.services.util.CookieHelper.getCookie;
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* Stateless object that manages authentication
|
||||
|
@ -223,7 +224,7 @@ public class AuthenticationManager {
|
|||
verifier.verifierContext(signatureVerifier);
|
||||
|
||||
AccessToken token = verifier.verify().getToken();
|
||||
UserSessionModel cookieSession = session.sessions().getUserSession(realm, token.getSessionState());
|
||||
UserSessionModel cookieSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, token.getSessionState()));
|
||||
if (cookieSession == null || !cookieSession.getId().equals(userSession.getId())) return true;
|
||||
expireIdentityCookie(realm, uriInfo, connection);
|
||||
return true;
|
||||
|
@ -309,9 +310,9 @@ public class AuthenticationManager {
|
|||
|
||||
// Check if "online" session still exists and remove it too
|
||||
String onlineUserSessionId = userSession.getNote(CORRESPONDING_SESSION_ID);
|
||||
UserSessionModel onlineUserSession = (onlineUserSessionId != null) ?
|
||||
UserSessionModel onlineUserSession = lockObjectsForModification(session, () -> (onlineUserSessionId != null) ?
|
||||
session.sessions().getUserSession(realm, onlineUserSessionId) :
|
||||
session.sessions().getUserSession(realm, userSession.getId());
|
||||
session.sessions().getUserSession(realm, userSession.getId()));
|
||||
|
||||
if (onlineUserSession != null) {
|
||||
session.sessions().removeUserSession(realm, onlineUserSession);
|
||||
|
@ -928,7 +929,7 @@ public class AuthenticationManager {
|
|||
if (split.length >= 3) {
|
||||
String oldSessionId = split[2];
|
||||
if (!oldSessionId.equals(userSession.getId())) {
|
||||
UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
|
||||
UserSessionModel oldSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, oldSessionId));
|
||||
if (oldSession != null) {
|
||||
logger.debugv("Removing old user session: session: {0}", oldSessionId);
|
||||
session.sessions().removeUserSession(realm, oldSession);
|
||||
|
|
|
@ -36,6 +36,8 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
|
@ -98,7 +100,7 @@ public class AuthenticationSessionManager {
|
|||
AuthSessionId authSessionId = decodeAuthSessionId(oldEncodedId);
|
||||
String sessionId = authSessionId.getDecodedId();
|
||||
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, sessionId));
|
||||
|
||||
if (userSession != null) {
|
||||
reencodeAuthSessionCookie(oldEncodedId, authSessionId, realm);
|
||||
|
@ -215,7 +217,7 @@ public class AuthenticationSessionManager {
|
|||
|
||||
// Check to see if we already have authenticationSession with same ID
|
||||
public UserSessionModel getUserSession(AuthenticationSessionModel authSession) {
|
||||
return session.sessions().getUserSession(authSession.getRealm(), authSession.getParentSession().getId());
|
||||
return lockObjectsForModification(session, () -> session.sessions().getUserSession(authSession.getRealm(), authSession.getParentSession().getId()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
|
@ -69,9 +71,9 @@ public class UserSessionCrossDCManager {
|
|||
String sessionId = authSessionId.getDecodedId();
|
||||
|
||||
// This will remove userSession "locally" if it doesn't exist on remoteCache
|
||||
kcSession.sessions().getUserSessionWithPredicate(realm, sessionId, false, (UserSessionModel userSession2) -> userSession2 == null);
|
||||
lockObjectsForModification(kcSession, () -> kcSession.sessions().getUserSessionWithPredicate(realm, sessionId, false, (UserSessionModel userSession2) -> userSession2 == null));
|
||||
|
||||
UserSessionModel userSession = kcSession.sessions().getUserSession(realm, sessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(kcSession, () -> kcSession.sessions().getUserSession(realm, sessionId));
|
||||
|
||||
if (userSession != null) {
|
||||
asm.reencodeAuthSessionCookie(oldEncodedId, authSessionId, realm);
|
||||
|
|
|
@ -43,6 +43,8 @@ import java.util.Objects;
|
|||
import java.util.function.Consumer;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
|
@ -121,7 +123,7 @@ public class LoginActionsServiceChecks {
|
|||
return;
|
||||
}
|
||||
|
||||
UserSessionModel userSession = context.getSession().sessions().getUserSession(context.getRealm(), authSessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(context.getSession(), () -> context.getSession().sessions().getUserSession(context.getRealm(), authSessionId));
|
||||
boolean hasNoRequiredActions =
|
||||
(userSession == null || userSession.getUser().getRequiredActionsStream().count() == 0)
|
||||
&&
|
||||
|
|
|
@ -47,6 +47,8 @@ import org.keycloak.representations.account.SessionRepresentation;
|
|||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -148,7 +150,7 @@ public class SessionResource {
|
|||
@NoCache
|
||||
public Response logout(@PathParam("id") String id) {
|
||||
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, id);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, id));
|
||||
if (userSession != null && userSession.getUser().equals(user)) {
|
||||
AuthenticationManager.backchannelLogout(session, userSession, true);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
import static org.keycloak.models.utils.StripSecretsUtils.stripForExport;
|
||||
import static org.keycloak.util.JsonSerialization.readValue;
|
||||
|
||||
|
@ -602,7 +603,7 @@ public class RealmAdminResource {
|
|||
public void deleteSession(@PathParam("session") String sessionId) {
|
||||
auth.users().requireManage();
|
||||
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(realm, sessionId));
|
||||
if (userSession == null) throw new NotFoundException("Sesssion not found");
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), connection, headers, true);
|
||||
adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_SESSION).resourcePath(session.getContext().getUri()).success();
|
||||
|
|
|
@ -116,6 +116,7 @@ import java.util.stream.Stream;
|
|||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
|
||||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
||||
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
||||
import static org.keycloak.utils.LockObjectsForModification.lockObjectsForModification;
|
||||
|
||||
/**
|
||||
* Base resource for managing users
|
||||
|
@ -313,7 +314,7 @@ public class UserResource {
|
|||
String sessionState = auth.adminAuth().getToken().getSessionState();
|
||||
if (authenticatedRealm.getId().equals(realm.getId()) && sessionState != null) {
|
||||
sameRealm = true;
|
||||
UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, sessionState);
|
||||
UserSessionModel userSession = lockObjectsForModification(session, () -> session.sessions().getUserSession(authenticatedRealm, sessionState));
|
||||
AuthenticationManager.expireIdentityCookie(realm, session.getContext().getUri(), clientConnection);
|
||||
AuthenticationManager.expireRememberMeCookie(realm, session.getContext().getUri(), clientConnection);
|
||||
AuthenticationManager.backchannelLogout(session, authenticatedRealm, userSession, session.getContext().getUri(), clientConnection, headers, true);
|
||||
|
|
|
@ -902,6 +902,7 @@
|
|||
<keycloak.role.map.storage.provider>jpa</keycloak.role.map.storage.provider>
|
||||
<keycloak.singleUseObject.map.storage.provider>jpa</keycloak.singleUseObject.map.storage.provider>
|
||||
<keycloak.user.map.storage.provider>jpa</keycloak.user.map.storage.provider>
|
||||
<keycloak.userSession.map.storage.provider>jpa</keycloak.userSession.map.storage.provider>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
|||
import org.keycloak.models.map.client.MapClientProviderFactory;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
|
||||
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
|
||||
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
||||
import org.keycloak.models.map.group.MapGroupProviderFactory;
|
||||
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
||||
|
@ -101,8 +102,8 @@ public class JpaMapStorage extends KeycloakModelParameters {
|
|||
.spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||
.spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||
.spi(EventStoreSpi.NAME).provider(MapEventStoreProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||
.config("storage-auth-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,6 @@ import static org.keycloak.testsuite.model.session.UserSessionPersisterProviderT
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
@RequireProvider(UserSessionPersisterProvider.class)
|
||||
@RequireProvider(UserSessionProvider.class)
|
||||
@RequireProvider(UserProvider.class)
|
||||
@RequireProvider(RealmProvider.class)
|
||||
|
|
Loading…
Reference in a new issue