parent
53ea60b8d5
commit
0c7a8c8684
13 changed files with 607 additions and 10 deletions
|
@ -23,4 +23,5 @@ public interface Constants {
|
||||||
public static final Integer CURRENT_SCHEMA_VERSION_GROUP = 1;
|
public static final Integer CURRENT_SCHEMA_VERSION_GROUP = 1;
|
||||||
public static final Integer CURRENT_SCHEMA_VERSION_REALM = 1;
|
public static final Integer CURRENT_SCHEMA_VERSION_REALM = 1;
|
||||||
public static final Integer CURRENT_SCHEMA_VERSION_ROLE = 1;
|
public static final Integer CURRENT_SCHEMA_VERSION_ROLE = 1;
|
||||||
|
public static final Integer CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
package org.keycloak.models.map.storage.jpa;
|
package org.keycloak.models.map.storage.jpa;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.models.map.common.AbstractEntity;
|
import org.keycloak.models.map.common.AbstractEntity;
|
||||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||||
import org.keycloak.models.map.storage.MapStorage;
|
import org.keycloak.models.map.storage.MapStorage;
|
||||||
|
@ -26,6 +29,8 @@ import org.keycloak.models.map.storage.MapStorageProviderFactory.Flag;
|
||||||
|
|
||||||
public class JpaMapStorageProvider implements MapStorageProvider {
|
public class JpaMapStorageProvider implements MapStorageProvider {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(JpaMapStorageProvider.class);
|
||||||
|
|
||||||
private final String SESSION_TX_PREFIX = "jpa-map-tx-";
|
private final String SESSION_TX_PREFIX = "jpa-map-tx-";
|
||||||
|
|
||||||
private final JpaMapStorageProviderFactory factory;
|
private final JpaMapStorageProviderFactory factory;
|
||||||
|
@ -46,6 +51,9 @@ public class JpaMapStorageProvider implements MapStorageProvider {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <V extends AbstractEntity, M> MapStorage<V, M> getStorage(Class<M> modelType, Flag... flags) {
|
public <V extends AbstractEntity, M> MapStorage<V, M> getStorage(Class<M> modelType, Flag... flags) {
|
||||||
|
if (modelType == UserLoginFailureModel.class) {
|
||||||
|
logger.warn("Enabling JPA storage for user login failures will result in testsuite failures until GHI #11230 is resolved");
|
||||||
|
}
|
||||||
factory.validateAndUpdateSchema(session, modelType);
|
factory.validateAndUpdateSchema(session, modelType);
|
||||||
return new MapStorage<V, M>() {
|
return new MapStorage<V, M>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -56,6 +56,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
import org.keycloak.models.dblock.DBLockProvider;
|
import org.keycloak.models.dblock.DBLockProvider;
|
||||||
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
||||||
import org.keycloak.models.map.client.MapProtocolMapperEntityImpl;
|
import org.keycloak.models.map.client.MapProtocolMapperEntityImpl;
|
||||||
|
@ -94,6 +95,8 @@ import org.keycloak.models.map.storage.jpa.group.JpaGroupMapKeycloakTransaction;
|
||||||
import org.keycloak.models.map.storage.jpa.group.entity.JpaGroupEntity;
|
import org.keycloak.models.map.storage.jpa.group.entity.JpaGroupEntity;
|
||||||
import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaEntityVersionListener;
|
import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaEntityVersionListener;
|
||||||
import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaOptimisticLockingListener;
|
import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaOptimisticLockingListener;
|
||||||
|
import org.keycloak.models.map.storage.jpa.loginFailure.JpaUserLoginFailureMapKeycloakTransaction;
|
||||||
|
import org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureEntity;
|
||||||
import org.keycloak.models.map.storage.jpa.realm.JpaRealmMapKeycloakTransaction;
|
import org.keycloak.models.map.storage.jpa.realm.JpaRealmMapKeycloakTransaction;
|
||||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaComponentEntity;
|
import org.keycloak.models.map.storage.jpa.realm.entity.JpaComponentEntity;
|
||||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmEntity;
|
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmEntity;
|
||||||
|
@ -129,7 +132,7 @@ public class JpaMapStorageProviderFactory implements
|
||||||
.constructor(JpaClientScopeEntity.class, JpaClientScopeEntity::new)
|
.constructor(JpaClientScopeEntity.class, JpaClientScopeEntity::new)
|
||||||
//group
|
//group
|
||||||
.constructor(JpaGroupEntity.class, JpaGroupEntity::new)
|
.constructor(JpaGroupEntity.class, JpaGroupEntity::new)
|
||||||
// realm
|
//realm
|
||||||
.constructor(JpaRealmEntity.class, JpaRealmEntity::new)
|
.constructor(JpaRealmEntity.class, JpaRealmEntity::new)
|
||||||
.constructor(JpaComponentEntity.class, JpaComponentEntity::new)
|
.constructor(JpaComponentEntity.class, JpaComponentEntity::new)
|
||||||
.constructor(MapAuthenticationExecutionEntity.class, MapAuthenticationExecutionEntityImpl::new)
|
.constructor(MapAuthenticationExecutionEntity.class, MapAuthenticationExecutionEntityImpl::new)
|
||||||
|
@ -144,6 +147,8 @@ public class JpaMapStorageProviderFactory implements
|
||||||
.constructor(MapWebAuthnPolicyEntity.class, MapWebAuthnPolicyEntityImpl::new)
|
.constructor(MapWebAuthnPolicyEntity.class, MapWebAuthnPolicyEntityImpl::new)
|
||||||
//role
|
//role
|
||||||
.constructor(JpaRoleEntity.class, JpaRoleEntity::new)
|
.constructor(JpaRoleEntity.class, JpaRoleEntity::new)
|
||||||
|
//user login-failure
|
||||||
|
.constructor(JpaUserLoginFailureEntity.class, JpaUserLoginFailureEntity::new)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final Map<Class<?>, Function<EntityManager, MapKeycloakTransaction>> MODEL_TO_TX = new HashMap<>();
|
private static final Map<Class<?>, Function<EntityManager, MapKeycloakTransaction>> MODEL_TO_TX = new HashMap<>();
|
||||||
|
@ -154,6 +159,7 @@ public class JpaMapStorageProviderFactory implements
|
||||||
MODEL_TO_TX.put(GroupModel.class, JpaGroupMapKeycloakTransaction::new);
|
MODEL_TO_TX.put(GroupModel.class, JpaGroupMapKeycloakTransaction::new);
|
||||||
MODEL_TO_TX.put(RealmModel.class, JpaRealmMapKeycloakTransaction::new);
|
MODEL_TO_TX.put(RealmModel.class, JpaRealmMapKeycloakTransaction::new);
|
||||||
MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new);
|
MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new);
|
||||||
|
MODEL_TO_TX.put(UserLoginFailureModel.class, JpaUserLoginFailureMapKeycloakTransaction::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapKeycloakTransaction createTransaction(Class<?> modelType, EntityManager em) {
|
public MapKeycloakTransaction createTransaction(Class<?> modelType, EntityManager em) {
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.models.map.storage.jpa.hibernate.jsonb;
|
package org.keycloak.models.map.storage.jpa.hibernate.jsonb;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaAuthenticationSessionMetadata;
|
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaAuthenticationSessionMetadata;
|
||||||
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionMetadata;
|
import org.keycloak.models.map.storage.jpa.authSession.entity.JpaRootAuthenticationSessionMetadata;
|
||||||
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientMetadata;
|
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientMetadata;
|
||||||
|
@ -35,6 +36,8 @@ import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaGroupMig
|
||||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRealmMigration;
|
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRealmMigration;
|
||||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRoleMigration;
|
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRoleMigration;
|
||||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRootAuthenticationSessionMigration;
|
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaRootAuthenticationSessionMigration;
|
||||||
|
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.migration.JpaUserLoginFailureMigration;
|
||||||
|
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.JpaComponentMetadata;
|
||||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmMetadata;
|
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmMetadata;
|
||||||
import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleMetadata;
|
import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleMetadata;
|
||||||
|
@ -45,6 +48,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_GROUP;
|
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_REALM;
|
||||||
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ROLE;
|
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ROLE;
|
||||||
|
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE;
|
||||||
|
|
||||||
public class JpaEntityMigration {
|
public class JpaEntityMigration {
|
||||||
|
|
||||||
|
@ -58,6 +62,7 @@ public class JpaEntityMigration {
|
||||||
MIGRATIONS.put(JpaGroupMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_GROUP, tree, JpaGroupMigration.MIGRATORS));
|
MIGRATIONS.put(JpaGroupMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_GROUP, tree, JpaGroupMigration.MIGRATORS));
|
||||||
MIGRATIONS.put(JpaRealmMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_REALM, tree, JpaRealmMigration.MIGRATORS));
|
MIGRATIONS.put(JpaRealmMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_REALM, tree, JpaRealmMigration.MIGRATORS));
|
||||||
MIGRATIONS.put(JpaRoleMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_ROLE, tree, JpaRoleMigration.MIGRATORS));
|
MIGRATIONS.put(JpaRoleMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_ROLE, tree, JpaRoleMigration.MIGRATORS));
|
||||||
|
MIGRATIONS.put(JpaUserLoginFailureMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE,tree, JpaUserLoginFailureMigration.MIGRATORS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectNode migrateTreeTo(int entityVersion, Integer supportedVersion, ObjectNode node, List<Function<ObjectNode, ObjectNode>> migrators) {
|
private static ObjectNode migrateTreeTo(int entityVersion, Integer supportedVersion, ObjectNode node, List<Function<ObjectNode, ObjectNode>> migrators) {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration functions for user login failures.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
|
||||||
|
*/
|
||||||
|
public class JpaUserLoginFailureMigration {
|
||||||
|
|
||||||
|
public static final List<Function<ObjectNode, ObjectNode>> MIGRATORS = Arrays.asList(
|
||||||
|
o -> o // no migration yet
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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.loginFailure;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Selection;
|
||||||
|
|
||||||
|
import org.keycloak.models.UserLoginFailureModel;
|
||||||
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||||
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntityDelegate;
|
||||||
|
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.loginFailure.delegate.JpaUserLoginFailureDelegateProvider;
|
||||||
|
import org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureEntity;
|
||||||
|
|
||||||
|
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link JpaMapKeycloakTransaction} implementation for user login failure entities.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
|
||||||
|
*/
|
||||||
|
public class JpaUserLoginFailureMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaUserLoginFailureEntity, MapUserLoginFailureEntity, UserLoginFailureModel> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public JpaUserLoginFailureMapKeycloakTransaction(EntityManager em) {
|
||||||
|
super(JpaUserLoginFailureEntity.class, em);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selection<JpaUserLoginFailureEntity> selectCbConstruct(CriteriaBuilder cb, Root<JpaUserLoginFailureEntity> root) {
|
||||||
|
return cb.construct(JpaUserLoginFailureEntity.class,
|
||||||
|
root.get("id"),
|
||||||
|
root.get("version"),
|
||||||
|
root.get("entityVersion"),
|
||||||
|
root.get("realmId"),
|
||||||
|
root.get("userId")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEntityVersion(JpaRootEntity entity) {
|
||||||
|
entity.setEntityVersion(CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JpaModelCriteriaBuilder createJpaModelCriteriaBuilder() {
|
||||||
|
return new JpaUserLoginFailureModelCriteriaBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MapUserLoginFailureEntity mapToEntityDelegate(JpaUserLoginFailureEntity original) {
|
||||||
|
return new MapUserLoginFailureEntityDelegate(new JpaUserLoginFailureDelegateProvider(original, em));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.loginFailure;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
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.loginFailure.entity.JpaUserLoginFailureEntity;
|
||||||
|
import org.keycloak.storage.SearchableModelField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link JpaModelCriteriaBuilder} implementation for user login failures.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
|
||||||
|
*/
|
||||||
|
public class JpaUserLoginFailureModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaUserLoginFailureEntity, UserLoginFailureModel, JpaUserLoginFailureModelCriteriaBuilder> {
|
||||||
|
|
||||||
|
public JpaUserLoginFailureModelCriteriaBuilder() {
|
||||||
|
super(JpaUserLoginFailureModelCriteriaBuilder::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JpaUserLoginFailureModelCriteriaBuilder(BiFunction<CriteriaBuilder, Root<JpaUserLoginFailureEntity>, Predicate> predicateFunc) {
|
||||||
|
super(JpaUserLoginFailureModelCriteriaBuilder::new, predicateFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JpaUserLoginFailureModelCriteriaBuilder compare(SearchableModelField<? super UserLoginFailureModel> modelField, Operator op, Object... value) {
|
||||||
|
switch (op) {
|
||||||
|
case EQ:
|
||||||
|
if (modelField.equals(UserLoginFailureModel.SearchableFields.REALM_ID) ||
|
||||||
|
modelField.equals(UserLoginFailureModel.SearchableFields.USER_ID)) {
|
||||||
|
|
||||||
|
validateValue(value, modelField, op, String.class);
|
||||||
|
|
||||||
|
return new JpaUserLoginFailureModelCriteriaBuilder((cb, root) ->
|
||||||
|
cb.equal(root.get(modelField.getName()), value[0])
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new CriterionNotSupportedException(modelField, op);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new CriterionNotSupportedException(modelField, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.loginFailure.delegate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.keycloak.models.map.common.EntityField;
|
||||||
|
import org.keycloak.models.map.common.delegate.DelegateProvider;
|
||||||
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||||
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntityFields;
|
||||||
|
import org.keycloak.models.map.storage.jpa.JpaDelegateProvider;
|
||||||
|
import org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DelegateProvider} implementation for {@link JpaUserLoginFailureEntity}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
|
||||||
|
*/
|
||||||
|
public class JpaUserLoginFailureDelegateProvider extends JpaDelegateProvider<JpaUserLoginFailureEntity> implements DelegateProvider<MapUserLoginFailureEntity> {
|
||||||
|
|
||||||
|
private final EntityManager em;
|
||||||
|
|
||||||
|
public JpaUserLoginFailureDelegateProvider(JpaUserLoginFailureEntity delegate, EntityManager em) {
|
||||||
|
super(delegate);
|
||||||
|
this.em = em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapUserLoginFailureEntity getDelegate(boolean isRead, Enum<? extends EntityField<MapUserLoginFailureEntity>> field, Object... parameters) {
|
||||||
|
if (getDelegate().isMetadataInitialized()) return getDelegate();
|
||||||
|
if (isRead) {
|
||||||
|
if (field instanceof MapUserLoginFailureEntityFields) {
|
||||||
|
switch ((MapUserLoginFailureEntityFields) field) {
|
||||||
|
case ID:
|
||||||
|
case REALM_ID:
|
||||||
|
case USER_ID:
|
||||||
|
return getDelegate();
|
||||||
|
default:
|
||||||
|
setDelegate(em.find(JpaUserLoginFailureEntity.class, UUID.fromString(getDelegate().getId())));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Not a valid user login failure field: " + field);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setDelegate(em.find(JpaUserLoginFailureEntity.class, UUID.fromString(getDelegate().getId())));
|
||||||
|
}
|
||||||
|
return getDelegate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* 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.loginFailure.entity;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.persistence.Basic;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
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.loginFailure.MapUserLoginFailureEntity;
|
||||||
|
import org.keycloak.models.map.storage.jpa.JpaRootVersionedEntity;
|
||||||
|
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||||
|
|
||||||
|
import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA {@link MapUserLoginFailureEntity} implementation. Some fields are annotated with {@code @Column(insertable = false, updatable = false)}
|
||||||
|
* to indicate that they are automatically generated from json fields. As such, these fields are non-insertable and non-updatable.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "kc_user_login_failure",
|
||||||
|
uniqueConstraints = {
|
||||||
|
@UniqueConstraint(
|
||||||
|
columnNames = {"realmId", "userId"}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@TypeDefs({@TypeDef(name = "jsonb", typeClass = JsonbType.class)})
|
||||||
|
public class JpaUserLoginFailureEntity extends MapUserLoginFailureEntity.AbstractUserLoginFailureEntity 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 JpaUserLoginFailureMetadata 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No-argument constructor, used by hibernate to instantiate entities.
|
||||||
|
*/
|
||||||
|
public JpaUserLoginFailureEntity() {
|
||||||
|
this.metadata = new JpaUserLoginFailureMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JpaUserLoginFailureEntity(DeepCloner cloner) {
|
||||||
|
this.metadata = new JpaUserLoginFailureMetadata(cloner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by hibernate when calling cb.construct from read(QueryParameters) method.
|
||||||
|
* It is used to select user login failure without metadata(json) field.
|
||||||
|
*/
|
||||||
|
public JpaUserLoginFailureEntity(UUID id, int version, Integer entityVersion, String realmId, String userId) {
|
||||||
|
this.id = id;
|
||||||
|
this.version = version;
|
||||||
|
this.entityVersion = entityVersion;
|
||||||
|
this.realmId = realmId;
|
||||||
|
this.userId = userId;
|
||||||
|
this.metadata = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMetadataInitialized() {
|
||||||
|
return this.metadata != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getEntityVersion() {
|
||||||
|
if (isMetadataInitialized()) return metadata.getEntityVersion();
|
||||||
|
return this.entityVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEntityVersion(Integer entityVersion) {
|
||||||
|
this.metadata.setEntityVersion(entityVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getCurrentSchemaVersion() {
|
||||||
|
return CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return this.id == null ? null : this.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 this.metadata.getRealmId();
|
||||||
|
return this.realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRealmId(String realmId) {
|
||||||
|
this.metadata.setRealmId(realmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserId() {
|
||||||
|
if (isMetadataInitialized()) return this.metadata.getUserId();
|
||||||
|
return this.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.metadata.setUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getFailedLoginNotBefore() {
|
||||||
|
return this.metadata.getFailedLoginNotBefore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFailedLoginNotBefore(Long failedLoginNotBefore) {
|
||||||
|
this.metadata.setFailedLoginNotBefore(failedLoginNotBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getNumFailures() {
|
||||||
|
return this.metadata.getNumFailures();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNumFailures(Integer numFailures) {
|
||||||
|
this.metadata.setNumFailures(numFailures);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLastFailure() {
|
||||||
|
return this.metadata.getLastFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastFailure(Long lastFailure) {
|
||||||
|
this.metadata.setLastFailure(lastFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLastIPFailure() {
|
||||||
|
return this.metadata.getLastIPFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastIPFailure(String lastIPFailure) {
|
||||||
|
this.metadata.setLastIPFailure(lastIPFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getClass().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (!(obj instanceof JpaUserLoginFailureEntity)) return false;
|
||||||
|
return Objects.equals(getId(), ((JpaUserLoginFailureEntity) obj).getId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.loginFailure.entity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntityImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that contains all the user login failure metadata that is written as JSON into the database.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
|
||||||
|
*/
|
||||||
|
public class JpaUserLoginFailureMetadata extends MapUserLoginFailureEntityImpl implements Serializable {
|
||||||
|
|
||||||
|
public JpaUserLoginFailureMetadata(DeepCloner cloner) {
|
||||||
|
super(cloner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JpaUserLoginFailureMetadata() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer entityVersion;
|
||||||
|
|
||||||
|
public Integer getEntityVersion() {
|
||||||
|
return entityVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntityVersion(Integer entityVersion) {
|
||||||
|
this.entityVersion = entityVersion;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
|
and other contributors as indicated by the @author tags.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<!-- format of id of changelog file names: jpa-user-login-failures-changelog-${org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE}.xml -->
|
||||||
|
<include file="META-INF/user-login-failures/jpa-user-login-failures-changelog-1.xml"/>
|
||||||
|
</databaseChangeLog>
|
|
@ -20,5 +20,7 @@
|
||||||
<!--roles-->
|
<!--roles-->
|
||||||
<class>org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity</class>
|
<class>org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity</class>
|
||||||
<class>org.keycloak.models.map.storage.jpa.role.entity.JpaRoleAttributeEntity</class>
|
<class>org.keycloak.models.map.storage.jpa.role.entity.JpaRoleAttributeEntity</class>
|
||||||
|
<!--user-login-failures-->
|
||||||
|
<class>org.keycloak.models.map.storage.jpa.loginFailure.entity.JpaUserLoginFailureEntity</class>
|
||||||
</persistence-unit>
|
</persistence-unit>
|
||||||
</persistence>
|
</persistence>
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
|
and other contributors as indicated by the @author tags.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
|
||||||
|
|
||||||
|
<!-- format of id of changeSet: user-login-failures-${org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_USER_LOGIN_FAILURE} -->
|
||||||
|
<changeSet author="keycloak" id="user-login-failures-1">
|
||||||
|
|
||||||
|
<createTable tableName="kc_user_login_failure">
|
||||||
|
<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_login_failure">
|
||||||
|
<ext:column name="entityversion" type="INTEGER" jsonColumn="metadata" jsonProperty="entityVersion"/>
|
||||||
|
<ext:column name="realmid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fRealmId"/>
|
||||||
|
<ext:column name="userid" type="KC_KEY" jsonColumn="metadata" jsonProperty="fUserId"/>
|
||||||
|
</ext:addGeneratedColumn>
|
||||||
|
<createIndex tableName="kc_user_login_failure" indexName="user_login_failure_entityVersion">
|
||||||
|
<column name="entityversion"/>
|
||||||
|
</createIndex>
|
||||||
|
<createIndex tableName="kc_user_login_failure" indexName="user_login_failure_realmId_userId" unique="true">
|
||||||
|
<column name="realmid"/>
|
||||||
|
<column name="userid"/>
|
||||||
|
</createIndex>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
Loading…
Reference in a new issue