KEYCLOAK-9711 REQUIRED authentictor in ALTERNATIVE subflow throws AuthenticationFlowException when the authentictor returns ATTEMPTED.
This commit is contained in:
parent
9af4276310
commit
d593ac3e6f
2 changed files with 225 additions and 1 deletions
|
@ -94,7 +94,18 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
}
|
||||
if (model.isAuthenticatorFlow()) {
|
||||
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
|
||||
Response flowChallenge = authenticationFlow.processAction(actionExecution);
|
||||
Response flowChallenge = null;
|
||||
try {
|
||||
flowChallenge = authenticationFlow.processAction(actionExecution);
|
||||
} catch (AuthenticationFlowException afe) {
|
||||
if (model.isAlternative()) {
|
||||
logger.debug("Thrown exception in alternative Subflow. Ignoring Subflow");
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.ATTEMPTED);
|
||||
return processFlow();
|
||||
} else {
|
||||
throw afe;
|
||||
}
|
||||
}
|
||||
if (flowChallenge == null) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
|
||||
if (model.isAlternative()) alternativeSuccessful = true;
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright 2019 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 <a href="mailto:n1330@me.com">Tomohiro Nagai</a>
|
||||
*/
|
||||
public class AuthenticatorSubflowsTest2 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 - username password
|
||||
execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(subflow1.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
execution.setAuthenticator(UsernamePasswordFormFactory.PROVIDER_ID);
|
||||
execution.setPriority(10);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
|
||||
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(20);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
|
||||
AuthenticatorConfigModel configModel = new AuthenticatorConfigModel();
|
||||
configModel.setAlias("bar1");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put(ExpectedParamAuthenticator.EXPECTED_VALUE, "bar1");
|
||||
configModel.setConfig(config);
|
||||
configModel = realm.addAuthenticatorConfig(configModel);
|
||||
execution.setAuthenticatorConfig(configModel.getId());
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
|
||||
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 subflow1 - username+password form, then move to subflow2.
|
||||
String loginFormUrl = oauth.getLoginFormUrl();
|
||||
log.info("loginFormUrl: " + loginFormUrl);
|
||||
|
||||
driver.navigate().to(loginFormUrl);
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
// Fill username+password. I am redirected push the button.
|
||||
oauth.fillLoginForm("test-user@localhost", "password");
|
||||
Assert.assertEquals("PushTheButton", driver.getTitle());
|
||||
|
||||
// Push the button. I am successfully authenticated.
|
||||
driver.findElement(By.name("submit1")).click();
|
||||
appPage.assertCurrent();
|
||||
|
||||
events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue