Properly execute AuthenticationFlowCallbackProviderTest with Map storage
Closes #10268, Closes #10225
This commit is contained in:
parent
a7c8aa1dd3
commit
8a0f1ccb34
4 changed files with 113 additions and 21 deletions
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue