diff --git a/core/src/main/java/org/keycloak/representations/idm/RequiredActionProviderSimpleRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RequiredActionProviderSimpleRepresentation.java new file mode 100644 index 00000000000..1e0a60d0782 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/RequiredActionProviderSimpleRepresentation.java @@ -0,0 +1,46 @@ +/* + * 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.representations.idm; + +/** + * Some endpoints (like register new required action) doesn't support all the fields (like setEnabled etc). + * So this is just simplified version of full RequiredActionProviderRepresentation + * + * @author Marek Posolda + */ +public class RequiredActionProviderSimpleRepresentation { + + private String name; + private String providerId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java index d5de7be0767..d28f6bef27f 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthenticationManagementResource.java @@ -24,6 +24,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigInfoRepresentation; import org.keycloak.representations.idm.AuthenticatorConfigRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; +import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -127,22 +128,17 @@ public interface AuthenticationManagementResource { @Path("/executions/{executionId}/config") @POST @Consumes(MediaType.APPLICATION_JSON) - Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigRepresentation config); - - @Path("/executions/{executionId}/config/{id}") - @GET - @Produces(MediaType.APPLICATION_JSON) - AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id); + Response newExecutionConfig(@PathParam("executionId") String executionId, AuthenticatorConfigRepresentation config); @Path("unregistered-required-actions") @GET @Produces(MediaType.APPLICATION_JSON) - List> getUnregisteredRequiredActions(); + List getUnregisteredRequiredActions(); @Path("register-required-action") @POST @Consumes(MediaType.APPLICATION_JSON) - void registereRequiredAction(Map data); + void registerRequiredAction(RequiredActionProviderSimpleRepresentation action); @Path("required-actions") @GET @@ -161,7 +157,7 @@ public interface AuthenticationManagementResource { @Path("required-actions/{alias}") @DELETE - void updateRequiredAction(@PathParam("alias") String alias); + void removeRequiredAction(@PathParam("alias") String alias); @Path("config-description/{providerId}") @GET @@ -173,11 +169,6 @@ public interface AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) Map> getPerClientConfigDescription(); - @Path("config") - @POST - @Consumes(MediaType.APPLICATION_JSON) - Response createAuthenticatorConfig(AuthenticatorConfigRepresentation config); - @Path("config/{id}") @GET @Produces(MediaType.APPLICATION_JSON) diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java index 0a39094e73b..a720a327c91 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java @@ -701,6 +701,7 @@ public class AuthenticationManagementResource { * * @param execution Execution id * @param id Configuration id + * @deprecated Use rather {@link #getAuthenticatorConfig(String)} */ @Path("/executions/{executionId}/config/{id}") @GET @@ -760,7 +761,7 @@ public class AuthenticationManagementResource { @POST @Consumes(MediaType.APPLICATION_JSON) @NoCache - public void registereRequiredAction(Map data) { + public void registerRequiredAction(Map data) { auth.requireManage(); String providerId = data.get("providerId"); @@ -857,7 +858,7 @@ public class AuthenticationManagementResource { */ @Path("required-actions/{alias}") @DELETE - public void updateRequiredAction(@PathParam("alias") String alias) { + public void removeRequiredAction(@PathParam("alias") String alias) { auth.requireManage(); RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias); @@ -937,6 +938,7 @@ public class AuthenticationManagementResource { /** * Create new authenticator configuration * @param rep JSON describing new authenticator configuration + * @deprecated Use {@link #newExecutionConfig(String, AuthenticatorConfigRepresentation)} instead */ @Path("config") @POST diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/DummyRequiredActionFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/actions/DummyRequiredActionFactory.java similarity index 94% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/DummyRequiredActionFactory.java rename to testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/actions/DummyRequiredActionFactory.java index f62cd9f65e3..fb2f74d4186 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/DummyRequiredActionFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/actions/DummyRequiredActionFactory.java @@ -28,6 +28,9 @@ import org.keycloak.models.KeycloakSessionFactory; * @version $Revision: 1 $ */ public class DummyRequiredActionFactory implements RequiredActionFactory { + + public static final String PROVIDER_ID = "dummy-action"; + @Override public String getDisplayText() { return "Dummy Action"; @@ -55,6 +58,6 @@ public class DummyRequiredActionFactory implements RequiredActionFactory { @Override public String getId() { - return "dummy-action"; + return PROVIDER_ID; } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory similarity index 100% rename from testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory rename to testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java index 267c8329299..fda999015d4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; @@ -27,6 +28,7 @@ import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentat import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -74,4 +76,34 @@ public class Assert extends org.junit.Assert { throw new IllegalArgumentException(); } + + + /** + * Assert all the fields from map available. Array "expected" contains pairs when first value from pair is expected key + * and second is the expected value from the map for target key. + * + * Example config = {"key1" -> "value1" , "key2" -> "value2" } + * then assertMap(config, "key1", "value1", "key2", "value2" will return true + * + */ + public static void assertMap(Map config, String... expected) { + if (expected == null) { + expected = new String[] {}; + } + + Assert.assertEquals(config.size() * 2, expected.length); + for (int i=0 ; i { realm.flows().lowerPriority("nosuch"); }, Resource.REALM, true); invoke((realm) -> { realm.flows().removeExecution("nosuch"); }, Resource.REALM, true); invoke((realm, response) -> { response.set(realm.flows().newExecutionConfig("nosuch", new AuthenticatorConfigRepresentation())); }, Resource.REALM, true); - invoke((realm) -> { realm.flows().getAuthenticatorConfig("nosuch", "nosuch"); }, Resource.REALM, false); invoke((realm) -> { realm.flows().getUnregisteredRequiredActions(); }, Resource.REALM, false); - invoke((realm) -> { realm.flows().registereRequiredAction(new HashMap<>()); }, Resource.REALM, true); + invoke((realm) -> { realm.flows().registerRequiredAction(new RequiredActionProviderSimpleRepresentation()); }, Resource.REALM, true); invoke((realm) -> { realm.flows().getRequiredActions(); }, Resource.REALM, false, true); invoke((realm) -> { realm.flows().getRequiredAction("nosuch"); }, Resource.REALM, false); - invoke((realm) -> { realm.flows().updateRequiredAction("nosuch"); }, Resource.REALM, true); + invoke((realm) -> { realm.flows().removeRequiredAction("nosuch"); }, Resource.REALM, true); invoke((realm) -> { realm.flows().updateRequiredAction("nosuch", new RequiredActionProviderRepresentation()); }, Resource.REALM, true); invoke((realm) -> { realm.flows().getAuthenticatorConfigDescription("nosuch"); }, Resource.REALM, false); invoke((realm) -> { realm.flows().getPerClientConfigDescription(); }, Resource.REALM, false, true); - invoke((realm, response) -> { response.set(realm.flows().createAuthenticatorConfig(new AuthenticatorConfigRepresentation())); }, Resource.REALM, true); invoke((realm) -> { realm.flows().getAuthenticatorConfig("nosuch"); }, Resource.REALM, false); invoke((realm) -> { realm.flows().removeAuthenticatorConfig("nosuch"); }, Resource.REALM, true); invoke((realm) -> { realm.flows().updateAuthenticatorConfig("nosuch", new AuthenticatorConfigRepresentation()); }, Resource.REALM, true); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java index 72989a53509..c1267ff5a40 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java @@ -270,15 +270,7 @@ public class UserFederationMapperTest extends AbstractAdminTest { Assert.assertEquals("ldap-1", rep.getFederationProviderDisplayName()); Assert.assertEquals(federationMapperType, rep.getFederationMapperType()); - if (config == null) { - config = new String[] {}; - } - Assert.assertEquals(rep.getConfig().size() * 2, config.length); - for (int i=0 ; i mappers, String name) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java index 7e702a08daf..4d0f9484b02 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java @@ -31,7 +31,6 @@ import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.LDAPConstants; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; -import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserFederationSyncResultRepresentation; @@ -65,8 +64,8 @@ public class UserFederationTest extends AbstractAdminTest { Assert.assertTrue(dummyConfiguredProvider.getOptions() == null || dummyConfiguredProvider.getOptions().isEmpty()); Assert.assertEquals("Dummy User Federation Provider Help Text", dummyConfiguredProvider.getHelpText()); Assert.assertEquals(2, dummyConfiguredProvider.getProperties().size()); - assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(0), "prop1", "Prop1", "prop1Default", "Prop1 HelpText", ProviderConfigProperty.STRING_TYPE); - assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(1), "prop2", "Prop2", "true", "Prop2 HelpText", ProviderConfigProperty.BOOLEAN_TYPE); + Assert.assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(0), "prop1", "Prop1", "prop1Default", "Prop1 HelpText", ProviderConfigProperty.STRING_TYPE); + Assert.assertProviderConfigProperty(dummyConfiguredProvider.getProperties().get(1), "prop2", "Prop2", "true", "Prop2 HelpText", ProviderConfigProperty.BOOLEAN_TYPE); try { userFederation().getProviderFactory("not-existent"); @@ -80,14 +79,6 @@ public class UserFederationTest extends AbstractAdminTest { return realm.userFederation(); } - private void assertProviderConfigProperty(ConfigPropertyRepresentation property, String name, String label, String defaultValue, String helpText, String type) { - Assert.assertEquals(name, property.getName()); - Assert.assertEquals(label, property.getLabel()); - Assert.assertEquals(defaultValue, property.getDefaultValue()); - Assert.assertEquals(helpText, property.getHelpText()); - Assert.assertEquals(type, property.getType()); - } - @Test public void testCreateProvider() { @@ -332,16 +323,8 @@ public class UserFederationTest extends AbstractAdminTest { Assert.assertEquals(fullSyncPeriod, rep.getFullSyncPeriod()); Assert.assertEquals(changeSyncPeriod, rep.getChangedSyncPeriod()); Assert.assertEquals(lastSync, rep.getLastSync()); - if (config == null) { - config = new String[] {}; - } - Assert.assertEquals(rep.getConfig().size() * 2, config.length); - for (int i=0 ; iMarek Posolda + */ +public class AuthenticatorConfigTest extends AbstractAuthenticationTest { + + private String executionId; + + @Before + public void beforeConfigTest() { + Response response = authMgmtResource.createFlow(newFlow("firstBrokerLogin2", "firstBrokerLogin2", "basic-flow", true, false)); + Assert.assertEquals(201, response.getStatus()); + response.close(); + + HashMap params = new HashMap<>(); + params.put("provider", IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID); + authMgmtResource.addExecution("firstBrokerLogin2", params); + + List executionReps = authMgmtResource.getExecutions("firstBrokerLogin2"); + AuthenticationExecutionInfoRepresentation exec = findExecutionByProvider(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, executionReps); + Assert.assertNotNull(exec); + executionId = exec.getId(); + } + + @Override + public void afterAbstractKeycloakTest() { + AuthenticationFlowRepresentation flowRep = findFlowByAlias("firstBrokerLogin2", authMgmtResource.getFlows()); + authMgmtResource.deleteFlow(flowRep.getId()); + } + + + @Test + public void testCreateConfig() { + AuthenticatorConfigRepresentation cfg = newConfig("foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true"); + + // Attempt to create config for non-existent execution + Response response = authMgmtResource.newExecutionConfig("exec-id-doesnt-exists", cfg); + Assert.assertEquals(404, response.getStatus()); + response.close(); + + // Create config success + String cfgId = createConfig(executionId, cfg); + + // Assert found + AuthenticatorConfigRepresentation cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId); + assertConfig(cfgRep, cfgId, "foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true"); + + // Cleanup + authMgmtResource.removeAuthenticatorConfig(cfgId); + } + + + @Test + public void testUpdateConfig() { + AuthenticatorConfigRepresentation cfg = newConfig("foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true"); + String cfgId = createConfig(executionId, cfg); + AuthenticatorConfigRepresentation cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId); + + // Try to update not existent config + try { + authMgmtResource.updateAuthenticatorConfig("not-existent", cfgRep); + Assert.fail("Config didn't found"); + } catch (NotFoundException nfe) { + // Expected + } + + // Assert nothing changed + cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId); + assertConfig(cfgRep, cfgId, "foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true"); + + // Update success + cfgRep.setAlias("foo2"); + cfgRep.getConfig().put("configKey2", "configValue2"); + authMgmtResource.updateAuthenticatorConfig(cfgRep.getId(), cfgRep); + + // Assert updated + cfgRep = authMgmtResource.getAuthenticatorConfig(cfgRep.getId()); + assertConfig(cfgRep, cfgId, "foo2", + IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true", + "configKey2", "configValue2"); + } + + + @Test + public void testRemoveConfig() { + AuthenticatorConfigRepresentation cfg = newConfig("foo", IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true"); + String cfgId = createConfig(executionId, cfg); + AuthenticatorConfigRepresentation cfgRep = authMgmtResource.getAuthenticatorConfig(cfgId); + + // Assert execution has our config + AuthenticationExecutionInfoRepresentation execution = findExecutionByProvider( + IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, authMgmtResource.getExecutions("firstBrokerLogin2")); + Assert.assertEquals(cfgRep.getId(), execution.getAuthenticationConfig()); + + + // Test remove not-existent + try { + authMgmtResource.removeAuthenticatorConfig("not-existent"); + Assert.fail("Config didn't found"); + } catch (NotFoundException nfe) { + // Expected + } + + // Test remove our config + authMgmtResource.removeAuthenticatorConfig(cfgRep.getId()); + + // Assert config not found + try { + authMgmtResource.getAuthenticatorConfig(cfgRep.getId()); + Assert.fail("Not expected to find config"); + } catch (NotFoundException nfe) { + // Expected + } + + // Assert execution doesn't have our config + execution = findExecutionByProvider( + IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, authMgmtResource.getExecutions("firstBrokerLogin2")); + Assert.assertNull(execution.getAuthenticationConfig()); + } + + + private String createConfig(String executionId, AuthenticatorConfigRepresentation cfg) { + Response resp = authMgmtResource.newExecutionConfig(executionId, cfg); + Assert.assertEquals(201, resp.getStatus()); + String cfgId = ApiUtil.getCreatedId(resp); + Assert.assertNotNull(cfgId); + return cfgId; + } + + private AuthenticatorConfigRepresentation newConfig(String alias, String cfgKey, String cfgValue) { + AuthenticatorConfigRepresentation cfg = new AuthenticatorConfigRepresentation(); + cfg.setAlias(alias); + Map cfgMap = new HashMap<>(); + cfgMap.put(cfgKey, cfgValue); + cfg.setConfig(cfgMap); + return cfg; + } + + private void assertConfig(AuthenticatorConfigRepresentation cfgRep, String id, String alias, String... fields) { + Assert.assertEquals(id, cfgRep.getId()); + Assert.assertEquals(alias, cfgRep.getAlias()); + Assert.assertMap(cfgRep.getConfig(), fields); + } +} 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 7852e22b5fb..be0b9c44b56 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 @@ -19,15 +19,19 @@ package org.keycloak.testsuite.admin.authentication; import org.junit.Assert; import org.junit.Test; +import org.keycloak.authentication.AuthenticationFlow; +import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; +import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; import org.keycloak.representations.idm.AuthenticationExecutionRepresentation; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import javax.ws.rs.BadRequestException; -import javax.ws.rs.core.GenericType; +import javax.ws.rs.NotFoundException; import javax.ws.rs.core.Response; import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @author Marko Strukelj @@ -44,6 +48,15 @@ public class ExecutionTest extends AbstractAuthenticationTest { authMgmtResource.addExecution("browser", params); Assert.fail("add execution to built-in flow should fail"); } catch (BadRequestException expected) { + // Expected + } + + // try add execution to not-existent flow + try { + authMgmtResource.addExecution("not-existent", params); + Assert.fail("add execution to not-existent flow should fail"); + } catch (BadRequestException expected) { + // Expected } // copy built-in flow so we get a new editable flow @@ -95,6 +108,7 @@ public class ExecutionTest extends AbstractAuthenticationTest { rep.setAuthenticator("auth-cookie"); rep.setRequirement(OPTIONAL); + // Should fail - missing parent flow response = authMgmtResource.addExecution(rep); try { Assert.assertEquals("added execution missing parent flow", 400, response.getStatus()); @@ -102,6 +116,25 @@ public class ExecutionTest extends AbstractAuthenticationTest { response.close(); } + // Should fail - not existent parent flow + rep.setParentFlow("not-existent-id"); + response = authMgmtResource.addExecution(rep); + try { + Assert.assertEquals("added execution missing parent flow", 400, response.getStatus()); + } finally { + response.close(); + } + + // Should fail - add execution to builtin flow + AuthenticationFlowRepresentation browserFlow = findFlowByAlias("browser", authMgmtResource.getFlows()); + rep.setParentFlow(browserFlow.getId()); + response = authMgmtResource.addExecution(rep); + try { + Assert.assertEquals("added execution to builtin flow", 400, response.getStatus()); + } finally { + response.close(); + } + // get Copy of browser flow id, and set it on execution List flows = authMgmtResource.getFlows(); AuthenticationFlowRepresentation flow = findFlowByAlias("Copy of browser", flows); @@ -145,4 +178,70 @@ public class ExecutionTest extends AbstractAuthenticationTest { AuthenticationExecutionInfoRepresentation exec2 = findExecutionByProvider("auth-cookie", executionReps); compareExecution(exec, exec2); } + + @Test + public void testClientFlowExecutions() { + // Create client flow + AuthenticationFlowRepresentation clientFlow = newFlow("new-client-flow", "desc", AuthenticationFlow.CLIENT_FLOW, true, false); + Response response = authMgmtResource.createFlow(clientFlow); + Assert.assertEquals(201, response.getStatus()); + response.close(); + + // Add execution to it + Map executionData = new HashMap<>(); + executionData.put("provider", ClientIdAndSecretAuthenticator.PROVIDER_ID); + authMgmtResource.addExecution("new-client-flow", executionData); + + // Check executions of not-existent flow - SHOULD FAIL + try { + authMgmtResource.getExecutions("not-existent"); + Assert.fail("Not expected to find executions"); + } catch (NotFoundException nfe) { + // Expected + } + + // Check existent executions + List executions = authMgmtResource.getExecutions("new-client-flow"); + AuthenticationExecutionInfoRepresentation executionRep = findExecutionByProvider(ClientIdAndSecretAuthenticator.PROVIDER_ID, executions); + Assert.assertNotNull(executionRep); + + // Update execution with not-existent flow - SHOULD FAIL + try { + authMgmtResource.updateExecutions("not-existent", executionRep); + Assert.fail("Not expected to update execution with not-existent flow"); + } catch (NotFoundException nfe) { + // Expected + } + + // Update execution with not-existent ID - SHOULD FAIL + try { + AuthenticationExecutionInfoRepresentation executionRep2 = new AuthenticationExecutionInfoRepresentation(); + executionRep2.setId("not-existent"); + authMgmtResource.updateExecutions("new-client-flow", executionRep2); + Assert.fail("Not expected to update not-existent execution"); + } catch (NotFoundException nfe) { + // Expected + } + + // Update success + executionRep.setRequirement(ALTERNATIVE); + authMgmtResource.updateExecutions("new-client-flow", executionRep); + + // Check updated + executionRep = findExecutionByProvider(ClientIdAndSecretAuthenticator.PROVIDER_ID, authMgmtResource.getExecutions("new-client-flow")); + Assert.assertEquals(ALTERNATIVE, executionRep.getRequirement()); + + // Remove execution with not-existent ID + try { + authMgmtResource.removeExecution("not-existent"); + Assert.fail("Didn't expect to find execution"); + } catch (NotFoundException nfe) { + // Expected + } + + // Successfuly remove execution and flow + authMgmtResource.removeExecution(executionRep.getId()); + AuthenticationFlowRepresentation rep = findFlowByAlias("new-client-flow", authMgmtResource.getFlows()); + authMgmtResource.deleteFlow(rep.getId()); + } } 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 301da3fe3eb..80d17bb52ba 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 @@ -23,6 +23,7 @@ import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentat import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import javax.ws.rs.BadRequestException; +import javax.ws.rs.NotFoundException; import javax.ws.rs.core.Response; import java.util.HashMap; import java.util.List; @@ -55,6 +56,15 @@ public class FlowTest extends AbstractAuthenticationTest { response.close(); } + // try create flow without alias + response = authMgmtResource.createFlow(newFlow(null, "Browser flow", "basic-flow", true, false)); + try { + Assert.assertEquals("createFlow using the alias of existing flow should fail", 409, response.getStatus()); + } finally { + response.close(); + } + + // create new flow that should succeed AuthenticationFlowRepresentation newFlow = newFlow("browser-2", "Browser flow", "basic-flow", true, false); response = authMgmtResource.createFlow(newFlow); @@ -71,25 +81,46 @@ public class FlowTest extends AbstractAuthenticationTest { Assert.assertNotNull("created flow visible in parent", found); compareFlows(newFlow, found); + // check lookup flow with unexistent ID + try { + authMgmtResource.getFlow("id-123-notExistent"); + Assert.fail("Not expected to find unexistent flow"); + } catch (NotFoundException nfe) { + // Expected + } + // 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 + // add execution flow to some parent flow Map data = new HashMap<>(); data.put("alias", "SomeFlow"); data.put("type", "basic-flow"); data.put("description", "Test flow"); data.put("provider", "registration-page-form"); + // inexistent parent flow - should fail try { authMgmtResource.addExecutionFlow("inexistent-parent-flow-alias", data); Assert.fail("addExecutionFlow for inexistent parent should have failed"); } catch (Exception expected) { + // Expected } + // already existent flow - should fail + try { + data.put("alias", "browser"); + authMgmtResource.addExecutionFlow("browser-2", data); + Assert.fail("addExecutionFlow should have failed as browser flow already exists"); + } catch (Exception expected) { + // Expected + } + + // Successfully add flow + data.put("alias", "SomeFlow"); authMgmtResource.addExecutionFlow("browser-2", data); // check that new flow is returned in a children list @@ -117,6 +148,14 @@ public class FlowTest extends AbstractAuthenticationTest { flows = authMgmtResource.getFlows(); found = findFlowByAlias("browser-2", flows); Assert.assertNull("flow deleted", found); + + // Check deleting flow second time will fail + try { + authMgmtResource.deleteFlow("id-123-notExistent"); + Assert.fail("Not expected to delete flow, which doesn't exists"); + } catch (NotFoundException nfe) { + // Expected + } } 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 0f8487b74e1..b2437110df7 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 @@ -61,7 +61,7 @@ public class InitialFlowsTest extends AbstractAuthenticationTest { // separately load referenced configurations String configId = exec.getAuthenticationConfig(); if (configId != null && !configs.containsKey(configId)) { - configs.put(configId, authMgmtResource.getAuthenticatorConfig(exec.getId(), configId)); + configs.put(configId, authMgmtResource.getAuthenticatorConfig(configId)); } } result.add(new FlowExecutions(flow, executionReps)); 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 05800622693..a6f899c1941 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 @@ -17,8 +17,11 @@ package org.keycloak.testsuite.admin.authentication; -import org.junit.Assert; import org.junit.Test; +import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory; +import org.keycloak.representations.idm.AuthenticatorConfigInfoRepresentation; +import org.keycloak.representations.idm.ConfigPropertyRepresentation; +import org.keycloak.testsuite.Assert; import java.util.ArrayList; import java.util.Collections; @@ -28,6 +31,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import javax.ws.rs.NotFoundException; + /** * @author Marko Strukelj */ @@ -80,6 +85,41 @@ public class ProvidersTest extends AbstractAuthenticationTest { compareProviders(expected, result); } + @Test + public void testPerClientConfigDescriptions() { + Map> configs = authMgmtResource.getPerClientConfigDescription(); + Assert.assertTrue(configs.containsKey("client-jwt")); + Assert.assertTrue(configs.containsKey("client-secret")); + Assert.assertTrue(configs.containsKey("testsuite-client-passthrough")); + Assert.assertTrue(configs.get("client-jwt").isEmpty()); + Assert.assertTrue(configs.get("client-secret").isEmpty()); + List cfg = configs.get("testsuite-client-passthrough"); + Assert.assertProviderConfigProperty(cfg.get(0), "passthroughauth.foo", "Foo Property", null, + "Foo Property of this authenticator, which does nothing", "String"); + Assert.assertProviderConfigProperty(cfg.get(1), "passthroughauth.bar", "Bar Property", null, + "Bar Property of this authenticator, which does nothing", "boolean"); + } + + @Test + public void testAuthenticatorConfigDescription() { + // Try some not-existent provider + try { + authMgmtResource.getAuthenticatorConfigDescription("not-existent"); + Assert.fail("Don't expected to find provider 'not-existent'"); + } catch (NotFoundException nfe) { + // Expected + } + + AuthenticatorConfigInfoRepresentation infoRep = authMgmtResource.getAuthenticatorConfigDescription(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID); + Assert.assertEquals("Create User If Unique", infoRep.getName()); + Assert.assertEquals(IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, infoRep.getProviderId()); + Assert.assertEquals("Detect if there is existing Keycloak account with same email like identity provider. If no, create new user", infoRep.getHelpText()); + Assert.assertEquals(1, infoRep.getProperties().size()); + Assert.assertProviderConfigProperty(infoRep.getProperties().get(0), "require.password.update.after.registration", "Require Password Update After Registration", + null, "If this option is true and new user is successfully imported from Identity Provider to Keycloak (there is no duplicated email or username detected in Keycloak DB), then this user is required to update his password", + "boolean"); + } + @Test public void testInitialAuthenticationProviders() { 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 index 0ac68649e4c..7418cb1bc99 100644 --- 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 @@ -20,6 +20,8 @@ package org.keycloak.testsuite.admin.authentication; import org.junit.Assert; import org.junit.Test; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; +import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation; +import org.keycloak.testsuite.actions.DummyRequiredActionFactory; import java.util.ArrayList; import java.util.Collections; @@ -28,6 +30,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import javax.ws.rs.NotFoundException; + /** * @author Marko Strukelj */ @@ -63,6 +67,58 @@ public class RequiredActionsTest extends AbstractAuthenticationTest { compareRequiredAction(forUpdate, updated); } + @Test + public void testCRUDRequiredAction() { + // Just Dummy RequiredAction is not registered in the realm + List result = authMgmtResource.getUnregisteredRequiredActions(); + Assert.assertEquals(1, result.size()); + RequiredActionProviderSimpleRepresentation action = result.get(0); + Assert.assertEquals(DummyRequiredActionFactory.PROVIDER_ID, action.getProviderId()); + Assert.assertEquals("Dummy Action", action.getName()); + + // Register it + authMgmtResource.registerRequiredAction(action); + + // Try to find not-existent action - should fail + try { + authMgmtResource.getRequiredAction("not-existent"); + Assert.fail("Didn't expect to find requiredAction of alias 'not-existent'"); + } catch (NotFoundException nfe) { + // Expected + } + + // Find existent + RequiredActionProviderRepresentation rep = authMgmtResource.getRequiredAction(DummyRequiredActionFactory.PROVIDER_ID); + compareRequiredAction(rep, newRequiredAction(DummyRequiredActionFactory.PROVIDER_ID, "Dummy Action", + true, false, Collections.emptyMap())); + + // Update not-existent - should fail + try { + authMgmtResource.updateRequiredAction("not-existent", rep); + Assert.fail("Not expected to update not-existent requiredAction"); + } catch (NotFoundException nfe) { + // Expected + } + + // Update (set it as defaultAction) + rep.setDefaultAction(true); + authMgmtResource.updateRequiredAction(DummyRequiredActionFactory.PROVIDER_ID, rep); + compareRequiredAction(rep, newRequiredAction(DummyRequiredActionFactory.PROVIDER_ID, "Dummy Action", + true, true, Collections.emptyMap())); + + // Remove unexistent - should fail + try { + authMgmtResource.removeRequiredAction("not-existent"); + Assert.fail("Not expected to remove not-existent requiredAction"); + } catch (NotFoundException nfe) { + // Expected + } + + // Remove success + authMgmtResource.removeRequiredAction(DummyRequiredActionFactory.PROVIDER_ID); + + } + private RequiredActionProviderRepresentation findRequiredActionByAlias(String alias, List list) { for (RequiredActionProviderRepresentation a: list) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java index f9d4f001bd1..9ce4f536e5a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ShiftExecutionTest.java @@ -21,7 +21,8 @@ import org.junit.Assert; import org.junit.Test; import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; -import javax.ws.rs.core.GenericType; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.BadRequestException; import javax.ws.rs.core.Response; import java.util.HashMap; import java.util.List; @@ -50,6 +51,14 @@ public class ShiftExecutionTest extends AbstractAuthenticationTest { AuthenticationExecutionInfoRepresentation last = executions.get(executions.size() - 1); AuthenticationExecutionInfoRepresentation oneButLast = executions.get(executions.size() - 2); + // Not possible to raisePriority of not-existent flow + try { + authMgmtResource.raisePriority("not-existent"); + Assert.fail("Not expected to raise priority of not existent flow"); + } catch (NotFoundException nfe) { + // Expected + } + // shift last execution up authMgmtResource.raisePriority(last.getId()); @@ -61,6 +70,14 @@ public class ShiftExecutionTest extends AbstractAuthenticationTest { Assert.assertEquals("Execution shifted up - N", last.getId(), oneButLast2.getId()); Assert.assertEquals("Execution shifted up - N-1", oneButLast.getId(), last2.getId()); + // Not possible to lowerPriority of not-existent flow + try { + authMgmtResource.lowerPriority("not-existent"); + Assert.fail("Not expected to raise priority of not existent flow"); + } catch (NotFoundException nfe) { + // Expected + } + // shift one before last down authMgmtResource.lowerPriority(oneButLast2.getId()); @@ -72,4 +89,29 @@ public class ShiftExecutionTest extends AbstractAuthenticationTest { Assert.assertEquals("Execution shifted down - N", last.getId(), last2.getId()); Assert.assertEquals("Execution shifted down - N-1", oneButLast.getId(), oneButLast2.getId()); } + + @Test + public void testBuiltinShiftNotAllowed() { + List executions = authMgmtResource.getExecutions("browser"); + + AuthenticationExecutionInfoRepresentation last = executions.get(executions.size() - 1); + AuthenticationExecutionInfoRepresentation oneButLast = executions.get(executions.size() - 2); + + // Not possible to raise - It's builtin flow + try { + authMgmtResource.raisePriority(last.getId()); + Assert.fail("Not expected to raise priority of builtin flow"); + } catch (BadRequestException nfe) { + // Expected + } + + // Not possible to lower - It's builtin flow + try { + authMgmtResource.lowerPriority(oneButLast.getId()); + Assert.fail("Not expected to lower priority of builtin flow"); + } catch (BadRequestException nfe) { + // Expected + } + + } }