keycloak-aplcache
Changes
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java 40(+37 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java 51(+49 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java 142(+97 -45)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialProvidersTest.java 101(+0 -101)
Details
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java
index 70013c5..fb7e966 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java
@@ -21,6 +21,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.keycloak.admin.client.resource.AuthenticationManagementResource;
import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
@@ -92,14 +93,41 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest {
Assert.assertEquals("Execution requirement choices - " + actual.getProviderId(), expected.getRequirementChoices(), actual.getRequirementChoices());
}
+ void compareExecution(AuthenticationExecutionExportRepresentation expected, AuthenticationExecutionExportRepresentation actual) {
+ Assert.assertEquals("Execution flowAlias - " + actual.getAuthenticator(), expected.getFlowAlias(), actual.getFlowAlias());
+ Assert.assertEquals("Execution authenticator - " + actual.getAuthenticator(), expected.getAuthenticator(), actual.getAuthenticator());
+ Assert.assertEquals("Execution userSetupAllowed - " + actual.getAuthenticator(), expected.isUserSetupAllowed(), actual.isUserSetupAllowed());
+ Assert.assertEquals("Execution authenticatorFlow - " + actual.getAuthenticator(), expected.isAutheticatorFlow(), actual.isAutheticatorFlow());
+ Assert.assertEquals("Execution authenticatorConfig - " + actual.getAuthenticator(), expected.getAuthenticatorConfig(), actual.getAuthenticatorConfig());
+ Assert.assertEquals("Execution priority - " + actual.getAuthenticator(), expected.getPriority(), actual.getPriority());
+ Assert.assertEquals("Execution requirement - " + actual.getAuthenticator(), expected.getRequirement(), actual.getRequirement());
+ }
+
+ void compareExecutions(List<AuthenticationExecutionExportRepresentation> expected, List<AuthenticationExecutionExportRepresentation> actual) {
+ Assert.assertNotNull("Executions should not be null", actual);
+ Assert.assertEquals("Size", expected.size(), actual.size());
+
+ for (int i = 0; i < expected.size(); i++) {
+ compareExecution(expected.get(i), actual.get(i));
+ }
+ }
+
void compareFlows(AuthenticationFlowRepresentation expected, AuthenticationFlowRepresentation actual) {
Assert.assertEquals("Flow alias", expected.getAlias(), actual.getAlias());
Assert.assertEquals("Flow description", expected.getDescription(), actual.getDescription());
Assert.assertEquals("Flow providerId", expected.getProviderId(), actual.getProviderId());
Assert.assertEquals("Flow top level", expected.isTopLevel(), actual.isTopLevel());
Assert.assertEquals("Flow built-in", expected.isBuiltIn(), actual.isBuiltIn());
- }
+ List<AuthenticationExecutionExportRepresentation> expectedExecs = expected.getAuthenticationExecutions();
+ List<AuthenticationExecutionExportRepresentation> actualExecs = actual.getAuthenticationExecutions();
+
+ if (expectedExecs == null) {
+ Assert.assertTrue("Executions should be null or empty", actualExecs == null || actualExecs.size() == 0);
+ } else {
+ compareExecutions(expectedExecs, actualExecs);
+ }
+ }
AuthenticationFlowRepresentation newFlow(String alias, String description,
String providerId, boolean topLevel, boolean builtIn) {
@@ -112,8 +140,8 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest {
return flow;
}
- AuthenticationExecutionInfoRepresentation newExecution(String displayName, String providerId, Boolean configurable,
- int level, int index, String requirement, Boolean authFlow, String[] choices) {
+ AuthenticationExecutionInfoRepresentation newExecInfo(String displayName, String providerId, Boolean configurable,
+ int level, int index, String requirement, Boolean authFlow, String[] choices) {
AuthenticationExecutionInfoRepresentation execution = new AuthenticationExecutionInfoRepresentation();
execution.setRequirement(requirement);
@@ -129,6 +157,12 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest {
return execution;
}
+ void addExecInfo(List<AuthenticationExecutionInfoRepresentation> target, String displayName, String providerId, Boolean configurable,
+ int level, int index, String requirement, Boolean authFlow, String[] choices) {
+
+ AuthenticationExecutionInfoRepresentation exec = newExecInfo(displayName, providerId, configurable, level, index, requirement, authFlow, choices);
+ target.add(exec);
+ }
AuthenticatorConfigRepresentation newConfig(String alias, String[] keyvalues) {
AuthenticatorConfigRepresentation config = new AuthenticatorConfigRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java
index f9c0bba..afe702f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java
@@ -83,7 +83,7 @@ public class ExecutionTest extends AbstractAuthenticationTest {
response.close();
}
- compareExecution(newExecution("Review Profile", "idp-review-profile", true, 0, 3, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec);
+ compareExecution(newExecInfo("Review Profile", "idp-review-profile", true, 0, 3, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec);
// remove execution
authMgmtResource.removeExecution(exec.getId());
@@ -143,7 +143,7 @@ public class ExecutionTest extends AbstractAuthenticationTest {
// Note: there is no checking in addExecution if requirement is one of requirementChoices
// Thus we can have OPTIONAL which is neither ALTERNATIVE, nor DISABLED
- compareExecution(newExecution("Cookie", "auth-cookie", false, 0, 2, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec);
+ compareExecution(newExecInfo("Cookie", "auth-cookie", false, 0, 2, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec);
}
@Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java
index 63c262a..3d4a307 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java
@@ -19,12 +19,14 @@ package org.keycloak.testsuite.admin.authentication;
import org.junit.Assert;
import org.junit.Test;
+import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@@ -62,13 +64,52 @@ public class FlowTest extends AbstractAuthenticationTest {
response.close();
}
- // check that new flow is returned
+ // check that new flow is returned in a children list
flows = authMgmtResource.getFlows();
AuthenticationFlowRepresentation found = findFlowByAlias("browser-2", flows);
- Assert.assertNotNull("created flow visible", found);
+ Assert.assertNotNull("created flow visible in parent", found);
compareFlows(newFlow, found);
+ // check that new flow is returned individually
+ AuthenticationFlowRepresentation found2 = authMgmtResource.getFlow(found.getId());
+ Assert.assertNotNull("created flow visible directly", found2);
+ compareFlows(newFlow, found2);
+
+
+ // add execution flow using a different method
+ Map<String, String> data = new HashMap<>();
+ data.put("alias", "SomeFlow");
+ data.put("type", "basic-flow");
+ data.put("description", "Test flow");
+ data.put("provider", "registration-page-form");
+
+ try {
+ authMgmtResource.addExecutionFlow("inexistent-parent-flow-alias", data);
+ Assert.fail("addExecutionFlow for inexistent parent should have failed");
+ } catch (Exception expected) {
+ }
+
+ authMgmtResource.addExecutionFlow("browser-2", data);
+
+ // check that new flow is returned in a children list
+ flows = authMgmtResource.getFlows();
+ found2 = findFlowByAlias("browser-2", flows);
+ Assert.assertNotNull("created flow visible in parent", found2);
+
+ List<AuthenticationExecutionExportRepresentation> execs = found2.getAuthenticationExecutions();
+ Assert.assertNotNull(execs);
+ Assert.assertEquals("Size one", 1, execs.size());
+
+ AuthenticationExecutionExportRepresentation expected = new AuthenticationExecutionExportRepresentation();
+ expected.setFlowAlias("SomeFlow");
+ expected.setUserSetupAllowed(false);
+ expected.setAuthenticator("registration-page-form");
+ expected.setAutheticatorFlow(true);
+ expected.setRequirement("DISABLED");
+ expected.setPriority(0);
+ compareExecution(expected, execs.get(0));
+
// delete non-built-in flow
authMgmtResource.deleteFlow(found.getId());
@@ -122,6 +163,12 @@ public class FlowTest extends AbstractAuthenticationTest {
// adjust expected values before comparing
browser.setAlias("Copy of browser");
browser.setBuiltIn(false);
+ browser.getAuthenticationExecutions().get(2).setFlowAlias("Copy of browser forms");
+ compareFlows(browser, copyOfBrowser);
+
+ // get new flow directly and compare
+ copyOfBrowser = authMgmtResource.getFlow(copyOfBrowser.getId());
+ Assert.assertNotNull(copyOfBrowser);
compareFlows(browser, copyOfBrowser);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java
index 1f42069..45f538f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java
@@ -19,6 +19,7 @@ package org.keycloak.testsuite.admin.authentication;
import org.junit.Assert;
import org.junit.Test;
+import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
@@ -81,12 +82,12 @@ public class InitialFlowsTest extends AbstractAuthenticationTest {
FlowExecutions fe2 = it2.next();
compareFlows(fe1.flow, fe2.flow);
- compareExecutions(fe1.executions, fe2.executions);
+ compareExecutionsInfo(fe1.executions, fe2.executions);
}
}
- private void compareExecutions(List<AuthenticationExecutionInfoRepresentation> expected, List<AuthenticationExecutionInfoRepresentation> actual) {
+ private void compareExecutionsInfo(List<AuthenticationExecutionInfoRepresentation> expected, List<AuthenticationExecutionInfoRepresentation> actual) {
Assert.assertEquals("Executions count", expected.size(), actual.size());
Iterator<AuthenticationExecutionInfoRepresentation> it1 = expected.iterator();
Iterator<AuthenticationExecutionInfoRepresentation> it2 = actual.iterator();
@@ -124,66 +125,117 @@ public class InitialFlowsTest extends AbstractAuthenticationTest {
LinkedList<FlowExecutions> expected = new LinkedList<>();
AuthenticationFlowRepresentation flow = newFlow("browser", "browser based authentication", "basic-flow", true, true);
- List<AuthenticationExecutionInfoRepresentation> executions = new LinkedList<>();
- executions.add(newExecution("Cookie", "auth-cookie", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}));
- executions.add(newExecution("Kerberos", "auth-spnego", false, 0, 1, DISABLED, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED}));
- executions.add(newExecution("forms", null, false, 0, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}));
- executions.add(newExecution("Username Password Form", "auth-username-password-form", false, 1, 0, REQUIRED, null, new String[]{REQUIRED}));
- executions.add(newExecution("OTP Form", "auth-otp-form", false, 1, 1, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, null, false, "auth-cookie", false, null, ALTERNATIVE, 10);
+ addExecExport(flow, null, false, "auth-spnego", false, null, DISABLED, 20);
+ addExecExport(flow, "forms", false, null, true, null, ALTERNATIVE, 30);
+
+ List<AuthenticationExecutionInfoRepresentation> execs = new LinkedList<>();
+ addExecInfo(execs, "Cookie", "auth-cookie", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
+ addExecInfo(execs, "Kerberos", "auth-spnego", false, 0, 1, DISABLED, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED});
+ addExecInfo(execs, "forms", null, false, 0, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED});
+ addExecInfo(execs, "Username Password Form", "auth-username-password-form", false, 1, 0, REQUIRED, null, new String[]{REQUIRED});
+ addExecInfo(execs, "OTP Form", "auth-otp-form", false, 1, 1, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED});
+ expected.add(new FlowExecutions(flow, execs));
flow = newFlow("clients", "Base authentication for clients", "client-flow", true, true);
- executions = new LinkedList<>();
- executions.add(newExecution("Client Id and Secret", "client-secret", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}));
- executions.add(newExecution("Signed Jwt", "client-jwt", false, 0, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, null, false, "client-secret", false, null, ALTERNATIVE, 10);
+ addExecExport(flow, null, false, "client-jwt", false, null, ALTERNATIVE, 20);
+
+ execs = new LinkedList<>();
+ addExecInfo(execs, "Client Id and Secret", "client-secret", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
+ addExecInfo(execs, "Signed Jwt", "client-jwt", false, 0, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
+ expected.add(new FlowExecutions(flow, execs));
flow = newFlow("direct grant", "OpenID Connect Resource Owner Grant", "basic-flow", true, true);
- executions = new LinkedList<>();
- executions.add(newExecution("Username Validation", "direct-grant-validate-username", false, 0, 0, REQUIRED, null, new String[]{REQUIRED}));
- executions.add(newExecution("Password", "direct-grant-validate-password", false, 0, 1, REQUIRED, null, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("OTP", "direct-grant-validate-otp", false, 0, 2, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, null, false, "direct-grant-validate-username", false, null, REQUIRED, 10);
+ addExecExport(flow, null, false, "direct-grant-validate-password", false, null, REQUIRED, 20);
+ addExecExport(flow, null, false, "direct-grant-validate-otp", false, null, OPTIONAL, 30);
+
+ execs = new LinkedList<>();
+ addExecInfo(execs, "Username Validation", "direct-grant-validate-username", false, 0, 0, REQUIRED, null, new String[]{REQUIRED});
+ addExecInfo(execs, "Password", "direct-grant-validate-password", false, 0, 1, REQUIRED, null, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "OTP", "direct-grant-validate-otp", false, 0, 2, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED});
+ expected.add(new FlowExecutions(flow, execs));
flow = newFlow("first broker login", "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
"basic-flow", true, true);
- executions = new LinkedList<>();
- executions.add(newExecution("Review Profile", "idp-review-profile", true, 0, 0, REQUIRED, null, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("Create User If Unique", "idp-create-user-if-unique", true, 0, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED}));
- executions.add(newExecution("Handle Existing Account", null, false, 0, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}));
- executions.add(newExecution("Confirm link existing account", "idp-confirm-link", false, 1, 0, REQUIRED, null, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("Verify existing account by Email", "idp-email-verification", false, 1, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED}));
- executions.add(newExecution("Verify Existing Account by Re-authentication", null, false, 1, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}));
- executions.add(newExecution("Username Password Form for identity provider reauthentication", "idp-username-password-form", false, 2, 0, REQUIRED, null, new String[]{REQUIRED}));
- executions.add(newExecution("OTP Form", "auth-otp-form", false, 2, 1, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, null, false, "idp-review-profile", false, "review profile config", REQUIRED, 10);
+ addExecExport(flow, null, false, "idp-create-user-if-unique", false, "create unique user config", ALTERNATIVE, 20);
+ addExecExport(flow, "Handle Existing Account", false, null, true, null, ALTERNATIVE, 30);
+
+ execs = new LinkedList<>();
+ addExecInfo(execs, "Review Profile", "idp-review-profile", true, 0, 0, REQUIRED, null, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "Create User If Unique", "idp-create-user-if-unique", true, 0, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED});
+ addExecInfo(execs, "Handle Existing Account", null, false, 0, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED});
+ addExecInfo(execs, "Confirm link existing account", "idp-confirm-link", false, 1, 0, REQUIRED, null, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "Verify existing account by Email", "idp-email-verification", false, 1, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED});
+ addExecInfo(execs, "Verify Existing Account by Re-authentication", null, false, 1, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED});
+ addExecInfo(execs, "Username Password Form for identity provider reauthentication", "idp-username-password-form", false, 2, 0, REQUIRED, null, new String[]{REQUIRED});
+ addExecInfo(execs, "OTP Form", "auth-otp-form", false, 2, 1, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED});
+ expected.add(new FlowExecutions(flow, execs));
flow = newFlow("registration", "registration flow", "basic-flow", true, true);
- executions = new LinkedList<>();
- executions.add(newExecution("registration form", "registration-page-form", false, 0, 0, REQUIRED, true, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("Registration User Creation", "registration-user-creation", false, 1, 0, REQUIRED, null, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("Profile Validation", "registration-profile-action", false, 1, 1, REQUIRED, null, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("Password Validation", "registration-password-action", false, 1, 2, REQUIRED, null, new String[]{REQUIRED, DISABLED}));
- executions.add(newExecution("Recaptcha", "registration-recaptcha-action", true, 1, 3, DISABLED, null, new String[]{REQUIRED, DISABLED}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, "registration form", false, "registration-page-form", true, null, REQUIRED, 10);
+
+ execs = new LinkedList<>();
+ addExecInfo(execs, "registration form", "registration-page-form", false, 0, 0, REQUIRED, true, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "Registration User Creation", "registration-user-creation", false, 1, 0, REQUIRED, null, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "Profile Validation", "registration-profile-action", false, 1, 1, REQUIRED, null, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "Password Validation", "registration-password-action", false, 1, 2, REQUIRED, null, new String[]{REQUIRED, DISABLED});
+ addExecInfo(execs, "Recaptcha", "registration-recaptcha-action", true, 1, 3, DISABLED, null, new String[]{REQUIRED, DISABLED});
+ expected.add(new FlowExecutions(flow, execs));
flow = newFlow("reset credentials", "Reset credentials for a user if they forgot their password or something", "basic-flow", true, true);
- executions = new LinkedList<>();
- executions.add(newExecution("Choose User", "reset-credentials-choose-user", false, 0, 0, REQUIRED, null, new String[]{REQUIRED}));
- executions.add(newExecution("Send Reset Email", "reset-credential-email", false, 0, 1, REQUIRED, null, new String[]{REQUIRED}));
- executions.add(newExecution("Reset Password", "reset-password", false, 0, 2, REQUIRED, null, new String[]{REQUIRED, OPTIONAL, DISABLED}));
- executions.add(newExecution("Reset OTP", "reset-otp", false, 0, 3, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, null, false, "reset-credentials-choose-user", false, null, REQUIRED, 10);
+ addExecExport(flow, null, false, "reset-credential-email", false, null, REQUIRED, 20);
+ addExecExport(flow, null, false, "reset-password", false, null, REQUIRED, 30);
+ addExecExport(flow, null, false, "reset-otp", false, null, OPTIONAL, 40);
+
+ execs = new LinkedList<>();
+ addExecInfo(execs, "Choose User", "reset-credentials-choose-user", false, 0, 0, REQUIRED, null, new String[]{REQUIRED});
+ addExecInfo(execs, "Send Reset Email", "reset-credential-email", false, 0, 1, REQUIRED, null, new String[]{REQUIRED});
+ addExecInfo(execs, "Reset Password", "reset-password", false, 0, 2, REQUIRED, null, new String[]{REQUIRED, OPTIONAL, DISABLED});
+ addExecInfo(execs, "Reset OTP", "reset-otp", false, 0, 3, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED});
+ expected.add(new FlowExecutions(flow, execs));
flow = newFlow("saml ecp", "SAML ECP Profile Authentication Flow", "basic-flow", true, true);
- executions = new LinkedList<>();
- executions.add(newExecution(null, "http-basic-authenticator", false, 0, 0, REQUIRED, null, new String[]{}));
- expected.add(new FlowExecutions(flow, executions));
+ addExecExport(flow, null, false, "http-basic-authenticator", false, null, REQUIRED, 10);
+
+ execs = new LinkedList<>();
+ addExecInfo(execs, null, "http-basic-authenticator", false, 0, 0, REQUIRED, null, new String[]{});
+ expected.add(new FlowExecutions(flow, execs));
return expected;
}
- static class FlowExecutions implements Comparable<FlowExecutions> {
+ private void addExecExport(AuthenticationFlowRepresentation flow, String flowAlias, boolean userSetupAllowed,
+ String authenticator, boolean authenticatorFlow, String authenticatorConfig,
+ String requirement, int priority) {
+
+ AuthenticationExecutionExportRepresentation rep = newExecutionExportRepresentation(flowAlias, userSetupAllowed,
+ authenticator, authenticatorFlow, authenticatorConfig, requirement, priority);
+
+ List<AuthenticationExecutionExportRepresentation> execs = flow.getAuthenticationExecutions();
+ if (execs == null) {
+ execs = new ArrayList<>();
+ flow.setAuthenticationExecutions(execs);
+ }
+ execs.add(rep);
+ }
+
+ private AuthenticationExecutionExportRepresentation newExecutionExportRepresentation(String flowAlias, boolean userSetupAllowed, String authenticator, boolean authenticatorFlow, String authenticatorConfig, String requirement, int priority) {
+ AuthenticationExecutionExportRepresentation rep = new AuthenticationExecutionExportRepresentation();
+ rep.setFlowAlias(flowAlias);
+ rep.setUserSetupAllowed(userSetupAllowed);
+ rep.setAuthenticator(authenticator);
+ rep.setAutheticatorFlow(authenticatorFlow);
+ rep.setAuthenticatorConfig(authenticatorConfig);
+ rep.setRequirement(requirement);
+ rep.setPriority(priority);
+ return rep;
+ }
+
+ private static class FlowExecutions implements Comparable<FlowExecutions> {
AuthenticationFlowRepresentation flow;
List<AuthenticationExecutionInfoRepresentation> executions;
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
new file mode 100644
index 0000000..9f679e3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2016 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.admin.authentication;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class ProvidersTest extends AbstractAuthenticationTest {
+
+ @Test
+ public void testFormProviders() {
+ List<Map<String, Object>> result = authMgmtResource.getFormProviders();
+
+ Assert.assertNotNull("null result", result);
+ Assert.assertEquals("size", 1, result.size());
+ Map<String, Object> item = result.get(0);
+
+ Assert.assertEquals("id", "registration-page-form", item.get("id"));
+ Assert.assertEquals("displayName", "Registration Page", item.get("displayName"));
+ Assert.assertEquals("description", "This is the controller for the registration page", item.get("description"));
+ }
+
+ @Test
+ public void testFormActionProviders() {
+ List<Map<String, Object>> result = authMgmtResource.getFormActionProviders();
+
+ List<Map<String, Object>> expected = new LinkedList<>();
+ addProviderInfo(expected, "registration-profile-action", "Profile Validation",
+ "Validates email, first name, and last name attributes and stores them in user data.");
+ addProviderInfo(expected, "registration-recaptcha-action", "Recaptcha",
+ "Adds Google Recaptcha button. Recaptchas verify that the entity that is registering is a human. " +
+ "This can only be used on the internet and must be configured after you add it.");
+ addProviderInfo(expected, "registration-password-action", "Password Validation",
+ "Validates that password matches password confirmation field. It also will store password in user's credential store.");
+ addProviderInfo(expected, "registration-user-creation", "Registration User Creation",
+ "This action must always be first! Validates the username of the user in validation phase. " +
+ "In success phase, this will create the user in the database.");
+
+ compareProviders(expected, result);
+ }
+
+ @Test
+ public void testClientAuthenticatorProviders() {
+ List<Map<String, Object>> result = authMgmtResource.getClientAuthenticatorProviders();
+
+ List<Map<String, Object>> expected = new LinkedList<>();
+ addProviderInfo(expected, "client-jwt", "Signed Jwt",
+ "Validates client based on signed JWT issued by client and signed with the Client private key");
+ addProviderInfo(expected, "client-secret", "Client Id and Secret", "Validates client based on 'client_id' and " +
+ "'client_secret' sent either in request parameters or in 'Authorization: Basic' header");
+
+ compareProviders(expected, result);
+ }
+
+
+ @Test
+ public void testInitialAuthenticationProviders() {
+
+ List<Map<String, Object>> providers = authMgmtResource.getAuthenticatorProviders();
+ providers = sortProviders(providers);
+
+ compareProviders(expectedAuthProviders(), providers);
+ }
+
+ private List<Map<String, Object>> expectedAuthProviders() {
+ ArrayList<Map<String, Object>> result = new ArrayList<>();
+ addProviderInfo(result, "auth-conditional-otp-form", "Conditional OTP Form",
+ "Validates a OTP on a separate OTP form. Only shown if required based on the configured conditions.");
+ addProviderInfo(result, "auth-cookie", "Cookie", "Validates the SSO cookie set by the auth server.");
+ addProviderInfo(result, "auth-otp-form", "OTP Form", "Validates a OTP on a separate OTP form.");
+ addProviderInfo(result, "auth-spnego", "Kerberos", "Initiates the SPNEGO protocol. Most often used with Kerberos.");
+ addProviderInfo(result, "auth-username-password-form", "Username Password Form",
+ "Validates a username and password from login form.");
+ addProviderInfo(result, "direct-grant-validate-otp", "OTP", "Validates the one time password supplied as a 'totp' form parameter in direct grant request");
+ addProviderInfo(result, "direct-grant-validate-password", "Password",
+ "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, "http-basic-authenticator", null, null);
+ addProviderInfo(result, "idp-confirm-link", "Confirm link existing account", "Show the form where user confirms if he wants " +
+ "to link identity provider with existing account or rather edit user profile data retrieved from identity provider to avoid conflict");
+ addProviderInfo(result, "idp-create-user-if-unique", "Create User If Unique", "Detect if there is existing Keycloak account " +
+ "with same email like identity provider. If no, create new user");
+ addProviderInfo(result, "idp-email-verification", "Verify existing account by Email", "Email verification of existing Keycloak " +
+ "user, that wants to link his user account with identity provider");
+ addProviderInfo(result, "idp-review-profile", "Review Profile",
+ "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, "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. " +
+ "Will also set it if execution is OPTIONAL and the OTP is currently configured for it.");
+ addProviderInfo(result, "reset-password", "Reset Password", "Sets the Update Password required action if execution is REQUIRED. " +
+ "Will also set it if execution is OPTIONAL and the password is currently configured for it.");
+ return result;
+ }
+
+ private List<Map<String, Object>> sortProviders(List<Map<String, Object>> providers) {
+ ArrayList<Map<String, Object>> sorted = new ArrayList<>(providers);
+ Collections.sort(sorted, new ProviderComparator());
+ return sorted;
+ }
+
+ private void compareProviders(List<Map<String, Object>> expected, List<Map<String, Object>> actual) {
+ Assert.assertEquals("Providers count", expected.size(), actual.size());
+ // compare ignoring list and map impl types
+ Assert.assertEquals(normalizeResults(expected), normalizeResults(actual));
+ }
+
+ private List<Map<String, Object>> normalizeResults(List<Map<String, Object>> list) {
+ ArrayList<Map<String, Object>> result = new ArrayList();
+ for (Map<String, Object> item: list) {
+ result.add(new HashMap(item));
+ }
+ return result;
+ }
+
+ private void addProviderInfo(List<Map<String, Object>> list, String id, String displayName, String description) {
+ HashMap<String, Object> item = new HashMap<>();
+ item.put("id", id);
+ item.put("displayName", displayName);
+ item.put("description", description);
+ list.add(item);
+ }
+
+ private static class ProviderComparator implements Comparator<Map<String, Object>> {
+ @Override
+ public int compare(Map<String, Object> o1, Map<String, Object> o2) {
+ return String.valueOf(o1.get("id")).compareTo(String.valueOf(o2.get("id")));
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java
new file mode 100644
index 0000000..0ac6864
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016 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.admin.authentication;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class RequiredActionsTest extends AbstractAuthenticationTest {
+
+ @Test
+ public void testRequiredActions() {
+ List<RequiredActionProviderRepresentation> result = authMgmtResource.getRequiredActions();
+
+ List<RequiredActionProviderRepresentation> expected = new ArrayList<>();
+ addRequiredAction(expected, "CONFIGURE_TOTP", "Configure Totp", true, false, null);
+ addRequiredAction(expected, "UPDATE_PASSWORD", "Update Password", true, false, null);
+ addRequiredAction(expected, "UPDATE_PROFILE", "Update Profile", true, false, null);
+ addRequiredAction(expected, "VERIFY_EMAIL", "Verify Email", true, false, null);
+ addRequiredAction(expected, "terms_and_conditions", "Terms and Conditions", false, false, null);
+
+ compareRequiredActions(expected, sort(result));
+
+ RequiredActionProviderRepresentation forUpdate = newRequiredAction("VERIFY_EMAIL", "Verify Email", false, false, null);
+ try {
+ authMgmtResource.updateRequiredAction(forUpdate.getAlias(), forUpdate);
+ Assert.fail("updateRequiredAction should fail due to null config");
+ } catch (Exception ignored) {
+ }
+
+ forUpdate.setConfig(Collections.<String, String>emptyMap());
+ authMgmtResource.updateRequiredAction(forUpdate.getAlias(), forUpdate);
+
+ result = authMgmtResource.getRequiredActions();
+ RequiredActionProviderRepresentation updated = findRequiredActionByAlias(forUpdate.getAlias(), result);
+
+ Assert.assertNotNull("Required Action still there", updated);
+ compareRequiredAction(forUpdate, updated);
+ }
+
+
+ private RequiredActionProviderRepresentation findRequiredActionByAlias(String alias, List<RequiredActionProviderRepresentation> list) {
+ for (RequiredActionProviderRepresentation a: list) {
+ if (alias.equals(a.getAlias())) {
+ return a;
+ }
+ }
+ return null;
+ }
+
+ private List<RequiredActionProviderRepresentation> sort(List<RequiredActionProviderRepresentation> list) {
+ ArrayList<RequiredActionProviderRepresentation> sorted = new ArrayList<>(list);
+ Collections.sort(sorted, new RequiredActionProviderComparator());
+ return sorted;
+ }
+
+ private void compareRequiredActions(List<RequiredActionProviderRepresentation> expected, List<RequiredActionProviderRepresentation> actual) {
+ Assert.assertNotNull("Actual null", actual);
+ Assert.assertEquals("Required actions count", expected.size(), actual.size());
+
+ Iterator<RequiredActionProviderRepresentation> ite = expected.iterator();
+ Iterator<RequiredActionProviderRepresentation> ita = actual.iterator();
+ while (ite.hasNext()) {
+ compareRequiredAction(ite.next(), ita.next());
+ }
+ }
+
+ private void compareRequiredAction(RequiredActionProviderRepresentation expected, RequiredActionProviderRepresentation actual) {
+ Assert.assertEquals("alias - " + expected.getAlias(), expected.getAlias(), actual.getAlias());
+ Assert.assertEquals("name - " + expected.getAlias(), expected.getName(), actual.getName());
+ Assert.assertEquals("enabled - " + expected.getAlias(), expected.isEnabled(), actual.isEnabled());
+ Assert.assertEquals("defaultAction - " + expected.getAlias(), expected.isDefaultAction(), actual.isDefaultAction());
+ Assert.assertEquals("config - " + expected.getAlias(), expected.getConfig() != null ? expected.getConfig() : Collections.emptyMap(), actual.getConfig());
+ }
+
+ private void addRequiredAction(List<RequiredActionProviderRepresentation> target, String alias, String name, boolean enabled, boolean defaultAction, Map conf) {
+ target.add(newRequiredAction(alias, name, enabled, defaultAction, conf));
+ }
+
+ private RequiredActionProviderRepresentation newRequiredAction(String alias, String name, boolean enabled, boolean defaultAction, Map conf) {
+ RequiredActionProviderRepresentation action = new RequiredActionProviderRepresentation();
+ action.setAlias(alias);
+ action.setName(name);
+ action.setEnabled(enabled);
+ action.setDefaultAction(defaultAction);
+ action.setConfig(conf);
+ return action;
+ }
+
+ private static class RequiredActionProviderComparator implements Comparator<RequiredActionProviderRepresentation> {
+ @Override
+ public int compare(RequiredActionProviderRepresentation o1, RequiredActionProviderRepresentation o2) {
+ return o1.getAlias().compareTo(o2.getAlias());
+ }
+ }
+}