Properly execute AuthenticationFlowCallbackProviderTest with Map storage

Closes #10268, Closes #10225
This commit is contained in:
Martin Bartoš 2022-02-23 09:11:03 +01:00 committed by Marek Posolda
parent a7c8aa1dd3
commit 8a0f1ccb34
4 changed files with 113 additions and 21 deletions

View file

@ -1,12 +1,17 @@
package org.keycloak.testsuite.pages; package org.keycloak.testsuite.pages;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
public class PageUtils { public class PageUtils {
public static String getPageTitle(WebDriver driver) { public static String getPageTitle(WebDriver driver) {
return driver.findElement(By.id("kc-page-title")).getText(); try {
return driver.findElement(By.id("kc-page-title")).getText();
} catch (NoSuchElementException e) {
return null;
}
} }
} }

View file

@ -81,9 +81,9 @@ import java.util.Scanner;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.keycloak.testsuite.admin.Users.setPasswordFor; import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST;
import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_PORT; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_PORT;
@ -188,12 +188,29 @@ public abstract class AbstractKeycloakTest {
adminClient = testContext.getAdminClient(); adminClient = testContext.getAdminClient();
} }
/**
* Executed before test realms import
* <p>
* In @Before block
*/
protected void beforeAbstractKeycloakTestRealmImport() throws Exception { protected void beforeAbstractKeycloakTestRealmImport() throws Exception {
} }
protected void postAfterAbstractKeycloak() throws Exception {
/**
* Executed after test realms import
* <p>
* In @Before block
*/
protected void afterAbstractKeycloakTestRealmImport() {
} }
protected void afterAbstractKeycloakTestRealmImport() {} /**
* Executed as the last task of each test case
* <p>
* In @After block
*/
protected void postAfterAbstractKeycloak() throws Exception {
}
@After @After
public void afterAbstractKeycloakTest() throws Exception { public void afterAbstractKeycloakTest() throws Exception {

View file

@ -18,12 +18,13 @@
package org.keycloak.testsuite.forms; package org.keycloak.testsuite.forms;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory; import org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory; import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
import org.keycloak.authentication.authenticators.conditional.ConditionalLoaAuthenticator; import org.keycloak.authentication.authenticators.conditional.ConditionalLoaAuthenticator;
import org.keycloak.authentication.authenticators.conditional.ConditionalLoaAuthenticatorFactory; import org.keycloak.authentication.authenticators.conditional.ConditionalLoaAuthenticatorFactory;
import org.keycloak.common.Profile;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
@ -36,7 +37,6 @@ import org.keycloak.testsuite.util.FlowUtil;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeThat;
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE; import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
/** /**
@ -45,6 +45,8 @@ import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerEx
@AuthServerContainerExclude(REMOTE) @AuthServerContainerExclude(REMOTE)
public class AuthenticationFlowCallbackProviderTest extends AbstractTestRealmKeycloakTest { public class AuthenticationFlowCallbackProviderTest extends AbstractTestRealmKeycloakTest {
protected static final String DEFAULT_FLOW = "newCallbackFlow";
@Page @Page
protected LoginPage loginPage; protected LoginPage loginPage;
@ -55,9 +57,18 @@ public class AuthenticationFlowCallbackProviderTest extends AbstractTestRealmKey
public void configureTestRealm(RealmRepresentation testRealm) { public void configureTestRealm(RealmRepresentation testRealm) {
} }
@Before
public void setUpFlow() {
setBrowserFlow();
}
@After
public void revertFlow() {
BrowserFlowTest.revertFlows(testRealm(), DEFAULT_FLOW);
}
@Test @Test
public void loaEssentialNonExisting() { public void loaEssentialNonExisting() {
setBrowserFlow();
LevelOfAssuranceFlowTest.openLoginFormWithAcrClaim(oauth, true, "4"); LevelOfAssuranceFlowTest.openLoginFormWithAcrClaim(oauth, true, "4");
loginPage.assertCurrent(); loginPage.assertCurrent();
@ -69,10 +80,6 @@ public class AuthenticationFlowCallbackProviderTest extends AbstractTestRealmKey
@Test @Test
public void errorWithCustomProvider() { public void errorWithCustomProvider() {
// Ignore test case for Map Storage - GitHub Issue #10225
assumeThat("This test case does not work properly with Map Storage", Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE), is(false));
setBrowserFlow();
LevelOfAssuranceFlowTest.openLoginFormWithAcrClaim(oauth, true, "1"); LevelOfAssuranceFlowTest.openLoginFormWithAcrClaim(oauth, true, "1");
loginPage.assertCurrent(); loginPage.assertCurrent();
@ -83,9 +90,9 @@ public class AuthenticationFlowCallbackProviderTest extends AbstractTestRealmKey
} }
protected void setBrowserFlow() { protected void setBrowserFlow() {
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow("newFlow")); testingClient.server(TEST_REALM_NAME).run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(DEFAULT_FLOW));
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session) testingClient.server(TEST_REALM_NAME).run(session -> FlowUtil.inCurrentRealm(session)
.selectFlow("newFlow") .selectFlow(DEFAULT_FLOW)
.inForms(forms -> forms .inForms(forms -> forms
.clear() .clear()
.addSubFlowExecution(AuthenticationExecutionModel.Requirement.CONDITIONAL, subflow -> subflow .addSubFlowExecution(AuthenticationExecutionModel.Requirement.CONDITIONAL, subflow -> subflow

View file

@ -8,16 +8,24 @@ import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.services.resources.admin.AuthenticationManagementResource; import org.keycloak.services.resources.admin.AuthenticationManagementResource;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.keycloak.models.utils.DefaultAuthenticationFlows.BROWSER_FLOW;
import static org.keycloak.models.utils.DefaultAuthenticationFlows.DIRECT_GRANT_FLOW;
import static org.keycloak.models.utils.DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW;
import static org.keycloak.models.utils.DefaultAuthenticationFlows.REGISTRATION_FLOW;
import static org.keycloak.models.utils.DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW;
public class FlowUtil { public class FlowUtil {
private RealmModel realm; private RealmModel realm;
private AuthenticationFlowModel currentFlow; private AuthenticationFlowModel currentFlow;
@ -67,23 +75,27 @@ public class FlowUtil {
} }
public FlowUtil copyBrowserFlow(String newFlowAlias) { public FlowUtil copyBrowserFlow(String newFlowAlias) {
return copyFlow(DefaultAuthenticationFlows.BROWSER_FLOW, newFlowAlias); checkAndRestoreDefaultFlow(realm::getBrowserFlow, realm::setBrowserFlow, newFlowAlias, BROWSER_FLOW);
return copyFlow(BROWSER_FLOW, newFlowAlias);
} }
public FlowUtil copyResetCredentialsFlow(String newFlowAlias) { public FlowUtil copyResetCredentialsFlow(String newFlowAlias) {
return copyFlow(DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW, newFlowAlias); checkAndRestoreDefaultFlow(realm::getResetCredentialsFlow, realm::setResetCredentialsFlow, newFlowAlias, RESET_CREDENTIALS_FLOW);
return copyFlow(RESET_CREDENTIALS_FLOW, newFlowAlias);
} }
public FlowUtil copyFirstBrokerLoginFlow(String newFlowAlias) { public FlowUtil copyFirstBrokerLoginFlow(String newFlowAlias) {
return copyFlow(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, newFlowAlias); return copyFlow(FIRST_BROKER_LOGIN_FLOW, newFlowAlias);
} }
public FlowUtil copyRegistrationFlow(String newFlowAlias) { public FlowUtil copyRegistrationFlow(String newFlowAlias) {
return copyFlow(DefaultAuthenticationFlows.REGISTRATION_FLOW, newFlowAlias); checkAndRestoreDefaultFlow(realm::getRegistrationFlow, realm::setRegistrationFlow, newFlowAlias, REGISTRATION_FLOW);
return copyFlow(REGISTRATION_FLOW, newFlowAlias);
} }
public FlowUtil copyDirectGrantFlow(String newFlowAlias) { public FlowUtil copyDirectGrantFlow(String newFlowAlias) {
return copyFlow(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW, newFlowAlias); checkAndRestoreDefaultFlow(realm::getDirectGrantFlow, realm::setDirectGrantFlow, newFlowAlias, DIRECT_GRANT_FLOW);
return copyFlow(DIRECT_GRANT_FLOW, newFlowAlias);
} }
public FlowUtil copyFlow(String original, String newFlowAlias) { public FlowUtil copyFlow(String original, String newFlowAlias) {
@ -92,6 +104,14 @@ public class FlowUtil {
if (existingBrowserFlow == null) { if (existingBrowserFlow == null) {
throw new FlowUtilException("Can't copy flow: " + original + " does not exist"); throw new FlowUtilException("Can't copy flow: " + original + " does not exist");
} }
// remove new authentication flow with 'newFlowAlias' alias if present
AuthenticationFlowModel foundFlow = realm.getFlowByAlias(newFlowAlias);
if (foundFlow != null) {
clearAuthenticationFlow(foundFlow.getId());
realm.removeAuthenticationFlow(foundFlow);
}
currentFlow = AuthenticationManagementResource.copyFlow(realm, existingBrowserFlow, newFlowAlias); currentFlow = AuthenticationManagementResource.copyFlow(realm, existingBrowserFlow, newFlowAlias);
return this; return this;
@ -119,7 +139,7 @@ public class FlowUtil {
} }
public FlowUtil clear() { public FlowUtil clear() {
realm.getAuthenticationExecutionsStream(currentFlow.getId()).forEachOrdered(realm::removeAuthenticatorExecution); clearAuthenticationFlow(currentFlow.getId());
return this; return this;
} }
@ -264,4 +284,47 @@ public class FlowUtil {
return this; return this;
} }
/**
* Remove authentication flows and executions included in the specified flow
*
* @param flowId id of flow, which content will be removed
*/
private void clearAuthenticationFlow(String flowId) {
realm.getAuthenticationExecutionsStream(flowId)
.filter(Objects::nonNull)
.forEachOrdered(f -> {
if (f.isAuthenticatorFlow() && f.getFlowId() != null) {
clearAuthenticationFlow(f.getFlowId());
realm.removeAuthenticationFlow(realm.getAuthenticationFlowById(f.getFlowId()));
}
realm.removeAuthenticatorExecution(f);
});
}
/**
* Check whether the new flow is set as default one
* If yes, restore the default one
* <p>
* Usable for removing flow, which must not be used as the default flow
*
* @param getFlow getter for obtaining the default flow
* @param setFlow setter for the setting of the default flow
* @param newFlowAlias alias of tested flow
* @param defaultFlowAlias default flow alias
*/
private void checkAndRestoreDefaultFlow(Supplier<AuthenticationFlowModel> getFlow,
Consumer<AuthenticationFlowModel> setFlow,
String newFlowAlias,
String defaultFlowAlias) {
if (getFlow == null || setFlow == null || newFlowAlias == null || defaultFlowAlias == null) return;
final String alias = Optional.ofNullable(getFlow.get())
.map(AuthenticationFlowModel::getAlias)
.orElse(null);
if (alias != null && alias.equals(newFlowAlias)) {
setFlow.accept(realm.getFlowByAlias(defaultFlowAlias));
}
}
} }