diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java index ce114d6333..67e7359fb7 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java @@ -26,6 +26,8 @@ import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; import org.keycloak.common.Profile; import org.keycloak.component.AmphibianProviderFactory; +import org.keycloak.events.Event; +import org.keycloak.events.admin.AdminEvent; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; @@ -47,6 +49,8 @@ import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity; import org.keycloak.models.map.clientscope.MapClientScopeEntity; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.StringKeyConverter; +import org.keycloak.models.map.events.MapAdminEventEntity; +import org.keycloak.models.map.events.MapAuthEventEntity; import org.keycloak.models.map.group.MapGroupEntity; import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity; import org.keycloak.models.map.realm.MapRealmEntity; @@ -77,6 +81,10 @@ import org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntity; import org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntityDelegate; import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity; import org.keycloak.models.map.storage.hotRod.common.HotRodEntityDelegate; +import org.keycloak.models.map.storage.hotRod.events.HotRodAdminEventEntity; +import org.keycloak.models.map.storage.hotRod.events.HotRodAdminEventEntityDelegate; +import org.keycloak.models.map.storage.hotRod.events.HotRodAuthEventEntity; +import org.keycloak.models.map.storage.hotRod.events.HotRodAuthEventEntityDelegate; import org.keycloak.models.map.storage.hotRod.loginFailure.HotRodUserLoginFailureEntity; import org.keycloak.models.map.storage.hotRod.loginFailure.HotRodUserLoginFailureEntityDelegate; import org.keycloak.models.map.storage.hotRod.role.HotRodRoleEntity; @@ -180,6 +188,9 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory .constructor(MapPolicyEntity.class, HotRodPolicyEntityDelegate::new) .constructor(MapPermissionTicketEntity.class, HotRodPermissionTicketEntityDelegate::new) + .constructor(MapAuthEventEntity.class, HotRodAuthEventEntityDelegate::new) + .constructor(MapAdminEventEntity.class, HotRodAdminEventEntityDelegate::new) + .build(); public static final Map, HotRodEntityDescriptor> ENTITY_DESCRIPTOR_MAP = new HashMap<>(); @@ -293,6 +304,17 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory return "authz"; } }); + + // Events + ENTITY_DESCRIPTOR_MAP.put(Event.class, + new HotRodEntityDescriptor<>(Event.class, + HotRodAuthEventEntity.class, + HotRodAuthEventEntityDelegate::new)); + + ENTITY_DESCRIPTOR_MAP.put(AdminEvent.class, + new HotRodEntityDescriptor<>(AdminEvent.class, + HotRodAdminEventEntity.class, + HotRodAdminEventEntityDelegate::new)); } @Override diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryMapModelCriteriaBuilder.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryMapModelCriteriaBuilder.java index 31c1b424c5..2670651518 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryMapModelCriteriaBuilder.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryMapModelCriteriaBuilder.java @@ -19,6 +19,7 @@ package org.keycloak.models.map.storage.hotRod; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.Resource; +import org.keycloak.events.Event; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; import org.keycloak.models.GroupModel; @@ -36,6 +37,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.function.UnaryOperator; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -49,10 +51,10 @@ public class IckleQueryMapModelCriteriaBuilder hotRodEntityClass; private final StringBuilder whereClauseBuilder = new StringBuilder(INITIAL_BUILDER_CAPACITY); private final Map parameters; + private static final Pattern LIKE_PATTERN_DELIMITER = Pattern.compile("%+"); private static final Pattern NON_ANALYZED_FIELD_REGEX = Pattern.compile("[%_\\\\]"); // private static final Pattern ANALYZED_FIELD_REGEX = Pattern.compile("[+!^\"~*?:\\\\]"); // TODO reevaluate once https://github.com/keycloak/keycloak/issues/9295 is fixed private static final Pattern ANALYZED_FIELD_REGEX = Pattern.compile("\\\\"); // escape "\" with extra "\" - private static final Pattern SINGLE_PERCENT_CHARACTER = Pattern.compile("^%+$"); public static final Map, String> INFINISPAN_NAME_OVERRIDES = new HashMap<>(); public static final Set> ANALYZED_MODEL_FIELDS = new HashSet<>(); @@ -87,6 +89,8 @@ public class IckleQueryMapModelCriteriaBuilder sanitizeSingleUnit, String replacement) { + return LIKE_PATTERN_DELIMITER.splitAsStream(value) + .map(sanitizeSingleUnit) + .collect(Collectors.joining(replacement)) + + (value.endsWith("%") ? replacement : ""); + } + + private static String sanitizeSingleUnitNonAnalyzed(String value) { + return NON_ANALYZED_FIELD_REGEX.matcher(value).replaceAll("\\\\\\\\" + "$0"); + } + + private static String sanitizeSingleUnitAnalyzed(String value) { + return ANALYZED_FIELD_REGEX.matcher(value).replaceAll("\\\\\\\\"); // escape "\" with extra "\" + // .replaceAll("\\\\\\\\" + "$0"); skipped for now because Infinispan is not able to escape + // special characters for analyzed fields + // TODO reevaluate once https://github.com/keycloak/keycloak/issues/9295 is fixed + } + + public static boolean isAnalyzedModelField(SearchableModelField modelField) { return ANALYZED_MODEL_FIELDS.contains(modelField); } diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryOperators.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryOperators.java index 00aa690b53..945df30ce6 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryOperators.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryOperators.java @@ -95,13 +95,13 @@ public class IckleQueryOperators { } private static String iLike(String modelFieldName, Object[] values, Map parameters) { - String sanitizedValue = (String) IckleQueryMapModelCriteriaBuilder.sanitize(values[0]); + String sanitizedValue = (String) IckleQueryMapModelCriteriaBuilder.sanitizeNonAnalyzed(values[0]); return singleValueOperator(ModelCriteriaBuilder.Operator.ILIKE) .combine(modelFieldName + "Lowercase", new String[] {sanitizedValue.toLowerCase()}, parameters); } private static String like(String modelFieldName, Object[] values, Map parameters) { - String sanitizedValue = (String) IckleQueryMapModelCriteriaBuilder.sanitize(values[0]); + String sanitizedValue = (String) IckleQueryMapModelCriteriaBuilder.sanitizeNonAnalyzed(values[0]); return singleValueOperator(ModelCriteriaBuilder.Operator.LIKE) .combine(modelFieldName, new String[] {sanitizedValue}, parameters); } diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryWhereClauses.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryWhereClauses.java index d7a68c5b57..c6fc748381 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryWhereClauses.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/IckleQueryWhereClauses.java @@ -18,17 +18,25 @@ package org.keycloak.models.map.storage.hotRod; import org.keycloak.authorization.model.Policy; +import org.keycloak.events.Event; +import org.keycloak.events.EventType; +import org.keycloak.events.admin.AdminEvent; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.ClientModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.map.storage.CriterionNotSupportedException; import org.keycloak.models.map.storage.ModelCriteriaBuilder; +import org.keycloak.models.map.storage.hotRod.common.HotRodTypesUtils; import org.keycloak.storage.SearchableModelField; import org.keycloak.storage.StorageId; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.keycloak.models.map.storage.hotRod.IckleQueryMapModelCriteriaBuilder.getFieldName; import static org.keycloak.models.map.storage.hotRod.IckleQueryMapModelCriteriaBuilder.sanitizeAnalyzed; @@ -58,6 +66,8 @@ public class IckleQueryWhereClauses { WHERE_CLAUSE_PRODUCER_OVERRIDES.put(UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, IckleQueryWhereClauses::whereClauseForConsentClientFederationLink); WHERE_CLAUSE_PRODUCER_OVERRIDES.put(UserSessionModel.SearchableFields.CORRESPONDING_SESSION_ID, IckleQueryWhereClauses::whereClauseForCorrespondingSessionId); WHERE_CLAUSE_PRODUCER_OVERRIDES.put(Policy.SearchableFields.CONFIG, IckleQueryWhereClauses::whereClauseForPolicyConfig); + WHERE_CLAUSE_PRODUCER_OVERRIDES.put(Event.SearchableFields.EVENT_TYPE, IckleQueryWhereClauses::whereClauseForEventType); + WHERE_CLAUSE_PRODUCER_OVERRIDES.put(AdminEvent.SearchableFields.OPERATION_TYPE, IckleQueryWhereClauses::whereClauseForOperationType); } @FunctionalInterface @@ -204,4 +214,31 @@ public class IckleQueryWhereClauses { String valueClause = IckleQueryOperators.combineExpressions(op, modelFieldName + ".value", realValues, parameters); return "(" + nameClause + ")" + " AND " + "(" + valueClause + ")"; } + + private static String whereClauseForEventType(String modelFieldName, ModelCriteriaBuilder.Operator op, Object[] values, Map parameters) { + if (values != null && values.length == 1) { + if (values[0] instanceof EventType) { + values[0] = HotRodTypesUtils.migrateEventTypeToHotRodEventType((EventType) values[0]); + } else if (values[0] instanceof Collection) { + values[0] = ((Collection) values[0]).stream().map(HotRodTypesUtils::migrateEventTypeToHotRodEventType).collect(Collectors.toSet()); + } else if (values[0] instanceof Stream) { + values[0] = ((Stream) values[0]).map(HotRodTypesUtils::migrateEventTypeToHotRodEventType); + } + } + + return produceWhereClause(modelFieldName, op, values, parameters); + } + + private static String whereClauseForOperationType(String modelFieldName, ModelCriteriaBuilder.Operator op, Object[] values, Map parameters) { + if (values != null && values.length == 1) { + if (values[0] instanceof OperationType) { + values[0] = HotRodTypesUtils.migrateOperationTypeToHotRodOperationType((OperationType) values[0]); + } else if (values[0] instanceof Collection) { + values[0] = ((Collection) values[0]).stream().map(HotRodTypesUtils::migrateOperationTypeToHotRodOperationType).collect(Collectors.toSet()); + } else if (values[0] instanceof Stream) { + values[0] = ((Stream) values[0]).map(HotRodTypesUtils::migrateOperationTypeToHotRodOperationType); + } + } + return produceWhereClause(modelFieldName, op, values, parameters); + } } diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java index 821a4b2ada..ef9dbc8791 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java @@ -17,6 +17,8 @@ package org.keycloak.models.map.storage.hotRod.common; +import org.keycloak.events.EventType; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.map.common.AbstractEntity; @@ -24,6 +26,8 @@ import org.keycloak.models.map.storage.hotRod.authSession.HotRodAuthenticationSe import org.keycloak.models.map.storage.hotRod.authorization.HotRodDecisionStrategy; import org.keycloak.models.map.storage.hotRod.authorization.HotRodLogic; import org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEnforcementMode; +import org.keycloak.models.map.storage.hotRod.events.HotRodEventType; +import org.keycloak.models.map.storage.hotRod.events.HotRodOperationType; import org.keycloak.models.map.storage.hotRod.realm.entity.HotRodLocalizationTexts; import org.keycloak.models.map.storage.hotRod.realm.entity.HotRodRequirement; import org.keycloak.models.map.storage.hotRod.user.HotRodUserConsentEntity; @@ -195,4 +199,20 @@ public class HotRodTypesUtils { public static Logic migrateHotRodLogicToLogic(HotRodLogic p0) { return p0 == null ? null : Logic.values()[p0.ordinal()]; } + + public static OperationType migrateHotRodOperationTypeToOperationType(HotRodOperationType p0) { + return p0 == null ? null : OperationType.values()[p0.ordinal()]; + } + + public static HotRodOperationType migrateOperationTypeToHotRodOperationType(OperationType p0) { + return p0 == null ? null : HotRodOperationType.values()[p0.ordinal()]; + } + + public static HotRodEventType migrateEventTypeToHotRodEventType(EventType p0) { + return p0 == null ? null : HotRodEventType.values()[p0.ordinal()]; + } + + public static EventType migrateHotRodEventTypeToEventType(HotRodEventType p0) { + return p0 == null ? null : EventType.values()[p0.ordinal()]; + } } diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/ProtoSchemaInitializer.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/ProtoSchemaInitializer.java index d71c8b7d0a..7da80cf3a0 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/ProtoSchemaInitializer.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/ProtoSchemaInitializer.java @@ -33,6 +33,10 @@ import org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntity; import org.keycloak.models.map.storage.hotRod.client.HotRodClientEntity; import org.keycloak.models.map.storage.hotRod.client.HotRodProtocolMapperEntity; import org.keycloak.models.map.storage.hotRod.clientscope.HotRodClientScopeEntity; +import org.keycloak.models.map.storage.hotRod.events.HotRodAdminEventEntity; +import org.keycloak.models.map.storage.hotRod.events.HotRodAuthEventEntity; +import org.keycloak.models.map.storage.hotRod.events.HotRodEventType; +import org.keycloak.models.map.storage.hotRod.events.HotRodOperationType; import org.keycloak.models.map.storage.hotRod.group.HotRodGroupEntity; import org.keycloak.models.map.storage.hotRod.loginFailure.HotRodUserLoginFailureEntity; import org.keycloak.models.map.storage.hotRod.realm.HotRodRealmEntity; @@ -123,6 +127,12 @@ import org.keycloak.models.map.storage.hotRod.userSession.HotRodUserSessionEntit HotRodLogic.class, HotRodPolicyEnforcementMode.class, + // Events + HotRodAuthEventEntity.class, + HotRodEventType.class, + HotRodAdminEventEntity.class, + HotRodOperationType.class, + // Common HotRodPair.class, HotRodStringPair.class, diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java new file mode 100644 index 0000000000..c87d534524 --- /dev/null +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java @@ -0,0 +1,111 @@ +/* + * 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.hotRod.events; + +import org.infinispan.protostream.annotations.ProtoDoc; +import org.infinispan.protostream.annotations.ProtoField; +import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation; +import org.keycloak.models.map.events.MapAdminEventEntity; +import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity; +import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl; + +@GenerateHotRodEntityImplementation( + implementInterface = "org.keycloak.models.map.events.MapAdminEventEntity", + inherits = "org.keycloak.models.map.storage.hotRod.events.HotRodAdminEventEntity.AbstractHotRodAdminEventEntityDelegate" +) +@ProtoDoc("@Indexed") +public class HotRodAdminEventEntity extends AbstractHotRodEntity { + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 1) + public Integer entityVersion = 1; + + @ProtoField(number = 2) + public String id; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 3) + public Long expiration; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 4) + public Long timestamp; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 5) + public HotRodOperationType operationType; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 6) + public String authClientId; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 7) + public String authIpAddress; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 8) + public String authRealmId; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 9) + public String authUserId; + + @ProtoField(number = 10) + public String error; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 11) + public String realmId; + + @ProtoField(number = 12) + public String representation; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 13) + public String resourcePath; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 14) + public String resourceType; + + public static abstract class AbstractHotRodAdminEventEntityDelegate extends UpdatableHotRodEntityDelegateImpl implements MapAdminEventEntity { + @Override + public String getId() { + return getHotRodEntity().id; + } + + @Override + public void setId(String id) { + HotRodAdminEventEntity entity = getHotRodEntity(); + if (entity.id != null) throw new IllegalStateException("Id cannot be changed"); + entity.id = id; + entity.updated |= id != null; + } + } + + @Override + public boolean equals(Object o) { + return HotRodAdminEventEntityDelegate.entityEquals(this, o); + } + + @Override + public int hashCode() { + return HotRodAdminEventEntityDelegate.entityHashCode(this); + } +} diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java new file mode 100644 index 0000000000..39c4c52f20 --- /dev/null +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java @@ -0,0 +1,104 @@ +/* + * 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.hotRod.events; + +import org.infinispan.protostream.annotations.ProtoDoc; +import org.infinispan.protostream.annotations.ProtoField; +import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation; +import org.keycloak.models.map.events.MapAuthEventEntity; +import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity; +import org.keycloak.models.map.storage.hotRod.common.HotRodPair; +import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl; + +import java.util.Set; +@GenerateHotRodEntityImplementation( + implementInterface = "org.keycloak.models.map.events.MapAuthEventEntity", + inherits = "org.keycloak.models.map.storage.hotRod.events.HotRodAuthEventEntity.AbstractHotRodAuthEventEntityDelegate" +) +@ProtoDoc("@Indexed") +public class HotRodAuthEventEntity extends AbstractHotRodEntity { + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 1) + public Integer entityVersion = 1; + + @ProtoField(number = 2) + public String id; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 3) + public HotRodEventType type; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 4) + public Long expiration; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 5) + public Long timestamp; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 6) + public String clientId; + + @ProtoField(number = 7) + public String error; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 8) + public String ipAddress; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 9) + public String realmId; + + @ProtoField(number = 10) + public String sessionId; + + @ProtoDoc("@Field(index = Index.YES, store = Store.YES)") + @ProtoField(number = 11) + public String userId; + + @ProtoField(number = 12) + public Set> details; + + public static abstract class AbstractHotRodAuthEventEntityDelegate extends UpdatableHotRodEntityDelegateImpl implements MapAuthEventEntity { + @Override + public String getId() { + return getHotRodEntity().id; + } + + @Override + public void setId(String id) { + HotRodAuthEventEntity entity = getHotRodEntity(); + if (entity.id != null) throw new IllegalStateException("Id cannot be changed"); + entity.id = id; + entity.updated |= id != null; + } + } + + @Override + public boolean equals(Object o) { + return HotRodAuthEventEntityDelegate.entityEquals(this, o); + } + + @Override + public int hashCode() { + return HotRodAuthEventEntityDelegate.entityHashCode(this); + } +} diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodEventType.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodEventType.java new file mode 100644 index 0000000000..a2dd3c440e --- /dev/null +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodEventType.java @@ -0,0 +1,238 @@ +/* + * 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.hotRod.events; + +import org.infinispan.protostream.annotations.ProtoEnumValue; + +public enum HotRodEventType { + + @ProtoEnumValue(number = 0) + LOGIN, + @ProtoEnumValue(number = 1) + LOGIN_ERROR, + @ProtoEnumValue(number = 2) + REGISTER, + @ProtoEnumValue(number = 3) + REGISTER_ERROR, + @ProtoEnumValue(number = 4) + LOGOUT, + @ProtoEnumValue(number = 5) + LOGOUT_ERROR, + @ProtoEnumValue(number = 6) + CODE_TO_TOKEN, + @ProtoEnumValue(number = 7) + CODE_TO_TOKEN_ERROR, + @ProtoEnumValue(number = 8) + CLIENT_LOGIN, + @ProtoEnumValue(number = 9) + CLIENT_LOGIN_ERROR, + @ProtoEnumValue(number = 10) + REFRESH_TOKEN, + @ProtoEnumValue(number = 11) + REFRESH_TOKEN_ERROR, + @ProtoEnumValue(number = 12) + /** + * @deprecated see KEYCLOAK-2266 + */ + @Deprecated + VALIDATE_ACCESS_TOKEN, + @ProtoEnumValue(number = 13) + @Deprecated + VALIDATE_ACCESS_TOKEN_ERROR, + @ProtoEnumValue(number = 14) + INTROSPECT_TOKEN, + @ProtoEnumValue(number = 15) + INTROSPECT_TOKEN_ERROR, + @ProtoEnumValue(number = 16) + FEDERATED_IDENTITY_LINK, + @ProtoEnumValue(number = 17) + FEDERATED_IDENTITY_LINK_ERROR, + @ProtoEnumValue(number = 18) + REMOVE_FEDERATED_IDENTITY, + @ProtoEnumValue(number = 19) + REMOVE_FEDERATED_IDENTITY_ERROR, + @ProtoEnumValue(number = 20) + UPDATE_EMAIL, + @ProtoEnumValue(number = 21) + UPDATE_EMAIL_ERROR, + @ProtoEnumValue(number = 22) + UPDATE_PROFILE, + @ProtoEnumValue(number = 23) + UPDATE_PROFILE_ERROR, + @ProtoEnumValue(number = 24) + UPDATE_PASSWORD, + @ProtoEnumValue(number = 25) + UPDATE_PASSWORD_ERROR, + @ProtoEnumValue(number = 26) + UPDATE_TOTP, + @ProtoEnumValue(number = 27) + UPDATE_TOTP_ERROR, + @ProtoEnumValue(number = 28) + VERIFY_EMAIL, + @ProtoEnumValue(number = 29) + VERIFY_EMAIL_ERROR, + @ProtoEnumValue(number = 30) + VERIFY_PROFILE, + @ProtoEnumValue(number = 31) + VERIFY_PROFILE_ERROR, + @ProtoEnumValue(number = 32) + REMOVE_TOTP, + @ProtoEnumValue(number = 33) + REMOVE_TOTP_ERROR, + @ProtoEnumValue(number = 34) + GRANT_CONSENT, + @ProtoEnumValue(number = 35) + GRANT_CONSENT_ERROR, + @ProtoEnumValue(number = 36) + UPDATE_CONSENT, + @ProtoEnumValue(number = 37) + UPDATE_CONSENT_ERROR, + @ProtoEnumValue(number = 38) + REVOKE_GRANT, + @ProtoEnumValue(number = 39) + REVOKE_GRANT_ERROR, + @ProtoEnumValue(number = 40) + SEND_VERIFY_EMAIL, + @ProtoEnumValue(number = 41) + SEND_VERIFY_EMAIL_ERROR, + @ProtoEnumValue(number = 42) + SEND_RESET_PASSWORD, + @ProtoEnumValue(number = 43) + SEND_RESET_PASSWORD_ERROR, + @ProtoEnumValue(number = 44) + SEND_IDENTITY_PROVIDER_LINK, + @ProtoEnumValue(number = 45) + SEND_IDENTITY_PROVIDER_LINK_ERROR, + @ProtoEnumValue(number = 46) + RESET_PASSWORD, + @ProtoEnumValue(number = 47) + RESET_PASSWORD_ERROR, + @ProtoEnumValue(number = 48) + RESTART_AUTHENTICATION, + @ProtoEnumValue(number = 49) + RESTART_AUTHENTICATION_ERROR, + @ProtoEnumValue(number = 50) + INVALID_SIGNATURE, + @ProtoEnumValue(number = 51) + INVALID_SIGNATURE_ERROR, + @ProtoEnumValue(number = 52) + REGISTER_NODE, + @ProtoEnumValue(number = 53) + REGISTER_NODE_ERROR, + @ProtoEnumValue(number = 54) + UNREGISTER_NODE, + @ProtoEnumValue(number = 55) + UNREGISTER_NODE_ERROR, + @ProtoEnumValue(number = 56) + USER_INFO_REQUEST, + @ProtoEnumValue(number = 57) + USER_INFO_REQUEST_ERROR, + @ProtoEnumValue(number = 58) + IDENTITY_PROVIDER_LINK_ACCOUNT, + @ProtoEnumValue(number = 59) + IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR, + @ProtoEnumValue(number = 60) + IDENTITY_PROVIDER_LOGIN, + @ProtoEnumValue(number = 61) + IDENTITY_PROVIDER_LOGIN_ERROR, + @ProtoEnumValue(number = 62) + IDENTITY_PROVIDER_FIRST_LOGIN, + @ProtoEnumValue(number = 63) + IDENTITY_PROVIDER_FIRST_LOGIN_ERROR, + @ProtoEnumValue(number = 64) + IDENTITY_PROVIDER_POST_LOGIN, + @ProtoEnumValue(number = 65) + IDENTITY_PROVIDER_POST_LOGIN_ERROR, + @ProtoEnumValue(number = 66) + IDENTITY_PROVIDER_RESPONSE, + @ProtoEnumValue(number = 67) + IDENTITY_PROVIDER_RESPONSE_ERROR, + @ProtoEnumValue(number = 68) + IDENTITY_PROVIDER_RETRIEVE_TOKEN, + @ProtoEnumValue(number = 69) + IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR, + @ProtoEnumValue(number = 70) + IMPERSONATE, + @ProtoEnumValue(number = 71) + IMPERSONATE_ERROR, + @ProtoEnumValue(number = 72) + CUSTOM_REQUIRED_ACTION, + @ProtoEnumValue(number = 73) + CUSTOM_REQUIRED_ACTION_ERROR, + @ProtoEnumValue(number = 74) + EXECUTE_ACTIONS, + @ProtoEnumValue(number = 75) + EXECUTE_ACTIONS_ERROR, + @ProtoEnumValue(number = 76) + EXECUTE_ACTION_TOKEN, + @ProtoEnumValue(number = 77) + EXECUTE_ACTION_TOKEN_ERROR, + @ProtoEnumValue(number = 78) + CLIENT_INFO, + @ProtoEnumValue(number = 79) + CLIENT_INFO_ERROR, + @ProtoEnumValue(number = 80) + CLIENT_REGISTER, + @ProtoEnumValue(number = 81) + CLIENT_REGISTER_ERROR, + @ProtoEnumValue(number = 82) + CLIENT_UPDATE, + @ProtoEnumValue(number = 83) + CLIENT_UPDATE_ERROR, + @ProtoEnumValue(number = 84) + CLIENT_DELETE, + @ProtoEnumValue(number = 85) + CLIENT_DELETE_ERROR, + @ProtoEnumValue(number = 86) + CLIENT_INITIATED_ACCOUNT_LINKING, + @ProtoEnumValue(number = 87) + CLIENT_INITIATED_ACCOUNT_LINKING_ERROR, + @ProtoEnumValue(number = 88) + TOKEN_EXCHANGE, + @ProtoEnumValue(number = 89) + TOKEN_EXCHANGE_ERROR, + @ProtoEnumValue(number = 90) + OAUTH2_DEVICE_AUTH, + @ProtoEnumValue(number = 91) + OAUTH2_DEVICE_AUTH_ERROR, + @ProtoEnumValue(number = 92) + OAUTH2_DEVICE_VERIFY_USER_CODE, + @ProtoEnumValue(number = 93) + OAUTH2_DEVICE_VERIFY_USER_CODE_ERROR, + @ProtoEnumValue(number = 94) + OAUTH2_DEVICE_CODE_TO_TOKEN, + @ProtoEnumValue(number = 95) + OAUTH2_DEVICE_CODE_TO_TOKEN_ERROR, + @ProtoEnumValue(number = 96) + AUTHREQID_TO_TOKEN, + @ProtoEnumValue(number = 97) + AUTHREQID_TO_TOKEN_ERROR, + @ProtoEnumValue(number = 98) + PERMISSION_TOKEN, + @ProtoEnumValue(number = 99) + PERMISSION_TOKEN_ERROR, + @ProtoEnumValue(number = 100) + DELETE_ACCOUNT, + @ProtoEnumValue(number = 101) + DELETE_ACCOUNT_ERROR, + @ProtoEnumValue(number = 102) + // PAR request. + PUSHED_AUTHORIZATION_REQUEST, + @ProtoEnumValue(number = 103) + PUSHED_AUTHORIZATION_REQUEST_ERROR; +} diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodOperationType.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodOperationType.java new file mode 100644 index 0000000000..143b07af9e --- /dev/null +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodOperationType.java @@ -0,0 +1,31 @@ +/* + * 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.hotRod.events; + +import org.infinispan.protostream.annotations.ProtoEnumValue; + +public enum HotRodOperationType { + @ProtoEnumValue(number = 0) + CREATE, + @ProtoEnumValue(number = 1) + UPDATE, + @ProtoEnumValue(number = 2) + DELETE, + @ProtoEnumValue(number = 3) + ACTION; +} diff --git a/model/map-hot-rod/src/main/resources/config/cacheConfig.xml b/model/map-hot-rod/src/main/resources/config/cacheConfig.xml index cca1644c7b..b77ea94787 100644 --- a/model/map-hot-rod/src/main/resources/config/cacheConfig.xml +++ b/model/map-hot-rod/src/main/resources/config/cacheConfig.xml @@ -100,5 +100,21 @@ + + + + + kc.HotRodAuthEventEntity + + + + + + + + kc.HotRodAdminEventEntity + + + diff --git a/model/map-hot-rod/src/main/resources/config/infinispan.xml b/model/map-hot-rod/src/main/resources/config/infinispan.xml index ce334c64af..02c48c787a 100644 --- a/model/map-hot-rod/src/main/resources/config/infinispan.xml +++ b/model/map-hot-rod/src/main/resources/config/infinispan.xml @@ -102,5 +102,21 @@ + + + + + kc.HotRodAuthEventEntity + + + + + + + + kc.HotRodAdminEventEntity + + + diff --git a/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java b/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java index 105d97a9f9..8464344081 100644 --- a/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java +++ b/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java @@ -34,7 +34,7 @@ public class AdminEvent { public static final SearchableModelField AUTH_USER_ID = new SearchableModelField<>("authUserId", String.class); public static final SearchableModelField AUTH_IP_ADDRESS = new SearchableModelField<>("authIpAddress", String.class); public static final SearchableModelField OPERATION_TYPE = new SearchableModelField<>("operationType", OperationType.class); - public static final SearchableModelField RESOURCE_TYPE = new SearchableModelField<>("resourceType", ResourceType.class); + public static final SearchableModelField RESOURCE_TYPE = new SearchableModelField<>("resourceType", String.class); public static final SearchableModelField RESOURCE_PATH = new SearchableModelField<>("resourcePath", String.class); } diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index a64f6f35bc..88ffc42f4d 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -1506,6 +1506,7 @@ hotrod hotrod hotrod + hotrod diff --git a/testsuite/model/src/main/resources/hotrod/infinispan.xml b/testsuite/model/src/main/resources/hotrod/infinispan.xml index fd7a03055f..39203384d2 100644 --- a/testsuite/model/src/main/resources/hotrod/infinispan.xml +++ b/testsuite/model/src/main/resources/hotrod/infinispan.xml @@ -98,5 +98,21 @@ + + + + + kc.HotRodAuthEventEntity + + + + + + + + kc.HotRodAdminEventEntity + + + diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java index 61ec065380..fbf6b9fac3 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java @@ -90,8 +90,8 @@ public class HotRodMapStorage extends KeycloakModelParameters { .config("storage-client-sessions.provider", HotRodMapStorageProviderFactory.PROVIDER_ID) .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) .spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) - .spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-admin-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) - .config("storage-auth-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID); + .spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-admin-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID) + .config("storage-auth-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID); cf.spi(MapStorageSpi.NAME) .provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index 35de2c2104..cd3f2ca004 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -305,6 +305,7 @@ keycloak.authorization.providermap keycloak.actionToken.providermap keycloak.singleUseObject.providermap + keycloak.eventsStore.providermap keycloak.authorizationCache.enabledfalse keycloak.realmCache.enabledfalse keycloak.userCache.enabledfalse diff --git a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json index b0381b3c3f..8381cbb621 100755 --- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json @@ -11,7 +11,15 @@ }, "eventsStore": { - "provider": "${keycloak.eventsStore.provider:}" + "provider": "${keycloak.eventsStore.provider:jpa}", + "map": { + "storage-admin-events": { + "provider": "${keycloak.eventStore.map.storage.provider:concurrenthashmap}" + }, + "storage-auth-events": { + "provider": "${keycloak.eventStore.map.storage.provider:concurrenthashmap}" + } + } }, "deploymentState": {