From e9035bb7b34fb305f03d419bbae7520a16424bdb Mon Sep 17 00:00:00 2001 From: Takashi Norimatsu Date: Thu, 8 Apr 2021 09:11:21 +0900 Subject: [PATCH] KEYCLOAK-17681 Client Policy - Executor : Limiting available period of Request Object with its configuration --- .../executor/SecureRequestObjectExecutor.java | 32 +++++++++++++++++-- .../client/AbstractClientPoliciesTest.java | 6 ++-- .../testsuite/client/ClientPoliciesTest.java | 5 +-- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java index dbf49ed2fc..d082386137 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java @@ -19,6 +19,7 @@ package org.keycloak.services.clientpolicy.executor; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.ws.rs.core.MultivaluedMap; @@ -34,27 +35,51 @@ import org.keycloak.services.Urls; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; +import org.keycloak.services.clientpolicy.executor.PKCEEnforceExecutor.Configuration; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; /** * @author Takashi Norimatsu */ -public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider { +public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider { private static final Logger logger = Logger.getLogger(SecureRequestObjectExecutor.class); public static final String INVALID_REQUEST_OBJECT = "invalid_request_object"; + public static final Integer DEFAULT_AVAILABLE_PERIOD = Integer.valueOf(3600); // (sec) from FAPI 1.0 Advanced requirement private final KeycloakSession session; + private Configuration configuration; public SecureRequestObjectExecutor(KeycloakSession session) { this.session = session; } + @Override + public void setupConfiguration(Configuration config) { + this.configuration = config; + } + + @Override + public Class getExecutorConfigurationClass() { + return Configuration.class; + } + @JsonIgnoreProperties(ignoreUnknown = true) - public static class Configuration { + public static class Configuration extends ClientPolicyExecutorConfiguration { + @JsonProperty("available-period") + protected Integer availablePeriod; + + public Integer getAvailablePeriod() { + return availablePeriod; + } + + public void setAvailablePeriod(Integer availablePeriod) { + this.availablePeriod = availablePeriod; + } } @Override @@ -139,7 +164,8 @@ public class SecureRequestObjectExecutor implements ClientPolicyExecutorProvider } // check whether request object's available period is short - if (exp - nbf > 3600) { + int availablePeriod = Optional.ofNullable(configuration.getAvailablePeriod()).orElse(DEFAULT_AVAILABLE_PERIOD).intValue(); + if (exp - nbf > availablePeriod) { logger.trace("request object's available period is long."); throw new ClientPolicyException(INVALID_REQUEST_OBJECT, "Request's available period is long"); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java index b68bd601bd..a32c3bbf5e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java @@ -865,8 +865,10 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest { return config; } - protected Object createSecureRequestObjectExecutorConfig() { - return new SecureRequestObjectExecutor.Configuration(); + protected Object createSecureRequestObjectExecutorConfig(Integer availablePeriod) { + SecureRequestObjectExecutor.Configuration config = new SecureRequestObjectExecutor.Configuration(); + if (availablePeriod != null) config.setAvailablePeriod(availablePeriod); + return config; } protected Object createSecureResponseTypeExecutorConfig() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java index 052f6175ef..b2ab94db68 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java @@ -912,11 +912,12 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { @Test public void testSecureRequestObjectExecutor() throws Exception, URISyntaxException, IOException { + Integer availablePeriod = Integer.valueOf(SecureRequestObjectExecutor.DEFAULT_AVAILABLE_PERIOD + 400); // register profiles String json = (new ClientProfilesBuilder()).addProfile( (new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvy Profil", Boolean.FALSE, null) .addExecutor(SecureRequestObjectExecutorFactory.PROVIDER_ID, - createSecureRequestObjectExecutorConfig()) + createSecureRequestObjectExecutorConfig(availablePeriod)) .toRepresentation() ).toString(); updateProfiles(json); @@ -1000,7 +1001,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { // check whether request object's available period is short requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId); - requestObject.exp(requestObject.getNbf() + 3601); + requestObject.exp(requestObject.getNbf() + availablePeriod.intValue() + 1); registerRequestObject(requestObject, clientId, Algorithm.ES256, false); oauth.openLoginForm(); assertEquals(SecureRequestObjectExecutor.INVALID_REQUEST_OBJECT, oauth.getCurrentQuery().get(OAuth2Constants.ERROR));