8000 Events Map JPA implementation by sguilhen · Pull Request #12233 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.keycloak.models.map.storage.jpa;

public interface Constants {
public static final Integer CURRENT_SCHEMA_VERSION_ADMIN_EVENT = 1;
public static final Integer CURRENT_SCHEMA_VERSION_AUTH_EVENT = 1;
public static final Integer CURRENT_SCHEMA_VERSION_AUTH_SESSION = 1;
public static final Integer CURRENT_SCHEMA_VERSION_AUTHZ_PERMISSION = 1;
public static final Integer CURRENT_SCHEMA_VERSION_AUTHZ_POLICY = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import org.keycloak.common.util.StackUtil;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.component.AmphibianProviderFactory;
import org.keycloak.events.Event;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.GroupModel;
Expand Down Expand Up @@ -108,6 +110,10 @@
import org.keycloak.models.map.storage.jpa.client.entity.JpaClientEntity;
import org.keycloak.models.map.storage.jpa.clientscope.JpaClientScopeMapKeycloakTransaction;
import org.keycloak.models.map.storage.jpa.clientscope.entity.JpaClientScopeEntity;
import org.keycloak.models.map.storage.jpa.event.admin.JpaAdminEventMapKeycloakTransaction;
import org.keycloak.models.map.storage.jpa.event.admin.entity.JpaAdminEventEntity;
import org.keycloak.models.map.storage.jpa.event.auth.JpaAuthEventMapKeycloakTransaction;
import org.keycloak.models.map.storage.jpa.event.auth.entity.JpaAuthEventEntity;
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.JpaAutoFlushListener;
Expand Down Expand Up @@ -158,6 +164,9 @@ public class JpaMapStorageProviderFactory implements
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
//client-scope
.constructor(JpaClientScopeEntity.class, JpaClientScopeEntity::new)
//event
.constructor(JpaAdminEventEntity.class, JpaAdminEventEntity::new)
.constructor(JpaAuthEventEntity.class, JpaAuthEventEntity::new)
//group
.constructor(JpaGroupEntity.class, JpaGroupEntity::new)
//realm
Expand All @@ -184,6 +193,9 @@ public class JpaMapStorageProviderFactory implements
MODEL_TO_TX.put(RootAuthenticationSessionModel.class, JpaRootAuthenticationSessionMapKeycloakTransaction::new);
MODEL_TO_TX.put(ClientScopeModel.class, JpaClientScopeMapKeycloakTransaction::new);
MODEL_TO_TX.put(ClientModel.class, JpaClientMapKeycloakTransaction::new);
//event
MODEL_TO_TX.put(AdminEvent.class, JpaAdminEventMapKeycloakTransaction::new);
MODEL_TO_TX.put(Event.class, JpaAuthEventMapKeycloakTransaction::new);
MODEL_TO_TX.put(GroupModel.class, JpaGroupMapKeycloakTransaction::new);
MODEL_TO_TX.put(RealmModel.class, JpaRealmMapKeycloakTransaction::new);
MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.keycloak.models.map.storage.jpa;

import com.fasterxml.jackson.core.JsonProcessingException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.event.admin;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;

import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.map.events.MapAdminEventEntity;
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.event.admin.entity.JpaAdminEventEntity;

import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSION_ADMIN_EVENT;

/**
* A {@link org.keycloak.models.map.storage.MapKeycloakTransaction} implementation for admin event entities.
*
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
*/
public class JpaAdminEventMapKeycloakTransaction extends JpaMapKeycloakTransaction<JpaAdminEventEntity, MapAdminEventEntity, AdminEv 10000 ent> {

public JpaAdminEventMapKeycloakTransaction(final EntityManager em) {
super(JpaAdminEventEntity.class, AdminEvent.class, em);
}

@Override
protected Selection<? extends JpaAdminEventEntity> selectCbConstruct(CriteriaBuilder cb, Root<JpaAdminEventEntity> root) {
return root;
}

@Override
protected void setEntityVersion(JpaRootEntity entity) {
entity.setEntityVersion(CURRENT_SCHEMA_VERSION_ADMIN_EVENT);
}

@Override
protected JpaModelCriteriaBuilder createJpaModelCriteriaBuilder() {
return new JpaAdminEventModelCriteriaBuilder();
}

@Override
protected MapAdminEventEntity mapToEntityDelegate(JpaAdminEventEntity original) {
return original;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* 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.event.admin;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

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.event.admin.entity.JpaAdminEventEntity;
import org.keycloak.storage.SearchableModelField;
import org.keycloak.util.EnumWithStableIndex;

/**
* A {@link JpaModelCriteriaBuilder} implementation for admin events.
*
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
*/
public class JpaAdminEventModelCriteriaBuilder extends JpaModelCriteriaBuilder<JpaAdminEventEntity, AdminEvent, JpaAdminEventModelCriteriaBuilder> {

private static final Map<String, String> FIELD_TO_JSON_PROP = new HashMap<>();
static {
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.AUTH_CLIENT_ID.getName(), "fAuthClientId");
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.AUTH_REALM_ID.getName(), "fAuthRealmId");
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.AUTH_USER_ID.getName(), "fAuthUserId");
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.AUTH_IP_ADDRESS.getName(), "fAuthIpAddress");
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.RESOURCE_PATH.getName(), "fResourcePath");
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.RESOURCE_TYPE.getName(), "fResourceType");
FIELD_TO_JSON_PROP.put(AdminEvent.SearchableFields.OPERATION_TYPE.getName(), "fOperationType");
}

public JpaAdminEventModelCriteriaBuilder() {
super(JpaAdminEventModelCriteriaBuilder::new);
}

private JpaAdminEventModelCriteriaBuilder(BiFunction<CriteriaBuilder, Root<JpaAdminEventEntity>, Predicate> predicateFunc) {
super(JpaAdminEventModelCriteriaBuilder::new, predicateFunc);
}

@Override
public JpaAdminEventModelCriteriaBuilder compare(SearchableModelField<? super AdminEvent> modelField, Operator op, Object... value) {
switch(op) {
case EQ:
if (modelField == AdminEvent.SearchableFields.REALM_ID) {

validateValue(value, modelField, op, String.class);
return new JpaAdminEventModelCriteriaBuilder((cb, root) ->
cb.equal(root.get(modelField.getName()), value[0])
);
} else if (modelField == AdminEvent.SearchableFields.AUTH_CLIENT_ID ||
modelField == AdminEvent.SearchableFields.AUTH_REALM_ID ||
modelField == AdminEvent.SearchableFields.AUTH_USER_ID ||
modelField == AdminEvent.SearchableFields.AUTH_IP_ADDRESS) {

validateValue(value, modelField, op, String.class);
return new JpaAdminEventModelCriteriaBuilder((cb, root) ->
cb.equal(
cb.function("->>", String.class, root.get("metadata"),
cb.literal(FIELD_TO_JSON_PROP.get(modelField.getName()))), value[0])
);
} else {
throw new CriterionNotSupportedException(modelField, op);
}
case LE:
if (modelField == AdminEvent.SearchableFields.TIMESTAMP) {

validateValue(value, modelField, op, Number.class);

return new JpaAdminEventModelCriteriaBuilder((cb, root) ->
cb.le(root.get(modelField.getName()), (Number) value[0])
);
} else {
throw new CriterionNotSupportedException(modelField, op);
}
case LT:
if (modelField == AdminEvent.SearchableFields.TIMESTAMP) {
validateValue(value, modelField, op, Number.class);

return new JpaAdminEventModelCriteriaBuilder((cb, root) ->
cb.lt(root.get(modelField.getName()), (Number) value[0])
);
} else {
throw new CriterionNotSupportedException(modelField, op);
}
case LIKE:
if (modelField == AdminEvent.SearchableFields.RESOURCE_PATH) {
validateValue(value, modelField, op, String.class);

return new JpaAdminEventModelCriteriaBuilder((cb, root) ->
cb.like(
cb.function("->>", String.class, root.get("metadata"), cb.literal(FIELD_TO_JSON_PROP.get(modelField.getName()))),
value[0].toString())
);
} else {
throw new CriterionNotSupportedException(modelField, op);
}
case GE:
if (modelField == AdminEvent.SearchableFields.TIMESTAMP) {
validateValue(value, modelField, op, Number.class);

return new JpaAdminEventModelCriteriaBuilder((cb, root) ->
cb.ge(root.get(modelField.getName()), (Number) value[0])
);
} else {
throw new CriterionNotSupportedException(modelField, op);
}
case IN:
if (modelField == AdminEvent.SearchableFields.OPERATION_TYPE) {
Set<Integer> values = super.getValuesForInOperator(value, modelField).stream()
.map(o -> ((EnumWithStableIndex) o).getStableIndex()).collect(Collectors.toSet());

if (values.isEmpty()) return new JpaAdminEventModelCriteriaBuilder((cb, root) -> cb.or());

return new JpaAdminEventModelCriteriaBuilder((cb, root) -> {
CriteriaBuilder.In<Integer> in = cb.in(cb.function("->>", String.class, root.get("metadata"),
cb.literal(FIELD_TO_JSON_PROP.get(modelField.getName()))).as(Integer.class));
values.forEach(in::value);
return in;
});
}
else if (modelField == AdminEvent.SearchableFields.RESOURCE_TYPE) {
Set<String> values = super.getValuesForInOperator(value, modelField).stream()
.map(Object::toString).collect(Collectors.toSet());

if (values.isEmpty()) return new JpaAdminEventModelCriteriaBuilder((cb, root) -> cb.or());

return new JpaAdminEventModelCriteriaBuilder((cb, root) -> {
CriteriaBuilder.In<String> in = cb.in(cb.function("->>", String.class, root.get("metadata"),
cb.literal(FIELD_TO_JSON_PROP.get(modelField.getName()))));
values.forEach(in::value);
return in;
});
} else {
throw new CriterionNotSupportedException(modelField, op);
}
default:
throw new CriterionNotSupportedException(modelField, op);
}
}
}
Loading
0