EYCLOAK-12741 Add name and description edit functionality to Authentication and Execution Flows
This commit is contained in:
parent
2ddfc94495
commit
8d6f8d0465
11 changed files with 323 additions and 17 deletions
|
@ -30,6 +30,7 @@ public class AuthenticationExecutionInfoRepresentation implements Serializable {
|
||||||
protected String requirement;
|
protected String requirement;
|
||||||
protected String displayName;
|
protected String displayName;
|
||||||
protected String alias;
|
protected String alias;
|
||||||
|
protected String description;
|
||||||
protected List<String> requirementChoices;
|
protected List<String> requirementChoices;
|
||||||
protected Boolean configurable;
|
protected Boolean configurable;
|
||||||
protected Boolean authenticationFlow;
|
protected Boolean authenticationFlow;
|
||||||
|
@ -63,6 +64,14 @@ public class AuthenticationExecutionInfoRepresentation implements Serializable {
|
||||||
this.alias = alias;
|
this.alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
public String getRequirement() {
|
public String getRequirement() {
|
||||||
return requirement;
|
return requirement;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,11 @@ public interface AuthenticationManagementResource {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
Response copy(@PathParam("flowAlias") String flowAlias, Map<String, String> data);
|
Response copy(@PathParam("flowAlias") String flowAlias, Map<String, String> data);
|
||||||
|
|
||||||
|
@Path("/flows/{id}")
|
||||||
|
@PUT
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
void updateFlow(@PathParam("id") String id, AuthenticationFlowRepresentation flow);
|
||||||
|
|
||||||
@Path("/flows/{flowAlias}/executions/flow")
|
@Path("/flows/{flowAlias}/executions/flow")
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
|
|
@ -255,6 +255,7 @@ public class AuthenticationManagementResource {
|
||||||
@PUT
|
@PUT
|
||||||
@NoCache
|
@NoCache
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Response updateFlow(@PathParam("id") String id, AuthenticationFlowRepresentation flow) {
|
public Response updateFlow(@PathParam("id") String id, AuthenticationFlowRepresentation flow) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
|
||||||
|
@ -264,10 +265,32 @@ public class AuthenticationManagementResource {
|
||||||
return ErrorResponse.exists("Failed to update flow with empty alias name");
|
return ErrorResponse.exists("Failed to update flow with empty alias name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//check if updating a correct flow
|
||||||
|
AuthenticationFlowModel checkFlow = realm.getAuthenticationFlowById(id);
|
||||||
|
if (checkFlow == null) {
|
||||||
|
session.getTransactionManager().setRollbackOnly();
|
||||||
|
throw new NotFoundException("Illegal execution");
|
||||||
|
}
|
||||||
|
|
||||||
|
//if a different flow with the same name does already exist, throw an exception
|
||||||
|
if (realm.getFlowByAlias(flow.getAlias()) != null && !checkFlow.getAlias().equals(flow.getAlias())) {
|
||||||
|
return ErrorResponse.exists("Flow alias name already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the name changed
|
||||||
|
if (!checkFlow.getAlias().equals(flow.getAlias())) {
|
||||||
|
checkFlow.setAlias(flow.getAlias());
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if the description changed
|
||||||
|
if (!checkFlow.getDescription().equals(flow.getDescription())) {
|
||||||
|
checkFlow.setDescription(flow.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
//update the flow
|
||||||
flow.setId(existingFlow.getId());
|
flow.setId(existingFlow.getId());
|
||||||
realm.updateAuthenticationFlow(RepresentationToModel.toModel(flow));
|
realm.updateAuthenticationFlow(RepresentationToModel.toModel(flow));
|
||||||
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(flow).success();
|
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(flow).success();
|
||||||
|
|
||||||
return Response.accepted(flow).build();
|
return Response.accepted(flow).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,6 +556,7 @@ public class AuthenticationManagementResource {
|
||||||
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name());
|
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name());
|
||||||
}
|
}
|
||||||
rep.setDisplayName(flowRef.getAlias());
|
rep.setDisplayName(flowRef.getAlias());
|
||||||
|
rep.setDescription(flowRef.getDescription());
|
||||||
rep.setConfigurable(false);
|
rep.setConfigurable(false);
|
||||||
rep.setId(execution.getId());
|
rep.setId(execution.getId());
|
||||||
rep.setAuthenticationFlow(execution.isAuthenticatorFlow());
|
rep.setAuthenticationFlow(execution.isAuthenticatorFlow());
|
||||||
|
@ -571,16 +595,16 @@ public class AuthenticationManagementResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update authentication executions of a flow
|
* Update authentication executions of a Flow
|
||||||
*
|
|
||||||
* @param flowAlias Flow alias
|
* @param flowAlias Flow alias
|
||||||
* @param rep
|
* @param rep AuthenticationExecutionInfoRepresentation
|
||||||
*/
|
*/
|
||||||
@Path("/flows/{flowAlias}/executions")
|
@Path("/flows/{flowAlias}/executions")
|
||||||
@PUT
|
@PUT
|
||||||
@NoCache
|
@NoCache
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public void updateExecutions(@PathParam("flowAlias") String flowAlias, AuthenticationExecutionInfoRepresentation rep) {
|
public Response updateExecutions(@PathParam("flowAlias") String flowAlias, AuthenticationExecutionInfoRepresentation rep) {
|
||||||
auth.realm().requireManageRealm();
|
auth.realm().requireManageRealm();
|
||||||
|
|
||||||
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
|
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
|
||||||
|
@ -599,7 +623,38 @@ public class AuthenticationManagementResource {
|
||||||
model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement()));
|
model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement()));
|
||||||
realm.updateAuthenticatorExecution(model);
|
realm.updateAuthenticatorExecution(model);
|
||||||
adminEvent.operation(OperationType.UPDATE).resource(ResourceType.AUTH_EXECUTION).resourcePath(session.getContext().getUri()).representation(rep).success();
|
adminEvent.operation(OperationType.UPDATE).resource(ResourceType.AUTH_EXECUTION).resourcePath(session.getContext().getUri()).representation(rep).success();
|
||||||
|
return Response.accepted(flow).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//executions can't have name and description updated
|
||||||
|
if (rep.getAuthenticationFlow() == null) { return Response.accepted(flow).build();}
|
||||||
|
|
||||||
|
//check if updating a correct flow
|
||||||
|
AuthenticationFlowModel checkFlow = realm.getAuthenticationFlowById(rep.getFlowId());
|
||||||
|
if (checkFlow == null) {
|
||||||
|
session.getTransactionManager().setRollbackOnly();
|
||||||
|
throw new NotFoundException("Illegal execution");
|
||||||
|
}
|
||||||
|
|
||||||
|
//if a different flow with the same name does already exist, throw an exception
|
||||||
|
if (realm.getFlowByAlias(rep.getDisplayName()) != null && !checkFlow.getAlias().equals(rep.getDisplayName())) {
|
||||||
|
return ErrorResponse.exists("Flow alias name already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the name changed
|
||||||
|
if (!checkFlow.getAlias().equals(rep.getDisplayName())) {
|
||||||
|
checkFlow.setAlias(rep.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if the description changed
|
||||||
|
if (!checkFlow.getDescription().equals(rep.getDescription())) {
|
||||||
|
checkFlow.setDescription(rep.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
//update the flow
|
||||||
|
realm.updateAuthenticationFlow(checkFlow);
|
||||||
|
adminEvent.operation(OperationType.UPDATE).resource(ResourceType.AUTH_EXECUTION).resourcePath(session.getContext().getUri()).representation(rep).success();
|
||||||
|
return Response.accepted(flow).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.AssertAdminEvents;
|
import org.keycloak.testsuite.util.AssertAdminEvents;
|
||||||
|
@ -44,8 +46,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasItems;
|
import static org.hamcrest.Matchers.hasItems;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -286,9 +286,9 @@ public class ExecutionTest extends AbstractAuthenticationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update execution with not-existent ID - SHOULD FAIL
|
// Update execution with not-existent ID - SHOULD FAIL
|
||||||
|
AuthenticationExecutionInfoRepresentation executionRep2 = new AuthenticationExecutionInfoRepresentation();
|
||||||
|
executionRep2.setId("not-existent");
|
||||||
try {
|
try {
|
||||||
AuthenticationExecutionInfoRepresentation executionRep2 = new AuthenticationExecutionInfoRepresentation();
|
|
||||||
executionRep2.setId("not-existent");
|
|
||||||
authMgmtResource.updateExecutions("new-client-flow", executionRep2);
|
authMgmtResource.updateExecutions("new-client-flow", executionRep2);
|
||||||
Assert.fail("Not expected to update not-existent execution");
|
Assert.fail("Not expected to update not-existent execution");
|
||||||
} catch (NotFoundException nfe) {
|
} catch (NotFoundException nfe) {
|
||||||
|
|
|
@ -19,25 +19,25 @@ package org.keycloak.testsuite.admin.authentication;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
||||||
|
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.AssertAdminEvents;
|
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.ClientErrorException;
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.Status;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.keycloak.testsuite.util.Matchers.*;
|
import static org.keycloak.testsuite.util.Matchers.body;
|
||||||
|
import static org.keycloak.testsuite.util.Matchers.statusCodeIs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -253,6 +253,7 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
copyOfBrowser = authMgmtResource.getFlow(copyOfBrowser.getId());
|
copyOfBrowser = authMgmtResource.getFlow(copyOfBrowser.getId());
|
||||||
Assert.assertNotNull(copyOfBrowser);
|
Assert.assertNotNull(copyOfBrowser);
|
||||||
compareFlows(browser, copyOfBrowser);
|
compareFlows(browser, copyOfBrowser);
|
||||||
|
authMgmtResource.deleteFlow(copyOfBrowser.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -275,4 +276,138 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AdminEventPaths.authAddExecutionFlowPath("parent"), params, ResourceType.AUTH_EXECUTION_FLOW);
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AdminEventPaths.authAddExecutionFlowPath("parent"), params, ResourceType.AUTH_EXECUTION_FLOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
//KEYCLOAK-12741
|
||||||
|
//test editing of authentication flows
|
||||||
|
public void editFlowTest() {
|
||||||
|
List<AuthenticationFlowRepresentation> flows;
|
||||||
|
|
||||||
|
//copy an existing one first
|
||||||
|
HashMap<String, String> params = new HashMap<>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//load the newly copied flow
|
||||||
|
flows = authMgmtResource.getFlows();
|
||||||
|
AuthenticationFlowRepresentation testFlow = findFlowByAlias("Copy of browser", flows);
|
||||||
|
//Set a new unique name. Should succeed
|
||||||
|
testFlow.setAlias("Copy of browser2");
|
||||||
|
authMgmtResource.updateFlow(testFlow.getId(), testFlow);
|
||||||
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authEditFlowPath(testFlow.getId()), ResourceType.AUTH_FLOW);
|
||||||
|
flows = authMgmtResource.getFlows();
|
||||||
|
Assert.assertEquals("Copy of browser2", findFlowByAlias("Copy of browser2", flows).getAlias());
|
||||||
|
|
||||||
|
//Create new flow and edit the old one to have the new ones name
|
||||||
|
AuthenticationFlowRepresentation newFlow = newFlow("New Flow", "Test description", "basic-flow", true, false);
|
||||||
|
createFlow(newFlow);
|
||||||
|
// check that new flow is returned in a children list
|
||||||
|
flows = authMgmtResource.getFlows();
|
||||||
|
AuthenticationFlowRepresentation found = findFlowByAlias("New Flow", flows);
|
||||||
|
|
||||||
|
Assert.assertNotNull("created flow visible in parent", found);
|
||||||
|
compareFlows(newFlow, found);
|
||||||
|
|
||||||
|
//try to update old flow with alias that already exists
|
||||||
|
testFlow.setAlias("New Flow");
|
||||||
|
try {
|
||||||
|
authMgmtResource.updateFlow(found.getId(), testFlow);
|
||||||
|
} catch (ClientErrorException exception){
|
||||||
|
//expoected
|
||||||
|
}
|
||||||
|
flows = authMgmtResource.getFlows();
|
||||||
|
|
||||||
|
//name should be the same for the old Flow
|
||||||
|
Assert.assertEquals("Copy of browser2", findFlowByAlias("Copy of browser2", flows).getAlias());
|
||||||
|
|
||||||
|
//Only update the description
|
||||||
|
found.setDescription("New description");
|
||||||
|
authMgmtResource.updateFlow(found.getId(), found);
|
||||||
|
flows = authMgmtResource.getFlows();
|
||||||
|
|
||||||
|
Assert.assertEquals("New description", findFlowByAlias("New Flow", flows).getDescription());
|
||||||
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authEditFlowPath(found.getId()), ResourceType.AUTH_FLOW);
|
||||||
|
|
||||||
|
//Update name and description
|
||||||
|
found.setAlias("New Flow2");
|
||||||
|
found.setDescription("New description2");
|
||||||
|
authMgmtResource.updateFlow(found.getId(), found);
|
||||||
|
flows = authMgmtResource.getFlows();
|
||||||
|
|
||||||
|
Assert.assertEquals("New Flow2", findFlowByAlias("New Flow2", flows).getAlias());
|
||||||
|
Assert.assertEquals("New description2", findFlowByAlias("New Flow2", flows).getDescription());
|
||||||
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authEditFlowPath(found.getId()), ResourceType.AUTH_FLOW);
|
||||||
|
Assert.assertNull(findFlowByAlias("New Flow", flows));
|
||||||
|
|
||||||
|
authMgmtResource.deleteFlow(testFlow.getId());
|
||||||
|
authMgmtResource.deleteFlow(found.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void editExecutionFlowTest() {
|
||||||
|
HashMap<String, String> params = new HashMap<>();
|
||||||
|
List<AuthenticationExecutionInfoRepresentation> executionReps;
|
||||||
|
//create new parent flow
|
||||||
|
AuthenticationFlowRepresentation newFlow = newFlow("Parent-Flow", "This is a parent flow", "basic-flow", true, false);
|
||||||
|
createFlow(newFlow);
|
||||||
|
|
||||||
|
//create a child sub flow
|
||||||
|
params.put("alias", "Child-Flow");
|
||||||
|
params.put("description", "This is a child flow");
|
||||||
|
params.put("provider", "registration-page-form");
|
||||||
|
params.put("type", "basic-flow");
|
||||||
|
|
||||||
|
authMgmtResource.addExecutionFlow("Parent-Flow", params);
|
||||||
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.CREATE, AdminEventPaths.authAddExecutionFlowPath("Parent-Flow"), params, ResourceType.AUTH_EXECUTION_FLOW);
|
||||||
|
|
||||||
|
executionReps = authMgmtResource.getExecutions("Parent-Flow");
|
||||||
|
|
||||||
|
//create another with the same name of the previous one. Should fail to create
|
||||||
|
params = new HashMap<>();
|
||||||
|
params.put("alias", "Child-Flow");
|
||||||
|
params.put("description", "This is another child flow");
|
||||||
|
params.put("provider", "registration-page-form");
|
||||||
|
params.put("type", "basic-flow");
|
||||||
|
|
||||||
|
try {
|
||||||
|
authMgmtResource.addExecutionFlow("Parent-Flow", params);
|
||||||
|
Assert.fail("addExecutionFlow the alias already exist");
|
||||||
|
} catch (Exception expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationExecutionInfoRepresentation found = executionReps.get(0);
|
||||||
|
found.setDisplayName("Parent-Flow");
|
||||||
|
|
||||||
|
try {
|
||||||
|
authMgmtResource.updateExecutions("Parent-Flow", found);
|
||||||
|
} catch (ClientErrorException exception){
|
||||||
|
//expected
|
||||||
|
}
|
||||||
|
|
||||||
|
//edit both name and description
|
||||||
|
found.setDisplayName("Child-Flow2");
|
||||||
|
found.setDescription("This is another child flow2");
|
||||||
|
|
||||||
|
authMgmtResource.updateExecutions("Parent-Flow", found);
|
||||||
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authUpdateExecutionPath("Parent-Flow"), ResourceType.AUTH_EXECUTION);
|
||||||
|
executionReps = authMgmtResource.getExecutions("Parent-Flow");
|
||||||
|
Assert.assertEquals("Child-Flow2", executionReps.get(0).getDisplayName());
|
||||||
|
Assert.assertEquals("This is another child flow2", executionReps.get(0).getDescription());
|
||||||
|
|
||||||
|
//edit only description
|
||||||
|
found.setDescription("This is another child flow3");
|
||||||
|
authMgmtResource.updateExecutions("Parent-Flow", found);
|
||||||
|
|
||||||
|
assertAdminEvents.assertEvent(REALM_NAME, OperationType.UPDATE, AdminEventPaths.authUpdateExecutionPath("Parent-Flow"), ResourceType.AUTH_EXECUTION);
|
||||||
|
executionReps = authMgmtResource.getExecutions("Parent-Flow");
|
||||||
|
Assert.assertEquals("Child-Flow2", executionReps.get(0).getDisplayName());
|
||||||
|
Assert.assertEquals("This is another child flow3", executionReps.get(0).getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -390,6 +390,11 @@ public class AdminEventPaths {
|
||||||
return uri.toString();
|
return uri.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String authEditFlowPath(String flowId) {
|
||||||
|
URI uri = UriBuilder.fromUri(authMgmtBasePath()).path(AuthenticationManagementResource.class, "updateFlow")
|
||||||
|
.build(flowId);
|
||||||
|
return uri.toString();
|
||||||
|
}
|
||||||
public static String authAddExecutionFlowPath(String flowAlias) {
|
public static String authAddExecutionFlowPath(String flowAlias) {
|
||||||
URI uri = UriBuilder.fromUri(authMgmtBasePath()).path(AuthenticationManagementResource.class, "addExecutionFlow")
|
URI uri = UriBuilder.fromUri(authMgmtBasePath()).path(AuthenticationManagementResource.class, "addExecutionFlow")
|
||||||
.build(flowAlias);
|
.build(flowAlias);
|
||||||
|
|
|
@ -1150,6 +1150,7 @@ cut=Cut
|
||||||
paste=Paste
|
paste=Paste
|
||||||
create-group=Create group
|
create-group=Create group
|
||||||
create-authenticator-execution=Create Authenticator Execution
|
create-authenticator-execution=Create Authenticator Execution
|
||||||
|
edit-flow=Edit Flow
|
||||||
create-form-action-execution=Create Form Action Execution
|
create-form-action-execution=Create Form Action Execution
|
||||||
create-top-level-form=Create Top Level Form
|
create-top-level-form=Create Top Level Form
|
||||||
flow.alias.tooltip=Specifies display name for the flow.
|
flow.alias.tooltip=Specifies display name for the flow.
|
||||||
|
@ -1284,6 +1285,7 @@ started=Started
|
||||||
logout-all-sessions=Log out all sessions
|
logout-all-sessions=Log out all sessions
|
||||||
logout=Logout
|
logout=Logout
|
||||||
new-name=New Name
|
new-name=New Name
|
||||||
|
new-description=New Description
|
||||||
ok=Ok
|
ok=Ok
|
||||||
attributes=Attributes
|
attributes=Attributes
|
||||||
role-mappings=Role Mappings
|
role-mappings=Role Mappings
|
||||||
|
|
|
@ -2229,9 +2229,9 @@ module.controller('CreateExecutionCtrl', function($scope, realm, parentFlow, for
|
||||||
|
|
||||||
|
|
||||||
module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow, LastFlowSelected, Dialog,
|
module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow, LastFlowSelected, Dialog,
|
||||||
AuthenticationFlows, AuthenticationFlowsCopy, AuthenticationFlowExecutions,
|
AuthenticationFlows, AuthenticationFlowsCopy, AuthenticationFlowsUpdate, AuthenticationFlowExecutions,
|
||||||
AuthenticationExecution, AuthenticationExecutionRaisePriority, AuthenticationExecutionLowerPriority,
|
AuthenticationExecution, AuthenticationExecutionRaisePriority, AuthenticationExecutionLowerPriority,
|
||||||
$modal, Notifications, CopyDialog, $location) {
|
$modal, Notifications, CopyDialog, UpdateDialog, $location) {
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.flows = flows;
|
$scope.flows = flows;
|
||||||
|
|
||||||
|
@ -2342,6 +2342,18 @@ module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flo
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.editFlow = function(flow) {
|
||||||
|
var copy = angular.copy(flow);
|
||||||
|
UpdateDialog.open('Update Authentication Flow', copy.alias, copy.description, function(name, desc) {
|
||||||
|
copy.alias = name;
|
||||||
|
copy.description = desc;
|
||||||
|
AuthenticationFlowsUpdate.update({realm: realm.realm, flow: flow.id}, copy, function() {
|
||||||
|
$location.url("/realms/" + realm.realm + '/authentication/flows/' + name);
|
||||||
|
Notifications.success("Flow updated");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
$scope.addFlow = function() {
|
$scope.addFlow = function() {
|
||||||
$location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/flow/execution/' + $scope.flow.id);
|
$location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/flow/execution/' + $scope.flow.id);
|
||||||
|
|
||||||
|
@ -2379,6 +2391,22 @@ module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flo
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.editExecutionFlow = function(execution) {
|
||||||
|
var copy = angular.copy(execution);
|
||||||
|
delete copy.empties;
|
||||||
|
delete copy.levels;
|
||||||
|
delete copy.preLevels;
|
||||||
|
delete copy.postLevels;
|
||||||
|
UpdateDialog.open('Update Execution Flow', copy.displayName, copy.description, function(name, desc) {
|
||||||
|
copy.displayName = name;
|
||||||
|
copy.description = desc;
|
||||||
|
AuthenticationFlowExecutions.update({realm: realm.realm, alias: $scope.flow.alias}, copy, function() {
|
||||||
|
Notifications.success("Execution Flow updated");
|
||||||
|
setupForm();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
$scope.removeExecution = function(execution) {
|
$scope.removeExecution = function(execution) {
|
||||||
console.log('removeExecution: ' + execution.id);
|
console.log('removeExecution: ' + execution.id);
|
||||||
var exeOrFlow = execution.authenticationFlow ? 'flow' : 'execution';
|
var exeOrFlow = execution.authenticationFlow ? 'flow' : 'execution';
|
||||||
|
|
|
@ -137,6 +137,35 @@ module.service('CopyDialog', function($modal) {
|
||||||
return dialog;
|
return dialog;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.service('UpdateDialog', function($modal) {
|
||||||
|
var dialog = {};
|
||||||
|
dialog.open = function (title, name, desc, success) {
|
||||||
|
var controller = function($scope, $modalInstance, title) {
|
||||||
|
$scope.title = title;
|
||||||
|
$scope.name = { value: name };
|
||||||
|
$scope.description = { value: desc };
|
||||||
|
$scope.ok = function () {
|
||||||
|
console.log('ok with name: ' + $scope.name + 'and description: ' + $scope.description);
|
||||||
|
$modalInstance.close();
|
||||||
|
success($scope.name.value, $scope.description.value);
|
||||||
|
};
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$modal.open({
|
||||||
|
templateUrl: resourceUrl + '/templates/kc-edit.html',
|
||||||
|
controller: controller,
|
||||||
|
resolve: {
|
||||||
|
title: function() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return dialog;
|
||||||
|
});
|
||||||
|
|
||||||
module.factory('Notifications', function($rootScope, $timeout) {
|
module.factory('Notifications', function($rootScope, $timeout) {
|
||||||
// time (in ms) the notifications are shown
|
// time (in ms) the notifications are shown
|
||||||
var delay = 5000;
|
var delay = 5000;
|
||||||
|
@ -1745,6 +1774,19 @@ module.factory('AuthenticationFlowsCopy', function($resource) {
|
||||||
alias : '@alias'
|
alias : '@alias'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('AuthenticationFlowsUpdate', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:flow', {
|
||||||
|
realm : '@realm',
|
||||||
|
flow : '@flow'
|
||||||
|
}, {
|
||||||
|
update : {
|
||||||
|
method : 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.factory('AuthenticationConfigDescription', function($resource) {
|
module.factory('AuthenticationConfigDescription', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', {
|
return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', {
|
||||||
realm : '@realm',
|
realm : '@realm',
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<button class="btn btn-default" data-ng-click="createFlow()">{{:: 'new' | translate}}</button>
|
<button class="btn btn-default" data-ng-click="createFlow()">{{:: 'new' | translate}}</button>
|
||||||
<button class="btn btn-default" data-ng-click="copyFlow()">{{:: 'copy' | translate}}</button>
|
<button class="btn btn-default" data-ng-click="copyFlow()">{{:: 'copy' | translate}}</button>
|
||||||
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="deleteFlow()">{{:: 'delete' | translate}}</button>
|
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="deleteFlow()">{{:: 'delete' | translate}}</button>
|
||||||
|
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="editFlow(flow)">{{:: 'edit-flow' | translate}}</button>
|
||||||
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addExecution()">{{:: 'add-execution' | translate}}</button>
|
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addExecution()">{{:: 'add-execution' | translate}}</button>
|
||||||
<button class="btn btn-default" data-ng-hide="flow.builtIn || flow.providerId === 'client-flow'" data-ng-click="addFlow()">{{:: 'add-flow' | translate}}</button>
|
<button class="btn btn-default" data-ng-hide="flow.builtIn || flow.providerId === 'client-flow'" data-ng-click="addFlow()">{{:: 'add-flow' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
<button data-ng-hide="flow.builtIn" data-ng-disabled="$first" class="btn btn-default btn-sm" data-ng-click="raisePriority(execution)"><i class="fa fa-angle-up"></i></button>
|
<button data-ng-hide="flow.builtIn" data-ng-disabled="$first" class="btn btn-default btn-sm" data-ng-click="raisePriority(execution)"><i class="fa fa-angle-up"></i></button>
|
||||||
<button data-ng-hide="flow.builtIn" data-ng-disabled="$last" class="btn btn-default btn-sm" data-ng-click="lowerPriority(execution)"><i class="fa fa-angle-down"></i></button>
|
<button data-ng-hide="flow.builtIn" data-ng-disabled="$last" class="btn btn-default btn-sm" data-ng-click="lowerPriority(execution)"><i class="fa fa-angle-down"></i></button>
|
||||||
<span>{{execution.displayName|capitalize}}<span ng-if="execution.alias">({{execution.alias}})</span></span>
|
<span>{{execution.displayName|capitalize}}<span ng-if="execution.alias">({{execution.alias}})</span></span>
|
||||||
|
<i data-ng-hide="!execution.authenticationFlow" class="fa fa-question-circle text-muted" tooltip-trigger="mouseover mouseout" tooltip="{{execution.description}}" tooltip-placement="right"> </i>
|
||||||
</td>
|
</td>
|
||||||
<td ng-repeat="lev in execution.postLevels"></td>
|
<td ng-repeat="lev in execution.postLevels"></td>
|
||||||
<td ng-repeat="choice in execution.requirementChoices">
|
<td ng-repeat="choice in execution.requirementChoices">
|
||||||
|
@ -53,6 +55,7 @@
|
||||||
<li data-ng-hide="flow.builtIn"><a href="" ng-click="removeExecution(execution)">{{:: 'delete' | translate}}</a></li>
|
<li data-ng-hide="flow.builtIn"><a href="" ng-click="removeExecution(execution)">{{:: 'delete' | translate}}</a></li>
|
||||||
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlowExecution(execution)">{{:: 'add-execution' | translate}}</a></li>
|
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlowExecution(execution)">{{:: 'add-execution' | translate}}</a></li>
|
||||||
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlow(execution)">{{:: 'add-flow' | translate}}</a></li>
|
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlow(execution)">{{:: 'add-flow' | translate}}</a></li>
|
||||||
|
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="editExecutionFlow(execution)">{{:: 'edit-flow' | translate}}</a></li>
|
||||||
<li data-ng-show="execution.configurable && execution.authenticationConfig == null"><a href="#/create/authentication/{{realm.realm}}/flows/{{flow.id}}/execution/{{execution.id}}/provider/{{execution.providerId}}">{{:: 'config' | translate}}</a></li>
|
<li data-ng-show="execution.configurable && execution.authenticationConfig == null"><a href="#/create/authentication/{{realm.realm}}/flows/{{flow.id}}/execution/{{execution.id}}/provider/{{execution.providerId}}">{{:: 'config' | translate}}</a></li>
|
||||||
<li data-ng-show="execution.configurable && execution.authenticationConfig != null"><a href="#/realms/{{realm.realm}}/authentication/flows/{{flow.id}}/config/{{execution.providerId}}/{{execution.authenticationConfig}}">{{:: 'config' | translate}}</a></li>
|
<li data-ng-show="execution.configurable && execution.authenticationConfig != null"><a href="#/realms/{{realm.realm}}/authentication/flows/{{flow.id}}/config/{{execution.providerId}}/{{execution.authenticationConfig}}">{{:: 'config' | translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" ng-click="cancel()">
|
||||||
|
<span class="pficon pficon-close"></span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">{{title}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div>
|
||||||
|
<label class="control-label" for="name">{{:: 'new-name' | translate}}</label>
|
||||||
|
<input class="form-control" type="text" id="name" data-ng-model="name.value">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="control-label" for="name">{{:: 'new-description' | translate}}</label>
|
||||||
|
<input class="form-control" type="text" id="description" data-ng-model="description.value">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="ok()">{{:: 'ok' | translate}}</button>
|
||||||
|
</div>
|
Loading…
Reference in a new issue