From c3d80651bfaaeb04a7f4d43ad3d6e24c3225bf42 Mon Sep 17 00:00:00 2001 From: vramik Date: Wed, 4 Dec 2019 11:41:01 +0100 Subject: [PATCH] KEYCLOAK-12473 Add possibility to specify length of event detail when storing to database --- .../events/jpa/JpaEventStoreProvider.java | 29 ++++++++++++++++--- .../jpa/JpaEventStoreProviderFactory.java | 4 ++- .../jboss-cli/keycloak-server-subsystem.cli | 3 ++ .../keycloak/testsuite/forms/LoginTest.java | 20 +++++++++++++ .../resources/META-INF/keycloak-server.json | 5 +++- 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java b/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java index aa3b2ac215..c23aa4c4cd 100755 --- a/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java +++ b/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java @@ -31,6 +31,7 @@ import org.keycloak.events.admin.OperationType; import javax.persistence.EntityManager; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -44,10 +45,12 @@ public class JpaEventStoreProvider implements EventStoreProvider { }; private static final Logger logger = Logger.getLogger(JpaEventStoreProvider.class); - private EntityManager em; + private final EntityManager em; + private final int maxDetailLength; - public JpaEventStoreProvider(EntityManager em) { + public JpaEventStoreProvider(EntityManager em, int maxDetailLength) { this.em = em; + this.maxDetailLength = maxDetailLength; } @Override @@ -104,7 +107,7 @@ public class JpaEventStoreProvider implements EventStoreProvider { public void close() { } - static EventEntity convertEvent(Event event) { + private EventEntity convertEvent(Event event) { EventEntity eventEntity = new EventEntity(); eventEntity.setId(UUID.randomUUID().toString()); eventEntity.setTime(event.getTime()); @@ -116,13 +119,31 @@ public class JpaEventStoreProvider implements EventStoreProvider { eventEntity.setIpAddress(event.getIpAddress()); eventEntity.setError(event.getError()); try { - eventEntity.setDetailsJson(mapper.writeValueAsString(event.getDetails())); + if (maxDetailLength > 0 && event.getDetails() != null) { + Map result = new HashMap<>(event.getDetails()); + result.entrySet().forEach(t -> t.setValue(trimToMaxLength(t.getValue()))); + + eventEntity.setDetailsJson(mapper.writeValueAsString(result)); + } else { + eventEntity.setDetailsJson(mapper.writeValueAsString(event.getDetails())); + } } catch (IOException ex) { logger.error("Failed to write log details", ex); } return eventEntity; } + private String trimToMaxLength(String detail) { + if (detail != null && detail.length() > maxDetailLength) { + // (maxDetailLength - 3) takes "..." into account + String result = detail.substring(0, maxDetailLength - 3).concat("..."); + logger.warn("Detail was truncated to " + result); + return result; + } else { + return detail; + } + } + static Event convertEvent(EventEntity eventEntity) { Event event = new Event(); event.setTime(eventEntity.getTime()); diff --git a/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProviderFactory.java b/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProviderFactory.java index 1ab78e76cf..3e54739c33 100755 --- a/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProviderFactory.java +++ b/model/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProviderFactory.java @@ -30,15 +30,17 @@ import org.keycloak.models.KeycloakSessionFactory; public class JpaEventStoreProviderFactory implements EventStoreProviderFactory { public static final String ID = "jpa"; + private int maxDetailLength; @Override public EventStoreProvider create(KeycloakSession session) { JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class); - return new JpaEventStoreProvider(connection.getEntityManager()); + return new JpaEventStoreProvider(connection.getEntityManager(), maxDetailLength); } @Override public void init(Config.Scope config) { + maxDetailLength = config.getInt("max-detail-length", 0); } @Override diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/jboss-cli/keycloak-server-subsystem.cli b/testsuite/integration-arquillian/servers/auth-server/jboss/common/jboss-cli/keycloak-server-subsystem.cli index e89d6e70c3..3362d7b189 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/jboss-cli/keycloak-server-subsystem.cli +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/jboss-cli/keycloak-server-subsystem.cli @@ -21,3 +21,6 @@ echo ** Adding provider ** module:org.keycloak.testsuite.integration-arquillian-testsuite-providers \ ] \ ) + +echo ** Adding max-detail-length to eventsStore spi ** +/subsystem=keycloak-server/spi=eventsStore/provider=jpa/:write-attribute(name=properties.max-detail-length,value=${keycloak.eventsStore.maxDetailLength:1000}) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index 3ded422fca..09fc04789b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -16,6 +16,8 @@ */ package org.keycloak.testsuite.forms; +import java.net.MalformedURLException; +import java.net.URI; import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.graphene.page.Page; import org.junit.Assert; @@ -68,6 +70,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.RandomStringUtils; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -210,6 +213,23 @@ public class LoginTest extends AbstractTestRealmKeycloakTest { client.close(); } + @Test + public void loginWithLongRedirectUri() throws Exception { + try (AutoCloseable c = new RealmAttributeUpdater(adminClient.realm("test")) + .updateWith(r -> r.setEventsEnabled(true)).update()) { + String randomLongString = RandomStringUtils.random(2500, true, true); + String longRedirectUri = oauth.getRedirectUri() + "?longQueryParameterValue=" + randomLongString; + UriBuilder longLoginUri = UriBuilder.fromUri(oauth.getLoginFormUrl()).replaceQueryParam(OAuth2Constants.REDIRECT_URI, longRedirectUri); + + DroneUtils.getCurrentDriver().navigate().to(longLoginUri.build().toString()); + + loginPage.assertCurrent(); + loginPage.login("login-test", "password"); + + events.expectLogin().user(userId).detail(OAuth2Constants.REDIRECT_URI, longRedirectUri).assertEvent(); + } + } + @Test public void loginChangeUserAfterInvalidPassword() { loginPage.open(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index ac03b0aed5..a1cd338345 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -22,7 +22,10 @@ }, "eventsStore": { - "provider": "${keycloak.eventsStore.provider:jpa}" + "provider": "${keycloak.eventsStore.provider:jpa}", + "jpa": { + "max-detail-length": "${keycloak.eventsStore.maxDetailLength:1000}" + } }, "eventsListener": {