From fba264433a038d6fac19af10c7567f967555855e Mon Sep 17 00:00:00 2001 From: Dmitry Telegin Date: Mon, 3 Jul 2017 02:57:58 +0300 Subject: [PATCH 1/3] KEYCLOAK-5131 ProviderFactory::postInit not called with hot deployment --- .../org/keycloak/services/DefaultKeycloakSessionFactory.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index 0869c5dfc3..21da694ca7 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -112,6 +112,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr public void deploy(ProviderManager pm) { Map, Map> copy = getFactoriesCopy(); Map, Map> newFactories = loadFactories(pm); + List deployed = new LinkedList<>(); List undeployed = new LinkedList<>(); for (Map.Entry, Map> entry : newFactories.entrySet()) { @@ -120,6 +121,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr copy.put(entry.getKey(), entry.getValue()); } else { for (ProviderFactory f : entry.getValue().values()) { + deployed.add(f); ProviderFactory old = current.remove(f.getId()); if (old != null) undeployed.add(old); } @@ -131,6 +133,9 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr for (ProviderFactory factory : undeployed) { factory.close(); } + for (ProviderFactory factory : deployed) { + factory.postInit(this); + } } @Override From badba7adafd786c00def5a855158c2c7f96871b2 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 14 Jul 2017 07:01:54 +0200 Subject: [PATCH 2/3] KEYCLOAK-5143 Run auth-server-wildfly profile on Travis (#4317) --- .travis.yml | 25 ++++----- .../servers/auth-server/jboss/wildfly/pom.xml | 8 +++ .../oauth/LoginStatusIframeEndpointTest.java | 25 +++++---- .../testsuite/runonserver/ServerVersion.java | 22 ++++++++ .../base/src/test/resources/log4j.properties | 10 +--- testsuite/pom.xml | 1 + testsuite/utils/pom.xml | 36 +++++++++++++ .../org/keycloak/testsuite/LogTrimmer.java | 44 ++++++++++++++++ travis-run-tests.sh | 52 +++++++++---------- 9 files changed, 162 insertions(+), 61 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/ServerVersion.java create mode 100755 testsuite/utils/pom.xml create mode 100644 testsuite/utils/src/main/java/org/keycloak/testsuite/LogTrimmer.java diff --git a/.travis.yml b/.travis.yml index ec6ed80dac..83b8707290 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,26 @@ language: java cache: - directories: - - $HOME/.m2 - -before_cache: - - rm -rf $HOME/.m2/repository/org/keycloak + cache: false env: global: - MAVEN_SKIP_RC=true - - MAVEN_OPTS="-Xms512m -Xmx2048m" + - MAVEN_OPTS="-Xms512m -Xmx1536m" matrix: - - TESTS=group1 - - TESTS=group2 - - TESTS=group3 - - TESTS=group4 + - TESTS=unit + - TESTS=server-group1 + - TESTS=server-group2 + - TESTS=server-group3 + - TESTS=server-group4 - TESTS=old jdk: - oraclejdk8 -before_script: - - export MAVEN_SKIP_RC=true +install: true -install: - - travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTestsuite -B -V -q - -script: +script: - ./travis-run-tests.sh $TESTS sudo: false diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/wildfly/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/wildfly/pom.xml index 5aa1659653..675104f219 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/wildfly/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/wildfly/pom.xml @@ -30,6 +30,14 @@ integration-arquillian-servers-auth-server-wildfly Auth Server - JBoss - Wildfly + + + + org.keycloak + keycloak-server-dist + zip + + wildfly diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java index e2ab0f858b..b480cdbca4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java @@ -42,6 +42,7 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.ActionURIUtils; import org.keycloak.testsuite.runonserver.RunOnServerDeployment; +import org.keycloak.testsuite.runonserver.ServerVersion; import java.io.IOException; import java.net.URLEncoder; @@ -63,7 +64,7 @@ public class LoginStatusIframeEndpointTest extends AbstractKeycloakTest { @Deployment public static WebArchive deploy() { - return RunOnServerDeployment.create(LoginStatusIframeEndpointTest.class); + return RunOnServerDeployment.create(LoginStatusIframeEndpointTest.class, ServerVersion.class); } @Test @@ -197,20 +198,24 @@ public class LoginStatusIframeEndpointTest extends AbstractKeycloakTest { @Test public void checkIframeCache() throws IOException { - String version = testingClient.server().fetch(session -> Version.RESOURCES_VERSION, String.class); + String version = testingClient.server().fetch(new ServerVersion()); CloseableHttpClient client = HttpClients.createDefault(); - HttpGet get = new HttpGet(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html"); - CloseableHttpResponse response = client.execute(get); + try { + HttpGet get = new HttpGet(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html"); + CloseableHttpResponse response = client.execute(get); - assertEquals(200, response.getStatusLine().getStatusCode()); - assertEquals("no-cache, must-revalidate, no-transform, no-store", response.getHeaders("Cache-Control")[0].getValue()); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertEquals("no-cache, must-revalidate, no-transform, no-store", response.getHeaders("Cache-Control")[0].getValue()); - get = new HttpGet(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html?version=" + version); - response = client.execute(get); + get = new HttpGet(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html?version=" + version); + response = client.execute(get); - assertEquals(200, response.getStatusLine().getStatusCode()); - assertTrue(response.getHeaders("Cache-Control")[0].getValue().contains("max-age")); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertTrue(response.getHeaders("Cache-Control")[0].getValue().contains("max-age")); + } finally { + client.close(); + } } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/ServerVersion.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/ServerVersion.java new file mode 100644 index 0000000000..3e565dc1a6 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/ServerVersion.java @@ -0,0 +1,22 @@ +package org.keycloak.testsuite.runonserver; + +import org.keycloak.common.Version; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.representations.idm.ComponentRepresentation; + +/** + * Created by st on 26.01.17. + */ +public class ServerVersion implements FetchOnServerWrapper { + + @Override + public FetchOnServer getRunOnServer() { + return (FetchOnServer) session -> Version.RESOURCES_VERSION; + } + + @Override + public Class getResultClass() { + return String.class; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties index 167c611c8f..904526ee24 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties @@ -21,21 +21,15 @@ log4j.appender.keycloak=org.apache.log4j.ConsoleAppender log4j.appender.keycloak.layout=org.apache.log4j.PatternLayout log4j.appender.keycloak.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n -log4j.appender.testsuite=org.apache.log4j.ConsoleAppender -log4j.appender.testsuite.layout=org.apache.log4j.PatternLayout -log4j.appender.testsuite.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%C{1}] %m%n - # Logging with "info" when running test from IDE, but disabled when running test with "mvn" . Both cases can be overriden by use system property "keycloak.logging.level" (eg. -Dkeycloak.logging.level=debug ) -keycloak.logging.level=info -log4j.logger.org.keycloak=${keycloak.logging.level} +log4j.logger.org.keycloak=${keycloak.logging.level:info} log4j.logger.org.jboss.resteasy.resteasy_jaxrs.i18n=off #log4j.logger.org.keycloak.keys.DefaultKeyManager=trace #log4j.logger.org.keycloak.services.managers.AuthenticationManager=trace -log4j.logger.org.keycloak.testsuite=debug, testsuite -log4j.additivity.org.keycloak.testsuite=false +log4j.logger.org.keycloak.testsuite=${keycloak.testsuite.logging.level:debug} # Enable to view events # log4j.logger.org.keycloak.events=debug diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 547d81449f..0adb305304 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -53,6 +53,7 @@ integration tomcat8 integration-arquillian + utils diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml new file mode 100755 index 0000000000..0bbf053514 --- /dev/null +++ b/testsuite/utils/pom.xml @@ -0,0 +1,36 @@ + + + + + + keycloak-testsuite-pom + org.keycloak + 3.3.0.CR1-SNAPSHOT + + 4.0.0 + + keycloak-testsuite-utils + Keycloak TestSuite Utils + + + + 1.8 + 1.8 + + diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/LogTrimmer.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/LogTrimmer.java new file mode 100644 index 0000000000..d53bf5b6ef --- /dev/null +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/LogTrimmer.java @@ -0,0 +1,44 @@ +package org.keycloak.testsuite; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * Created by st on 03/07/17. + */ +public class LogTrimmer { + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + String testRunning = null; + StringBuilder sb = new StringBuilder(); + for(String l = br.readLine(); l != null; l = br.readLine()) { + if (testRunning == null) { + if (l.startsWith("Running")) { + testRunning = l.split(" ")[1]; + System.out.println(l); + } else { + System.out.println("-- " + l); + } + } else { + if (l.contains("Tests run:")) { + if (!(l.contains("Failures: 0") && l.contains("Errors: 0"))) { + System.out.println("--------- " + testRunning + " output start ---------"); + System.out.println(sb.toString()); + System.out.println("--------- " + testRunning + " output end ---------"); + } + System.out.println(l); + + + testRunning = null; + sb = new StringBuilder(); + } else { + sb.append(testRunning.substring(testRunning.lastIndexOf('.') + 1) + " ++ " + l); + sb.append("\n"); + } + } + } + } + +} diff --git a/travis-run-tests.sh b/travis-run-tests.sh index fd5e5e3bbe..4d4f905bdb 100755 --- a/travis-run-tests.sh +++ b/travis-run-tests.sh @@ -1,42 +1,40 @@ #!/bin/bash -e -mvn install --no-snapshot-updates -DskipTests=true -f testsuite +function run-server-tests { + cd testsuite/integration-arquillian + mvn install -B -nsu -Pauth-server-wildfly -DskipTests + + cd tests/base + mvn test -B -nsu -Pauth-server-wildfly -Dtest=$1 2>&1 | java -cp ../../../utils/target/classes org.keycloak.testsuite.LogTrimmer + exit ${PIPESTATUS[0]} +} + +mvn install -B -nsu -Pdistribution -DskipTests -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn if [ $1 == "old" ]; then - mvn test -B --no-snapshot-updates -f testsuite/integration - mvn test -B --no-snapshot-updates -f testsuite/jetty - mvn test -B --no-snapshot-updates -f testsuite/tomcat6 - mvn test -B --no-snapshot-updates -f testsuite/tomcat7 - mvn test -B --no-snapshot-updates -f testsuite/tomcat8 + cd testsuite + mvn test -B -nsu -f integration + mvn test -B -nsu -f jetty + mvn test -B -nsu -f tomcat7 + mvn test -B -nsu -f tomcat8 fi -if [ $1 == "group1" ]; then - cd testsuite/integration-arquillian/tests/base - mvn test -B --no-snapshot-updates -Dtest=org.keycloak.testsuite.ad*.**.*Test +if [ $1 == "unit" ]; then + mvn -B test -DskipTestsuite fi -if [ $1 == "group2" ]; then - cd testsuite/integration-arquillian/tests/base - mvn test -B --no-snapshot-updates -Dtest=org.keycloak.testsuite.ac*.**.*Test,org.keycloak.testsuite.b*.**.*Test,org.keycloak.testsuite.cli*.**.*Test,org.keycloak.testsuite.co*.**.*Test +if [ $1 == "server-group1" ]; then + run-server-tests org.keycloak.testsuite.ad*.**.*Test,!**/adapter/undertow/**/*Test fi -if [ $1 == "group3" ]; then - cd testsuite/integration-arquillian/tests/base - mvn test -B --no-snapshot-updates -Dtest=org.keycloak.testsuite.au*.**.*Test,org.keycloak.testsuite.d*.**.*Test,org.keycloak.testsuite.e*.**.*Test,org.keycloak.testsuite.f*.**.*Test,org.keycloak.testsuite.i*.**.*Test +if [ $1 == "server-group2" ]; then + run-server-tests org.keycloak.testsuite.ac*.**.*Test,org.keycloak.testsuite.b*.**.*Test,org.keycloak.testsuite.cli*.**.*Test,org.keycloak.testsuite.co*.**.*Test fi -if [ $1 == "group4" ]; then - cd testsuite/integration-arquillian/tests/base - mvn test -B --no-snapshot-updates -Dtest=org.keycloak.testsuite.k*.**.*Test,org.keycloak.testsuite.m*.**.*Test,org.keycloak.testsuite.o*.**.*Test,org.keycloak.testsuite.s*.**.*Test +if [ $1 == "server-group3" ]; then + run-server-tests org.keycloak.testsuite.au*.**.*Test,org.keycloak.testsuite.d*.**.*Test,org.keycloak.testsuite.e*.**.*Test,org.keycloak.testsuite.f*.**.*Test,org.keycloak.testsuite.i*.**.*Test fi -if [ $1 == "adapter" ]; then - cd testsuite/integration-arquillian/tests/other/adapters - mvn test -B --no-snapshot-updates +if [ $1 == "server-group4" ]; then + run-server-tests org.keycloak.testsuite.k*.**.*Test,org.keycloak.testsuite.m*.**.*Test,org.keycloak.testsuite.o*.**.*Test,org.keycloak.testsuite.s*.**.*Test fi - -if [ $1 == "console" ]; then - cd testsuite/integration-arquillian/tests/other/console - mvn test -B --no-snapshot-updates -fi - From ddcbee2bff6ee7ec1994f2b6020f7577c0539d0d Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Fri, 14 Jul 2017 13:31:47 +0200 Subject: [PATCH 3/3] KEYCLOAK-4187 Minor updates in API --- .../actiontoken/AbstractActionTokenHander.java | 7 ++++--- .../actiontoken/ActionTokenHandler.java | 2 +- .../actiontoken/DefaultActionToken.java | 4 ++-- .../actiontoken/DefaultActionTokenKey.java | 3 +++ .../services/resources/LoginActionsService.java | 16 +++++++++------- .../resources/LoginActionsServiceChecks.java | 9 +++++---- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java b/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java index 52d94d95f7..ccdc2f8cd9 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java @@ -20,6 +20,7 @@ import org.keycloak.Config.Scope; import org.keycloak.events.EventType; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.representations.JsonWebToken; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.sessions.AuthenticationSessionModel; @@ -27,7 +28,7 @@ import org.keycloak.sessions.AuthenticationSessionModel; * * @author hmlnarik */ -public abstract class AbstractActionTokenHander implements ActionTokenHandler, ActionTokenHandlerFactory { +public abstract class AbstractActionTokenHander implements ActionTokenHandler, ActionTokenHandlerFactory { private final String id; private final Class tokenClass; @@ -86,8 +87,8 @@ public abstract class AbstractActionTokenHander im } @Override - public String getAuthenticationSessionIdFromToken(T token) { - return token == null ? null : token.getAuthenticationSessionId(); + public String getAuthenticationSessionIdFromToken(T token, ActionTokenContext tokenContext) { + return token instanceof DefaultActionToken ? ((DefaultActionToken) token).getAuthenticationSessionId() : null; } @Override diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java b/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java index f8d02d3468..b61c9c0c57 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java @@ -64,7 +64,7 @@ public interface ActionTokenHandler extends Provider { * @param token Token. Can be {@code null} * @return authentication session ID */ - String getAuthenticationSessionIdFromToken(T token); + String getAuthenticationSessionIdFromToken(T token, ActionTokenContext tokenContext); /** * Returns a event type logged with {@link EventBuilder} class. diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java index ba4488039a..0f514d0d04 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java @@ -39,7 +39,7 @@ public class DefaultActionToken extends DefaultActionTokenKey implements ActionT public static final String JSON_FIELD_AUTHENTICATION_SESSION_ID = "asid"; - public static final Predicate ACTION_TOKEN_BASIC_CHECKS = t -> { + public static final Predicate ACTION_TOKEN_BASIC_CHECKS = t -> { if (t.getActionVerificationNonce() == null) { throw new VerificationException("Nonce not present."); } @@ -131,7 +131,7 @@ public class DefaultActionToken extends DefaultActionTokenKey implements ActionT *
  • {@code issuer}: URI of the given realm
  • *
  • {@code audience}: URI of the given realm (same as issuer)
  • * - * + * * @param session * @param realm * @param uri diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java index b41681f303..cc4ba32e3c 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java @@ -36,6 +36,9 @@ public class DefaultActionTokenKey extends JsonWebToken implements ActionTokenKe @JsonProperty(value = JSON_FIELD_ACTION_VERIFICATION_NONCE, required = true) private UUID actionVerificationNonce; + public DefaultActionTokenKey() { + } + public DefaultActionTokenKey(String userId, String actionId, int absoluteExpirationInSecs, UUID actionVerificationNonce) { this.subject = userId; this.type = actionId; diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index 054cc6ef0a..5e8b0fe8bf 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -16,7 +16,6 @@ */ package org.keycloak.services.resources; -import org.keycloak.authentication.actiontoken.DefaultActionToken; import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; @@ -27,6 +26,7 @@ import org.keycloak.authentication.RequiredActionContextResult; import org.keycloak.authentication.RequiredActionFactory; import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.TokenVerifier; +import org.keycloak.authentication.ExplainedVerificationException; import org.keycloak.authentication.actiontoken.*; import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionTokenHandler; import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator; @@ -59,6 +59,7 @@ import org.keycloak.protocol.LoginProtocol.Error; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.utils.OIDCResponseMode; import org.keycloak.protocol.oidc.utils.OIDCResponseType; +import org.keycloak.representations.JsonWebToken; import org.keycloak.services.ErrorPage; import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; @@ -405,7 +406,7 @@ public class LoginActionsService { return handleActionToken(key, execution, clientId); } - protected Response handleActionToken(String tokenString, String execution, String clientId) { + protected Response handleActionToken(String tokenString, String execution, String clientId) { T token; ActionTokenHandler handler; ActionTokenContext tokenContext; @@ -430,8 +431,8 @@ public class LoginActionsService { throw new ExplainedTokenVerificationException(null, Errors.NOT_ALLOWED, Messages.INVALID_REQUEST); } - TokenVerifier tokenVerifier = TokenVerifier.create(tokenString, DefaultActionToken.class); - DefaultActionToken aToken = tokenVerifier.getToken(); + TokenVerifier tokenVerifier = TokenVerifier.create(tokenString, DefaultActionTokenKey.class); + DefaultActionTokenKey aToken = tokenVerifier.getToken(); event .detail(Details.TOKEN_ID, aToken.getId()) @@ -477,6 +478,8 @@ public class LoginActionsService { return handleActionTokenVerificationException(null, ex, Errors.EXPIRED_CODE, Messages.EXPIRED_ACTION_TOKEN_NO_SESSION); } catch (ExplainedTokenVerificationException ex) { return handleActionTokenVerificationException(null, ex, ex.getErrorEvent(), ex.getMessage()); + } catch (ExplainedVerificationException ex) { + return handleActionTokenVerificationException(null, ex, ex.getErrorEvent(), ex.getMessage()); } catch (VerificationException ex) { return handleActionTokenVerificationException(null, ex, eventError, defaultErrorMessage); } @@ -485,7 +488,7 @@ public class LoginActionsService { tokenContext = new ActionTokenContext(session, realm, uriInfo, clientConnection, request, event, handler, execution, this::processFlow, this::brokerLoginFlow); try { - String tokenAuthSessionId = handler.getAuthenticationSessionIdFromToken(token); + String tokenAuthSessionId = handler.getAuthenticationSessionIdFromToken(token, tokenContext); if (tokenAuthSessionId != null) { // This can happen if the token contains ID but user opens the link in a new browser @@ -541,7 +544,6 @@ public class LoginActionsService { } } - private Response processFlowFromPath(String flowPath, AuthenticationSessionModel authSession, String errorMessage) { if (AUTHENTICATE_PATH.equals(flowPath)) { return processAuthentication(false, null, authSession, errorMessage); @@ -555,7 +557,7 @@ public class LoginActionsService { } - private ActionTokenHandler resolveActionTokenHandler(String actionId) throws VerificationException { + private ActionTokenHandler resolveActionTokenHandler(String actionId) throws VerificationException { if (actionId == null) { throw new VerificationException("Action token operation not set"); } diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java index 9edc513b44..b9031cb25b 100644 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java @@ -18,7 +18,7 @@ package org.keycloak.services.resources; import org.keycloak.TokenVerifier.Predicate; import org.keycloak.authentication.AuthenticationProcessor; -import org.keycloak.authentication.actiontoken.DefaultActionToken; +import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; import org.keycloak.authentication.ExplainedVerificationException; import org.keycloak.authentication.actiontoken.ActionTokenContext; import org.keycloak.authentication.actiontoken.ExplainedTokenVerificationException; @@ -152,7 +152,7 @@ public class LoginActionsServiceChecks { * Verifies whether the user given by ID both exists in the current realm. If yes, * it optionally also injects the user using the given function (e.g. into session context). */ - public static void checkIsUserValid(T token, ActionTokenContext context) throws VerificationException { + public static void checkIsUserValid(T token, ActionTokenContext context) throws VerificationException { try { checkIsUserValid(context.getSession(), context.getRealm(), token.getUserId(), context.getAuthenticationSession()::setAuthenticatedUser); } catch (ExplainedVerificationException ex) { @@ -178,7 +178,7 @@ public class LoginActionsServiceChecks { * Verifies whether the client denoted by client ID in token's {@code iss} ({@code issuedFor}) * field both exists and is enabled. */ - public static void checkIsClientValid(T token, ActionTokenContext context) throws VerificationException { + public static void checkIsClientValid(T token, ActionTokenContext context) throws VerificationException { String clientId = token.getIssuedFor(); AuthenticationSessionModel authSession = context.getAuthenticationSession(); ClientModel client = authSession == null ? null : authSession.getClient(); @@ -297,8 +297,9 @@ public class LoginActionsServiceChecks { return true; } - public static void checkTokenWasNotUsedYet(T token, ActionTokenContext context) throws VerificationException { + public static void checkTokenWasNotUsedYet(T token, ActionTokenContext context) throws VerificationException { ActionTokenStoreProvider actionTokenStore = context.getSession().getProvider(ActionTokenStoreProvider.class); + if (actionTokenStore.get(token) != null) { throw new ExplainedTokenVerificationException(token, Errors.EXPIRED_CODE, Messages.EXPIRED_ACTION); }