/* * 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.authentication.AuthenticationFlow; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; import org.keycloak.representations.idm.AuthenticationExecutionRepresentation; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.AssertAdminEvents; import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; 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> */ public class ExecutionTest extends AbstractAuthenticationTest { @Test public void testAddRemoveExecution() { // try add execution to built-in flow HashMap<String, String> params = new HashMap<>(); params.put("provider", "idp-review-profile"); try { 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 params.put("newName", "Copy-of-browser"); Response response = authMgmtResource.copy("browser", params); assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AdminEventPaths.authCopyFlowPath("browser"), params, ResourceType.AUTH_FLOW); try { Assert.assertEquals("Copy flow", 201, response.getStatus()); } finally { response.close(); } // add execution using inexistent provider params.put("provider", "test-execution"); try { authMgmtResource.addExecution("CopyOfBrowser", params); Assert.fail("add execution with inexistent provider should fail"); } catch(BadRequestException expected) { // Expected } // add execution - should succeed params.put("provider", "idp-review-profile"); authMgmtResource.addExecution("Copy-of-browser", params); assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AdminEventPaths.authAddExecutionPath("Copy-of-browser"), params, ResourceType.AUTH_EXECUTION); // check execution was added List<AuthenticationExecutionInfoRepresentation> executionReps = authMgmtResource.getExecutions("Copy-of-browser"); AuthenticationExecutionInfoRepresentation exec = findExecutionByProvider("idp-review-profile", executionReps); Assert.assertNotNull("idp-review-profile added", exec); // we'll need auth-cookie later AuthenticationExecutionInfoRepresentation authCookieExec = findExecutionByProvider("auth-cookie", executionReps); compareExecution(newExecInfo("Review Profile", "idp-review-profile", true, 0, 4, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec); // remove execution authMgmtResource.removeExecution(exec.getId()); assertAdminEvents.assertEvent(REALM_NAME, OperationType.DELETE, AdminEventPaths.authExecutionPath(exec.getId()), ResourceType.AUTH_EXECUTION); // check execution was removed executionReps = authMgmtResource.getExecutions("Copy-of-browser"); exec = findExecutionByProvider("idp-review-profile", executionReps); Assert.assertNull("idp-review-profile removed", exec); // now add the execution again using a different method and representation // delete auth-cookie authMgmtResource.removeExecution(authCookieExec.getId()); assertAdminEvents.assertEvent(REALM_NAME, OperationType.DELETE, AdminEventPaths.authExecutionPath(authCookieExec.getId()), ResourceType.AUTH_EXECUTION); AuthenticationExecutionRepresentation rep = new AuthenticationExecutionRepresentation(); rep.setPriority(10); 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()); } finally { 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<AuthenticationFlowRepresentation> flows = authMgmtResource.getFlows(); AuthenticationFlowRepresentation flow = findFlowByAlias("Copy-of-browser", flows); rep.setParentFlow(flow.getId()); // add execution - should succeed response = authMgmtResource.addExecution(rep); assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AssertAdminEvents.isExpectedPrefixFollowedByUuid(AdminEventPaths.authMgmtBasePath() + "/executions"), rep, ResourceType.AUTH_EXECUTION); try { Assert.assertEquals("added execution", 201, response.getStatus()); } finally { response.close(); } // check execution was added List<AuthenticationExecutionInfoRepresentation> executions = authMgmtResource.getExecutions("Copy-of-browser"); exec = findExecutionByProvider("auth-cookie", executions); Assert.assertNotNull("auth-cookie added", exec); // 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(newExecInfo("Cookie", "auth-cookie", false, 0, 3, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec); } @Test public void testUpdateExecution() { // get current auth-cookie execution List<AuthenticationExecutionInfoRepresentation> executionReps = authMgmtResource.getExecutions("browser"); AuthenticationExecutionInfoRepresentation exec = findExecutionByProvider("auth-cookie", executionReps); Assert.assertEquals("auth-cookie set to ALTERNATIVE", ALTERNATIVE, exec.getRequirement()); // switch from DISABLED to ALTERNATIVE exec.setRequirement(DISABLED); authMgmtResource.updateExecutions("browser", exec); assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authUpdateExecutionPath("browser"), exec, ResourceType.AUTH_EXECUTION); // make sure the change is visible executionReps = authMgmtResource.getExecutions("browser"); // get current auth-cookie execution 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); createFlow(clientFlow); // Add execution to it Map<String, String> executionData = new HashMap<>(); executionData.put("provider", ClientIdAndSecretAuthenticator.PROVIDER_ID); authMgmtResource.addExecution("new-client-flow", executionData); assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AdminEventPaths.authAddExecutionPath("new-client-flow"), executionData, ResourceType.AUTH_EXECUTION); // 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<AuthenticationExecutionInfoRepresentation> 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); assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authUpdateExecutionPath("new-client-flow"), executionRep, ResourceType.AUTH_EXECUTION); // 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()); assertAdminEvents.assertEvent(REALM_NAME, OperationType.DELETE, AdminEventPaths.authExecutionPath(executionRep.getId()), ResourceType.AUTH_EXECUTION); AuthenticationFlowRepresentation rep = findFlowByAlias("new-client-flow", authMgmtResource.getFlows()); authMgmtResource.deleteFlow(rep.getId()); assertAdminEvents.assertEvent(REALM_NAME, OperationType.DELETE, AdminEventPaths.authFlowPath(rep.getId()), ResourceType.AUTH_FLOW); } }