From 1b910b3090de8c1013391500e2f031fd37d8a16c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 30 Oct 2024 18:07:18 +0000 Subject: [PATCH] Make OidcRequestContextProperties modifiable --- .../common/OidcRequestContextProperties.java | 36 +++++++++++++- .../keycloak/TokenRequestResponseFilter.java | 49 +++++++++++++++++++ .../it/keycloak/TokenResponseFilter.java | 31 ------------ .../src/main/resources/application.properties | 4 +- .../keycloak/CodeFlowAuthorizationTest.java | 2 +- 5 files changed, 86 insertions(+), 36 deletions(-) create mode 100644 integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenRequestResponseFilter.java delete mode 100644 integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenResponseFilter.java diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java index e5dee80db7fe3e..ddb3500144a301 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java @@ -1,6 +1,5 @@ package io.quarkus.oidc.common; -import java.util.Collections; import java.util.Map; public class OidcRequestContextProperties { @@ -19,22 +18,55 @@ public OidcRequestContextProperties(Map properties) { this.properties = properties; } + /** + * Get property value + * + * @param name property name + * @return property value + */ public T get(String name) { @SuppressWarnings("unchecked") T value = (T) properties.get(name); return value; } + /** + * Get property value as String + * + * @param name property name + * @return property value as String + */ public String getString(String name) { return (String) get(name); } + /** + * Get typed property value + * + * @param name property name + * @param type property type + * @return typed property value + */ public T get(String name, Class type) { return type.cast(get(name)); } + /** + * Get all properties + * + * @return all properties + */ public Map getAll() { - return Collections.unmodifiableMap(properties); + return properties; } + /** + * Set the property + * + * @param name property name + * @param value property value + */ + public void set(String name, Object value) { + properties.put(name, value); + } } diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenRequestResponseFilter.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenRequestResponseFilter.java new file mode 100644 index 00000000000000..c00f540db653c9 --- /dev/null +++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenRequestResponseFilter.java @@ -0,0 +1,49 @@ +package io.quarkus.it.keycloak; + +import java.time.Instant; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.jboss.logging.Logger; + +import io.quarkus.arc.Unremovable; +import io.quarkus.oidc.common.OidcEndpoint; +import io.quarkus.oidc.common.OidcEndpoint.Type; +import io.quarkus.oidc.common.OidcRequestFilter; +import io.quarkus.oidc.common.OidcResponseFilter; +import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.oidc.runtime.OidcUtils; + +@ApplicationScoped +@Unremovable +@OidcEndpoint(value = Type.TOKEN) +public class TokenRequestResponseFilter implements OidcRequestFilter, OidcResponseFilter { + private static final Logger LOG = Logger.getLogger(TokenRequestResponseFilter.class); + + private ConcurrentHashMap instants = new ConcurrentHashMap<>(); + + @Override + public void filter(OidcRequestContext rc) { + final Instant now = Instant.now(); + instants.put(rc.contextProperties().get(OidcUtils.TENANT_ID_ATTRIBUTE), now); + rc.contextProperties().set("instant", now); + } + + @Override + public void filter(OidcResponseContext rc) { + Instant instant1 = instants.remove(rc.requestProperties().get(OidcUtils.TENANT_ID_ATTRIBUTE)); + Instant instant2 = rc.requestProperties().get("instant"); + boolean instantsAreTheSame = instant1 == instant2; + if (rc.statusCode() == 200 + && instantsAreTheSame + && rc.responseHeaders().get("Content-Type").equals("application/json") + && OidcConstants.AUTHORIZATION_CODE.equals(rc.requestProperties().get(OidcConstants.GRANT_TYPE)) + && "code-flow-user-info-github-cached-in-idtoken" + .equals(rc.requestProperties().get(OidcUtils.TENANT_ID_ATTRIBUTE))) { + LOG.debug("Authorization code completed for tenant 'code-flow-user-info-github-cached-in-idtoken' in an instant: " + + instantsAreTheSame); + } + } + +} diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenResponseFilter.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenResponseFilter.java deleted file mode 100644 index 0a4dcb731fc7d7..00000000000000 --- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TokenResponseFilter.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.quarkus.it.keycloak; - -import jakarta.enterprise.context.ApplicationScoped; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.Unremovable; -import io.quarkus.oidc.common.OidcEndpoint; -import io.quarkus.oidc.common.OidcEndpoint.Type; -import io.quarkus.oidc.common.OidcResponseFilter; -import io.quarkus.oidc.common.runtime.OidcConstants; -import io.quarkus.oidc.runtime.OidcUtils; - -@ApplicationScoped -@Unremovable -@OidcEndpoint(value = Type.TOKEN) -public class TokenResponseFilter implements OidcResponseFilter { - private static final Logger LOG = Logger.getLogger(TokenResponseFilter.class); - - @Override - public void filter(OidcResponseContext rc) { - if (rc.statusCode() == 200 - && rc.responseHeaders().get("Content-Type").equals("application/json") - && OidcConstants.AUTHORIZATION_CODE.equals(rc.requestProperties().get(OidcConstants.GRANT_TYPE)) - && "code-flow-user-info-github-cached-in-idtoken" - .equals(rc.requestProperties().get(OidcUtils.TENANT_ID_ATTRIBUTE))) { - LOG.debug("Authorization code completed for tenant 'code-flow-user-info-github-cached-in-idtoken'"); - } - } - -} diff --git a/integration-tests/oidc-wiremock/src/main/resources/application.properties b/integration-tests/oidc-wiremock/src/main/resources/application.properties index edeeaceebf8423..dab898294d1c59 100644 --- a/integration-tests/oidc-wiremock/src/main/resources/application.properties +++ b/integration-tests/oidc-wiremock/src/main/resources/application.properties @@ -244,8 +244,8 @@ quarkus.log.category."io.quarkus.oidc.runtime.OidcProviderClient".min-level=TRAC quarkus.log.category."io.quarkus.oidc.runtime.OidcProviderClient".level=TRACE quarkus.log.category."io.quarkus.it.keycloak.SignedUserInfoResponseFilter".min-level=TRACE quarkus.log.category."io.quarkus.it.keycloak.SignedUserInfoResponseFilter".level=TRACE -quarkus.log.category."io.quarkus.it.keycloak.TokenResponseFilter".min-level=TRACE -quarkus.log.category."io.quarkus.it.keycloak.TokenResponseFilter".level=TRACE +quarkus.log.category."io.quarkus.it.keycloak.TokenRequestResponseFilter".min-level=TRACE +quarkus.log.category."io.quarkus.it.keycloak.TokenRequestResponseFilter".level=TRACE quarkus.log.file.enable=true quarkus.log.file.format=%C - %s%n diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java index 3c0f6461154c6b..0741e298c52360 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java @@ -467,7 +467,7 @@ public void run() throws Throwable { } else if (line.contains("Response contains signed UserInfo")) { signedUserInfoResponseFilterMessageDetected = true; } else if (line.contains( - "Authorization code completed for tenant 'code-flow-user-info-github-cached-in-idtoken'")) { + "Authorization code completed for tenant 'code-flow-user-info-github-cached-in-idtoken' in an instant: true")) { codeFlowCompletedResponseFilterMessageDetected = true; } if (lineConfirmingVerificationDetected