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_REALM = 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;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
|
@ -26,6 +29,8 @@ import org.keycloak.models.map.storage.MapStorageProviderFactory.Flag;
|
|||
|
||||
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 JpaMapStorageProviderFactory factory;
|
||||
|
@ -46,6 +51,9 @@ public class JpaMapStorageProvider implements MapStorageProvider {
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
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);
|
||||
return new MapStorage<V, M>() {
|
||||
@Override
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.dblock.DBLockProvider;
|
||||
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
||||
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.hibernate.listeners.JpaEntityVersionListener;
|
||||
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.entity.JpaComponentEntity;
|
||||
import org.keycloak.models.map.storage.jpa.realm.entity.JpaRealmEntity;
|
||||
|
@ -129,7 +132,7 @@ public class JpaMapStorageProviderFactory implements
|
|||
.constructor(JpaClientScopeEntity.class, JpaClientScopeEntity::new)
|
||||
//group
|
||||
.constructor(JpaGroupEntity.class, JpaGroupEntity::new)
|
||||
// realm
|
||||
//realm
|
||||
.constructor(JpaRealmEntity.class, JpaRealmEntity::new)
|
||||
.constructor(JpaComponentEntity.class, JpaComponentEntity::new)
|
||||
.constructor(MapAuthenticationExecutionEntity.class, MapAuthenticationExecutionEntityImpl::new)
|
||||
|
@ -144,6 +147,8 @@ public class JpaMapStorageProviderFactory implements
|
|||
.constructor(MapWebAuthnPolicyEntity.class, MapWebAuthnPolicyEntityImpl::new)
|
||||
//role
|
||||
.constructor(JpaRoleEntity.class, JpaRoleEntity::new)
|
||||
//user login-failure
|
||||
.constructor(JpaUserLoginFailureEntity.class, JpaUserLoginFailureEntity::new)
|
||||
.build();
|
||||
|
||||
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(RealmModel.class, JpaRealmMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new);
|
||||
MODEL_TO_TX.put(UserLoginFailureModel.class, JpaUserLoginFailureMapKeycloakTransaction::new);
|
||||
}
|
||||
|
||||
public MapKeycloakTransaction createTransaction(Class<?> modelType, EntityManager em) {
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
*/
|
||||
package org.keycloak.models.map.storage.jpa.hibernate.jsonb;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
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.JpaRootAuthenticationSessionMetadata;
|
||||
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.JpaRoleMigration;
|
||||
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.JpaRealmMetadata;
|
||||
import org.keycloak.models.map.storage.jpa.role.entity.JpaRoleMetadata;
|
||||
|
@ -45,19 +48,21 @@ 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_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_USER_LOGIN_FAILURE;
|
||||
|
||||
public class JpaEntityMigration {
|
||||
|
||||
static final Map<Class<?>, BiFunction<ObjectNode, Integer, ObjectNode>> MIGRATIONS = new HashMap<>();
|
||||
static {
|
||||
MIGRATIONS.put(JpaAuthenticationSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_AUTH_SESSION, tree, JpaAuthenticationSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaRootAuthenticationSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_AUTH_SESSION, tree, JpaRootAuthenticationSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT, tree, JpaClientMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientScopeMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT_SCOPE, tree, JpaClientScopeMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaComponentMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_REALM, tree, JpaComponentMigration.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(JpaRoleMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_ROLE, tree, JpaRoleMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaAuthenticationSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_AUTH_SESSION, tree, JpaAuthenticationSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaRootAuthenticationSessionMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_AUTH_SESSION, tree, JpaRootAuthenticationSessionMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT, tree, JpaClientMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaClientScopeMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_CLIENT_SCOPE, tree, JpaClientScopeMigration.MIGRATORS));
|
||||
MIGRATIONS.put(JpaComponentMetadata.class, (tree, entityVersion) -> migrateTreeTo(entityVersion, CURRENT_SCHEMA_VERSION_REALM, tree, JpaComponentMigration.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(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) {
|
||||
|
|
|
@ -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-->
|
||||
<class>org.keycloak.models.map.storage.jpa.role.entity.JpaRoleEntity</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>
|
||||
|
|
|
@ -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