From 756d996a4adaeba369ca8487e7dbb3fb880dcfc9 Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 22 Jun 2017 22:23:20 +0200 Subject: [PATCH] KEYCLOAK-5085 RHSSO-1027 Fix to handle the exception thrown from alternative flow --- .../DefaultAuthenticationFlow.java | 17 +- .../ExpectedParamAuthenticator.java | 87 +++++ .../ExpectedParamAuthenticatorFactory.java | 128 +++++++ .../PushButtonAuthenticator.java | 90 +++++ .../PushButtonAuthenticatorFactory.java | 109 ++++++ ...ycloak.authentication.AuthenticatorFactory | 4 +- .../admin/authentication/ProvidersTest.java | 4 + .../forms/AuthenticatorSubflowsTest.java | 350 ++++++++++++++++++ .../base/src/test/resources/log4j.properties | 2 + .../src/test/resources/log4j.properties | 3 +- 10 files changed, 790 insertions(+), 4 deletions(-) create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticator.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticatorFactory.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticator.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticatorFactory.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/AuthenticatorSubflowsTest.java diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java index 3a9c53cf2a..89471e7a8f 100755 --- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java +++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java @@ -121,7 +121,20 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow { if (model.isAuthenticatorFlow()) { logger.debug("execution is flow"); AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model); - Response flowChallenge = authenticationFlow.processFlow(); + + Response flowChallenge = null; + try { + flowChallenge = authenticationFlow.processFlow(); + } catch (AuthenticationFlowException afe) { + if (model.isAlternative()) { + logger.debug("Thrown exception in alternative Subflow. Ignoring Subflow"); + processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.ATTEMPTED); + continue; + } else { + throw afe; + } + } + if (flowChallenge == null) { processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS); if (model.isAlternative()) alternativeSuccessful = true; @@ -183,7 +196,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow { // if (redirect != null) return redirect; AuthenticationProcessor.Result context = processor.createAuthenticatorContext(model, authenticator, executions); - logger.debug("invoke authenticator.authenticate"); + logger.debugv("invoke authenticator.authenticate: {0}", factory.getId()); authenticator.authenticate(context); Response response = processResult(context, false); if (response != null) return response; diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticator.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticator.java new file mode 100644 index 0000000000..08b475fbbb --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticator.java @@ -0,0 +1,87 @@ +/* + * Copyright 2017 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.testsuite.authentication; + +import org.jboss.logging.Logger; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.Authenticator; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint; + +/** + * @author Marek Posolda + */ +public class ExpectedParamAuthenticator implements Authenticator { + + public static final String EXPECTED_VALUE = "expected_value"; + + public static final String LOGGED_USER = "logged_user"; + + + private static final Logger logger = Logger.getLogger(ExpectedParamAuthenticator.class); + + @Override + public void authenticate(AuthenticationFlowContext context) { + String paramValue = context.getAuthenticationSession().getClientNote(AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + "foo"); + String expectedValue = context.getAuthenticatorConfig().getConfig().get(EXPECTED_VALUE); + logger.info("Value: " + paramValue + ", expectedValue: " + expectedValue); + + if (paramValue != null && paramValue.equals(expectedValue)) { + + String loggedUser = context.getAuthenticatorConfig().getConfig().get(LOGGED_USER); + if (loggedUser == null) { + logger.info("Successfully authenticated, but don't set any authenticated user"); + } else { + UserModel user = context.getSession().users().getUserByUsername(loggedUser, context.getRealm()); + logger.info("Successfully authenticated as user " + user.getUsername()); + context.setUser(user); + } + + context.success(); + } else { + context.attempted(); + } + } + + @Override + public void action(AuthenticationFlowContext context) { + } + + @Override + public boolean requiresUser() { + return false; + } + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + return true; + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + + } + + + @Override + public void close() { + + } +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticatorFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticatorFactory.java new file mode 100644 index 0000000000..2a10ab1caa --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/ExpectedParamAuthenticatorFactory.java @@ -0,0 +1,128 @@ +/* + * Copyright 2017 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.testsuite.authentication; + +import java.util.ArrayList; +import java.util.List; + +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.authentication.ConfigurableAuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ExpectedParamAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory { + + public static final String PROVIDER_ID = "expected-param-authenticator"; + + private static final ExpectedParamAuthenticator SINGLETON = new ExpectedParamAuthenticator(); + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public Authenticator create(KeycloakSession session) { + return SINGLETON; + } + + private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { + AuthenticationExecutionModel.Requirement.REQUIRED, + AuthenticationExecutionModel.Requirement.ALTERNATIVE, + AuthenticationExecutionModel.Requirement.DISABLED + }; + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public boolean isUserSetupAllowed() { + return false; + } + + @Override + public boolean isConfigurable() { + return true; + } + + @Override + public String getHelpText() { + return "You will be approved if you send query string parameter 'foo' with expected value."; + } + + @Override + public String getDisplayType() { + return "TEST: Expected Parameter"; + } + + @Override + public String getReferenceCategory() { + return "Expected Parameter"; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + private static final List configProperties = new ArrayList(); + + static { + ProviderConfigProperty property; + property = new ProviderConfigProperty(); + property.setName(ExpectedParamAuthenticator.EXPECTED_VALUE); + property.setLabel("Expected query parameter value"); + property.setType(ProviderConfigProperty.STRING_TYPE); + property.setHelpText("Expected value of query parameter foo. Authenticator will success if request to OIDC authz endpoint has this parameter"); + configProperties.add(property); + + property = new ProviderConfigProperty(); + property.setName(ExpectedParamAuthenticator.LOGGED_USER); + property.setLabel("Automatically logged user"); + property.setType(ProviderConfigProperty.STRING_TYPE); + property.setHelpText("This user will be successfully authenticated automatically when present"); + configProperties.add(property); + } + + + @Override + public List getConfigProperties() { + return configProperties; + } + + +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticator.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticator.java new file mode 100644 index 0000000000..bb7dcd3266 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticator.java @@ -0,0 +1,90 @@ +/* + * Copyright 2017 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.testsuite.authentication; + +import javax.ws.rs.core.Response; + +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.Authenticator; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; + +/** + * @author Marek Posolda + */ +public class PushButtonAuthenticator implements Authenticator { + + @Override + public void authenticate(AuthenticationFlowContext context) { + String accessCode = context.generateAccessCode(); + String actionUrl = context.getActionUrl(accessCode).toString(); + + StringBuilder response = new StringBuilder("PushTheButton"); + + UserModel user = context.getUser(); + if (user == null) { + response.append("No authenticated user
"); + } else { + response.append("Authenticated user: " + user.getUsername() + "
"); + } + + response.append("
"); + response.append(" This is the Test Approver. Press login to continue.
"); + response.append(" "); + response.append("
"); + String html = response.toString(); + + Response jaxrsResponse = Response + .status(Response.Status.OK) + .type("text/html") + .entity(html) + .build(); + + context.challenge(jaxrsResponse); + +// Response challenge = context.form().createForm("login-approve.ftl"); +// context.challenge(challenge); + } + + @Override + public void action(AuthenticationFlowContext context) { + context.success(); + } + + @Override + public boolean requiresUser() { + return false; + } + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + return false; + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + + } + + + @Override + public void close() { + + } +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticatorFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticatorFactory.java new file mode 100644 index 0000000000..84b177be14 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authentication/PushButtonAuthenticatorFactory.java @@ -0,0 +1,109 @@ +/* + * Copyright 2017 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.testsuite.authentication; + +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.authentication.ConfigurableAuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PushButtonAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory { + + public static final String PROVIDER_ID = "push-button-authenticator"; + private static final PushButtonAuthenticator SINGLETON = new PushButtonAuthenticator(); + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public Authenticator create(KeycloakSession session) { + return SINGLETON; + } + + private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { + AuthenticationExecutionModel.Requirement.REQUIRED, + AuthenticationExecutionModel.Requirement.ALTERNATIVE, + AuthenticationExecutionModel.Requirement.DISABLED + }; + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public boolean isUserSetupAllowed() { + return false; + } + + @Override + public boolean isConfigurable() { + return false; + } + + @Override + public String getHelpText() { + return "Just press the button to login."; + } + + @Override + public String getDisplayType() { + return "TEST: Button Login"; + } + + @Override + public String getReferenceCategory() { + return "Button Login"; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + private static final List configProperties = new ArrayList(); + + @Override + public List getConfigProperties() { + return configProperties; + } + + +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 660f91ff4d..18317d048c 100755 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -17,4 +17,6 @@ org.keycloak.testsuite.forms.PassThroughAuthenticator org.keycloak.testsuite.forms.PassThroughRegistration -org.keycloak.testsuite.forms.ClickThroughAuthenticator \ No newline at end of file +org.keycloak.testsuite.forms.ClickThroughAuthenticator +org.keycloak.testsuite.authentication.ExpectedParamAuthenticatorFactory +org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java index 5a50b081ba..e13794dc8e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java @@ -151,6 +151,8 @@ public class ProvidersTest extends AbstractAuthenticationTest { "Validates the password supplied as a 'password' form parameter in direct grant request"); addProviderInfo(result, "direct-grant-validate-username", "Username Validation", "Validates the username supplied as a 'username' form parameter in direct grant request"); + addProviderInfo(result, "expected-param-authenticator", "TEST: Expected Parameter", + "You will be approved if you send query string parameter 'foo' with expected value."); addProviderInfo(result, "http-basic-authenticator", "HTTP Basic Authentication", "Validates username and password from Authorization HTTP header"); addProviderInfo(result, "identity-provider-redirector", "Identity Provider Redirector", "Redirects to default Identity Provider or Identity Provider specified with kc_idp_hint query parameter"); addProviderInfo(result, "idp-confirm-link", "Confirm link existing account", "Show the form where user confirms if he wants " + @@ -163,6 +165,8 @@ public class ProvidersTest extends AbstractAuthenticationTest { "User reviews and updates profile data retrieved from Identity Provider in the displayed form"); addProviderInfo(result, "idp-username-password-form", "Username Password Form for identity provider reauthentication", "Validates a password from login form. Username is already known from identity provider authentication"); + addProviderInfo(result, "push-button-authenticator", "TEST: Button Login", + "Just press the button to login."); addProviderInfo(result, "reset-credential-email", "Send Reset Email", "Send email to user and wait for response."); addProviderInfo(result, "reset-credentials-choose-user", "Choose User", "Choose a user to reset credentials for"); addProviderInfo(result, "reset-otp", "Reset OTP", "Sets the Configure OTP required action if execution is REQUIRED. " + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/AuthenticatorSubflowsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/AuthenticatorSubflowsTest.java new file mode 100644 index 0000000000..d057ace07c --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/AuthenticatorSubflowsTest.java @@ -0,0 +1,350 @@ +/* + * Copyright 2017 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.testsuite.forms; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.graphene.page.Page; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory; +import org.keycloak.events.Details; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticationFlowModel; +import org.keycloak.models.AuthenticatorConfigModel; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.authentication.ExpectedParamAuthenticator; +import org.keycloak.testsuite.authentication.ExpectedParamAuthenticatorFactory; +import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory; +import org.keycloak.testsuite.pages.AppPage; +import org.keycloak.testsuite.pages.ErrorPage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.runonserver.RunOnServerDeployment; +import org.openqa.selenium.By; + +/** + * @author Marek Posolda + */ +public class AuthenticatorSubflowsTest extends AbstractTestRealmKeycloakTest { + + @Rule + public AssertEvents events = new AssertEvents(this); + + @Page + protected AppPage appPage; + + @Page + protected LoginPage loginPage; + + @Page + protected ErrorPage errorPage; + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + } + + @Deployment + public static WebArchive deploy() { + return RunOnServerDeployment.create(UserResource.class) + .addPackages(true, "org.keycloak.testsuite"); + } + + + @Before + public void setupFlows() { + testingClient.server().run(session -> { + RealmModel realm = session.realms().getRealmByName("test"); + + if (realm.getBrowserFlow().getAlias().equals("parent-flow")) { + return; + } + + // Parent flow + AuthenticationFlowModel browser = new AuthenticationFlowModel(); + browser.setAlias("parent-flow"); + browser.setDescription("browser based authentication"); + browser.setProviderId("basic-flow"); + browser.setTopLevel(true); + browser.setBuiltIn(true); + browser = realm.addAuthenticationFlow(browser); + realm.setBrowserFlow(browser); + + // Subflow1 + AuthenticationFlowModel subflow1 = new AuthenticationFlowModel(); + subflow1.setTopLevel(false); + subflow1.setBuiltIn(true); + subflow1.setAlias("subflow-1"); + subflow1.setDescription("Parameter 'foo=bar1' AND username+password"); + subflow1.setProviderId("basic-flow"); + subflow1 = realm.addAuthenticationFlow(subflow1); + + AuthenticationExecutionModel execution = new AuthenticationExecutionModel(); + execution.setParentFlow(browser.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setFlowId(subflow1.getId()); + execution.setPriority(10); + execution.setAuthenticatorFlow(true); + realm.addAuthenticatorExecution(execution); + + // Subflow1 - foo=bar1 + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow1.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator(ExpectedParamAuthenticatorFactory.PROVIDER_ID); + execution.setPriority(10); + execution.setAuthenticatorFlow(false); + + AuthenticatorConfigModel configModel = new AuthenticatorConfigModel(); + configModel.setAlias("bar1"); + Map config = new HashMap<>(); + config.put(ExpectedParamAuthenticator.EXPECTED_VALUE, "bar1"); + configModel.setConfig(config); + configModel = realm.addAuthenticatorConfig(configModel); + execution.setAuthenticatorConfig(configModel.getId()); + + realm.addAuthenticatorExecution(execution); + + // Subflow1 - username password + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow1.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator(UsernamePasswordFormFactory.PROVIDER_ID); + execution.setPriority(20); + execution.setAuthenticatorFlow(false); + + realm.addAuthenticatorExecution(execution); + + + + // Subflow2 + AuthenticationFlowModel subflow2 = new AuthenticationFlowModel(); + subflow2.setTopLevel(false); + subflow2.setBuiltIn(true); + subflow2.setAlias("subflow-2"); + subflow2.setDescription("username+password AND pushButton"); + subflow2.setProviderId("basic-flow"); + subflow2 = realm.addAuthenticationFlow(subflow2); + + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(browser.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setFlowId(subflow2.getId()); + execution.setPriority(20); + execution.setAuthenticatorFlow(true); + realm.addAuthenticatorExecution(execution); + + // Subflow2 - push the button + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow2.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator(PushButtonAuthenticatorFactory.PROVIDER_ID); + execution.setPriority(10); + execution.setAuthenticatorFlow(false); + + realm.addAuthenticatorExecution(execution); + + // Subflow2 - username-password + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow2.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator(UsernamePasswordFormFactory.PROVIDER_ID); + execution.setPriority(20); + execution.setAuthenticatorFlow(false); + + realm.addAuthenticatorExecution(execution); + + // Subflow3 + AuthenticationFlowModel subflow3 = new AuthenticationFlowModel(); + subflow3.setTopLevel(false); + subflow3.setBuiltIn(true); + subflow3.setAlias("subflow-3"); + subflow3.setDescription("alternative subflow with child subflows"); + subflow3.setProviderId("basic-flow"); + subflow3 = realm.addAuthenticationFlow(subflow3); + + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(browser.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setFlowId(subflow3.getId()); + execution.setPriority(30); + execution.setAuthenticatorFlow(true); + realm.addAuthenticatorExecution(execution); + + // Subflow3-1 + AuthenticationFlowModel subflow31 = new AuthenticationFlowModel(); + subflow31.setTopLevel(false); + subflow31.setBuiltIn(true); + subflow31.setAlias("subflow-31"); + subflow31.setDescription("subflow-31"); + subflow31.setProviderId("basic-flow"); + subflow31 = realm.addAuthenticationFlow(subflow31); + + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow3.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setFlowId(subflow31.getId()); + execution.setPriority(10); + execution.setAuthenticatorFlow(true); + realm.addAuthenticatorExecution(execution); + + // Subflow3-1 - foo=bar2 + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow31.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator(ExpectedParamAuthenticatorFactory.PROVIDER_ID); + execution.setPriority(10); + execution.setAuthenticatorFlow(false); + + configModel = new AuthenticatorConfigModel(); + configModel.setAlias("bar2"); + config = new HashMap<>(); + config.put(ExpectedParamAuthenticator.EXPECTED_VALUE, "bar2"); + config.put(ExpectedParamAuthenticator.LOGGED_USER, "john-doh@localhost"); + configModel.setConfig(config); + configModel = realm.addAuthenticatorConfig(configModel); + execution.setAuthenticatorConfig(configModel.getId()); + + realm.addAuthenticatorExecution(execution); + + // Subflow3-1 - push the button + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow31.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); + execution.setAuthenticator(PushButtonAuthenticatorFactory.PROVIDER_ID); + execution.setPriority(20); + execution.setAuthenticatorFlow(false); + + realm.addAuthenticatorExecution(execution); + + // Subflow3 - foo=bar3 + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(subflow3.getId()); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setAuthenticator(ExpectedParamAuthenticatorFactory.PROVIDER_ID); + execution.setPriority(20); + execution.setAuthenticatorFlow(false); + + configModel = new AuthenticatorConfigModel(); + configModel.setAlias("bar3"); + config = new HashMap<>(); + config.put(ExpectedParamAuthenticator.EXPECTED_VALUE, "bar3"); + config.put(ExpectedParamAuthenticator.LOGGED_USER, "keycloak-user@localhost"); + configModel.setConfig(config); + configModel = realm.addAuthenticatorConfig(configModel); + execution.setAuthenticatorConfig(configModel.getId()); + + realm.addAuthenticatorExecution(execution); + + + }); + } + + +// @Test +// public void testSleep() throws Exception { +// log.info("Start sleeping"); +// Thread.sleep(1000000); +// } + + + @Test + public void testSubflow1() throws Exception { + // Add foo=bar1 . I am redirected to subflow1 - username+password form + String loginFormUrl = oauth.getLoginFormUrl(); + loginFormUrl = loginFormUrl + "&foo=bar1"; + log.info("loginFormUrl: " + loginFormUrl); + + //Thread.sleep(10000000); + + driver.navigate().to(loginFormUrl); + + loginPage.assertCurrent(); + + // Fill username+password. I am successfully authenticated + oauth.fillLoginForm("test-user@localhost", "password"); + appPage.assertCurrent(); + + events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent(); + } + + + @Test + public void testSubflow2() throws Exception { + // Don't add 'foo' parameter. I am redirected to subflow2 - push the button + String loginFormUrl = oauth.getLoginFormUrl(); + log.info("loginFormUrl: " + loginFormUrl); + + //Thread.sleep(10000000); + + driver.navigate().to(loginFormUrl); + + Assert.assertEquals("PushTheButton", driver.getTitle()); + + // Push the button. I am redirected to username+password form + driver.findElement(By.name("submit1")).click(); + + + loginPage.assertCurrent(); + + // Fill username+password. I am successfully authenticated + oauth.fillLoginForm("test-user@localhost", "password"); + appPage.assertCurrent(); + + events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent(); + } + + +// @Test +// public void testSubflow31() { +// // Fill foo=bar2. I am see the pushButton +// String loginFormUrl = oauth.getLoginFormUrl(); +// loginFormUrl = loginFormUrl + "&foo=bar2"; +// log.info("loginFormUrl: " + loginFormUrl); +// +// //Thread.sleep(10000000); +// +// driver.navigate().to(loginFormUrl); +// Assert.assertEquals("PushTheButton", driver.getTitle()); +// +// // Confirm push button. I am authenticated as john-doh@localhost +// driver.findElement(By.name("submit1")).click(); +// +// appPage.assertCurrent(); +// +// events.expectLogin().detail(Details.USERNAME, "john-doh@localhost").assertEvent(); +// } +// +// +// @Test +// public void testSubflow32() { +// // Fill foo=bar3. I am login automatically as "keycloak-user@localhost" +// +// +// } + + +} 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 9c9bfc259c..1c425196dc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties @@ -69,3 +69,5 @@ log4j.logger.org.apache.directory.server.core=warn # log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace # log4j.logger.org.keycloak.keys.infinispan=trace log4j.logger.org.keycloak.services.clientregistration.policy=debug + +#log4j.logger.org.keycloak.authentication=debug diff --git a/testsuite/integration/src/test/resources/log4j.properties b/testsuite/integration/src/test/resources/log4j.properties index 6439950f9f..5f0d60b153 100755 --- a/testsuite/integration/src/test/resources/log4j.properties +++ b/testsuite/integration/src/test/resources/log4j.properties @@ -91,4 +91,5 @@ log4j.logger.org.apache.directory.server.ldap.LdapProtocolHandler=error #log4j.logger.org.keycloak.protocol=debug #log4j.logger.org.keycloak.services.resources.LoginActionsService=debug #log4j.logger.org.keycloak.services.managers=debug -#log4j.logger.org.keycloak.services.resources.SessionCodeChecks=debug \ No newline at end of file +#log4j.logger.org.keycloak.services.resources.SessionCodeChecks=debug +#log4j.logger.org.keycloak.authentication=debug \ No newline at end of file